mirror of
https://github.com/ppy/osu.git
synced 2026-05-16 14:23:22 +08:00
Compare commits
109 Commits
@@ -1,22 +1,24 @@
|
||||
# osu! [](https://ci.appveyor.com/project/peppy/osu) [](https://www.codefactor.io/repository/github/ppy/osu) [](https://discord.gg/ppy)
|
||||
# osu!
|
||||
|
||||
[](https://ci.appveyor.com/project/peppy/osu) [](https://www.codefactor.io/repository/github/ppy/osu) [](https://discord.gg/ppy)
|
||||
|
||||
Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Commonly known by the codename "osu!lazer". Pew pew.
|
||||
|
||||
# Status
|
||||
## Status
|
||||
|
||||
This project is still heavily under development, but is in a state where users are encouraged to try it out and keep it installed alongside the stable osu! client. It will continue to evolve over the coming months and hopefully bring some new unique features to the table.
|
||||
|
||||
We are accepting bug reports (please report with as much detail as possible). Feature requests are welcome as long as you read and understand the contribution guidelines listed below.
|
||||
|
||||
# Requirements
|
||||
## 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 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
|
||||
- Note that there are **[additional requirements for Windows 7 and Windows 8.1](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** which you may need to manually install if your operating system is not up-to-date.
|
||||
|
||||
# Running osu!
|
||||
## Running osu!
|
||||
|
||||
## Releases
|
||||
### Releases
|
||||
|
||||
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.
|
||||
|
||||
@@ -26,7 +28,7 @@ If you are not interested in developing the game, please head over to the [relea
|
||||
|
||||
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
|
||||
|
||||
## Downloading the source code
|
||||
### Downloading the source code
|
||||
|
||||
Clone the repository **including submodules**:
|
||||
|
||||
@@ -41,7 +43,7 @@ To update the source code to the latest commit, run the following command inside
|
||||
git pull
|
||||
```
|
||||
|
||||
## Building
|
||||
### 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.
|
||||
|
||||
@@ -57,7 +59,7 @@ If you are not interested in debugging osu!, you can add `-c Release` to gain pe
|
||||
|
||||
If the build fails, try to restore nuget packages with `dotnet restore`.
|
||||
|
||||
### A note for Linux users
|
||||
#### 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`.
|
||||
|
||||
@@ -69,15 +71,15 @@ For example, you can run osu! with the following command:
|
||||
LD_LIBRARY_PATH="$(pwd)/osu.Desktop/bin/Debug/netcoreapp2.2" dotnet run --project osu.Desktop
|
||||
```
|
||||
|
||||
## Testing with resource/framework modifications
|
||||
### Testing with resource/framework modifications
|
||||
|
||||
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be achieved by running some commands as documented on the [osu-resources](https://github.com/ppy/osu-resources/wiki/Testing-local-resources-checkout-with-other-projects) and [osu-framework](https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects) wiki pages.
|
||||
|
||||
## Code analysis
|
||||
### 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). Alternatively, you can install resharper or use rider to get inline support in your IDE of choice.
|
||||
|
||||
# Contributing
|
||||
## Contributing
|
||||
|
||||
We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted.
|
||||
|
||||
@@ -87,7 +89,7 @@ Contributions can be made via pull requests to this repository. We hope to credi
|
||||
|
||||
Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible.
|
||||
|
||||
# Licence
|
||||
## Licence
|
||||
|
||||
The osu! client code and framework are licensed under the [MIT licence](https://opensource.org/licenses/MIT). Please see [the licence file](LICENCE) for more information. [tl;dr](https://tldrlegal.com/license/mit-license) you can do whatever you want as long as you include the original copyright and license notice in any copy of the software/source.
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
@@ -17,13 +19,30 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
protected override IBeatmap CreateBeatmap(Ruleset ruleset)
|
||||
{
|
||||
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
|
||||
var beatmap = new Beatmap
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
Ruleset = ruleset.RulesetInfo,
|
||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f }
|
||||
}
|
||||
};
|
||||
|
||||
// Should produce a hperdash
|
||||
beatmap.HitObjects.Add(new Fruit { StartTime = 816, X = 308 / 512f, NewCombo = true });
|
||||
beatmap.HitObjects.Add(new Fruit { StartTime = 1008, X = 56 / 512f, });
|
||||
|
||||
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 });
|
||||
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = 2000 + i * 100, NewCombo = i % 8 == 0 });
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
protected override void AddCheckSteps(Func<Player> player)
|
||||
{
|
||||
base.AddCheckSteps(player);
|
||||
AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
CatchHitObject nextObject = objectWithDroplets[i + 1];
|
||||
|
||||
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
|
||||
double timeToNext = nextObject.StartTime - currentObject.StartTime;
|
||||
double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable
|
||||
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
||||
float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext);
|
||||
if (distanceToHyper < 0)
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetConfigCache configCache)
|
||||
{
|
||||
var config = (ManiaConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
||||
config.BindWith(ManiaSetting.ScrollDirection, direction);
|
||||
var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
||||
config.BindWith(ManiaRulesetSetting.ScrollDirection, direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -8,9 +8,9 @@ using osu.Game.Rulesets.Mania.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Configuration
|
||||
{
|
||||
public class ManiaConfigManager : RulesetConfigManager<ManiaSetting>
|
||||
public class ManiaRulesetConfigManager : RulesetConfigManager<ManiaRulesetSetting>
|
||||
{
|
||||
public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
|
||||
public ManiaRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
|
||||
: base(settings, ruleset, variant)
|
||||
{
|
||||
}
|
||||
@@ -19,17 +19,17 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
||||
{
|
||||
base.InitialiseDefaults();
|
||||
|
||||
Set(ManiaSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
|
||||
Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||
Set(ManiaRulesetSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
|
||||
Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||
}
|
||||
|
||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||
{
|
||||
new TrackedSetting<double>(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
|
||||
new TrackedSetting<double>(ManiaRulesetSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
|
||||
};
|
||||
}
|
||||
|
||||
public enum ManiaSetting
|
||||
public enum ManiaRulesetSetting
|
||||
{
|
||||
ScrollTime,
|
||||
ScrollDirection
|
||||
@@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
|
||||
|
||||
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaConfigManager(settings, RulesetInfo);
|
||||
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaRulesetConfigManager(settings, RulesetInfo);
|
||||
|
||||
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
|
||||
|
||||
|
||||
@@ -22,19 +22,19 @@ namespace osu.Game.Rulesets.Mania
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var config = (ManiaConfigManager)Config;
|
||||
var config = (ManiaRulesetConfigManager)Config;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsEnumDropdown<ManiaScrollingDirection>
|
||||
{
|
||||
LabelText = "Scrolling direction",
|
||||
Bindable = config.GetBindable<ManiaScrollingDirection>(ManiaSetting.ScrollDirection)
|
||||
Bindable = config.GetBindable<ManiaScrollingDirection>(ManiaRulesetSetting.ScrollDirection)
|
||||
},
|
||||
new SettingsSlider<double, TimeSlider>
|
||||
{
|
||||
LabelText = "Scroll speed",
|
||||
Bindable = config.GetBindable<double>(ManiaSetting.ScrollTime)
|
||||
Bindable = config.GetBindable<double>(ManiaRulesetSetting.ScrollTime)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public IEnumerable<BarLine> BarLines;
|
||||
|
||||
protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
|
||||
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
||||
|
||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||
|
||||
@@ -74,10 +74,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
BarLines.ForEach(Playfield.Add);
|
||||
|
||||
Config.BindWith(ManiaSetting.ScrollDirection, configDirection);
|
||||
Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
|
||||
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
|
||||
|
||||
Config.BindWith(ManiaSetting.ScrollTime, TimeRange);
|
||||
Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Configuration
|
||||
{
|
||||
public class OsuRulesetConfigManager : RulesetConfigManager<OsuRulesetSetting>
|
||||
{
|
||||
public OsuRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
|
||||
: base(settings, ruleset, variant)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void InitialiseDefaults()
|
||||
{
|
||||
base.InitialiseDefaults();
|
||||
|
||||
Set(OsuRulesetSetting.SnakingInSliders, true);
|
||||
Set(OsuRulesetSetting.SnakingOutSliders, true);
|
||||
}
|
||||
}
|
||||
|
||||
public enum OsuRulesetSetting
|
||||
{
|
||||
SnakingInSliders,
|
||||
SnakingOutSliders
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Configuration;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
@@ -33,6 +33,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||
private readonly IBindable<SliderPath> pathBindable = new Bindable<SliderPath>();
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private OsuRulesetConfigManager config { get; set; }
|
||||
|
||||
public DrawableSlider(Slider s)
|
||||
: base(s)
|
||||
{
|
||||
@@ -94,10 +97,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
private void load()
|
||||
{
|
||||
config.BindWith(OsuSetting.SnakingInSliders, Body.SnakingIn);
|
||||
config.BindWith(OsuSetting.SnakingOutSliders, Body.SnakingOut);
|
||||
config?.BindWith(OsuRulesetSetting.SnakingInSliders, Body.SnakingIn);
|
||||
config?.BindWith(OsuRulesetSetting.SnakingOutSliders, Body.SnakingOut);
|
||||
|
||||
positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
||||
scaleBindable.BindValueChanged(scale =>
|
||||
|
||||
@@ -16,8 +16,11 @@ using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Configuration;
|
||||
using osu.Game.Rulesets.Osu.Difficulty;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
@@ -144,12 +147,14 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
public override string ShortName => "osu";
|
||||
|
||||
public override RulesetSettingsSubsection CreateSettings() => new OsuSettings(this);
|
||||
public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this);
|
||||
|
||||
public override int? LegacyID => 0;
|
||||
|
||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
|
||||
|
||||
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
|
||||
|
||||
public OsuRuleset(RulesetInfo rulesetInfo = null)
|
||||
: base(rulesetInfo)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Handlers;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Configuration;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
@@ -20,6 +21,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
public class OsuRulesetContainer : RulesetContainer<OsuPlayfield, OsuHitObject>
|
||||
{
|
||||
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
|
||||
|
||||
public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
|
||||
+8
-6
@@ -3,34 +3,36 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Osu.Configuration;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
public class OsuSettings : RulesetSettingsSubsection
|
||||
public class OsuSettingsSubsection : RulesetSettingsSubsection
|
||||
{
|
||||
protected override string Header => "osu!";
|
||||
|
||||
public OsuSettings(Ruleset ruleset)
|
||||
public OsuSettingsSubsection(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
private void load()
|
||||
{
|
||||
var config = (OsuRulesetConfigManager)Config;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Snaking in sliders",
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.SnakingInSliders)
|
||||
Bindable = config.GetBindable<bool>(OsuRulesetSetting.SnakingInSliders)
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = "Snaking out sliders",
|
||||
Bindable = config.GetBindable<bool>(OsuSetting.SnakingOutSliders)
|
||||
Bindable = config.GetBindable<bool>(OsuRulesetSetting.SnakingOutSliders)
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -333,6 +333,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.AreEqual("hit_2.wav", getTestableSampleInfo(hitObjects[1]).LookupNames.First());
|
||||
Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
|
||||
Assert.AreEqual("hit_1.wav", getTestableSampleInfo(hitObjects[3]).LookupNames.First());
|
||||
Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume);
|
||||
}
|
||||
|
||||
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
int fireCount = 0;
|
||||
|
||||
// ReSharper disable once AccessToModifiedClosure
|
||||
manager.ItemAdded += (_, __, ___) => fireCount++;
|
||||
manager.ItemAdded += (_, __) => fireCount++;
|
||||
manager.ItemRemoved += _ => fireCount++;
|
||||
|
||||
var imported = LoadOszIntoOsu(osu);
|
||||
|
||||
@@ -13,4 +13,4 @@ SampleSet: Normal
|
||||
255,193,2170,1,0,0:0:0:0:hit_1.wav
|
||||
256,191,2638,5,0,0:0:0:0:hit_2.wav
|
||||
255,193,3107,1,0,0:0:0:0:
|
||||
256,191,3576,1,0,0:0:0:0:hit_1.wav
|
||||
256,191,3576,1,0,0:0:0:70:hit_1.wav
|
||||
|
||||
@@ -188,10 +188,10 @@ namespace osu.Game.Tests.Visual
|
||||
public void PauseTest()
|
||||
{
|
||||
performFullSetup(true);
|
||||
AddStep("Pause", () => player.CurrentPauseContainer.Pause());
|
||||
AddStep("Pause", () => player.CurrentPausableGameplayContainer.Pause());
|
||||
waitForDim();
|
||||
AddAssert("Screen is dimmed", () => songSelect.IsBackgroundDimmed());
|
||||
AddStep("Unpause", () => player.CurrentPauseContainer.Resume());
|
||||
AddStep("Unpause", () => player.CurrentPausableGameplayContainer.Resume());
|
||||
waitForDim();
|
||||
AddAssert("Screen is dimmed", () => songSelect.IsBackgroundDimmed());
|
||||
}
|
||||
@@ -328,7 +328,7 @@ namespace osu.Game.Tests.Visual
|
||||
};
|
||||
}
|
||||
|
||||
public PauseContainer CurrentPauseContainer => PauseContainer;
|
||||
public PausableGameplayContainer CurrentPausableGameplayContainer => PausableGameplayContainer;
|
||||
|
||||
public UserDimContainer CurrentStoryboardContainer => StoryboardContainer;
|
||||
|
||||
|
||||
@@ -23,9 +23,6 @@ namespace osu.Game.Tests.Visual
|
||||
[System.ComponentModel.Description("in BeatmapOverlay")]
|
||||
public class TestCaseBeatmapScoresContainer : OsuTestCase
|
||||
{
|
||||
private readonly IEnumerable<APIScoreInfo> scores;
|
||||
private readonly IEnumerable<APIScoreInfo> anotherScores;
|
||||
private readonly APIScoreInfo topScoreInfo;
|
||||
private readonly Box background;
|
||||
|
||||
public TestCaseBeatmapScoresContainer()
|
||||
@@ -47,15 +44,7 @@ namespace osu.Game.Tests.Visual
|
||||
}
|
||||
};
|
||||
|
||||
AddStep("scores pack 1", () => scoresContainer.Scores = scores);
|
||||
AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores);
|
||||
AddStep("only top score", () => scoresContainer.Scores = new[] { topScoreInfo });
|
||||
AddStep("remove scores", () => scoresContainer.Scores = null);
|
||||
AddStep("resize to big", () => container.ResizeWidthTo(1, 300));
|
||||
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
|
||||
AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
|
||||
|
||||
scores = new[]
|
||||
IEnumerable<APIScoreInfo> scores = new[]
|
||||
{
|
||||
new APIScoreInfo
|
||||
{
|
||||
@@ -168,7 +157,7 @@ namespace osu.Game.Tests.Visual
|
||||
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
|
||||
}
|
||||
|
||||
anotherScores = new[]
|
||||
IEnumerable<APIScoreInfo> anotherScores = new[]
|
||||
{
|
||||
new APIScoreInfo
|
||||
{
|
||||
@@ -280,7 +269,7 @@ namespace osu.Game.Tests.Visual
|
||||
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
|
||||
}
|
||||
|
||||
topScoreInfo = new APIScoreInfo
|
||||
var topScoreInfo = new APIScoreInfo
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
@@ -305,6 +294,14 @@ namespace osu.Game.Tests.Visual
|
||||
topScoreInfo.Statistics.Add(HitResult.Great, RNG.Next(2000));
|
||||
topScoreInfo.Statistics.Add(HitResult.Good, RNG.Next(2000));
|
||||
topScoreInfo.Statistics.Add(HitResult.Meh, RNG.Next(2000));
|
||||
|
||||
AddStep("scores pack 1", () => scoresContainer.Scores = scores);
|
||||
AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores);
|
||||
AddStep("only top score", () => scoresContainer.Scores = new[] { topScoreInfo });
|
||||
AddStep("remove scores", () => scoresContainer.Scores = null);
|
||||
AddStep("resize to big", () => container.ResizeWidthTo(1, 300));
|
||||
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
|
||||
AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseDirectPanel : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(DirectGridPanel),
|
||||
typeof(DirectListPanel),
|
||||
typeof(IconPill)
|
||||
};
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var beatmap = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null);
|
||||
beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true;
|
||||
beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true;
|
||||
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Padding = new MarginPadding(20),
|
||||
Spacing = new Vector2(0, 20),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DirectGridPanel(beatmap.BeatmapSetInfo),
|
||||
new DirectListPanel(beatmap.BeatmapSetInfo)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,15 +17,15 @@ namespace osu.Game.Tests.Visual
|
||||
[Description("player pause/fail screens")]
|
||||
public class TestCaseGameplayMenuOverlay : ManualInputManagerTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) };
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PausableGameplayContainer) };
|
||||
|
||||
private FailOverlay failOverlay;
|
||||
private PauseContainer.PauseOverlay pauseOverlay;
|
||||
private PausableGameplayContainer.PauseOverlay pauseOverlay;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Add(pauseOverlay = new PauseContainer.PauseOverlay
|
||||
Add(pauseOverlay = new PausableGameplayContainer.PauseOverlay
|
||||
{
|
||||
OnResume = () => Logger.Log(@"Resume"),
|
||||
OnRetry = () => Logger.Log(@"Retry"),
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Framework.Timing;
|
||||
@@ -19,14 +20,20 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
private readonly StopwatchClock clock;
|
||||
|
||||
[Cached]
|
||||
private readonly GameplayClock gameplayClock;
|
||||
|
||||
private readonly FramedClock framedClock;
|
||||
|
||||
public TestCaseSongProgress()
|
||||
{
|
||||
clock = new StopwatchClock(true);
|
||||
|
||||
gameplayClock = new GameplayClock(framedClock = new FramedClock(clock));
|
||||
|
||||
Add(progress = new SongProgress
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AudioClock = new StopwatchClock(true),
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
});
|
||||
@@ -68,8 +75,13 @@ namespace osu.Game.Tests.Visual
|
||||
progress.Objects = objects;
|
||||
graph.Objects = objects;
|
||||
|
||||
progress.AudioClock = clock;
|
||||
progress.OnSeek = pos => clock.Seek(pos);
|
||||
progress.RequestSeek = pos => clock.Seek(pos);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
framedClock.ProcessFrame();
|
||||
}
|
||||
|
||||
private class TestSongProgressGraph : SongProgressGraph
|
||||
|
||||
@@ -11,7 +11,6 @@ using Microsoft.EntityFrameworkCore;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
@@ -50,11 +49,6 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
public event Action<DownloadBeatmapSetRequest> BeatmapDownloadFailed;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a beatmap load is requested (into the interactive game UI).
|
||||
/// </summary>
|
||||
public Action<BeatmapSetInfo> PresentBeatmap;
|
||||
|
||||
/// <summary>
|
||||
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
|
||||
/// </summary>
|
||||
@@ -151,8 +145,7 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
var downloadNotification = new DownloadNotification
|
||||
{
|
||||
CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!",
|
||||
Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
|
||||
Text = $"Downloading {beatmapSetInfo}",
|
||||
};
|
||||
|
||||
var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);
|
||||
@@ -165,20 +158,10 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
request.Success += filename =>
|
||||
{
|
||||
downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}";
|
||||
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
// This gets scheduled back to the update thread, but we want the import to run in the background.
|
||||
var importedBeatmap = Import(filename);
|
||||
|
||||
downloadNotification.CompletionClickAction = () =>
|
||||
{
|
||||
PresentCompletedImport(importedBeatmap.Yield());
|
||||
return true;
|
||||
};
|
||||
downloadNotification.State = ProgressNotificationState.Completed;
|
||||
|
||||
Import(downloadNotification, filename);
|
||||
currentDownloads.Remove(request);
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
};
|
||||
@@ -221,12 +204,6 @@ namespace osu.Game.Beatmaps
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void PresentCompletedImport(IEnumerable<BeatmapSetInfo> imported)
|
||||
{
|
||||
base.PresentCompletedImport(imported);
|
||||
PresentBeatmap?.Invoke(imported.LastOrDefault());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an existing download request if it exists.
|
||||
/// </summary>
|
||||
|
||||
@@ -48,11 +48,11 @@ namespace osu.Game.Beatmaps.Drawables
|
||||
|
||||
var rating = beatmap.StarDifficulty;
|
||||
|
||||
if (rating < 1.5) return DifficultyRating.Easy;
|
||||
if (rating < 2.25) return DifficultyRating.Normal;
|
||||
if (rating < 3.75) return DifficultyRating.Hard;
|
||||
if (rating < 5.25) return DifficultyRating.Insane;
|
||||
if (rating < 6.75) return DifficultyRating.Expert;
|
||||
if (rating < 2.0) return DifficultyRating.Easy;
|
||||
if (rating < 2.7) return DifficultyRating.Normal;
|
||||
if (rating < 4.0) return DifficultyRating.Hard;
|
||||
if (rating < 5.3) return DifficultyRating.Insane;
|
||||
if (rating < 6.5) return DifficultyRating.Expert;
|
||||
|
||||
return DifficultyRating.ExpertPlus;
|
||||
}
|
||||
|
||||
@@ -72,9 +72,6 @@ namespace osu.Game.Configuration
|
||||
|
||||
Set(OsuSetting.MenuParallax, true);
|
||||
|
||||
Set(OsuSetting.SnakingInSliders, true);
|
||||
Set(OsuSetting.SnakingOutSliders, true);
|
||||
|
||||
// Gameplay
|
||||
Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01);
|
||||
Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
|
||||
@@ -150,8 +147,6 @@ namespace osu.Game.Configuration
|
||||
DisplayStarsMinimum,
|
||||
DisplayStarsMaximum,
|
||||
RandomSelectAlgorithm,
|
||||
SnakingInSliders,
|
||||
SnakingOutSliders,
|
||||
ShowFpsDisplay,
|
||||
ChatDisplayHeight,
|
||||
Version,
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace osu.Game.Database
|
||||
where TModel : class, IHasFiles<TFileModel>, IHasPrimaryKey, ISoftDelete
|
||||
where TFileModel : INamedFileInfo, new()
|
||||
{
|
||||
public delegate void ItemAddedDelegate(TModel model, bool existing, bool silent);
|
||||
public delegate void ItemAddedDelegate(TModel model, bool existing);
|
||||
|
||||
/// <summary>
|
||||
/// Set an endpoint for notifications to be posted to.
|
||||
@@ -113,7 +113,7 @@ namespace osu.Game.Database
|
||||
ContextFactory = contextFactory;
|
||||
|
||||
ModelStore = modelStore;
|
||||
ModelStore.ItemAdded += (item, silent) => handleEvent(() => ItemAdded?.Invoke(item, false, silent));
|
||||
ModelStore.ItemAdded += item => handleEvent(() => ItemAdded?.Invoke(item, false));
|
||||
ModelStore.ItemRemoved += s => handleEvent(() => ItemRemoved?.Invoke(s));
|
||||
|
||||
Files = new FileStore(contextFactory, storage);
|
||||
@@ -131,14 +131,18 @@ namespace osu.Game.Database
|
||||
/// <param name="paths">One or more archive locations on disk.</param>
|
||||
public void Import(params string[] paths)
|
||||
{
|
||||
var notification = new ProgressNotification
|
||||
{
|
||||
Text = "Import is initialising...",
|
||||
Progress = 0,
|
||||
State = ProgressNotificationState.Active,
|
||||
};
|
||||
var notification = new ProgressNotification { State = ProgressNotificationState.Active };
|
||||
|
||||
PostNotification?.Invoke(notification);
|
||||
Import(notification, paths);
|
||||
}
|
||||
|
||||
protected void Import(ProgressNotification notification, params string[] paths)
|
||||
{
|
||||
notification.Progress = 0;
|
||||
notification.Text = "Import is initialising...";
|
||||
|
||||
var term = $"{typeof(TModel).Name.Replace("Info", "").ToLower()}";
|
||||
|
||||
List<TModel> imported = new List<TModel>();
|
||||
|
||||
@@ -151,7 +155,18 @@ namespace osu.Game.Database
|
||||
|
||||
try
|
||||
{
|
||||
notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}";
|
||||
var text = "Importing ";
|
||||
|
||||
if (path.Length > 1)
|
||||
text += $"{++current} of {paths.Length} {term}s..";
|
||||
else
|
||||
text += $"{term}..";
|
||||
|
||||
// only show the filename if it isn't a temporary one (as those look ugly).
|
||||
if (!path.Contains(Path.GetTempPath()))
|
||||
text += $"\n{Path.GetFileName(path)}";
|
||||
|
||||
notification.Text = text;
|
||||
|
||||
imported.Add(Import(path));
|
||||
|
||||
@@ -171,13 +186,20 @@ namespace osu.Game.Database
|
||||
}
|
||||
else
|
||||
{
|
||||
notification.CompletionText = $"Imported {current} {typeof(TModel).Name.Replace("Info", "").ToLower()}s!";
|
||||
notification.CompletionClickAction += () =>
|
||||
notification.CompletionText = imported.Count == 1
|
||||
? $"Imported {imported.First()}!"
|
||||
: $"Imported {current} {term}s!";
|
||||
|
||||
if (imported.Count > 0 && PresentImport != null)
|
||||
{
|
||||
if (imported.Count > 0)
|
||||
PresentCompletedImport(imported);
|
||||
return true;
|
||||
};
|
||||
notification.CompletionText += " Click to view.";
|
||||
notification.CompletionClickAction = () =>
|
||||
{
|
||||
PresentImport?.Invoke(imported);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
}
|
||||
}
|
||||
@@ -210,9 +232,10 @@ namespace osu.Game.Database
|
||||
return import;
|
||||
}
|
||||
|
||||
protected virtual void PresentCompletedImport(IEnumerable<TModel> imported)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Fired when the user requests to view the resulting import.
|
||||
/// </summary>
|
||||
public Action<IEnumerable<TModel>> PresentImport;
|
||||
|
||||
/// <summary>
|
||||
/// Import an item from an <see cref="ArchiveReader"/>.
|
||||
@@ -228,7 +251,7 @@ namespace osu.Game.Database
|
||||
|
||||
model.Hash = computeHash(archive);
|
||||
|
||||
return Import(model, false, archive);
|
||||
return Import(model, archive);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -262,9 +285,8 @@ namespace osu.Game.Database
|
||||
/// Import an item from a <see cref="TModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The model to be imported.</param>
|
||||
/// <param name="silent">Whether the user should be notified fo the import.</param>
|
||||
/// <param name="archive">An optional archive to use for model population.</param>
|
||||
public TModel Import(TModel item, bool silent = false, ArchiveReader archive = null)
|
||||
public TModel Import(TModel item, ArchiveReader archive = null)
|
||||
{
|
||||
delayEvents();
|
||||
|
||||
@@ -284,7 +306,7 @@ namespace osu.Game.Database
|
||||
{
|
||||
Undelete(existing);
|
||||
Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
|
||||
handleEvent(() => ItemAdded?.Invoke(existing, true, silent));
|
||||
handleEvent(() => ItemAdded?.Invoke(existing, true));
|
||||
return existing;
|
||||
}
|
||||
|
||||
@@ -294,7 +316,7 @@ namespace osu.Game.Database
|
||||
Populate(item, archive);
|
||||
|
||||
// import to store
|
||||
ModelStore.Add(item, silent);
|
||||
ModelStore.Add(item);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -16,9 +16,7 @@ namespace osu.Game.Database
|
||||
public abstract class MutableDatabaseBackedStore<T> : DatabaseBackedStore
|
||||
where T : class, IHasPrimaryKey, ISoftDelete
|
||||
{
|
||||
public delegate void ItemAddedDelegate(T model, bool silent);
|
||||
|
||||
public event ItemAddedDelegate ItemAdded;
|
||||
public event Action<T> ItemAdded;
|
||||
public event Action<T> ItemRemoved;
|
||||
|
||||
protected MutableDatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null)
|
||||
@@ -35,8 +33,7 @@ namespace osu.Game.Database
|
||||
/// Add a <see cref="T"/> to the database.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to add.</param>
|
||||
/// <param name="silent">Whether the user should be notified of the addition.</param>
|
||||
public void Add(T item, bool silent)
|
||||
public void Add(T item)
|
||||
{
|
||||
using (var usage = ContextFactory.GetForWrite())
|
||||
{
|
||||
@@ -44,7 +41,7 @@ namespace osu.Game.Database
|
||||
context.Attach(item);
|
||||
}
|
||||
|
||||
ItemAdded?.Invoke(item, silent);
|
||||
ItemAdded?.Invoke(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,7 +54,7 @@ namespace osu.Game.Database
|
||||
usage.Context.Update(item);
|
||||
|
||||
ItemRemoved?.Invoke(item);
|
||||
ItemAdded?.Invoke(item, true);
|
||||
ItemAdded?.Invoke(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,7 +91,7 @@ namespace osu.Game.Database
|
||||
item.DeletePending = false;
|
||||
}
|
||||
|
||||
ItemAdded?.Invoke(item, true);
|
||||
ItemAdded?.Invoke(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,12 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
protected override bool BlockNonPositionalInput => true;
|
||||
|
||||
/// <summary>
|
||||
/// Temporary to allow for overlays in the main screen content to not dim theirselves.
|
||||
/// Should be eventually replaced by dimming which is aware of the target dim container (traverse parent for certain interface type?).
|
||||
/// </summary>
|
||||
protected virtual bool DimMainContent => true;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private OsuGame osuGame { get; set; }
|
||||
|
||||
@@ -95,7 +101,7 @@ namespace osu.Game.Graphics.Containers
|
||||
if (OverlayActivationMode.Value != OverlayActivation.Disabled)
|
||||
{
|
||||
if (PlaySamplesOnStateChange) samplePopIn?.Play();
|
||||
if (BlockScreenWideMouse) osuGame?.AddBlockingOverlay(this);
|
||||
if (BlockScreenWideMouse && DimMainContent) osuGame?.AddBlockingOverlay(this);
|
||||
}
|
||||
else
|
||||
State = Visibility.Hidden;
|
||||
|
||||
@@ -59,7 +59,6 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
public abstract void Increment(T amount);
|
||||
|
||||
|
||||
public float TextSize
|
||||
{
|
||||
get => DisplayedCountSpriteText.Font.Size;
|
||||
|
||||
@@ -36,11 +36,12 @@ namespace osu.Game.Online.API.Requests
|
||||
|
||||
[Description("Ranked & Approved")]
|
||||
RankedApproved = 0,
|
||||
Approved = 1,
|
||||
Qualified = 3,
|
||||
Loved = 8,
|
||||
Favourites = 2,
|
||||
Qualified = 3,
|
||||
Pending = 4,
|
||||
|
||||
[Description("Pending & WIP")]
|
||||
PendingWIP = 4,
|
||||
Graveyard = 5,
|
||||
|
||||
[Description("My Maps")]
|
||||
|
||||
@@ -213,12 +213,6 @@ namespace osu.Game.Online.Leaderboards
|
||||
pendingUpdateScores?.Cancel();
|
||||
pendingUpdateScores = Schedule(() =>
|
||||
{
|
||||
if (api?.IsLoggedIn != true)
|
||||
{
|
||||
PlaceholderState = PlaceholderState.NotLoggedIn;
|
||||
return;
|
||||
}
|
||||
|
||||
PlaceholderState = PlaceholderState.Retrieving;
|
||||
loading.Show();
|
||||
|
||||
@@ -231,6 +225,12 @@ namespace osu.Game.Online.Leaderboards
|
||||
if (getScoresRequest == null)
|
||||
return;
|
||||
|
||||
if (api?.IsLoggedIn != true)
|
||||
{
|
||||
PlaceholderState = PlaceholderState.NotLoggedIn;
|
||||
return;
|
||||
}
|
||||
|
||||
getScoresRequest.Failure += e => Schedule(() =>
|
||||
{
|
||||
if (e is OperationCanceledException)
|
||||
@@ -243,6 +243,11 @@ namespace osu.Game.Online.Leaderboards
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a fetch/refresh of scores to be displayed.
|
||||
/// </summary>
|
||||
/// <param name="scoresCallback">A callback which should be called when fetching is completed. Scheduling is not required.</param>
|
||||
/// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns>
|
||||
protected abstract APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback);
|
||||
|
||||
private Placeholder currentPlaceholder;
|
||||
|
||||
+81
-79
@@ -167,8 +167,6 @@ namespace osu.Game
|
||||
{
|
||||
this.frameworkConfig = frameworkConfig;
|
||||
|
||||
ScoreManager.ItemAdded += (score, _, silent) => Schedule(() => LoadScore(score, silent));
|
||||
|
||||
if (!Host.IsPrimaryInstance)
|
||||
{
|
||||
Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error);
|
||||
@@ -217,63 +215,12 @@ namespace osu.Game
|
||||
externalLinkOpener.OpenUrlExternally(url);
|
||||
}
|
||||
|
||||
private ScheduledDelegate scoreLoad;
|
||||
|
||||
/// <summary>
|
||||
/// Show a beatmap set as an overlay.
|
||||
/// </summary>
|
||||
/// <param name="setId">The set to display.</param>
|
||||
public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId);
|
||||
|
||||
/// <summary>
|
||||
/// Present a beatmap at song select.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The beatmap to select.</param>
|
||||
public void PresentBeatmap(BeatmapSetInfo beatmap)
|
||||
{
|
||||
if (menuScreen == null)
|
||||
{
|
||||
Schedule(() => PresentBeatmap(beatmap));
|
||||
return;
|
||||
}
|
||||
|
||||
CloseAllOverlays(false);
|
||||
|
||||
void setBeatmap()
|
||||
{
|
||||
if (Beatmap.Disabled)
|
||||
{
|
||||
Schedule(setBeatmap);
|
||||
return;
|
||||
}
|
||||
|
||||
var databasedSet = beatmap.OnlineBeatmapSetID != null ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash);
|
||||
|
||||
if (databasedSet != null)
|
||||
{
|
||||
// Use first beatmap available for current ruleset, else switch ruleset.
|
||||
var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
|
||||
|
||||
ruleset.Value = first.Ruleset;
|
||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
|
||||
}
|
||||
}
|
||||
|
||||
switch (screenStack.CurrentScreen)
|
||||
{
|
||||
case SongSelect _:
|
||||
break;
|
||||
default:
|
||||
// navigate to song select if we are not already there.
|
||||
|
||||
menuScreen.MakeCurrent();
|
||||
menuScreen.LoadToSolo();
|
||||
break;
|
||||
}
|
||||
|
||||
setBeatmap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show a user's profile as an overlay.
|
||||
/// </summary>
|
||||
@@ -286,19 +233,44 @@ namespace osu.Game
|
||||
/// <param name="beatmapId">The beatmap to show.</param>
|
||||
public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId);
|
||||
|
||||
protected void LoadScore(ScoreInfo score, bool silent)
|
||||
/// <summary>
|
||||
/// Present a beatmap at song select immediately.
|
||||
/// The user should have already requested this interactively.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The beatmap to select.</param>
|
||||
public void PresentBeatmap(BeatmapSetInfo beatmap)
|
||||
{
|
||||
if (silent)
|
||||
return;
|
||||
var databasedSet = beatmap.OnlineBeatmapSetID != null
|
||||
? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID)
|
||||
: BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash);
|
||||
|
||||
scoreLoad?.Cancel();
|
||||
|
||||
if (menuScreen == null)
|
||||
if (databasedSet == null)
|
||||
{
|
||||
scoreLoad = Schedule(() => LoadScore(score, false));
|
||||
Logger.Log("The requested beatmap could not be loaded.", LoggingTarget.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
performFromMainMenu(() =>
|
||||
{
|
||||
// we might already be at song select, so a check is required before performing the load to solo.
|
||||
if (menuScreen.IsCurrentScreen())
|
||||
menuScreen.LoadToSolo();
|
||||
|
||||
// Use first beatmap available for current ruleset, else switch ruleset.
|
||||
var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
|
||||
|
||||
ruleset.Value = first.Ruleset;
|
||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
|
||||
}, $"load {beatmap}", bypassScreenAllowChecks: true, targetScreen: typeof(PlaySongSelect));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Present a score's replay immediately.
|
||||
/// The user should have already requested this interactively.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The beatmap to select.</param>
|
||||
public void PresentScore(ScoreInfo score)
|
||||
{
|
||||
var databasedScore = ScoreManager.GetScore(score);
|
||||
var databasedScoreInfo = databasedScore.ScoreInfo;
|
||||
if (databasedScore.Replay == null)
|
||||
@@ -314,14 +286,40 @@ namespace osu.Game
|
||||
return;
|
||||
}
|
||||
|
||||
if ((screenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false)
|
||||
performFromMainMenu(() =>
|
||||
{
|
||||
ruleset.Value = databasedScoreInfo.Ruleset;
|
||||
|
||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
|
||||
Beatmap.Value.Mods.Value = databasedScoreInfo.Mods;
|
||||
|
||||
menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore)));
|
||||
}, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true);
|
||||
}
|
||||
|
||||
private ScheduledDelegate performFromMainMenuTask;
|
||||
|
||||
/// <summary>
|
||||
/// Perform an action only after returning to the main menu.
|
||||
/// Eagerly tries to exit the current screen until it succeeds.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to perform once we are in the correct state.</param>
|
||||
/// <param name="taskName">The task name to display in a notification (if we can't immediately reach the main menu state).</param>
|
||||
/// <param name="targetScreen">An optional target screen type. If this screen is already current we can immediately perform the action without returning to the menu.</param>
|
||||
/// <param name="bypassScreenAllowChecks">Whether checking <see cref="IOsuScreen.AllowExternalScreenChange"/> should be bypassed.</param>
|
||||
private void performFromMainMenu(Action action, string taskName, Type targetScreen = null, bool bypassScreenAllowChecks = false)
|
||||
{
|
||||
performFromMainMenuTask?.Cancel();
|
||||
|
||||
// if the current screen does not allow screen changing, give the user an option to try again later.
|
||||
if (!bypassScreenAllowChecks && (screenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false)
|
||||
{
|
||||
notifications.Post(new SimpleNotification
|
||||
{
|
||||
Text = $"Click here to watch {databasedScoreInfo.User.Username} on {databasedScoreInfo.Beatmap}",
|
||||
Text = $"Click here to {taskName}",
|
||||
Activated = () =>
|
||||
{
|
||||
loadScore();
|
||||
performFromMainMenu(action, taskName, targetScreen, true);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@@ -329,24 +327,26 @@ namespace osu.Game
|
||||
return;
|
||||
}
|
||||
|
||||
loadScore();
|
||||
CloseAllOverlays(false);
|
||||
|
||||
void loadScore()
|
||||
// we may already be at the target screen type.
|
||||
if (targetScreen != null && screenStack.CurrentScreen?.GetType() == targetScreen)
|
||||
{
|
||||
if (!menuScreen.IsCurrentScreen() || Beatmap.Disabled)
|
||||
{
|
||||
menuScreen.MakeCurrent();
|
||||
this.Delay(500).Schedule(loadScore, out scoreLoad);
|
||||
return;
|
||||
}
|
||||
|
||||
ruleset.Value = databasedScoreInfo.Ruleset;
|
||||
|
||||
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
|
||||
Beatmap.Value.Mods.Value = databasedScoreInfo.Mods;
|
||||
|
||||
menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore)));
|
||||
action();
|
||||
return;
|
||||
}
|
||||
|
||||
// all conditions have been met to continue with the action.
|
||||
if (menuScreen?.IsCurrentScreen() == true && !Beatmap.Disabled)
|
||||
{
|
||||
action();
|
||||
return;
|
||||
}
|
||||
|
||||
// menuScreen may not be initialised yet (null check required).
|
||||
menuScreen?.MakeCurrent();
|
||||
|
||||
performFromMainMenuTask = Schedule(() => performFromMainMenu(action, taskName));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
@@ -370,8 +370,10 @@ namespace osu.Game
|
||||
|
||||
BeatmapManager.PostNotification = n => notifications?.Post(n);
|
||||
BeatmapManager.GetStableStorage = GetStorageForStableInstall;
|
||||
BeatmapManager.PresentImport = items => PresentBeatmap(items.First());
|
||||
|
||||
BeatmapManager.PresentBeatmap = PresentBeatmap;
|
||||
ScoreManager.PostNotification = n => notifications?.Post(n);
|
||||
ScoreManager.PresentImport = items => PresentScore(items.First());
|
||||
|
||||
Container logoContainer;
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace osu.Game.Overlays.Chat
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: TextSize * 0.75f, weight: FontWeight.Bold, fixedWidth: true)
|
||||
Font = OsuFont.GetFont(size: TextSize * 0.75f, weight: FontWeight.SemiBold, fixedWidth: true)
|
||||
},
|
||||
new MessageSender(message.Sender)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
|
||||
namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
@@ -23,6 +24,7 @@ namespace osu.Game.Overlays.Direct
|
||||
private const float vertical_padding = 5;
|
||||
private const float height = 70;
|
||||
|
||||
private FillFlowContainer statusContainer;
|
||||
private PlayButton playButton;
|
||||
private Box progressBar;
|
||||
|
||||
@@ -108,10 +110,24 @@ namespace osu.Game.Overlays.Direct
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = 20,
|
||||
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
|
||||
Children = GetDifficultyIcons(),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
statusContainer = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Vertical = vertical_padding, Horizontal = 5 },
|
||||
Spacing = new Vector2(5),
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = 20,
|
||||
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
|
||||
Children = GetDifficultyIcons(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -194,6 +210,23 @@ namespace osu.Game.Overlays.Direct
|
||||
Colour = colours.Yellow,
|
||||
},
|
||||
});
|
||||
|
||||
if (SetInfo.OnlineInfo?.HasVideo ?? false)
|
||||
{
|
||||
statusContainer.Add(new IconPill(FontAwesome.fa_film) { IconSize = new Vector2(20) });
|
||||
}
|
||||
|
||||
if (SetInfo.OnlineInfo?.HasStoryboard ?? false)
|
||||
{
|
||||
statusContainer.Add(new IconPill(FontAwesome.fa_image) { IconSize = new Vector2(20) });
|
||||
}
|
||||
|
||||
statusContainer.Add(new BeatmapSetOnlineStatusPill
|
||||
{
|
||||
TextSize = 12,
|
||||
TextPadding = new MarginPadding { Horizontal = 10, Vertical = 4 },
|
||||
Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace osu.Game.Overlays.Direct
|
||||
|
||||
private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null));
|
||||
|
||||
private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable);
|
||||
private void setAdded(BeatmapSetInfo s, bool existing) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable);
|
||||
|
||||
private void setRemoved(BeatmapSetInfo s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded);
|
||||
|
||||
|
||||
@@ -12,6 +12,14 @@ namespace osu.Game.Overlays.Direct
|
||||
{
|
||||
public class IconPill : CircularContainer
|
||||
{
|
||||
public Vector2 IconSize
|
||||
{
|
||||
get => iconContainer.Size;
|
||||
set => iconContainer.Size = value;
|
||||
}
|
||||
|
||||
private readonly Container iconContainer;
|
||||
|
||||
public IconPill(FontAwesome icon)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
@@ -25,16 +33,16 @@ namespace osu.Game.Overlays.Direct
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
new Container
|
||||
iconContainer = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding(5),
|
||||
Size = new Vector2(22),
|
||||
Padding = new MarginPadding(5),
|
||||
Child = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Icon = icon,
|
||||
Size = new Vector2(12),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
protected override bool BlockNonPositionalInput => false;
|
||||
|
||||
protected override bool DimMainContent => false;
|
||||
|
||||
protected readonly FillFlowContainer<ModSection> ModSectionsContainer;
|
||||
|
||||
protected readonly Bindable<IEnumerable<Mod>> SelectedMods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Music
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager beatmaps, IBindable<WorkingBeatmap> beatmap)
|
||||
{
|
||||
beatmaps.GetAllUsableBeatmapSets().ForEach(b => addBeatmapSet(b, false, false));
|
||||
beatmaps.GetAllUsableBeatmapSets().ForEach(b => addBeatmapSet(b, false));
|
||||
beatmaps.ItemAdded += addBeatmapSet;
|
||||
beatmaps.ItemRemoved += removeBeatmapSet;
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace osu.Game.Overlays.Music
|
||||
beatmapBacking.ValueChanged += _ => updateSelectedSet();
|
||||
}
|
||||
|
||||
private void addBeatmapSet(BeatmapSetInfo obj, bool existing, bool silent) => Schedule(() =>
|
||||
private void addBeatmapSet(BeatmapSetInfo obj, bool existing) => Schedule(() =>
|
||||
{
|
||||
if (existing)
|
||||
return;
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace osu.Game.Overlays
|
||||
beatmapSets.Insert(index, beatmapSetInfo);
|
||||
}
|
||||
|
||||
private void handleBeatmapAdded(BeatmapSetInfo obj, bool existing, bool silent)
|
||||
private void handleBeatmapAdded(BeatmapSetInfo obj, bool existing)
|
||||
{
|
||||
if (existing)
|
||||
return;
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
|
||||
private void itemRemoved(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != s.ID).ToArray());
|
||||
|
||||
private void itemAdded(SkinInfo s, bool existing, bool silent)
|
||||
private void itemAdded(SkinInfo s, bool existing)
|
||||
{
|
||||
if (existing)
|
||||
return;
|
||||
|
||||
@@ -139,6 +139,7 @@ namespace osu.Game.Overlays.Toolbar
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
HoverBackground.FlashColour(Color4.White.Opacity(100), 500, Easing.OutQuint);
|
||||
tooltipContainer.FadeOut(100);
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
|
||||
@@ -301,7 +301,16 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
{
|
||||
// Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario
|
||||
if (!string.IsNullOrEmpty(bankInfo.Filename))
|
||||
return new List<SampleInfo> { new FileSampleInfo { Filename = bankInfo.Filename } };
|
||||
{
|
||||
return new List<SampleInfo>
|
||||
{
|
||||
new FileSampleInfo
|
||||
{
|
||||
Filename = bankInfo.Filename,
|
||||
Volume = bankInfo.Volume
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var soundTypes = new List<SampleInfo>
|
||||
{
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace osu.Game.Rulesets.UI
|
||||
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||
{
|
||||
InternalChild = KeyBindingContainer = CreateKeyBindingContainer(ruleset, variant, unique);
|
||||
gameplayClock = new GameplayClock(framedClock = new FramedClock(manualClock = new ManualClock()));
|
||||
}
|
||||
|
||||
#region Action mapping (for replays)
|
||||
@@ -86,22 +87,28 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
#region Clock control
|
||||
|
||||
private ManualClock clock;
|
||||
private IFrameBasedClock parentClock;
|
||||
private readonly ManualClock manualClock;
|
||||
|
||||
private readonly FramedClock framedClock;
|
||||
|
||||
[Cached]
|
||||
private GameplayClock gameplayClock;
|
||||
|
||||
private IFrameBasedClock parentGameplayClock;
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuConfigManager config, GameplayClock clock)
|
||||
{
|
||||
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
||||
|
||||
if (clock != null)
|
||||
parentGameplayClock = clock;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
//our clock will now be our parent's clock, but we want to replace this to allow manual control.
|
||||
parentClock = Clock;
|
||||
|
||||
ProcessCustomClock = false;
|
||||
Clock = new FramedClock(clock = new ManualClock
|
||||
{
|
||||
CurrentTime = parentClock.CurrentTime,
|
||||
Rate = parentClock.Rate,
|
||||
});
|
||||
setClock();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -147,25 +154,28 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
private void updateClock()
|
||||
{
|
||||
if (parentClock == null) return;
|
||||
if (parentGameplayClock == null)
|
||||
setClock(); // LoadComplete may not be run yet, but we still want the clock.
|
||||
|
||||
clock.Rate = parentClock.Rate;
|
||||
clock.IsRunning = parentClock.IsRunning;
|
||||
validState = true;
|
||||
|
||||
var newProposedTime = parentClock.CurrentTime;
|
||||
manualClock.Rate = parentGameplayClock.Rate;
|
||||
manualClock.IsRunning = parentGameplayClock.IsRunning;
|
||||
|
||||
var newProposedTime = parentGameplayClock.CurrentTime;
|
||||
|
||||
try
|
||||
{
|
||||
if (Math.Abs(clock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f)
|
||||
if (Math.Abs(manualClock.CurrentTime - newProposedTime) > sixty_frame_time * 1.2f)
|
||||
{
|
||||
newProposedTime = clock.Rate > 0
|
||||
? Math.Min(newProposedTime, clock.CurrentTime + sixty_frame_time)
|
||||
: Math.Max(newProposedTime, clock.CurrentTime - sixty_frame_time);
|
||||
newProposedTime = manualClock.Rate > 0
|
||||
? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time)
|
||||
: Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time);
|
||||
}
|
||||
|
||||
if (!isAttached)
|
||||
{
|
||||
clock.CurrentTime = newProposedTime;
|
||||
manualClock.CurrentTime = newProposedTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -177,35 +187,39 @@ namespace osu.Game.Rulesets.UI
|
||||
validState = false;
|
||||
|
||||
requireMoreUpdateLoops = true;
|
||||
clock.CurrentTime = newProposedTime;
|
||||
manualClock.CurrentTime = newProposedTime;
|
||||
return;
|
||||
}
|
||||
|
||||
clock.CurrentTime = newTime.Value;
|
||||
manualClock.CurrentTime = newTime.Value;
|
||||
}
|
||||
|
||||
requireMoreUpdateLoops = clock.CurrentTime != parentClock.CurrentTime;
|
||||
requireMoreUpdateLoops = manualClock.CurrentTime != parentGameplayClock.CurrentTime;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// The manual clock time has changed in the above code. The framed clock now needs to be updated
|
||||
// to ensure that the its time is valid for our children before input is processed
|
||||
Clock.ProcessFrame();
|
||||
framedClock.ProcessFrame();
|
||||
}
|
||||
}
|
||||
|
||||
private void setClock()
|
||||
{
|
||||
// in case a parent gameplay clock isn't available, just use the parent clock.
|
||||
if (parentGameplayClock == null)
|
||||
parentGameplayClock = Clock;
|
||||
|
||||
Clock = gameplayClock;
|
||||
ProcessCustomClock = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Setting application (disables etc.)
|
||||
|
||||
private Bindable<bool> mouseDisabled;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
||||
}
|
||||
|
||||
protected override bool Handle(UIEvent e)
|
||||
{
|
||||
switch (e)
|
||||
|
||||
@@ -178,5 +178,7 @@ namespace osu.Game.Scoring
|
||||
{
|
||||
public string Acronym { get; set; }
|
||||
}
|
||||
|
||||
public override string ToString() => $"{User} playing {Beatmap}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Height = 0.5f,
|
||||
Icon = FontAwesome.fa_search_plus,
|
||||
Action = () => timeline.Zoom++
|
||||
Action = () => changeZoom(1)
|
||||
},
|
||||
new TimelineButton
|
||||
{
|
||||
@@ -100,7 +100,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Height = 0.5f,
|
||||
Icon = FontAwesome.fa_search_minus,
|
||||
Action = () => timeline.Zoom--
|
||||
Action = () => changeZoom(-1)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -124,5 +124,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
timeline.WaveformVisible.BindTo(waveformCheckbox.Current);
|
||||
}
|
||||
|
||||
private void changeZoom(float change) => timeline.Zoom += change;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace osu.Game.Screens
|
||||
|
||||
/// <summary>
|
||||
/// Whether a top-level component should be allowed to exit the current screen to, for example,
|
||||
/// complete an import.
|
||||
/// complete an import. Note that this can be overridden by a user if they specifically request.
|
||||
/// </summary>
|
||||
bool AllowExternalScreenChange { get; }
|
||||
|
||||
|
||||
@@ -5,14 +5,17 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
@@ -33,13 +36,15 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private const float icon_y = -85;
|
||||
|
||||
private readonly Bindable<User> currentUser = new Bindable<User>();
|
||||
|
||||
public Disclaimer()
|
||||
{
|
||||
ValidForResume = false;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load(OsuColour colours, APIAccess api)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
@@ -70,7 +75,7 @@ namespace osu.Game.Screens.Menu
|
||||
textFlow.AddParagraph("Things may not work as expected", t => t.Font = t.Font.With(size: 20));
|
||||
textFlow.NewParagraph();
|
||||
|
||||
Action<SpriteText> format = t => t.Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold);
|
||||
Action<SpriteText> format = t => t.Font = OsuFont.GetFont(size: 15, weight: FontWeight.SemiBold);
|
||||
|
||||
textFlow.AddParagraph("Detailed bug reports are welcomed via github issues.", format);
|
||||
textFlow.NewParagraph();
|
||||
@@ -96,6 +101,13 @@ namespace osu.Game.Screens.Menu
|
||||
}).First());
|
||||
|
||||
iconColour = colours.Yellow;
|
||||
|
||||
currentUser.BindTo(api.LocalUser);
|
||||
currentUser.BindValueChanged(e =>
|
||||
{
|
||||
if (e.NewValue.IsSupporter)
|
||||
supporterDrawables.ForEach(d => d.FadeOut(500, Easing.OutQuint).Expire());
|
||||
}, true);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID) != null;
|
||||
}
|
||||
|
||||
private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent)
|
||||
private void beatmapAdded(BeatmapSetInfo model, bool existing)
|
||||
{
|
||||
if (Beatmap.Value == null)
|
||||
return;
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace osu.Game.Screens.Multi.Match
|
||||
/// <summary>
|
||||
/// Handle the case where a beatmap is imported (and can be used by this match).
|
||||
/// </summary>
|
||||
private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() =>
|
||||
private void beatmapAdded(BeatmapSetInfo model, bool existing) => Schedule(() =>
|
||||
{
|
||||
if (Beatmap.Value != beatmapManager.DefaultBeatmap)
|
||||
return;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@@ -40,13 +41,7 @@ namespace osu.Game.Screens.Play
|
||||
private readonly BreakInfo info;
|
||||
private readonly BreakArrows breakArrows;
|
||||
|
||||
public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor)
|
||||
: this(letterboxing)
|
||||
{
|
||||
bindProcessor(scoreProcessor);
|
||||
}
|
||||
|
||||
public BreakOverlay(bool letterboxing)
|
||||
public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor = null)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Child = fadeContainer = new Container
|
||||
@@ -98,6 +93,14 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (scoreProcessor != null) bindProcessor(scoreProcessor);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(GameplayClock clock)
|
||||
{
|
||||
if (clock != null) Clock = clock;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Timing;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
/// <summary>
|
||||
/// A clock which is used for gameplay elements that need to follow audio time 1:1.
|
||||
/// Exposed via DI by <see cref="PausableGameplayContainer"/>.
|
||||
/// <remarks>
|
||||
/// The main purpose of this clock is to stop components using it from accidentally processing the main
|
||||
/// <see cref="IFrameBasedClock"/>, as this should only be done once to ensure accuracy.
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
public class GameplayClock : IFrameBasedClock
|
||||
{
|
||||
private readonly IFrameBasedClock underlyingClock;
|
||||
|
||||
public GameplayClock(IFrameBasedClock underlyingClock)
|
||||
{
|
||||
this.underlyingClock = underlyingClock;
|
||||
}
|
||||
|
||||
public double CurrentTime => underlyingClock.CurrentTime;
|
||||
|
||||
public double Rate => underlyingClock.Rate;
|
||||
|
||||
public bool IsRunning => underlyingClock.IsRunning;
|
||||
|
||||
public void ProcessFrame()
|
||||
{
|
||||
// we do not want to process the underlying clock.
|
||||
// this is handled by PauseContainer.
|
||||
}
|
||||
|
||||
public double ElapsedFrameTime => underlyingClock.ElapsedFrameTime;
|
||||
|
||||
public double FramesPerSecond => underlyingClock.FramesPerSecond;
|
||||
|
||||
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private static bool hasShownNotificationOnce;
|
||||
|
||||
public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, WorkingBeatmap working, IClock offsetClock, IAdjustableClock adjustableClock)
|
||||
public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, WorkingBeatmap working, IAdjustableClock adjustableClock)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace osu.Game.Screens.Play
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
KeyCounter = CreateKeyCounter(adjustableClock as IFrameBasedClock),
|
||||
KeyCounter = CreateKeyCounter(),
|
||||
HoldToQuit = CreateHoldForMenuButton(),
|
||||
}
|
||||
}
|
||||
@@ -91,9 +91,8 @@ namespace osu.Game.Screens.Play
|
||||
BindRulesetContainer(rulesetContainer);
|
||||
|
||||
Progress.Objects = rulesetContainer.Objects;
|
||||
Progress.AudioClock = offsetClock;
|
||||
Progress.AllowSeeking = rulesetContainer.HasReplayLoaded.Value;
|
||||
Progress.OnSeek = pos => adjustableClock.Seek(pos);
|
||||
Progress.RequestSeek = pos => adjustableClock.Seek(pos);
|
||||
|
||||
ModDisplay.Current.BindTo(working.Mods);
|
||||
|
||||
@@ -202,13 +201,12 @@ namespace osu.Game.Screens.Play
|
||||
Margin = new MarginPadding { Top = 20 }
|
||||
};
|
||||
|
||||
protected virtual KeyCounterCollection CreateKeyCounter(IFrameBasedClock offsetClock) => new KeyCounterCollection
|
||||
protected virtual KeyCounterCollection CreateKeyCounter() => new KeyCounterCollection
|
||||
{
|
||||
FadeTime = 50,
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Margin = new MarginPadding(10),
|
||||
AudioClock = offsetClock
|
||||
};
|
||||
|
||||
protected virtual SongProgress CreateProgress() => new SongProgress
|
||||
|
||||
@@ -71,9 +71,12 @@ namespace osu.Game.Screens.Play
|
||||
Name = name;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(TextureStore textures, GameplayClock clock)
|
||||
{
|
||||
if (clock != null)
|
||||
Clock = clock;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
buttonSprite = new Sprite
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Configuration;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@@ -37,9 +36,6 @@ namespace osu.Game.Screens.Play
|
||||
key.FadeTime = FadeTime;
|
||||
key.KeyDownTextColor = KeyDownTextColor;
|
||||
key.KeyUpTextColor = KeyUpTextColor;
|
||||
// Use the same clock object as SongProgress for saving KeyCounter state
|
||||
if (AudioClock != null)
|
||||
key.Clock = AudioClock;
|
||||
}
|
||||
|
||||
public void ResetCount()
|
||||
@@ -125,8 +121,6 @@ namespace osu.Game.Screens.Play
|
||||
public override bool HandleNonPositionalInput => receptor == null;
|
||||
public override bool HandlePositionalInput => receptor == null;
|
||||
|
||||
public IFrameBasedClock AudioClock { get; set; }
|
||||
|
||||
private Receptor receptor;
|
||||
|
||||
public Receptor GetReceptor()
|
||||
|
||||
+24
-15
@@ -14,10 +14,11 @@ using osuTK.Graphics;
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
/// <summary>
|
||||
/// A container which handles pausing children, displaying a pause overlay with choices etc.
|
||||
/// A container which handles pausing children, displaying a pause overlay with choices and processing the clock.
|
||||
/// Exposes a <see cref="GameplayClock"/> to children via DI.
|
||||
/// This alleviates a lot of the intricate pause logic from being in <see cref="Player"/>
|
||||
/// </summary>
|
||||
public class PauseContainer : Container
|
||||
public class PausableGameplayContainer : Container
|
||||
{
|
||||
public readonly BindableBool IsPaused = new BindableBool();
|
||||
|
||||
@@ -43,24 +44,32 @@ namespace osu.Game.Screens.Play
|
||||
public Action OnRetry;
|
||||
public Action OnQuit;
|
||||
|
||||
private readonly FramedClock framedClock;
|
||||
private readonly DecoupleableInterpolatingFramedClock decoupledClock;
|
||||
private readonly FramedClock offsetClock;
|
||||
private readonly DecoupleableInterpolatingFramedClock adjustableClock;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="PauseContainer"/>.
|
||||
/// The final clock which is exposed to underlying components.
|
||||
/// </summary>
|
||||
/// <param name="framedClock">The gameplay clock. This is the clock that will process frames.</param>
|
||||
/// <param name="decoupledClock">The seekable clock. This is the clock that will be paused and resumed.</param>
|
||||
public PauseContainer(FramedClock framedClock, DecoupleableInterpolatingFramedClock decoupledClock)
|
||||
[Cached]
|
||||
private readonly GameplayClock gameplayClock;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="PausableGameplayContainer"/>.
|
||||
/// </summary>
|
||||
/// <param name="offsetClock">The gameplay clock. This is the clock that will process frames. Includes user/system offsets.</param>
|
||||
/// <param name="adjustableClock">The seekable clock. This is the clock that will be paused and resumed. Should not be processed (it is processed automatically by <see cref="offsetClock"/>).</param>
|
||||
public PausableGameplayContainer(FramedClock offsetClock, DecoupleableInterpolatingFramedClock adjustableClock)
|
||||
{
|
||||
this.framedClock = framedClock;
|
||||
this.decoupledClock = decoupledClock;
|
||||
this.offsetClock = offsetClock;
|
||||
this.adjustableClock = adjustableClock;
|
||||
|
||||
gameplayClock = new GameplayClock(offsetClock);
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
AddInternal(content = new Container
|
||||
{
|
||||
Clock = this.framedClock,
|
||||
Clock = this.offsetClock,
|
||||
ProcessCustomClock = false,
|
||||
RelativeSizeAxes = Axes.Both
|
||||
});
|
||||
@@ -84,7 +93,7 @@ namespace osu.Game.Screens.Play
|
||||
if (IsPaused.Value) return;
|
||||
|
||||
// stop the seekable clock (stops the audio eventually)
|
||||
decoupledClock.Stop();
|
||||
adjustableClock.Stop();
|
||||
IsPaused.Value = true;
|
||||
|
||||
pauseOverlay.Show();
|
||||
@@ -102,8 +111,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
// Seeking the decoupled clock to its current time ensures that its source clock will be seeked to the same time
|
||||
// This accounts for the audio clock source potentially taking time to enter a completely stopped state
|
||||
decoupledClock.Seek(decoupledClock.CurrentTime);
|
||||
decoupledClock.Start();
|
||||
adjustableClock.Seek(adjustableClock.CurrentTime);
|
||||
adjustableClock.Start();
|
||||
|
||||
pauseOverlay.Hide();
|
||||
}
|
||||
@@ -123,7 +132,7 @@ namespace osu.Game.Screens.Play
|
||||
Pause();
|
||||
|
||||
if (!IsPaused.Value)
|
||||
framedClock.ProcessFrame();
|
||||
offsetClock.ProcessFrame();
|
||||
|
||||
base.Update();
|
||||
}
|
||||
@@ -72,7 +72,7 @@ namespace osu.Game.Screens.Play
|
||||
[Resolved]
|
||||
private ScoreManager scoreManager { get; set; }
|
||||
|
||||
protected PauseContainer PauseContainer { get; private set; }
|
||||
protected PausableGameplayContainer PausableGameplayContainer { get; private set; }
|
||||
|
||||
private RulesetInfo ruleset;
|
||||
|
||||
@@ -175,10 +175,10 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
PauseContainer = new PauseContainer(offsetClock, adjustableClock)
|
||||
PausableGameplayContainer = new PausableGameplayContainer(offsetClock, adjustableClock)
|
||||
{
|
||||
Retries = RestartCount,
|
||||
OnRetry = Restart,
|
||||
OnRetry = restart,
|
||||
OnQuit = performUserRequestedExit,
|
||||
CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded.Value,
|
||||
Children = new Container[]
|
||||
@@ -196,32 +196,26 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
ProcessCustomClock = false,
|
||||
Breaks = beatmap.Breaks
|
||||
},
|
||||
new ScalingContainer(ScalingMode.Gameplay)
|
||||
{
|
||||
Child = RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
|
||||
},
|
||||
HUDOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
|
||||
HUDOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, adjustableClock)
|
||||
{
|
||||
Clock = Clock, // hud overlay doesn't want to use the audio clock directly
|
||||
ProcessCustomClock = false,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
},
|
||||
new SkipOverlay(RulesetContainer.GameplayStartTime)
|
||||
{
|
||||
Clock = Clock, // skip button doesn't want to use the audio clock directly
|
||||
ProcessCustomClock = false,
|
||||
AdjustableClock = adjustableClock,
|
||||
FramedClock = offsetClock,
|
||||
RequestSeek = time => adjustableClock.Seek(time)
|
||||
},
|
||||
}
|
||||
},
|
||||
failOverlay = new FailOverlay
|
||||
{
|
||||
OnRetry = Restart,
|
||||
OnRetry = restart,
|
||||
OnQuit = performUserRequestedExit,
|
||||
},
|
||||
new HotkeyRetryOverlay
|
||||
@@ -231,7 +225,7 @@ namespace osu.Game.Screens.Play
|
||||
if (!this.IsCurrentScreen()) return;
|
||||
|
||||
fadeOut(true);
|
||||
Restart();
|
||||
restart();
|
||||
},
|
||||
}
|
||||
};
|
||||
@@ -239,7 +233,7 @@ namespace osu.Game.Screens.Play
|
||||
HUDOverlay.HoldToQuit.Action = performUserRequestedExit;
|
||||
HUDOverlay.KeyCounter.Visible.BindTo(RulesetContainer.HasReplayLoaded);
|
||||
|
||||
RulesetContainer.IsPaused.BindTo(PauseContainer.IsPaused);
|
||||
RulesetContainer.IsPaused.BindTo(PausableGameplayContainer.IsPaused);
|
||||
|
||||
if (ShowStoryboard.Value)
|
||||
initializeStoryboard(false);
|
||||
@@ -268,7 +262,7 @@ namespace osu.Game.Screens.Play
|
||||
this.Exit();
|
||||
}
|
||||
|
||||
public void Restart()
|
||||
private void restart()
|
||||
{
|
||||
if (!this.IsCurrentScreen()) return;
|
||||
|
||||
@@ -298,7 +292,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
var score = CreateScore();
|
||||
if (RulesetContainer.ReplayScore == null)
|
||||
scoreManager.Import(score, true);
|
||||
scoreManager.Import(score);
|
||||
|
||||
this.Push(CreateResults(score));
|
||||
|
||||
@@ -372,7 +366,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
this.Delay(750).Schedule(() =>
|
||||
{
|
||||
if (!PauseContainer.IsPaused.Value)
|
||||
if (!PausableGameplayContainer.IsPaused.Value)
|
||||
{
|
||||
adjustableClock.Start();
|
||||
}
|
||||
@@ -380,8 +374,8 @@ namespace osu.Game.Screens.Play
|
||||
});
|
||||
});
|
||||
|
||||
PauseContainer.Alpha = 0;
|
||||
PauseContainer.FadeIn(750, Easing.OutQuint);
|
||||
PausableGameplayContainer.Alpha = 0;
|
||||
PausableGameplayContainer.FadeIn(750, Easing.OutQuint);
|
||||
}
|
||||
|
||||
public override void OnSuspending(IScreen next)
|
||||
@@ -399,7 +393,7 @@ namespace osu.Game.Screens.Play
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((!AllowPause || HasFailed || !ValidForResume || PauseContainer?.IsPaused.Value != false || RulesetContainer?.HasReplayLoaded.Value != false) && (!PauseContainer?.IsResuming ?? true))
|
||||
if ((!AllowPause || HasFailed || !ValidForResume || PausableGameplayContainer?.IsPaused.Value != false || RulesetContainer?.HasReplayLoaded.Value != false) && (!PausableGameplayContainer?.IsResuming ?? true))
|
||||
{
|
||||
// In the case of replays, we may have changed the playback rate.
|
||||
applyRateFromMods();
|
||||
@@ -408,7 +402,7 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
|
||||
if (LoadedBeatmapSuccessfully)
|
||||
PauseContainer?.Pause();
|
||||
PausableGameplayContainer?.Pause();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -422,7 +416,7 @@ namespace osu.Game.Screens.Play
|
||||
storyboardReplacesBackground.Value = false;
|
||||
}
|
||||
|
||||
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !PauseContainer.IsPaused.Value;
|
||||
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !PausableGameplayContainer.IsPaused.Value;
|
||||
|
||||
private void initializeStoryboard(bool asyncLoad)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Play.PlayerSettings;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@@ -296,6 +297,7 @@ namespace osu.Game.Screens.Play
|
||||
private readonly WorkingBeatmap beatmap;
|
||||
private LoadingAnimation loading;
|
||||
private Sprite backgroundSprite;
|
||||
private ModDisplay modDisplay;
|
||||
|
||||
public bool Loading
|
||||
{
|
||||
@@ -322,7 +324,7 @@ namespace osu.Game.Screens.Play
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var metadata = beatmap?.BeatmapInfo?.Metadata ?? new BeatmapMetadata();
|
||||
var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata();
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Children = new Drawable[]
|
||||
@@ -391,6 +393,14 @@ namespace osu.Game.Screens.Play
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
},
|
||||
new ModDisplay
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Top = 20 },
|
||||
Current = beatmap.Mods
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@@ -9,7 +9,6 @@ using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Screens.Ranking;
|
||||
@@ -27,8 +26,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
private readonly double startTime;
|
||||
|
||||
public IAdjustableClock AdjustableClock;
|
||||
public IFrameBasedClock FramedClock;
|
||||
public Action<double> RequestSeek;
|
||||
|
||||
private Button button;
|
||||
private Box remainingTimeBox;
|
||||
@@ -54,16 +52,13 @@ namespace osu.Game.Screens.Play
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuColour colours, GameplayClock clock)
|
||||
{
|
||||
var baseClock = Clock;
|
||||
|
||||
if (FramedClock != null)
|
||||
{
|
||||
Clock = FramedClock;
|
||||
ProcessCustomClock = false;
|
||||
}
|
||||
if (clock != null)
|
||||
Clock = clock;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@@ -111,7 +106,7 @@ namespace osu.Game.Screens.Play
|
||||
using (BeginAbsoluteSequence(beginFadeTime))
|
||||
this.FadeOut(fade_time);
|
||||
|
||||
button.Action = () => AdjustableClock?.Seek(startTime - skip_required_cutoff - fade_time);
|
||||
button.Action = () => RequestSeek?.Invoke(startTime - skip_required_cutoff - fade_time);
|
||||
|
||||
displayTime = Time.Current;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ using osu.Game.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@@ -29,18 +28,11 @@ namespace osu.Game.Screens.Play
|
||||
private readonly SongProgressGraph graph;
|
||||
private readonly SongProgressInfo info;
|
||||
|
||||
public Action<double> OnSeek;
|
||||
public Action<double> RequestSeek;
|
||||
|
||||
public override bool HandleNonPositionalInput => AllowSeeking;
|
||||
public override bool HandlePositionalInput => AllowSeeking;
|
||||
|
||||
private IClock audioClock;
|
||||
|
||||
public IClock AudioClock
|
||||
{
|
||||
set => audioClock = info.AudioClock = value;
|
||||
}
|
||||
|
||||
private double lastHitTime => ((objects.Last() as IHasEndTime)?.EndTime ?? objects.Last().StartTime) + 1;
|
||||
|
||||
private double firstHitTime => objects.First().StartTime;
|
||||
@@ -63,9 +55,14 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private readonly BindableBool replayLoaded = new BindableBool();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private GameplayClock gameplayClock;
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuColour colours, GameplayClock clock)
|
||||
{
|
||||
if (clock != null)
|
||||
gameplayClock = clock;
|
||||
|
||||
graph.FillColour = bar.FillColour = colours.BlueLighter;
|
||||
}
|
||||
|
||||
@@ -99,7 +96,7 @@ namespace osu.Game.Screens.Play
|
||||
Alpha = 0,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
OnSeek = position => OnSeek?.Invoke(position),
|
||||
OnSeek = time => RequestSeek?.Invoke(time),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -157,14 +154,11 @@ namespace osu.Game.Screens.Play
|
||||
if (objects == null)
|
||||
return;
|
||||
|
||||
double position = audioClock?.CurrentTime ?? Time.Current;
|
||||
double progress = (position - firstHitTime) / (lastHitTime - firstHitTime);
|
||||
double position = gameplayClock?.CurrentTime ?? Time.Current;
|
||||
double progress = Math.Min(1, (position - firstHitTime) / (lastHitTime - firstHitTime));
|
||||
|
||||
if (progress < 1)
|
||||
{
|
||||
bar.CurrentTime = position;
|
||||
graph.Progress = (int)(graph.ColumnCount * progress);
|
||||
}
|
||||
bar.CurrentTime = position;
|
||||
graph.Progress = (int)(graph.ColumnCount * progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using System;
|
||||
@@ -27,8 +26,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private const int margin = 10;
|
||||
|
||||
public IClock AudioClock;
|
||||
|
||||
public double StartTime
|
||||
{
|
||||
set => startTime = value;
|
||||
@@ -39,9 +36,14 @@ namespace osu.Game.Screens.Play
|
||||
set => endTime = value;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private GameplayClock gameplayClock;
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuColour colours, GameplayClock clock)
|
||||
{
|
||||
if (clock != null)
|
||||
gameplayClock = clock;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
timeCurrent = new OsuSpriteText
|
||||
@@ -80,7 +82,9 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
base.Update();
|
||||
|
||||
double songCurrentTime = AudioClock.CurrentTime - startTime;
|
||||
var time = gameplayClock?.CurrentTime ?? Time.Current;
|
||||
|
||||
double songCurrentTime = time - startTime;
|
||||
int currentPercent = Math.Max(0, Math.Min(100, (int)(songCurrentTime / songLength * 100)));
|
||||
int currentSecond = (int)Math.Floor(songCurrentTime / 1000.0);
|
||||
|
||||
@@ -93,7 +97,7 @@ namespace osu.Game.Screens.Play
|
||||
if (currentSecond != previousSecond && songCurrentTime < songLength)
|
||||
{
|
||||
timeCurrent.Text = formatTime(TimeSpan.FromSeconds(currentSecond));
|
||||
timeLeft.Text = formatTime(TimeSpan.FromMilliseconds(endTime - AudioClock.CurrentTime));
|
||||
timeLeft.Text = formatTime(TimeSpan.FromMilliseconds(endTime - time));
|
||||
|
||||
previousSecond = currentSecond;
|
||||
}
|
||||
|
||||
@@ -242,7 +242,11 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
// Reverse drawableRows so when iterating through them they start at the bottom
|
||||
drawableRows.Reverse();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
fillActive();
|
||||
}
|
||||
|
||||
|
||||
@@ -582,7 +582,7 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
}
|
||||
|
||||
private void onBeatmapSetAdded(BeatmapSetInfo s, bool existing, bool silent) => Carousel.UpdateBeatmapSet(s);
|
||||
private void onBeatmapSetAdded(BeatmapSetInfo s, bool existing) => Carousel.UpdateBeatmapSet(s);
|
||||
private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s);
|
||||
private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
||||
private void onBeatmapHidden(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using osu.Game.Beatmaps;
|
||||
@@ -21,6 +22,18 @@ namespace osu.Game.Tests.Beatmaps
|
||||
HitObjects = baseBeatmap.HitObjects;
|
||||
|
||||
BeatmapInfo.Ruleset = ruleset;
|
||||
BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata;
|
||||
BeatmapInfo.BeatmapSet.Beatmaps = new List<BeatmapInfo> { BeatmapInfo };
|
||||
BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo
|
||||
{
|
||||
Status = BeatmapSetOnlineStatus.Ranked,
|
||||
Covers = new BeatmapSetOnlineCovers
|
||||
{
|
||||
Cover = "https://assets.ppy.sh/beatmaps/163112/covers/cover.jpg",
|
||||
Card = "https://assets.ppy.sh/beatmaps/163112/covers/card.jpg",
|
||||
List = "https://assets.ppy.sh/beatmaps/163112/covers/list.jpg"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Beatmap createTestBeatmap()
|
||||
|
||||
@@ -230,6 +230,8 @@
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTLINE_TYPE_PARAMETER_LIST/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">NEXT_LINE</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INITIALIZER_BRACES/@EntryValue">NEXT_LINE</s:String>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/LINE_FEED_AT_FILE_END/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
|
||||
Reference in New Issue
Block a user