mirror of
https://github.com/ppy/osu.git
synced 2025-01-26 22:23:22 +08:00
Merge remote-tracking branch 'upstream/master' into ekrctb-fix-disable-mouse
This commit is contained in:
commit
96ef526474
30
.vscode/launch.json
vendored
30
.vscode/launch.json
vendored
@ -11,7 +11,11 @@
|
|||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build tests (Debug)",
|
"preLaunchTask": "Build tests (Debug)",
|
||||||
"env": {},
|
"linux": {
|
||||||
|
"env": {
|
||||||
|
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
|
||||||
|
}
|
||||||
|
},
|
||||||
"console": "internalConsole"
|
"console": "internalConsole"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -24,7 +28,11 @@
|
|||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build tests (Release)",
|
"preLaunchTask": "Build tests (Release)",
|
||||||
"env": {},
|
"linux": {
|
||||||
|
"env": {
|
||||||
|
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
|
||||||
|
}
|
||||||
|
},
|
||||||
"console": "internalConsole"
|
"console": "internalConsole"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -33,11 +41,15 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll",
|
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1/osu!.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build osu! (Debug)",
|
"preLaunchTask": "Build osu! (Debug)",
|
||||||
"env": {},
|
"linux": {
|
||||||
|
"env": {
|
||||||
|
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
|
||||||
|
}
|
||||||
|
},
|
||||||
"console": "internalConsole"
|
"console": "internalConsole"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -46,12 +58,16 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "dotnet",
|
"program": "dotnet",
|
||||||
"args": [
|
"args": [
|
||||||
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1/osu!.dll",
|
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1/osu!.dll"
|
||||||
],
|
],
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"preLaunchTask": "Build osu! (Release)",
|
"preLaunchTask": "Build osu! (Release)",
|
||||||
"env": {},
|
"linux": {
|
||||||
|
"env": {
|
||||||
|
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.1:${env:LD_LIBRARY_PATH}"
|
||||||
|
}
|
||||||
|
},
|
||||||
"console": "internalConsole"
|
"console": "internalConsole"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
26
README.md
26
README.md
@ -1,18 +1,32 @@
|
|||||||
# osu! [![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy)
|
# osu! [![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](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!
|
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 is still heavily under development and is not intended for end-user use. This repository is intended for developer collaboration. You're welcome to try and use it but please do not submit bug reports without a patch. Please do not ask for help building or using this software.
|
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 that can compile .NET 4.7.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here.
|
- A desktop platform with the [.NET Core SDK 2.1](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).
|
||||||
|
|
||||||
# Getting Started
|
# Building and running
|
||||||
- Clone the repository including submodules (`git clone --recurse-submodules https://github.com/ppy/osu`)
|
|
||||||
- Build in your IDE of choice (recommended IDEs automatically restore nuget packages; if you are using an alternative make sure to `nuget restore`)
|
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).
|
||||||
|
|
||||||
|
Clone the repository including submodules
|
||||||
|
|
||||||
|
`git clone --recurse-submodules https://github.com/ppy/osu`
|
||||||
|
|
||||||
|
Build and run
|
||||||
|
|
||||||
|
- Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included)
|
||||||
|
- From command line using `dotnet run --project osu.Desktop --framework netcoreapp2.1`
|
||||||
|
|
||||||
|
The above methods should automatically do so, but if you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`).
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ using osu.Framework.Platform;
|
|||||||
using osu.Game;
|
using osu.Game;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
using osu.Desktop.Updater;
|
||||||
using osu.Framework.Platform.Windows;
|
using osu.Framework.Platform.Windows;
|
||||||
|
|
||||||
namespace osu.Desktop
|
namespace osu.Desktop
|
||||||
@ -38,6 +39,52 @@ namespace osu.Desktop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
if (!noVersionOverlay)
|
||||||
|
{
|
||||||
|
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
|
||||||
|
{
|
||||||
|
Add(v);
|
||||||
|
v.State = Visibility.Visible;
|
||||||
|
});
|
||||||
|
|
||||||
|
#if NET_FRAMEWORK
|
||||||
|
Add(new SquirrelUpdateManager());
|
||||||
|
#else
|
||||||
|
Add(new SimpleUpdateManager());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetHost(GameHost host)
|
||||||
|
{
|
||||||
|
base.SetHost(host);
|
||||||
|
var desktopWindow = host.Window as DesktopGameWindow;
|
||||||
|
if (desktopWindow != null)
|
||||||
|
{
|
||||||
|
desktopWindow.CursorState |= CursorState.Hidden;
|
||||||
|
|
||||||
|
desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"));
|
||||||
|
desktopWindow.Title = Name;
|
||||||
|
|
||||||
|
desktopWindow.FileDrop += fileDrop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fileDrop(object sender, FileDropEventArgs e)
|
||||||
|
{
|
||||||
|
var filePaths = new[] { e.FileName };
|
||||||
|
|
||||||
|
var firstExtension = Path.GetExtension(filePaths.First());
|
||||||
|
|
||||||
|
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
|
||||||
|
|
||||||
|
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A method of accessing an osu-stable install in a controlled fashion.
|
/// A method of accessing an osu-stable install in a controlled fashion.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -77,45 +124,5 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
if (!noVersionOverlay)
|
|
||||||
{
|
|
||||||
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
|
|
||||||
{
|
|
||||||
Add(v);
|
|
||||||
v.State = Visibility.Visible;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetHost(GameHost host)
|
|
||||||
{
|
|
||||||
base.SetHost(host);
|
|
||||||
var desktopWindow = host.Window as DesktopGameWindow;
|
|
||||||
if (desktopWindow != null)
|
|
||||||
{
|
|
||||||
desktopWindow.CursorState |= CursorState.Hidden;
|
|
||||||
|
|
||||||
desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"));
|
|
||||||
desktopWindow.Title = Name;
|
|
||||||
|
|
||||||
desktopWindow.FileDrop += fileDrop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fileDrop(object sender, FileDropEventArgs e)
|
|
||||||
{
|
|
||||||
var filePaths = new[] { e.FileName };
|
|
||||||
|
|
||||||
var firstExtension = Path.GetExtension(filePaths.First());
|
|
||||||
|
|
||||||
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
|
|
||||||
|
|
||||||
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,10 +91,6 @@ namespace osu.Desktop.Overlays
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#if NET_FRAMEWORK
|
|
||||||
Add(new SquirrelUpdateManager());
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.IPC;
|
using osu.Game.IPC;
|
||||||
|
|
||||||
#if NET_FRAMEWORK
|
#if NET_FRAMEWORK
|
||||||
using System.Runtime;
|
using System.Runtime;
|
||||||
#endif
|
#endif
|
||||||
@ -18,10 +19,7 @@ namespace osu.Desktop
|
|||||||
[STAThread]
|
[STAThread]
|
||||||
public static int Main(string[] args)
|
public static int Main(string[] args)
|
||||||
{
|
{
|
||||||
// required to initialise native SQLite libraries on some platforms.
|
useMultiCoreJit();
|
||||||
|
|
||||||
if (!RuntimeInfo.IsMono)
|
|
||||||
useMulticoreJit();
|
|
||||||
|
|
||||||
// Back up the cwd before DesktopGameHost changes it
|
// Back up the cwd before DesktopGameHost changes it
|
||||||
var cwd = Environment.CurrentDirectory;
|
var cwd = Environment.CurrentDirectory;
|
||||||
@ -54,7 +52,7 @@ namespace osu.Desktop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void useMulticoreJit()
|
private static void useMultiCoreJit()
|
||||||
{
|
{
|
||||||
#if NET_FRAMEWORK
|
#if NET_FRAMEWORK
|
||||||
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
|
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
|
||||||
|
103
osu.Desktop/Updater/SimpleUpdateManager.cs
Normal file
103
osu.Desktop/Updater/SimpleUpdateManager.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using osu.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.IO.Network;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Game;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Notifications;
|
||||||
|
|
||||||
|
namespace osu.Desktop.Updater
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An update manager that shows notifications if a newer release is detected.
|
||||||
|
/// Installation is left up to the user.
|
||||||
|
/// </summary>
|
||||||
|
internal class SimpleUpdateManager : CompositeDrawable
|
||||||
|
{
|
||||||
|
private NotificationOverlay notificationOverlay;
|
||||||
|
private string version;
|
||||||
|
private GameHost host;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(NotificationOverlay notification, OsuGameBase game, GameHost host)
|
||||||
|
{
|
||||||
|
notificationOverlay = notification;
|
||||||
|
|
||||||
|
this.host = host;
|
||||||
|
version = game.Version;
|
||||||
|
|
||||||
|
if (game.IsDeployedBuild)
|
||||||
|
Schedule(() => Task.Run(() => checkForUpdateAsync()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void checkForUpdateAsync()
|
||||||
|
{
|
||||||
|
var releases = new JsonWebRequest<GitHubRelease>("https://api.github.com/repos/ppy/osu/releases/latest");
|
||||||
|
await releases.PerformAsync();
|
||||||
|
|
||||||
|
var latest = releases.ResponseObject;
|
||||||
|
|
||||||
|
if (latest.TagName != version)
|
||||||
|
{
|
||||||
|
notificationOverlay.Post(new SimpleNotification
|
||||||
|
{
|
||||||
|
Text = $"A newer release of osu! has been found ({version} → {latest.TagName}).\n\n"
|
||||||
|
+ "Click here to download the new version, which can be installed over the top of your existing installation",
|
||||||
|
Icon = FontAwesome.fa_upload,
|
||||||
|
Activated = () =>
|
||||||
|
{
|
||||||
|
host.OpenUrlExternally(getBestUrl(latest));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string getBestUrl(GitHubRelease release)
|
||||||
|
{
|
||||||
|
GitHubAsset bestAsset = null;
|
||||||
|
|
||||||
|
switch (RuntimeInfo.OS)
|
||||||
|
{
|
||||||
|
case RuntimeInfo.Platform.Windows:
|
||||||
|
bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".exe"));
|
||||||
|
break;
|
||||||
|
case RuntimeInfo.Platform.MacOsx:
|
||||||
|
bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".dmg"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestAsset?.BrowserDownloadUrl ?? release.HtmlUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GitHubRelease
|
||||||
|
{
|
||||||
|
[JsonProperty("html_url")]
|
||||||
|
public string HtmlUrl { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("tag_name")]
|
||||||
|
public string TagName { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("assets")]
|
||||||
|
public List<GitHubAsset> Assets { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GitHubAsset
|
||||||
|
{
|
||||||
|
[JsonProperty("name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("browser_download_url")]
|
||||||
|
public string BrowserDownloadUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#if NET_FRAMEWORK
|
#if NET_FRAMEWORK
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
@ -16,7 +17,7 @@ using OpenTK;
|
|||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using Squirrel;
|
using Squirrel;
|
||||||
|
|
||||||
namespace osu.Desktop.Overlays
|
namespace osu.Desktop.Updater
|
||||||
{
|
{
|
||||||
public class SquirrelUpdateManager : Component
|
public class SquirrelUpdateManager : Component
|
||||||
{
|
{
|
||||||
@ -35,7 +36,7 @@ namespace osu.Desktop.Overlays
|
|||||||
notificationOverlay = notification;
|
notificationOverlay = notification;
|
||||||
|
|
||||||
if (game.IsDeployedBuild)
|
if (game.IsDeployedBuild)
|
||||||
Schedule(() => checkForUpdateAsync());
|
Schedule(() => Task.Run(() => checkForUpdateAsync()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
|
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
|
@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperdashState(status ? 2 : 1);
|
public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperDashState(status ? 2 : 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,9 @@ using osu.Game.Rulesets.Catch.Objects;
|
|||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
|
public class TestCaseHyperDash : Game.Tests.Visual.TestCasePlayer
|
||||||
{
|
{
|
||||||
public TestCaseHyperdash()
|
public TestCaseHyperDash()
|
||||||
: base(new CatchRuleset())
|
: base(new CatchRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
@ -61,12 +61,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
return new CatchDifficultyAttributes(mods, 0);
|
return new CatchDifficultyAttributes(mods, 0);
|
||||||
|
|
||||||
// this is the same as osu!, so there's potential to share the implementation... maybe
|
// this is the same as osu!, so there's potential to share the implementation... maybe
|
||||||
double preEmpt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
|
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
|
||||||
double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor;
|
double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor;
|
||||||
|
|
||||||
return new CatchDifficultyAttributes(mods, starRating)
|
return new CatchDifficultyAttributes(mods, starRating)
|
||||||
{
|
{
|
||||||
ApproachRate = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0,
|
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
|
||||||
MaxCombo = difficultyHitObjects.Count
|
MaxCombo = difficultyHitObjects.Count
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -255,11 +255,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
double positionDifference = target.X * CatchPlayfield.BASE_WIDTH - catcherPosition;
|
double positionDifference = target.X * CatchPlayfield.BASE_WIDTH - catcherPosition;
|
||||||
double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
|
double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
|
||||||
|
|
||||||
SetHyperdashState(Math.Abs(velocity), target.X);
|
SetHyperDashState(Math.Abs(velocity), target.X);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetHyperdashState();
|
SetHyperDashState();
|
||||||
}
|
}
|
||||||
|
|
||||||
return validCatch;
|
return validCatch;
|
||||||
@ -270,18 +270,18 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
private float hyperDashTargetPosition;
|
private float hyperDashTargetPosition;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether we are hypderdashing or not.
|
/// Whether we are hyper-dashing or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HyperDashing => hyperDashModifier != 1;
|
public bool HyperDashing => hyperDashModifier != 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set hyperdash state.
|
/// Set hyper-dash state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="modifier">The speed multiplier. If this is less or equals to 1, this catcher will be non-hyperdashing state.</param>
|
/// <param name="modifier">The speed multiplier. If this is less or equals to 1, this catcher will be non-hyper-dashing state.</param>
|
||||||
/// <param name="targetPosition">When this catcher crosses this position, this catcher ends hyperdashing.</param>
|
/// <param name="targetPosition">When this catcher crosses this position, this catcher ends hyper-dashing.</param>
|
||||||
public void SetHyperdashState(double modifier = 1, float targetPosition = -1)
|
public void SetHyperDashState(double modifier = 1, float targetPosition = -1)
|
||||||
{
|
{
|
||||||
const float hyperdash_transition_length = 180;
|
const float hyper_dash_transition_length = 180;
|
||||||
|
|
||||||
bool previouslyHyperDashing = HyperDashing;
|
bool previouslyHyperDashing = HyperDashing;
|
||||||
if (modifier <= 1 || X == targetPosition)
|
if (modifier <= 1 || X == targetPosition)
|
||||||
@ -291,8 +291,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
if (previouslyHyperDashing)
|
if (previouslyHyperDashing)
|
||||||
{
|
{
|
||||||
this.FadeColour(Color4.White, hyperdash_transition_length, Easing.OutQuint);
|
this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint);
|
||||||
this.FadeTo(1, hyperdash_transition_length, Easing.OutQuint);
|
this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -303,8 +303,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
if (!previouslyHyperDashing)
|
if (!previouslyHyperDashing)
|
||||||
{
|
{
|
||||||
this.FadeColour(Color4.OrangeRed, hyperdash_transition_length, Easing.OutQuint);
|
this.FadeColour(Color4.OrangeRed, hyper_dash_transition_length, Easing.OutQuint);
|
||||||
this.FadeTo(0.2f, hyperdash_transition_length, Easing.OutQuint);
|
this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint);
|
||||||
Trail = true;
|
Trail = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -370,7 +370,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
hyperDashDirection < 0 && hyperDashTargetPosition > X)
|
hyperDashDirection < 0 && hyperDashTargetPosition > X)
|
||||||
{
|
{
|
||||||
X = hyperDashTargetPosition;
|
X = hyperDashTargetPosition;
|
||||||
SetHyperdashState();
|
SetHyperDashState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
@ -13,11 +15,10 @@ using osu.Game.Tests.Beatmaps;
|
|||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ManiaConvertMapping, ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||||
|
|
||||||
[NonParallelizable]
|
|
||||||
[TestCase("basic")]
|
[TestCase("basic")]
|
||||||
public new void Test(string name)
|
public new void Test(string name)
|
||||||
{
|
{
|
||||||
@ -34,9 +35,35 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ManiaConvertMapping CreateConvertMapping() => new ManiaConvertMapping(Converter);
|
||||||
|
|
||||||
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ManiaConvertMapping : ConvertMapping<ConvertValue>, IEquatable<ManiaConvertMapping>
|
||||||
|
{
|
||||||
|
public uint RandomW;
|
||||||
|
public uint RandomX;
|
||||||
|
public uint RandomY;
|
||||||
|
public uint RandomZ;
|
||||||
|
|
||||||
|
public ManiaConvertMapping()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManiaConvertMapping(IBeatmapConverter converter)
|
||||||
|
{
|
||||||
|
var maniaConverter = (ManiaBeatmapConverter)converter;
|
||||||
|
RandomW = maniaConverter.Random.W;
|
||||||
|
RandomX = maniaConverter.Random.X;
|
||||||
|
RandomY = maniaConverter.Random.Y;
|
||||||
|
RandomZ = maniaConverter.Random.Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(ManiaConvertMapping other) => other != null && RandomW == other.RandomW && RandomX == other.RandomX && RandomY == other.RandomY && RandomZ == other.RandomZ;
|
||||||
|
public override bool Equals(ConvertMapping<ConvertValue> other) => base.Equals(other) && Equals(other as ManiaConvertMapping);
|
||||||
|
}
|
||||||
|
|
||||||
public struct ConvertValue : IEquatable<ConvertValue>
|
public struct ConvertValue : IEquatable<ConvertValue>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -5,6 +5,7 @@ using osu.Game.Rulesets.Mania.Objects;
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
@ -28,8 +29,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
public int TargetColumns;
|
public int TargetColumns;
|
||||||
public readonly bool IsForCurrentRuleset;
|
public readonly bool IsForCurrentRuleset;
|
||||||
|
|
||||||
|
// Internal for testing purposes
|
||||||
|
internal FastRandom Random { get; private set; }
|
||||||
|
|
||||||
private Pattern lastPattern = new Pattern();
|
private Pattern lastPattern = new Pattern();
|
||||||
private FastRandom random;
|
|
||||||
|
|
||||||
private ManiaBeatmap beatmap;
|
private ManiaBeatmap beatmap;
|
||||||
|
|
||||||
@ -62,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
|
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
|
||||||
|
|
||||||
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
|
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
|
||||||
random = new FastRandom(seed);
|
Random = new FastRandom(seed);
|
||||||
|
|
||||||
return base.ConvertBeatmap(original);
|
return base.ConvertBeatmap(original);
|
||||||
}
|
}
|
||||||
@ -100,7 +103,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
private double lastTime;
|
private double lastTime;
|
||||||
private Vector2 lastPosition;
|
private Vector2 lastPosition;
|
||||||
private PatternType lastStair;
|
private PatternType lastStair = PatternType.Stair;
|
||||||
private void recordNote(double time, Vector2 position)
|
private void recordNote(double time, Vector2 position)
|
||||||
{
|
{
|
||||||
lastTime = time;
|
lastTime = time;
|
||||||
@ -115,12 +118,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
/// <returns>The hit objects generated.</returns>
|
/// <returns>The hit objects generated.</returns>
|
||||||
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, IBeatmap originalBeatmap)
|
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, IBeatmap originalBeatmap)
|
||||||
{
|
{
|
||||||
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
|
var generator = new SpecificBeatmapPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap);
|
||||||
|
|
||||||
Pattern newPattern = generator.Generate();
|
foreach (var newPattern in generator.Generate())
|
||||||
lastPattern = newPattern;
|
{
|
||||||
|
lastPattern = newPattern;
|
||||||
|
|
||||||
return newPattern.HitObjects;
|
foreach (var obj in newPattern.HitObjects)
|
||||||
|
yield return obj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -138,27 +144,44 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
Patterns.PatternGenerator conversion = null;
|
Patterns.PatternGenerator conversion = null;
|
||||||
|
|
||||||
if (distanceData != null)
|
if (distanceData != null)
|
||||||
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
|
{
|
||||||
|
var generator = new DistanceObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap);
|
||||||
|
conversion = generator;
|
||||||
|
|
||||||
|
for (double time = original.StartTime; !Precision.DefinitelyBigger(time, generator.EndTime); time += generator.SegmentDuration)
|
||||||
|
{
|
||||||
|
recordNote(time, positionData?.Position ?? Vector2.Zero);
|
||||||
|
computeDensity(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (endTimeData != null)
|
else if (endTimeData != null)
|
||||||
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap);
|
{
|
||||||
|
conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap);
|
||||||
|
|
||||||
|
recordNote(endTimeData.EndTime, new Vector2(256, 192));
|
||||||
|
computeDensity(endTimeData.EndTime);
|
||||||
|
}
|
||||||
else if (positionData != null)
|
else if (positionData != null)
|
||||||
{
|
{
|
||||||
computeDensity(original.StartTime);
|
computeDensity(original.StartTime);
|
||||||
|
|
||||||
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
|
conversion = new HitObjectPatternGenerator(Random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
|
||||||
|
|
||||||
recordNote(original.StartTime, positionData.Position);
|
recordNote(original.StartTime, positionData.Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversion == null)
|
if (conversion == null)
|
||||||
return null;
|
yield break;
|
||||||
|
|
||||||
Pattern newPattern = conversion.Generate();
|
foreach (var newPattern in conversion.Generate())
|
||||||
|
{
|
||||||
|
lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
|
||||||
|
lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
|
||||||
|
|
||||||
lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
|
foreach (var obj in newPattern.HitObjects)
|
||||||
lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
|
yield return obj;
|
||||||
|
|
||||||
return newPattern.HitObjects;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -171,7 +194,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Pattern Generate()
|
public override IEnumerable<Pattern> Generate()
|
||||||
|
{
|
||||||
|
yield return generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern generate()
|
||||||
{
|
{
|
||||||
var endTimeData = HitObject as IHasEndTime;
|
var endTimeData = HitObject as IHasEndTime;
|
||||||
var positionData = HitObject as IHasXPosition;
|
var positionData = HitObject as IHasXPosition;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.MathUtils;
|
using osu.Game.Rulesets.Mania.MathUtils;
|
||||||
@ -24,8 +25,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const float osu_base_scoring_distance = 100;
|
private const float osu_base_scoring_distance = 100;
|
||||||
|
|
||||||
private readonly double endTime;
|
public readonly double EndTime;
|
||||||
private readonly double segmentDuration;
|
public readonly double SegmentDuration;
|
||||||
|
|
||||||
private readonly int spanCount;
|
private readonly int spanCount;
|
||||||
|
|
||||||
private PatternType convertType;
|
private PatternType convertType;
|
||||||
@ -52,53 +54,81 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
// The duration of the osu! hit object
|
// The duration of the osu! hit object
|
||||||
double osuDuration = distance / osuVelocity;
|
double osuDuration = distance / osuVelocity;
|
||||||
|
|
||||||
endTime = hitObject.StartTime + osuDuration;
|
EndTime = hitObject.StartTime + osuDuration;
|
||||||
segmentDuration = (endTime - HitObject.StartTime) / spanCount;
|
SegmentDuration = (EndTime - HitObject.StartTime) / spanCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Pattern Generate()
|
public override IEnumerable<Pattern> Generate()
|
||||||
|
{
|
||||||
|
var originalPattern = generate();
|
||||||
|
|
||||||
|
if (originalPattern.HitObjects.Count() == 1)
|
||||||
|
{
|
||||||
|
yield return originalPattern;
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to split the intermediate pattern into two new patterns:
|
||||||
|
// 1. A pattern containing all objects that do not end at our EndTime.
|
||||||
|
// 2. A pattern containing all objects that end at our EndTime. This will be used for further pattern generation.
|
||||||
|
var intermediatePattern = new Pattern();
|
||||||
|
var endTimePattern = new Pattern();
|
||||||
|
|
||||||
|
foreach (var obj in originalPattern.HitObjects)
|
||||||
|
{
|
||||||
|
if (!Precision.AlmostEquals(EndTime, (obj as IHasEndTime)?.EndTime ?? obj.StartTime))
|
||||||
|
intermediatePattern.Add(obj);
|
||||||
|
else
|
||||||
|
endTimePattern.Add(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return intermediatePattern;
|
||||||
|
yield return endTimePattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern generate()
|
||||||
{
|
{
|
||||||
if (TotalColumns == 1)
|
if (TotalColumns == 1)
|
||||||
{
|
{
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
addToPattern(pattern, 0, HitObject.StartTime, endTime);
|
addToPattern(pattern, 0, HitObject.StartTime, EndTime);
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spanCount > 1)
|
if (spanCount > 1)
|
||||||
{
|
{
|
||||||
if (segmentDuration <= 90)
|
if (SegmentDuration <= 90)
|
||||||
return generateRandomHoldNotes(HitObject.StartTime, 1);
|
return generateRandomHoldNotes(HitObject.StartTime, 1);
|
||||||
|
|
||||||
if (segmentDuration <= 120)
|
if (SegmentDuration <= 120)
|
||||||
{
|
{
|
||||||
convertType |= PatternType.ForceNotStack;
|
convertType |= PatternType.ForceNotStack;
|
||||||
return generateRandomNotes(HitObject.StartTime, spanCount + 1);
|
return generateRandomNotes(HitObject.StartTime, spanCount + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segmentDuration <= 160)
|
if (SegmentDuration <= 160)
|
||||||
return generateStair(HitObject.StartTime);
|
return generateStair(HitObject.StartTime);
|
||||||
|
|
||||||
if (segmentDuration <= 200 && ConversionDifficulty > 3)
|
if (SegmentDuration <= 200 && ConversionDifficulty > 3)
|
||||||
return generateRandomMultipleNotes(HitObject.StartTime);
|
return generateRandomMultipleNotes(HitObject.StartTime);
|
||||||
|
|
||||||
double duration = endTime - HitObject.StartTime;
|
double duration = EndTime - HitObject.StartTime;
|
||||||
if (duration >= 4000)
|
if (duration >= 4000)
|
||||||
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
|
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
|
||||||
|
|
||||||
if (segmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
|
if (SegmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
|
||||||
return generateTiledHoldNotes(HitObject.StartTime);
|
return generateTiledHoldNotes(HitObject.StartTime);
|
||||||
|
|
||||||
return generateHoldAndNormalNotes(HitObject.StartTime);
|
return generateHoldAndNormalNotes(HitObject.StartTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segmentDuration <= 110)
|
if (SegmentDuration <= 110)
|
||||||
{
|
{
|
||||||
if (PreviousPattern.ColumnWithObjects < TotalColumns)
|
if (PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||||
convertType |= PatternType.ForceNotStack;
|
convertType |= PatternType.ForceNotStack;
|
||||||
else
|
else
|
||||||
convertType &= ~PatternType.ForceNotStack;
|
convertType &= ~PatternType.ForceNotStack;
|
||||||
return generateRandomNotes(HitObject.StartTime, segmentDuration < 80 ? 1 : 2);
|
return generateRandomNotes(HitObject.StartTime, SegmentDuration < 80 ? 1 : 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 6.5)
|
||||||
@ -148,7 +178,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
{
|
{
|
||||||
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
|
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
addToPattern(pattern, nextColumn, startTime, endTime);
|
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is can't be combined with the above loop due to RNG
|
// This is can't be combined with the above loop due to RNG
|
||||||
@ -156,7 +186,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
{
|
{
|
||||||
while (pattern.ColumnHasObject(nextColumn))
|
while (pattern.ColumnHasObject(nextColumn))
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
addToPattern(pattern, nextColumn, startTime, endTime);
|
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
@ -193,7 +223,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
|
|
||||||
lastColumn = nextColumn;
|
lastColumn = nextColumn;
|
||||||
startTime += segmentDuration;
|
startTime += SegmentDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
@ -223,7 +253,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
for (int i = 0; i <= spanCount; i++)
|
for (int i = 0; i <= spanCount; i++)
|
||||||
{
|
{
|
||||||
addToPattern(pattern, column, startTime, startTime);
|
addToPattern(pattern, column, startTime, startTime);
|
||||||
startTime += segmentDuration;
|
startTime += SegmentDuration;
|
||||||
|
|
||||||
// Check if we're at the borders of the stage, and invert the pattern if so
|
// Check if we're at the borders of the stage, and invert the pattern if so
|
||||||
if (increasing)
|
if (increasing)
|
||||||
@ -284,7 +314,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
addToPattern(pattern, nextColumn, startTime, startTime);
|
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||||
|
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
startTime += segmentDuration;
|
startTime += SegmentDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
@ -372,8 +402,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
while (pattern.ColumnHasObject(nextColumn))
|
while (pattern.ColumnHasObject(nextColumn))
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
|
|
||||||
addToPattern(pattern, nextColumn, startTime, endTime);
|
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||||
startTime += segmentDuration;
|
startTime += SegmentDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
@ -402,7 +432,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the hold note
|
// Create the hold note
|
||||||
addToPattern(pattern, holdColumn, startTime, endTime);
|
addToPattern(pattern, holdColumn, startTime, EndTime);
|
||||||
|
|
||||||
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
int noteCount;
|
int noteCount;
|
||||||
@ -434,7 +464,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
pattern.Add(rowPattern);
|
pattern.Add(rowPattern);
|
||||||
rowPattern.Clear();
|
rowPattern.Clear();
|
||||||
|
|
||||||
startTime += segmentDuration;
|
startTime += SegmentDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
@ -452,7 +482,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
if (curveData == null)
|
if (curveData == null)
|
||||||
return HitObject.Samples;
|
return HitObject.Samples;
|
||||||
|
|
||||||
double segmentTime = (endTime - HitObject.StartTime) / spanCount;
|
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
|
||||||
|
|
||||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||||
return curveData.RepeatSamples[index];
|
return curveData.RepeatSamples[index];
|
||||||
|
@ -19,12 +19,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap)
|
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap)
|
||||||
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
|
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
|
||||||
{
|
{
|
||||||
var endtimeData = HitObject as IHasEndTime;
|
endTime = (HitObject as IHasEndTime)?.EndTime ?? 0;
|
||||||
|
|
||||||
endTime = endtimeData?.EndTime ?? 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Pattern Generate()
|
public override IEnumerable<Pattern> Generate()
|
||||||
|
{
|
||||||
|
yield return generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern generate()
|
||||||
{
|
{
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
@ -82,127 +83,133 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
{
|
{
|
||||||
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
|
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
|
||||||
convertType |= PatternType.Mirror;
|
convertType |= PatternType.Mirror;
|
||||||
else
|
else if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP))
|
||||||
convertType |= PatternType.Gathered;
|
convertType |= PatternType.Gathered;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Pattern Generate()
|
public override IEnumerable<Pattern> Generate()
|
||||||
{
|
{
|
||||||
if (TotalColumns == 1)
|
yield return generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pattern generate()
|
||||||
|
{
|
||||||
|
var pattern = new Pattern();
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var pattern = new Pattern();
|
if (TotalColumns == 1)
|
||||||
addToPattern(pattern, 0);
|
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
|
|
||||||
|
|
||||||
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
|
|
||||||
{
|
|
||||||
// Generate a new pattern by copying the last hit objects in reverse-column order
|
|
||||||
var pattern = new Pattern();
|
|
||||||
|
|
||||||
for (int i = RandomStart; i < TotalColumns; i++)
|
|
||||||
if (PreviousPattern.ColumnHasObject(i))
|
|
||||||
addToPattern(pattern, RandomStart + TotalColumns - i - 1);
|
|
||||||
|
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1
|
|
||||||
// If we convert to 7K + 1, let's not overload the special key
|
|
||||||
&& (TotalColumns != 8 || lastColumn != 0)
|
|
||||||
// Make sure the last column was not the centre column
|
|
||||||
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
|
||||||
{
|
|
||||||
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
|
||||||
var pattern = new Pattern();
|
|
||||||
|
|
||||||
int column = RandomStart + TotalColumns - lastColumn - 1;
|
|
||||||
addToPattern(pattern, column);
|
|
||||||
|
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any())
|
|
||||||
{
|
|
||||||
// Generate a new pattern by placing on the already filled columns
|
|
||||||
var pattern = new Pattern();
|
|
||||||
|
|
||||||
for (int i = RandomStart; i < TotalColumns; i++)
|
|
||||||
if (PreviousPattern.ColumnHasObject(i))
|
|
||||||
addToPattern(pattern, i);
|
|
||||||
|
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((convertType & PatternType.Stair) > 0 && PreviousPattern.HitObjects.Count() == 1)
|
|
||||||
{
|
|
||||||
// Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
|
|
||||||
var pattern = new Pattern();
|
|
||||||
|
|
||||||
int targetColumn = lastColumn + 1;
|
|
||||||
if (targetColumn == TotalColumns)
|
|
||||||
{
|
{
|
||||||
targetColumn = RandomStart;
|
addToPattern(pattern, 0);
|
||||||
StairType = PatternType.ReverseStair;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
addToPattern(pattern, targetColumn);
|
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((convertType & PatternType.ReverseStair) > 0 && PreviousPattern.HitObjects.Count() == 1)
|
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
|
||||||
{
|
|
||||||
// Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
|
|
||||||
var pattern = new Pattern();
|
|
||||||
|
|
||||||
int targetColumn = lastColumn - 1;
|
|
||||||
if (targetColumn == RandomStart - 1)
|
|
||||||
{
|
{
|
||||||
targetColumn = TotalColumns - 1;
|
// Generate a new pattern by copying the last hit objects in reverse-column order
|
||||||
StairType = PatternType.Stair;
|
for (int i = RandomStart; i < TotalColumns; i++)
|
||||||
|
if (PreviousPattern.ColumnHasObject(i))
|
||||||
|
addToPattern(pattern, RandomStart + TotalColumns - i - 1);
|
||||||
|
|
||||||
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
addToPattern(pattern, targetColumn);
|
if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1
|
||||||
return pattern;
|
// If we convert to 7K + 1, let's not overload the special key
|
||||||
}
|
&& (TotalColumns != 8 || lastColumn != 0)
|
||||||
|
// Make sure the last column was not the centre column
|
||||||
|
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
||||||
|
{
|
||||||
|
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
||||||
|
int column = RandomStart + TotalColumns - lastColumn - 1;
|
||||||
|
addToPattern(pattern, column);
|
||||||
|
|
||||||
if ((convertType & PatternType.KeepSingle) > 0)
|
return pattern;
|
||||||
return generateRandomNotes(1);
|
}
|
||||||
|
|
||||||
|
if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any())
|
||||||
|
{
|
||||||
|
// Generate a new pattern by placing on the already filled columns
|
||||||
|
for (int i = RandomStart; i < TotalColumns; i++)
|
||||||
|
if (PreviousPattern.ColumnHasObject(i))
|
||||||
|
addToPattern(pattern, i);
|
||||||
|
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PreviousPattern.HitObjects.Count() == 1)
|
||||||
|
{
|
||||||
|
if ((convertType & PatternType.Stair) > 0)
|
||||||
|
{
|
||||||
|
// Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
|
||||||
|
int targetColumn = lastColumn + 1;
|
||||||
|
if (targetColumn == TotalColumns)
|
||||||
|
targetColumn = RandomStart;
|
||||||
|
|
||||||
|
addToPattern(pattern, targetColumn);
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((convertType & PatternType.ReverseStair) > 0)
|
||||||
|
{
|
||||||
|
// Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
|
||||||
|
int targetColumn = lastColumn - 1;
|
||||||
|
if (targetColumn == RandomStart - 1)
|
||||||
|
targetColumn = TotalColumns - 1;
|
||||||
|
|
||||||
|
addToPattern(pattern, targetColumn);
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((convertType & PatternType.KeepSingle) > 0)
|
||||||
|
return pattern = generateRandomNotes(1);
|
||||||
|
|
||||||
|
if ((convertType & PatternType.Mirror) > 0)
|
||||||
|
{
|
||||||
|
if (ConversionDifficulty > 6.5)
|
||||||
|
return pattern = generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
||||||
|
if (ConversionDifficulty > 4)
|
||||||
|
return pattern = generateRandomPatternWithMirrored(0.12, 0.17, 0);
|
||||||
|
return pattern = generateRandomPatternWithMirrored(0.12, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if ((convertType & PatternType.Mirror) > 0)
|
|
||||||
{
|
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 6.5)
|
||||||
return generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
{
|
||||||
|
if ((convertType & PatternType.LowProbability) > 0)
|
||||||
|
return pattern = generateRandomPattern(0.78, 0.42, 0, 0);
|
||||||
|
return pattern = generateRandomPattern(1, 0.62, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 4)
|
if (ConversionDifficulty > 4)
|
||||||
return generateRandomPatternWithMirrored(0.12, 0.17, 0);
|
{
|
||||||
return generateRandomPatternWithMirrored(0.12, 0, 0);
|
if ((convertType & PatternType.LowProbability) > 0)
|
||||||
}
|
return pattern = generateRandomPattern(0.35, 0.08, 0, 0);
|
||||||
|
return pattern = generateRandomPattern(0.52, 0.15, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 2)
|
||||||
|
{
|
||||||
|
if ((convertType & PatternType.LowProbability) > 0)
|
||||||
|
return pattern = generateRandomPattern(0.18, 0, 0, 0);
|
||||||
|
return pattern = generateRandomPattern(0.45, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pattern = generateRandomPattern(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
finally
|
||||||
{
|
{
|
||||||
if ((convertType & PatternType.LowProbability) > 0)
|
foreach (var obj in pattern.HitObjects)
|
||||||
return generateRandomPattern(0.78, 0.42, 0, 0);
|
{
|
||||||
return generateRandomPattern(1, 0.62, 0, 0);
|
if ((convertType & PatternType.Stair) > 0 && obj.Column == TotalColumns - 1)
|
||||||
|
StairType = PatternType.ReverseStair;
|
||||||
|
if ((convertType & PatternType.ReverseStair) > 0 && obj.Column == RandomStart)
|
||||||
|
StairType = PatternType.Stair;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 4)
|
|
||||||
{
|
|
||||||
if ((convertType & PatternType.LowProbability) > 0)
|
|
||||||
return generateRandomPattern(0.35, 0.08, 0, 0);
|
|
||||||
return generateRandomPattern(0.52, 0.15, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ConversionDifficulty > 2)
|
|
||||||
{
|
|
||||||
if ((convertType & PatternType.LowProbability) > 0)
|
|
||||||
return generateRandomPattern(0.18, 0, 0, 0);
|
|
||||||
return generateRandomPattern(0.45, 0, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return generateRandomPattern(0, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -103,17 +103,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault();
|
HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault();
|
||||||
HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault();
|
HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault();
|
||||||
|
|
||||||
double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0);
|
// Drain time in seconds
|
||||||
drainTime -= OriginalBeatmap.TotalBreakTime;
|
int drainTime = (int)(((lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0) - OriginalBeatmap.TotalBreakTime) / 1000);
|
||||||
|
|
||||||
if (drainTime == 0)
|
if (drainTime == 0)
|
||||||
drainTime = 10000000;
|
drainTime = 10000;
|
||||||
|
|
||||||
// We need this in seconds
|
|
||||||
drainTime /= 1000;
|
|
||||||
|
|
||||||
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
|
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
|
||||||
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count() / drainTime * 9f) / 38f * 5f / 1.15;
|
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count() / drainTime * 9f) / 38f * 5f / 1.15;
|
||||||
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
|
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
|
||||||
|
|
||||||
return conversionDifficulty.Value;
|
return conversionDifficulty.Value;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
||||||
@ -42,9 +43,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates the pattern for <see cref="HitObject"/>, filled with hit objects.
|
/// Generates the patterns for <see cref="HitObject"/>, each filled with hit objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/>s containing the hit objects.</returns>
|
||||||
public abstract Pattern Generate();
|
public abstract IEnumerable<Pattern> Generate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,15 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
|||||||
private const uint y = 842502087;
|
private const uint y = 842502087;
|
||||||
private const uint z = 3579807591;
|
private const uint z = 3579807591;
|
||||||
private const uint w = 273326509;
|
private const uint w = 273326509;
|
||||||
private uint _x, _y = y, _z = z, _w = w;
|
|
||||||
|
internal uint X { get; private set; }
|
||||||
|
internal uint Y { get; private set; } = y;
|
||||||
|
internal uint Z { get; private set; } = z;
|
||||||
|
internal uint W { get; private set; } = w;
|
||||||
|
|
||||||
public FastRandom(int seed)
|
public FastRandom(int seed)
|
||||||
{
|
{
|
||||||
_x = (uint)seed;
|
X = (uint)seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FastRandom()
|
public FastRandom()
|
||||||
@ -33,11 +37,11 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
|||||||
/// <returns>The random value.</returns>
|
/// <returns>The random value.</returns>
|
||||||
public uint NextUInt()
|
public uint NextUInt()
|
||||||
{
|
{
|
||||||
uint t = _x ^ _x << 11;
|
uint t = X ^ X << 11;
|
||||||
_x = _y;
|
X = Y;
|
||||||
_y = _z;
|
Y = Z;
|
||||||
_z = _w;
|
Z = W;
|
||||||
return _w = _w ^ _w >> 19 ^ t ^ t >> 8;
|
return W = W ^ W >> 19 ^ t ^ t >> 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,103 +1,132 @@
|
|||||||
{
|
{
|
||||||
"Mappings": [{
|
"Mappings": [{
|
||||||
"StartTime": 500,
|
"RandomW": 2659373485,
|
||||||
"Objects": [{
|
"RandomX": 3579807591,
|
||||||
"StartTime": 500,
|
"RandomY": 273326509,
|
||||||
"EndTime": 2500,
|
"RandomZ": 272969173,
|
||||||
"Column": 0
|
"StartTime": 500.0,
|
||||||
},
|
"Objects": [{
|
||||||
{
|
"StartTime": 500.0,
|
||||||
"StartTime": 1500,
|
"EndTime": 2500.0,
|
||||||
"EndTime": 2500,
|
"Column": 0
|
||||||
"Column": 1
|
}, {
|
||||||
}
|
"StartTime": 1500.0,
|
||||||
]
|
"EndTime": 2500.0,
|
||||||
},
|
"Column": 1
|
||||||
{
|
}]
|
||||||
"StartTime": 3000,
|
}, {
|
||||||
"Objects": [{
|
"RandomW": 3083803045,
|
||||||
"StartTime": 3000,
|
"RandomX": 273326509,
|
||||||
"EndTime": 4000,
|
"RandomY": 272969173,
|
||||||
"Column": 2
|
"RandomZ": 2659373485,
|
||||||
}]
|
"StartTime": 3000.0,
|
||||||
},
|
"Objects": [{
|
||||||
{
|
"StartTime": 3000.0,
|
||||||
"StartTime": 4500,
|
"EndTime": 4000.0,
|
||||||
"Objects": [{
|
"Column": 2
|
||||||
"StartTime": 4500,
|
}]
|
||||||
"EndTime": 5500,
|
}, {
|
||||||
"Column": 4
|
"RandomW": 4073554232,
|
||||||
}]
|
"RandomX": 272969173,
|
||||||
},
|
"RandomY": 2659373485,
|
||||||
{
|
"RandomZ": 3083803045,
|
||||||
"StartTime": 6000,
|
"StartTime": 4500.0,
|
||||||
"Objects": [{
|
"Objects": [{
|
||||||
"StartTime": 6000,
|
"StartTime": 4500.0,
|
||||||
"EndTime": 6500,
|
"EndTime": 5500.0,
|
||||||
"Column": 2
|
"Column": 4
|
||||||
}]
|
}]
|
||||||
},
|
}, {
|
||||||
{
|
"RandomW": 3420401969,
|
||||||
"StartTime": 7000,
|
"RandomX": 2659373485,
|
||||||
"Objects": [{
|
"RandomY": 3083803045,
|
||||||
"StartTime": 7000,
|
"RandomZ": 4073554232,
|
||||||
"EndTime": 8000,
|
"StartTime": 6000.0,
|
||||||
"Column": 2
|
"Objects": [{
|
||||||
}]
|
"StartTime": 6000.0,
|
||||||
},
|
"EndTime": 6500.0,
|
||||||
{
|
"Column": 2
|
||||||
"StartTime": 8500,
|
}]
|
||||||
"Objects": [{
|
}, {
|
||||||
"StartTime": 8500,
|
"RandomW": 1129881182,
|
||||||
"EndTime": 11000,
|
"RandomX": 3083803045,
|
||||||
"Column": 0
|
"RandomY": 4073554232,
|
||||||
}]
|
"RandomZ": 3420401969,
|
||||||
},
|
"StartTime": 7000.0,
|
||||||
{
|
"Objects": [{
|
||||||
"StartTime": 11500,
|
"StartTime": 7000.0,
|
||||||
"Objects": [{
|
"EndTime": 8000.0,
|
||||||
"StartTime": 11500,
|
"Column": 2
|
||||||
"EndTime": 12000,
|
}]
|
||||||
"Column": 1
|
}, {
|
||||||
}]
|
"RandomW": 315568458,
|
||||||
},
|
"RandomX": 3420401969,
|
||||||
{
|
"RandomY": 1129881182,
|
||||||
"StartTime": 12500,
|
"RandomZ": 2358617505,
|
||||||
"Objects": [{
|
"StartTime": 8500.0,
|
||||||
"StartTime": 12500,
|
"Objects": [{
|
||||||
"EndTime": 16500,
|
"StartTime": 8500.0,
|
||||||
"Column": 4
|
"EndTime": 11000.0,
|
||||||
}]
|
"Column": 0
|
||||||
},
|
}]
|
||||||
{
|
}, {
|
||||||
"StartTime": 17000,
|
"RandomW": 548134043,
|
||||||
"Objects": [{
|
"RandomX": 1129881182,
|
||||||
"StartTime": 17000,
|
"RandomY": 2358617505,
|
||||||
"EndTime": 18000,
|
"RandomZ": 315568458,
|
||||||
"Column": 2
|
"StartTime": 11500.0,
|
||||||
}]
|
"Objects": [{
|
||||||
},
|
"StartTime": 11500.0,
|
||||||
{
|
"EndTime": 12000.0,
|
||||||
"StartTime": 18500,
|
"Column": 1
|
||||||
"Objects": [{
|
}]
|
||||||
"StartTime": 18500,
|
}, {
|
||||||
"EndTime": 19450,
|
"RandomW": 3979422122,
|
||||||
"Column": 0
|
"RandomX": 548134043,
|
||||||
}]
|
"RandomY": 2810584254,
|
||||||
},
|
"RandomZ": 2250186050,
|
||||||
{
|
"StartTime": 12500.0,
|
||||||
"StartTime": 19875,
|
"Objects": [{
|
||||||
"Objects": [{
|
"StartTime": 12500.0,
|
||||||
"StartTime": 19875,
|
"EndTime": 16500.0,
|
||||||
"EndTime": 23875,
|
"Column": 4
|
||||||
"Column": 1
|
}]
|
||||||
},
|
}, {
|
||||||
{
|
"RandomW": 2466283411,
|
||||||
"StartTime": 19875,
|
"RandomX": 2810584254,
|
||||||
"EndTime": 23875,
|
"RandomY": 2250186050,
|
||||||
"Column": 0
|
"RandomZ": 3979422122,
|
||||||
}
|
"StartTime": 17000.0,
|
||||||
]
|
"Objects": [{
|
||||||
}
|
"StartTime": 17000.0,
|
||||||
]
|
"EndTime": 18000.0,
|
||||||
|
"Column": 2
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"RandomW": 83157665,
|
||||||
|
"RandomX": 2250186050,
|
||||||
|
"RandomY": 3979422122,
|
||||||
|
"RandomZ": 2466283411,
|
||||||
|
"StartTime": 18500.0,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 18500.0,
|
||||||
|
"EndTime": 19450.0,
|
||||||
|
"Column": 0
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"RandomW": 2383087700,
|
||||||
|
"RandomX": 83157665,
|
||||||
|
"RandomY": 2055150192,
|
||||||
|
"RandomZ": 510071020,
|
||||||
|
"StartTime": 19875.0,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 19875.0,
|
||||||
|
"EndTime": 23875.0,
|
||||||
|
"Column": 1
|
||||||
|
}, {
|
||||||
|
"StartTime": 19875.0,
|
||||||
|
"EndTime": 23875.0,
|
||||||
|
"Column": 0
|
||||||
|
}]
|
||||||
|
}]
|
||||||
}
|
}
|
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
public struct ConvertValue : IEquatable<ConvertValue>
|
public struct ConvertValue : IEquatable<ConvertValue>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A sane value to account for osu!stable using ints everwhere.
|
/// A sane value to account for osu!stable using <see cref="int"/>s everywhere.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const double conversion_lenience = 2;
|
private const double conversion_lenience = 2;
|
||||||
|
|
||||||
|
@ -61,19 +61,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
||||||
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
|
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
|
||||||
|
|
||||||
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
|
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||||
double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate;
|
double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate;
|
||||||
double preEmpt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
|
double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
|
||||||
|
|
||||||
int maxCombo = beatmap.HitObjects.Count();
|
int maxCombo = beatmap.HitObjects.Count();
|
||||||
// Add the ticks + tail of the slider. 1 is subtracted because the "headcircle" would be counted twice (once for the slider itself in the line above)
|
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
|
||||||
maxCombo += beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
|
maxCombo += beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
|
||||||
|
|
||||||
return new OsuDifficultyAttributes(mods, starRating)
|
return new OsuDifficultyAttributes(mods, starRating)
|
||||||
{
|
{
|
||||||
AimStrain = aimRating,
|
AimStrain = aimRating,
|
||||||
SpeedStrain = speedRating,
|
SpeedStrain = speedRating,
|
||||||
ApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5,
|
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
|
||||||
OverallDifficulty = (80 - hitWindowGreat) / 6,
|
OverallDifficulty = (80 - hitWindowGreat) / 6,
|
||||||
MaxCombo = maxCombo
|
MaxCombo = maxCombo
|
||||||
};
|
};
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public override void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
public override void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||||
{
|
{
|
||||||
void adjustFadeIn(OsuHitObject h) => h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier;
|
void adjustFadeIn(OsuHitObject h) => h.TimeFadeIn = h.TimePreempt * fade_in_duration_multiplier;
|
||||||
|
|
||||||
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
|
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
|
||||||
{
|
{
|
||||||
@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
var h = d.HitObject;
|
var h = d.HitObject;
|
||||||
|
|
||||||
var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadein;
|
var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadeIn;
|
||||||
var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier;
|
var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier;
|
||||||
|
|
||||||
// new duration from completed fade in to end (before fading out)
|
// new duration from completed fade in to end (before fading out)
|
||||||
|
@ -96,12 +96,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
|
|
||||||
using (fp.BeginAbsoluteSequence(fadeInTime))
|
using (fp.BeginAbsoluteSequence(fadeInTime))
|
||||||
{
|
{
|
||||||
fp.FadeIn(currHitObject.TimeFadein);
|
fp.FadeIn(currHitObject.TimeFadeIn);
|
||||||
fp.ScaleTo(1, currHitObject.TimeFadein, Easing.Out);
|
fp.ScaleTo(1, currHitObject.TimeFadeIn, Easing.Out);
|
||||||
|
|
||||||
fp.MoveTo(pointEndPosition, currHitObject.TimeFadein, Easing.Out);
|
fp.MoveTo(pointEndPosition, currHitObject.TimeFadeIn, Easing.Out);
|
||||||
|
|
||||||
fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadein);
|
fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadeIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
fp.Expire(true);
|
fp.Expire(true);
|
||||||
|
@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.UpdatePreemptState();
|
base.UpdatePreemptState();
|
||||||
|
|
||||||
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadein * 2, HitObject.TimePreempt));
|
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt));
|
||||||
ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt);
|
ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
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?)null) ?? Color4.White;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein);
|
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn);
|
||||||
|
|
||||||
protected virtual void UpdateCurrentState(ArmedState state)
|
protected virtual void UpdateCurrentState(ArmedState state)
|
||||||
{
|
{
|
||||||
|
@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
|
Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
|
||||||
if (!spmCounter.IsPresent && Disc.Tracking)
|
if (!spmCounter.IsPresent && Disc.Tracking)
|
||||||
spmCounter.FadeIn(HitObject.TimeFadein);
|
spmCounter.FadeIn(HitObject.TimeFadeIn);
|
||||||
|
|
||||||
base.Update();
|
base.Update();
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
var spanProgress = slider.ProgressAt(completionProgress);
|
var spanProgress = slider.ProgressAt(completionProgress);
|
||||||
|
|
||||||
double start = 0;
|
double start = 0;
|
||||||
double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1;
|
double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
|
||||||
|
|
||||||
if (span >= slider.SpanCount() - 1)
|
if (span >= slider.SpanCount() - 1)
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
public event Action<Vector2> PositionChanged;
|
public event Action<Vector2> PositionChanged;
|
||||||
|
|
||||||
public double TimePreempt = 600;
|
public double TimePreempt = 600;
|
||||||
public double TimeFadein = 400;
|
public double TimeFadeIn = 400;
|
||||||
|
|
||||||
private Vector2 position;
|
private Vector2 position;
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
||||||
TimeFadein = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);
|
TimeFadeIn = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);
|
||||||
|
|
||||||
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
// This is so on repeats ticks don't appear too late to be visually processed by the player.
|
// This is so on repeats ticks don't appear too late to be visually processed by the player.
|
||||||
offset = 200;
|
offset = 200;
|
||||||
else
|
else
|
||||||
offset = TimeFadein * 0.66f;
|
offset = TimeFadeIn * 0.66f;
|
||||||
|
|
||||||
TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
|
TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ using osu.Game.Audio;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Beatmaps.Formats
|
namespace osu.Game.Tests.Beatmaps.Formats
|
||||||
@ -211,5 +212,41 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
|
Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeCustomSamples()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||||
|
using (var resStream = Resource.OpenResource("custom-samples.osu"))
|
||||||
|
using (var stream = new StreamReader(resStream))
|
||||||
|
{
|
||||||
|
var hitObjects = decoder.Decode(stream).HitObjects;
|
||||||
|
|
||||||
|
Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[0]).LookupNames.First());
|
||||||
|
Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[1]).LookupNames.First());
|
||||||
|
Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
|
||||||
|
Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[3]).LookupNames.First());
|
||||||
|
}
|
||||||
|
|
||||||
|
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(new SampleInfo { Name = "hitnormal" });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDecodeCustomHitObjectSamples()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
|
||||||
|
using (var resStream = Resource.OpenResource("custom-hitobject-samples.osu"))
|
||||||
|
using (var stream = new StreamReader(resStream))
|
||||||
|
{
|
||||||
|
var hitObjects = decoder.Decode(stream).HitObjects;
|
||||||
|
|
||||||
|
Assert.AreEqual("hit_1.wav", hitObjects[0].Samples[0].LookupNames.First());
|
||||||
|
Assert.AreEqual("hit_2.wav", hitObjects[1].Samples[0].LookupNames.First());
|
||||||
|
Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
|
||||||
|
Assert.AreEqual("hit_1.wav", hitObjects[3].Samples[0].LookupNames.First());
|
||||||
|
}
|
||||||
|
|
||||||
|
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(new SampleInfo { Name = "hitnormal" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,11 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
public void TestParity(string beatmap)
|
public void TestParity(string beatmap)
|
||||||
{
|
{
|
||||||
var legacy = decode(beatmap, out Beatmap json);
|
var legacy = decode(beatmap, out Beatmap json);
|
||||||
json.WithDeepEqual(legacy).IgnoreProperty(r => r.DeclaringType == typeof(HitWindows)).Assert();
|
json.WithDeepEqual(legacy)
|
||||||
|
.IgnoreProperty(r => r.DeclaringType == typeof(HitWindows)
|
||||||
|
// Todo: CustomSampleBank shouldn't exist going forward, we need a conversion mechanism
|
||||||
|
|| r.Name == nameof(LegacyDecoder<Beatmap>.LegacySampleControlPoint.CustomSampleBank))
|
||||||
|
.Assert();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
16
osu.Game.Tests/Resources/custom-hitobject-samples.osu
Normal file
16
osu.Game.Tests/Resources/custom-hitobject-samples.osu
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[General]
|
||||||
|
SampleSet: Normal
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
2170,468.75,4,1,0,40,1,0
|
||||||
|
2638,-100,4,1,1,40,0,0
|
||||||
|
3107,-100,4,1,2,40,0,0
|
||||||
|
3576,-100,4,1,0,40,0,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
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
|
16
osu.Game.Tests/Resources/custom-samples.osu
Normal file
16
osu.Game.Tests/Resources/custom-samples.osu
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[General]
|
||||||
|
SampleSet: Normal
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
2170,468.75,4,1,0,40,1,0
|
||||||
|
2638,-100,4,1,1,40,0,0
|
||||||
|
3107,-100,4,1,2,40,0,0
|
||||||
|
3576,-100,4,1,0,40,0,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
255,193,2170,1,0,0:0:0:0:
|
||||||
|
256,191,2638,5,0,0:0:0:0:
|
||||||
|
255,193,3107,1,0,0:0:0:0:
|
||||||
|
256,191,3576,1,0,0:0:0:0:
|
@ -110,8 +110,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
private void testInfoLabels(int expectedCount)
|
private void testInfoLabels(int expectedCount)
|
||||||
{
|
{
|
||||||
AddAssert("check infolabels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any());
|
AddAssert("check info labels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any());
|
||||||
AddAssert("check infolabels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount);
|
AddAssert("check info labels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testNullBeatmap()
|
private void testNullBeatmap()
|
||||||
@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
AddAssert("check default title", () => infoWedge.Info.TitleLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Title);
|
AddAssert("check default title", () => infoWedge.Info.TitleLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Title);
|
||||||
AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Artist);
|
AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Artist);
|
||||||
AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any());
|
AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any());
|
||||||
AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
|
AddAssert("check no info labels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectBeatmap(IBeatmap b)
|
private void selectBeatmap(IBeatmap b)
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
@ -13,6 +16,13 @@ namespace osu.Game.Tests.Visual
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseButtonSystem : OsuTestCase
|
public class TestCaseButtonSystem : OsuTestCase
|
||||||
{
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(ButtonSystem),
|
||||||
|
typeof(ButtonArea),
|
||||||
|
typeof(Button)
|
||||||
|
};
|
||||||
|
|
||||||
public TestCaseButtonSystem()
|
public TestCaseButtonSystem()
|
||||||
{
|
{
|
||||||
OsuLogo logo;
|
OsuLogo logo;
|
||||||
@ -30,6 +40,9 @@ namespace osu.Game.Tests.Visual
|
|||||||
};
|
};
|
||||||
|
|
||||||
buttons.SetOsuLogo(logo);
|
buttons.SetOsuLogo(logo);
|
||||||
|
|
||||||
|
foreach (var s in Enum.GetValues(typeof(ButtonSystemState)).OfType<ButtonSystemState>().Skip(1))
|
||||||
|
AddStep($"State to {s}", () => buttons.State = s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour);
|
testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour);
|
||||||
testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour);
|
testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour);
|
||||||
|
|
||||||
testUnimplmentedMod(autoPilotMod);
|
testUnimplementedMod(autoPilotMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testManiaMods(ManiaRuleset ruleset)
|
private void testManiaMods(ManiaRuleset ruleset)
|
||||||
@ -154,7 +154,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
checkNotSelected(mod);
|
checkNotSelected(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testUnimplmentedMod(Mod mod)
|
private void testUnimplementedMod(Mod mod)
|
||||||
{
|
{
|
||||||
selectNext(mod);
|
selectNext(mod);
|
||||||
checkNotSelected(mod);
|
checkNotSelected(mod);
|
||||||
|
@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
private class TestOnScreenDisplay : OnScreenDisplay
|
private class TestOnScreenDisplay : OnScreenDisplay
|
||||||
{
|
{
|
||||||
protected override void Display(Drawable toDisplay) => toDisplay.FadeIn().ResizeHeightTo(110);
|
protected override void DisplayTemporarily(Drawable toDisplay) => toDisplay.FadeIn().ResizeHeightTo(110);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
typeof(ToolbarButton),
|
typeof(ToolbarButton),
|
||||||
typeof(ToolbarModeSelector),
|
typeof(ToolbarRulesetSelector),
|
||||||
typeof(ToolbarModeButton),
|
typeof(ToolbarRulesetButton),
|
||||||
typeof(ToolbarNotificationButton),
|
typeof(ToolbarNotificationButton),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,7 +53,6 @@ namespace osu.Game.Tests.Visual
|
|||||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg",
|
||||||
JoinDate = DateTimeOffset.Now.AddDays(-1),
|
JoinDate = DateTimeOffset.Now.AddDays(-1),
|
||||||
LastVisit = DateTimeOffset.Now,
|
LastVisit = DateTimeOffset.Now,
|
||||||
Age = 1,
|
|
||||||
ProfileOrder = new[] { "me" },
|
ProfileOrder = new[] { "me" },
|
||||||
Statistics = new UserStatistics
|
Statistics = new UserStatistics
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace osu.Game.Audio
|
namespace osu.Game.Audio
|
||||||
{
|
{
|
||||||
@ -28,9 +29,37 @@ namespace osu.Game.Audio
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name;
|
public string Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An optional suffix to provide priority lookup. Falls back to non-suffixed <see cref="Name"/>.
|
||||||
|
/// </summary>
|
||||||
|
public string Suffix;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sample volume.
|
/// The sample volume.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Volume;
|
public int Volume;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first).
|
||||||
|
/// </summary>
|
||||||
|
public virtual IEnumerable<string> LookupNames
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Namespace))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Suffix))
|
||||||
|
yield return $"{Namespace}/{Bank}-{Name}{Suffix}";
|
||||||
|
yield return $"{Namespace}/{Bank}-{Name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// check non-namespace as a fallback even when we have a namespace
|
||||||
|
if (!string.IsNullOrEmpty(Suffix))
|
||||||
|
yield return $"{Bank}-{Name}{Suffix}";
|
||||||
|
yield return $"{Bank}-{Name}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SampleInfo Clone() => (SampleInfo)MemberwiseClone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,6 +339,8 @@ namespace osu.Game.Beatmaps
|
|||||||
{
|
{
|
||||||
var beatmapInfos = new List<BeatmapInfo>();
|
var beatmapInfos = new List<BeatmapInfo>();
|
||||||
|
|
||||||
|
bool invalidateOnlineIDs = false;
|
||||||
|
|
||||||
foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu")))
|
foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu")))
|
||||||
{
|
{
|
||||||
using (var raw = reader.GetStream(name))
|
using (var raw = reader.GetStream(name))
|
||||||
@ -355,9 +357,18 @@ namespace osu.Game.Beatmaps
|
|||||||
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
|
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
|
||||||
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
|
beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash();
|
||||||
|
|
||||||
// check that no existing beatmap exists that is imported with the same online beatmap ID. if so, give it precedence.
|
if (beatmap.BeatmapInfo.OnlineBeatmapID.HasValue)
|
||||||
if (beatmap.BeatmapInfo.OnlineBeatmapID.HasValue && QueryBeatmap(b => b.OnlineBeatmapID.Value == beatmap.BeatmapInfo.OnlineBeatmapID.Value) != null)
|
{
|
||||||
beatmap.BeatmapInfo.OnlineBeatmapID = null;
|
var ourId = beatmap.BeatmapInfo.OnlineBeatmapID;
|
||||||
|
|
||||||
|
// check that no existing beatmap in database exists that is imported with the same online beatmap ID. if so, give it precedence.
|
||||||
|
if (QueryBeatmap(b => b.OnlineBeatmapID.Value == ourId) != null)
|
||||||
|
beatmap.BeatmapInfo.OnlineBeatmapID = null;
|
||||||
|
|
||||||
|
// check that no other beatmap in this imported set has a conflicting online beatmap ID. If so, presume *all* are incorrect.
|
||||||
|
if (beatmapInfos.Any(b => b.OnlineBeatmapID == ourId))
|
||||||
|
invalidateOnlineIDs = true;
|
||||||
|
}
|
||||||
|
|
||||||
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
|
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
|
||||||
|
|
||||||
@ -375,6 +386,9 @@ namespace osu.Game.Beatmaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (invalidateOnlineIDs)
|
||||||
|
beatmapInfos.ForEach(b => b.OnlineBeatmapID = null);
|
||||||
|
|
||||||
return beatmapInfos;
|
return beatmapInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ namespace osu.Game.Beatmaps
|
|||||||
public class BeatmapMetadata : IEquatable<BeatmapMetadata>
|
public class BeatmapMetadata : IEquatable<BeatmapMetadata>
|
||||||
{
|
{
|
||||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
[JsonIgnore]
|
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
|
@ -14,6 +14,15 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
|
|
||||||
public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time);
|
public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time);
|
||||||
|
|
||||||
public bool Equals(ControlPoint other) => Time.Equals(other?.Time);
|
/// <summary>
|
||||||
|
/// Whether this <see cref="ControlPoint"/> provides the same parametric changes as another <see cref="ControlPoint"/>.
|
||||||
|
/// Basically an equality check without considering the <see cref="Time"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The <see cref="ControlPoint"/> to compare to.</param>
|
||||||
|
/// <returns>Whether this <see cref="ControlPoint"/> is equivalent to <paramref name="other"/>.</returns>
|
||||||
|
public virtual bool EquivalentTo(ControlPoint other) => true;
|
||||||
|
|
||||||
|
public bool Equals(ControlPoint other)
|
||||||
|
=> EquivalentTo(other) && Time.Equals(other?.Time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,5 +17,10 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
}
|
}
|
||||||
|
|
||||||
private double speedMultiplier = 1;
|
private double speedMultiplier = 1;
|
||||||
|
|
||||||
|
public override bool EquivalentTo(ControlPoint other)
|
||||||
|
=> base.EquivalentTo(other)
|
||||||
|
&& other is DifficultyControlPoint difficulty
|
||||||
|
&& SpeedMultiplier.Equals(difficulty.SpeedMultiplier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,5 +14,11 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// Whether the first bar line of this control point is ignored.
|
/// Whether the first bar line of this control point is ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool OmitFirstBarLine;
|
public bool OmitFirstBarLine;
|
||||||
|
|
||||||
|
public override bool EquivalentTo(ControlPoint other)
|
||||||
|
=> base.EquivalentTo(other)
|
||||||
|
&& other is EffectControlPoint effect
|
||||||
|
&& KiaiMode.Equals(effect.KiaiMode)
|
||||||
|
&& OmitFirstBarLine.Equals(effect.OmitFirstBarLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,5 +30,25 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
Name = sampleName,
|
Name = sampleName,
|
||||||
Volume = SampleVolume,
|
Volume = SampleVolume,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies <see cref="SampleBank"/> and <see cref="SampleVolume"/> to a <see cref="SampleInfo"/> if necessary, returning the modified <see cref="SampleInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sampleInfo">The <see cref="SampleInfo"/>. This will not be modified.</param>
|
||||||
|
/// <returns>The modified <see cref="SampleInfo"/>. This does not share a reference with <paramref name="sampleInfo"/>.</returns>
|
||||||
|
public virtual SampleInfo ApplyTo(SampleInfo sampleInfo)
|
||||||
|
{
|
||||||
|
var newSampleInfo = sampleInfo.Clone();
|
||||||
|
newSampleInfo.Bank = sampleInfo.Bank ?? SampleBank;
|
||||||
|
newSampleInfo.Name = sampleInfo.Name;
|
||||||
|
newSampleInfo.Volume = sampleInfo.Volume > 0 ? sampleInfo.Volume : SampleVolume;
|
||||||
|
return newSampleInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool EquivalentTo(ControlPoint other)
|
||||||
|
=> base.EquivalentTo(other)
|
||||||
|
&& other is SampleControlPoint sample
|
||||||
|
&& SampleBank.Equals(sample.SampleBank)
|
||||||
|
&& SampleVolume.Equals(sample.SampleVolume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,5 +23,11 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
}
|
}
|
||||||
|
|
||||||
private double beatLength = 1000;
|
private double beatLength = 1000;
|
||||||
|
|
||||||
|
public override bool EquivalentTo(ControlPoint other)
|
||||||
|
=> base.EquivalentTo(other)
|
||||||
|
&& other is TimingControlPoint timing
|
||||||
|
&& TimeSignature.Equals(timing.TimeSignature)
|
||||||
|
&& BeatLength.Equals(timing.BeatLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,9 +289,9 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
if (split.Length >= 4)
|
if (split.Length >= 4)
|
||||||
sampleSet = (LegacySampleBank)int.Parse(split[3]);
|
sampleSet = (LegacySampleBank)int.Parse(split[3]);
|
||||||
|
|
||||||
//SampleBank sampleBank = SampleBank.Default;
|
int customSampleBank = 0;
|
||||||
//if (split.Length >= 5)
|
if (split.Length >= 5)
|
||||||
// sampleBank = (SampleBank)int.Parse(split[4]);
|
customSampleBank = int.Parse(split[4]);
|
||||||
|
|
||||||
int sampleVolume = defaultSampleVolume;
|
int sampleVolume = defaultSampleVolume;
|
||||||
if (split.Length >= 6)
|
if (split.Length >= 6)
|
||||||
@ -314,13 +314,9 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
if (stringSampleSet == @"none")
|
if (stringSampleSet == @"none")
|
||||||
stringSampleSet = @"normal";
|
stringSampleSet = @"normal";
|
||||||
|
|
||||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time);
|
|
||||||
SampleControlPoint samplePoint = beatmap.ControlPointInfo.SamplePointAt(time);
|
|
||||||
EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time);
|
|
||||||
|
|
||||||
if (timingChange)
|
if (timingChange)
|
||||||
{
|
{
|
||||||
beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint
|
handleTimingControlPoint(new TimingControlPoint
|
||||||
{
|
{
|
||||||
Time = time,
|
Time = time,
|
||||||
BeatLength = beatLength,
|
BeatLength = beatLength,
|
||||||
@ -328,41 +324,68 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (speedMultiplier != difficultyPoint.SpeedMultiplier)
|
handleDifficultyControlPoint(new DifficultyControlPoint
|
||||||
{
|
{
|
||||||
beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time);
|
Time = time,
|
||||||
beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint
|
SpeedMultiplier = speedMultiplier
|
||||||
{
|
});
|
||||||
Time = time,
|
|
||||||
SpeedMultiplier = speedMultiplier
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stringSampleSet != samplePoint.SampleBank || sampleVolume != samplePoint.SampleVolume)
|
handleEffectControlPoint(new EffectControlPoint
|
||||||
{
|
{
|
||||||
beatmap.ControlPointInfo.SamplePoints.Add(new SampleControlPoint
|
Time = time,
|
||||||
{
|
KiaiMode = kiaiMode,
|
||||||
Time = time,
|
OmitFirstBarLine = omitFirstBarSignature
|
||||||
SampleBank = stringSampleSet,
|
});
|
||||||
SampleVolume = sampleVolume
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine)
|
handleSampleControlPoint(new LegacySampleControlPoint
|
||||||
{
|
{
|
||||||
beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint
|
Time = time,
|
||||||
{
|
SampleBank = stringSampleSet,
|
||||||
Time = time,
|
SampleVolume = sampleVolume,
|
||||||
KiaiMode = kiaiMode,
|
CustomSampleBank = customSampleBank
|
||||||
OmitFirstBarLine = omitFirstBarSignature
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (FormatException e)
|
catch (FormatException e)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleTimingControlPoint(TimingControlPoint newPoint)
|
||||||
|
{
|
||||||
|
beatmap.ControlPointInfo.TimingPoints.Add(newPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleDifficultyControlPoint(DifficultyControlPoint newPoint)
|
||||||
|
{
|
||||||
|
var existing = beatmap.ControlPointInfo.DifficultyPointAt(newPoint.Time);
|
||||||
|
|
||||||
|
if (newPoint.EquivalentTo(existing))
|
||||||
|
return;
|
||||||
|
|
||||||
|
beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == newPoint.Time);
|
||||||
|
beatmap.ControlPointInfo.DifficultyPoints.Add(newPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEffectControlPoint(EffectControlPoint newPoint)
|
||||||
|
{
|
||||||
|
var existing = beatmap.ControlPointInfo.EffectPointAt(newPoint.Time);
|
||||||
|
|
||||||
|
if (newPoint.EquivalentTo(existing))
|
||||||
|
return;
|
||||||
|
|
||||||
|
beatmap.ControlPointInfo.EffectPoints.Add(newPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSampleControlPoint(SampleControlPoint newPoint)
|
||||||
|
{
|
||||||
|
var existing = beatmap.ControlPointInfo.SamplePointAt(newPoint.Time);
|
||||||
|
|
||||||
|
if (newPoint.EquivalentTo(existing))
|
||||||
|
return;
|
||||||
|
|
||||||
|
beatmap.ControlPointInfo.SamplePoints.Add(newPoint);
|
||||||
|
}
|
||||||
|
|
||||||
private void handleHitObject(string line)
|
private void handleHitObject(string line)
|
||||||
{
|
{
|
||||||
// If the ruleset wasn't specified, assume the osu!standard ruleset.
|
// If the ruleset wasn't specified, assume the osu!standard ruleset.
|
||||||
|
@ -5,6 +5,8 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.Formats
|
namespace osu.Game.Beatmaps.Formats
|
||||||
@ -167,5 +169,25 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
Pass = 2,
|
Pass = 2,
|
||||||
Foreground = 3
|
Foreground = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class LegacySampleControlPoint : SampleControlPoint
|
||||||
|
{
|
||||||
|
public int CustomSampleBank;
|
||||||
|
|
||||||
|
public override SampleInfo ApplyTo(SampleInfo sampleInfo)
|
||||||
|
{
|
||||||
|
var baseInfo = base.ApplyTo(sampleInfo);
|
||||||
|
|
||||||
|
if (CustomSampleBank > 1)
|
||||||
|
baseInfo.Suffix = CustomSampleBank.ToString();
|
||||||
|
|
||||||
|
return baseInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool EquivalentTo(ControlPoint other)
|
||||||
|
=> base.EquivalentTo(other)
|
||||||
|
&& other is LegacySampleControlPoint legacy
|
||||||
|
&& CustomSampleBank == legacy.CustomSampleBank;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
return base.OnClick(state);
|
return base.OnClick(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnPressed(GlobalAction action)
|
public virtual bool OnPressed(GlobalAction action)
|
||||||
{
|
{
|
||||||
if (action == GlobalAction.Back)
|
if (action == GlobalAction.Back)
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,9 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
private readonly IBindable<bool> screenshotCursorVisibility = new Bindable<bool>(true);
|
private readonly IBindable<bool> screenshotCursorVisibility = new Bindable<bool>(true);
|
||||||
public override bool IsPresent => screenshotCursorVisibility.Value && base.IsPresent;
|
public override bool IsPresent => screenshotCursorVisibility.Value && base.IsPresent;
|
||||||
|
|
||||||
protected override Drawable CreateCursor() => new Cursor();
|
protected override Drawable CreateCursor() => activeCursor = new Cursor();
|
||||||
|
|
||||||
|
private Cursor activeCursor;
|
||||||
|
|
||||||
private Bindable<bool> cursorRotate;
|
private Bindable<bool> cursorRotate;
|
||||||
private DragRotationState dragRotationState;
|
private DragRotationState dragRotationState;
|
||||||
@ -54,12 +56,12 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
|
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
|
||||||
|
|
||||||
// Always rotate in the direction of least distance
|
// Always rotate in the direction of least distance
|
||||||
float diff = (degrees - ActiveCursor.Rotation) % 360;
|
float diff = (degrees - activeCursor.Rotation) % 360;
|
||||||
if (diff < -180) diff += 360;
|
if (diff < -180) diff += 360;
|
||||||
if (diff > 180) diff -= 360;
|
if (diff > 180) diff -= 360;
|
||||||
degrees = ActiveCursor.Rotation + diff;
|
degrees = activeCursor.Rotation + diff;
|
||||||
|
|
||||||
ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint);
|
activeCursor.RotateTo(degrees, 600, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,11 +70,15 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
|
|
||||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||||
{
|
{
|
||||||
ActiveCursor.Scale = new Vector2(1);
|
// only trigger animation for main mouse buttons
|
||||||
ActiveCursor.ScaleTo(0.90f, 800, Easing.OutQuint);
|
if (args.Button <= MouseButton.Right)
|
||||||
|
{
|
||||||
|
activeCursor.Scale = new Vector2(1);
|
||||||
|
activeCursor.ScaleTo(0.90f, 800, Easing.OutQuint);
|
||||||
|
|
||||||
((Cursor)ActiveCursor).AdditiveLayer.Alpha = 0;
|
activeCursor.AdditiveLayer.Alpha = 0;
|
||||||
((Cursor)ActiveCursor).AdditiveLayer.FadeInFromZero(800, Easing.OutQuint);
|
activeCursor.AdditiveLayer.FadeInFromZero(800, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
if (args.Button == MouseButton.Left && cursorRotate)
|
if (args.Button == MouseButton.Left && cursorRotate)
|
||||||
{
|
{
|
||||||
@ -86,36 +92,29 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
{
|
{
|
||||||
if (!state.Mouse.HasMainButtonPressed)
|
if (!state.Mouse.HasMainButtonPressed)
|
||||||
{
|
{
|
||||||
((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint);
|
activeCursor.AdditiveLayer.FadeOutFromOne(500, Easing.OutQuint);
|
||||||
ActiveCursor.ScaleTo(1, 500, Easing.OutElastic);
|
activeCursor.ScaleTo(1, 500, Easing.OutElastic);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.Button == MouseButton.Left)
|
if (args.Button == MouseButton.Left)
|
||||||
{
|
{
|
||||||
if (dragRotationState == DragRotationState.Rotating)
|
if (dragRotationState == DragRotationState.Rotating)
|
||||||
ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf);
|
activeCursor.RotateTo(0, 600 * (1 + Math.Abs(activeCursor.Rotation / 720)), Easing.OutElasticHalf);
|
||||||
dragRotationState = DragRotationState.NotDragging;
|
dragRotationState = DragRotationState.NotDragging;
|
||||||
}
|
}
|
||||||
return base.OnMouseUp(state, args);
|
return base.OnMouseUp(state, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnClick(InputState state)
|
|
||||||
{
|
|
||||||
((Cursor)ActiveCursor).AdditiveLayer.FadeOutFromOne(500, Easing.OutQuint);
|
|
||||||
|
|
||||||
return base.OnClick(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
{
|
{
|
||||||
ActiveCursor.FadeTo(1, 250, Easing.OutQuint);
|
activeCursor.FadeTo(1, 250, Easing.OutQuint);
|
||||||
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
|
activeCursor.ScaleTo(1, 400, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
{
|
{
|
||||||
ActiveCursor.FadeTo(0, 250, Easing.OutQuint);
|
activeCursor.FadeTo(0, 250, Easing.OutQuint);
|
||||||
ActiveCursor.ScaleTo(0.6f, 250, Easing.In);
|
activeCursor.ScaleTo(0.6f, 250, Easing.In);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Cursor : Container
|
public class Cursor : Container
|
||||||
|
@ -39,7 +39,10 @@ namespace osu.Game.Input.Bindings
|
|||||||
new KeyBinding(InputKey.F4, GlobalAction.ToggleMute),
|
new KeyBinding(InputKey.F4, GlobalAction.ToggleMute),
|
||||||
|
|
||||||
new KeyBinding(InputKey.Escape, GlobalAction.Back),
|
new KeyBinding(InputKey.Escape, GlobalAction.Back),
|
||||||
new KeyBinding(InputKey.MouseButton1, GlobalAction.Back)
|
new KeyBinding(InputKey.MouseButton1, GlobalAction.Back),
|
||||||
|
|
||||||
|
new KeyBinding(InputKey.Space, GlobalAction.Select),
|
||||||
|
new KeyBinding(InputKey.Enter, GlobalAction.Select),
|
||||||
};
|
};
|
||||||
|
|
||||||
public IEnumerable<KeyBinding> InGameKeyBindings => new[]
|
public IEnumerable<KeyBinding> InGameKeyBindings => new[]
|
||||||
@ -86,7 +89,7 @@ namespace osu.Game.Input.Bindings
|
|||||||
[Description("Toggle gameplay mouse buttons")]
|
[Description("Toggle gameplay mouse buttons")]
|
||||||
ToggleGameplayMouseButtons,
|
ToggleGameplayMouseButtons,
|
||||||
|
|
||||||
[Description("Go back")]
|
[Description("Back")]
|
||||||
Back,
|
Back,
|
||||||
|
|
||||||
[Description("Increase scroll speed")]
|
[Description("Increase scroll speed")]
|
||||||
@ -94,5 +97,8 @@ namespace osu.Game.Input.Bindings
|
|||||||
|
|
||||||
[Description("Decrease scroll speed")]
|
[Description("Decrease scroll speed")]
|
||||||
DecreaseScrollSpeed,
|
DecreaseScrollSpeed,
|
||||||
|
|
||||||
|
[Description("Select")]
|
||||||
|
Select,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,14 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
[JsonProperty(@"beatmapset")]
|
[JsonProperty(@"beatmapset")]
|
||||||
private BeatmapMetadata metadata
|
private BeatmapMetadata metadata
|
||||||
{
|
{
|
||||||
set => Beatmap.Metadata = value;
|
set
|
||||||
|
{
|
||||||
|
// extract the set ID to its correct place.
|
||||||
|
Beatmap.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = value.ID };
|
||||||
|
value.ID = 0;
|
||||||
|
|
||||||
|
Beatmap.Metadata = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty(@"statistics")]
|
[JsonProperty(@"statistics")]
|
||||||
|
@ -250,7 +250,8 @@ namespace osu.Game
|
|||||||
new VolumeControlReceptor
|
new VolumeControlReceptor
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
ActionRequested = action => volume.Adjust(action)
|
ActionRequested = action => volume.Adjust(action),
|
||||||
|
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
|
||||||
},
|
},
|
||||||
mainContent = new Container { RelativeSizeAxes = Axes.Both },
|
mainContent = new Container { RelativeSizeAxes = Axes.Both },
|
||||||
overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue },
|
overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue },
|
||||||
|
@ -20,6 +20,7 @@ using osu.Game.Graphics.Cursor;
|
|||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Framework.Graphics.Performance;
|
using osu.Framework.Graphics.Performance;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
@ -30,6 +31,7 @@ using osu.Game.IO;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using OpenTK.Input;
|
||||||
using DebugUtils = osu.Game.Utils.DebugUtils;
|
using DebugUtils = osu.Game.Utils.DebugUtils;
|
||||||
|
|
||||||
namespace osu.Game
|
namespace osu.Game
|
||||||
@ -98,6 +100,8 @@ namespace osu.Game
|
|||||||
|
|
||||||
private DatabaseContextFactory contextFactory;
|
private DatabaseContextFactory contextFactory;
|
||||||
|
|
||||||
|
protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager();
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
@ -267,5 +271,31 @@ namespace osu.Game
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class OsuUserInputManager : UserInputManager
|
||||||
|
{
|
||||||
|
protected override MouseButtonEventManager CreateButtonManagerFor(MouseButton button)
|
||||||
|
{
|
||||||
|
switch (button)
|
||||||
|
{
|
||||||
|
case MouseButton.Right:
|
||||||
|
return new RightMouseManager(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.CreateButtonManagerFor(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RightMouseManager : MouseButtonEventManager
|
||||||
|
{
|
||||||
|
public RightMouseManager(MouseButton button)
|
||||||
|
: base(button)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool EnableDrag => true; // allow right-mouse dragging for absolute scroll in scroll containers.
|
||||||
|
public override bool EnableClick => false;
|
||||||
|
public override bool ChangeFocusOnClick => false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
@ -69,10 +70,10 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
{
|
{
|
||||||
if (actionInvoked) return;
|
if (actionInvoked) return;
|
||||||
|
|
||||||
Hide();
|
|
||||||
|
|
||||||
actionInvoked = true;
|
actionInvoked = true;
|
||||||
action?.Invoke();
|
action?.Invoke();
|
||||||
|
|
||||||
|
Hide();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -192,16 +193,22 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool OnPressed(GlobalAction action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case GlobalAction.Select:
|
||||||
|
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerOnClick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnPressed(action);
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||||
{
|
{
|
||||||
if (args.Repeat) return false;
|
if (args.Repeat) return false;
|
||||||
|
|
||||||
if (args.Key == Key.Enter || args.Key == Key.KeypadEnter)
|
|
||||||
{
|
|
||||||
Buttons.OfType<PopupDialogOkButton>().FirstOrDefault()?.TriggerOnClick();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// press button at number if 1-9 on number row or keypad are pressed
|
// press button at number if 1-9 on number row or keypad are pressed
|
||||||
var k = args.Key;
|
var k = args.Key;
|
||||||
if (k >= Key.Number1 && k <= Key.Number9)
|
if (k >= Key.Number1 && k <= Key.Number9)
|
||||||
|
@ -186,7 +186,7 @@ namespace osu.Game.Overlays.KeyBinding
|
|||||||
{
|
{
|
||||||
if (bindTarget.IsHovered)
|
if (bindTarget.IsHovered)
|
||||||
{
|
{
|
||||||
bindTarget.UpdateKeyCombination(new KeyCombination(KeyCombination.FromInputState(state).Keys.Append(state.Mouse.ScrollDelta.Y > 0 ? InputKey.MouseWheelUp : InputKey.MouseWheelDown)));
|
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state, state.Mouse.ScrollDelta));
|
||||||
finalise();
|
finalise();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@ using osu.Game.Graphics;
|
|||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics.Transforms;
|
||||||
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
@ -135,7 +137,7 @@ namespace osu.Game.Overlays
|
|||||||
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is already being tracked from the same <paramref name="source"/>.</exception>
|
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is already being tracked from the same <paramref name="source"/>.</exception>
|
||||||
public void BeginTracking(object source, ITrackableConfigManager configManager)
|
public void BeginTracking(object source, ITrackableConfigManager configManager)
|
||||||
{
|
{
|
||||||
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
|
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
|
||||||
|
|
||||||
if (trackedConfigManagers.ContainsKey((source, configManager)))
|
if (trackedConfigManagers.ContainsKey((source, configManager)))
|
||||||
throw new InvalidOperationException($"{nameof(configManager)} is already registered.");
|
throw new InvalidOperationException($"{nameof(configManager)} is already registered.");
|
||||||
@ -159,7 +161,7 @@ namespace osu.Game.Overlays
|
|||||||
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is not being tracked from the same <see cref="source"/>.</exception>
|
/// <exception cref="InvalidOperationException">If <paramref name="configManager"/> is not being tracked from the same <see cref="source"/>.</exception>
|
||||||
public void StopTracking(object source, ITrackableConfigManager configManager)
|
public void StopTracking(object source, ITrackableConfigManager configManager)
|
||||||
{
|
{
|
||||||
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
|
if (configManager == null) throw new ArgumentNullException(nameof(configManager));
|
||||||
|
|
||||||
if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing))
|
if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing))
|
||||||
throw new InvalidOperationException($"{nameof(configManager)} is not registered.");
|
throw new InvalidOperationException($"{nameof(configManager)} is not registered.");
|
||||||
@ -181,7 +183,7 @@ namespace osu.Game.Overlays
|
|||||||
if (string.IsNullOrEmpty(textLine3.Text))
|
if (string.IsNullOrEmpty(textLine3.Text))
|
||||||
textLine3.Text = "NO KEY BOUND";
|
textLine3.Text = "NO KEY BOUND";
|
||||||
|
|
||||||
Display(box);
|
DisplayTemporarily(box);
|
||||||
|
|
||||||
int optionCount = 0;
|
int optionCount = 0;
|
||||||
int selectedOption = -1;
|
int selectedOption = -1;
|
||||||
@ -213,15 +215,29 @@ namespace osu.Game.Overlays
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Display(Drawable toDisplay)
|
private TransformSequence<Drawable> fadeIn;
|
||||||
|
private ScheduledDelegate fadeOut;
|
||||||
|
|
||||||
|
protected virtual void DisplayTemporarily(Drawable toDisplay)
|
||||||
{
|
{
|
||||||
toDisplay.Animate(
|
// avoid starting a new fade-in if one is already active.
|
||||||
b => b.FadeIn(500, Easing.OutQuint),
|
if (fadeIn == null)
|
||||||
b => b.ResizeHeightTo(height, 500, Easing.OutQuint)
|
{
|
||||||
).Then(
|
fadeIn = toDisplay.Animate(
|
||||||
b => b.FadeOutFromOne(1500, Easing.InQuint),
|
b => b.FadeIn(500, Easing.OutQuint),
|
||||||
b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint)
|
b => b.ResizeHeightTo(height, 500, Easing.OutQuint)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
fadeIn.Finally(_ => fadeIn = null);
|
||||||
|
}
|
||||||
|
|
||||||
|
fadeOut?.Cancel();
|
||||||
|
fadeOut = Scheduler.AddDelayed(() =>
|
||||||
|
{
|
||||||
|
toDisplay.Animate(
|
||||||
|
b => b.FadeOutFromOne(1500, Easing.InQuint),
|
||||||
|
b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint));
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class OptionLight : Container
|
private class OptionLight : Container
|
||||||
|
@ -360,11 +360,6 @@ namespace osu.Game.Overlays.Profile
|
|||||||
Text = text
|
Text = text
|
||||||
};
|
};
|
||||||
|
|
||||||
if (user.Age != null)
|
|
||||||
{
|
|
||||||
infoTextLeft.AddText($"{user.Age} years old ", boldItalic);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.Country != null)
|
if (user.Country != null)
|
||||||
{
|
{
|
||||||
infoTextLeft.AddText("From ", lightText);
|
infoTextLeft.AddText("From ", lightText);
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Localisation;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Profile.Sections
|
namespace osu.Game.Overlays.Profile.Sections
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -32,7 +33,10 @@ namespace osu.Game.Overlays.Profile.Sections
|
|||||||
{
|
{
|
||||||
Action = () =>
|
Action = () =>
|
||||||
{
|
{
|
||||||
if (beatmap.BeatmapSet?.OnlineBeatmapSetID != null) beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.BeatmapSet.OnlineBeatmapSetID.Value);
|
if (beatmap.OnlineBeatmapID != null)
|
||||||
|
beatmapSetOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value);
|
||||||
|
else if (beatmap.BeatmapSet?.OnlineBeatmapSetID != null)
|
||||||
|
beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.BeatmapSet.OnlineBeatmapSetID.Value);
|
||||||
};
|
};
|
||||||
|
|
||||||
Child = new FillFlowContainer
|
Child = new FillFlowContainer
|
||||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
{
|
{
|
||||||
Action = () => OnHome?.Invoke()
|
Action = () => OnHome?.Invoke()
|
||||||
},
|
},
|
||||||
new ToolbarModeSelector()
|
new ToolbarRulesetSelector()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
|
@ -7,7 +7,7 @@ using OpenTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Toolbar
|
namespace osu.Game.Overlays.Toolbar
|
||||||
{
|
{
|
||||||
public class ToolbarModeButton : ToolbarButton
|
public class ToolbarRulesetButton : ToolbarButton
|
||||||
{
|
{
|
||||||
private RulesetInfo ruleset;
|
private RulesetInfo ruleset;
|
||||||
public RulesetInfo Ruleset
|
public RulesetInfo Ruleset
|
@ -16,18 +16,18 @@ using osu.Game.Rulesets;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Toolbar
|
namespace osu.Game.Overlays.Toolbar
|
||||||
{
|
{
|
||||||
public class ToolbarModeSelector : Container
|
public class ToolbarRulesetSelector : Container
|
||||||
{
|
{
|
||||||
private const float padding = 10;
|
private const float padding = 10;
|
||||||
|
|
||||||
private readonly FillFlowContainer modeButtons;
|
private readonly FillFlowContainer modeButtons;
|
||||||
private readonly Drawable modeButtonLine;
|
private readonly Drawable modeButtonLine;
|
||||||
private ToolbarModeButton activeButton;
|
private ToolbarRulesetButton activeButton;
|
||||||
|
|
||||||
private RulesetStore rulesets;
|
private RulesetStore rulesets;
|
||||||
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||||
|
|
||||||
public ToolbarModeSelector()
|
public ToolbarRulesetSelector()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
this.rulesets = rulesets;
|
this.rulesets = rulesets;
|
||||||
foreach (var r in rulesets.AvailableRulesets)
|
foreach (var r in rulesets.AvailableRulesets)
|
||||||
{
|
{
|
||||||
modeButtons.Add(new ToolbarModeButton
|
modeButtons.Add(new ToolbarRulesetButton
|
||||||
{
|
{
|
||||||
Ruleset = r,
|
Ruleset = r,
|
||||||
Action = delegate { ruleset.Value = r; }
|
Action = delegate { ruleset.Value = r; }
|
||||||
@ -115,7 +115,7 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
|
|
||||||
private void rulesetChanged(RulesetInfo ruleset)
|
private void rulesetChanged(RulesetInfo ruleset)
|
||||||
{
|
{
|
||||||
foreach (ToolbarModeButton m in modeButtons.Children.Cast<ToolbarModeButton>())
|
foreach (ToolbarRulesetButton m in modeButtons.Children.Cast<ToolbarRulesetButton>())
|
||||||
{
|
{
|
||||||
bool isActive = m.Ruleset.ID == ruleset.ID;
|
bool isActive = m.Ruleset.ID == ruleset.ID;
|
||||||
m.Active = isActive;
|
m.Active = isActive;
|
@ -9,11 +9,13 @@ using osu.Game.Input.Bindings;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Volume
|
namespace osu.Game.Overlays.Volume
|
||||||
{
|
{
|
||||||
public class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>, IHandleGlobalInput
|
public class VolumeControlReceptor : Container, IScrollBindingHandler<GlobalAction>, IHandleGlobalInput
|
||||||
{
|
{
|
||||||
public Func<GlobalAction, bool> ActionRequested;
|
public Func<GlobalAction, bool> ActionRequested;
|
||||||
|
public Func<GlobalAction, float, bool, bool> ScrollActionRequested;
|
||||||
|
|
||||||
public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false;
|
public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false;
|
||||||
|
public bool OnScroll(GlobalAction action, float amount, bool isPrecise) => ScrollActionRequested?.Invoke(action, amount, isPrecise) ?? false;
|
||||||
public bool OnReleased(GlobalAction action) => false;
|
public bool OnReleased(GlobalAction action) => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,17 +12,15 @@ using osu.Framework.Graphics.Effects;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Bindings;
|
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Input.Bindings;
|
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Volume
|
namespace osu.Game.Overlays.Volume
|
||||||
{
|
{
|
||||||
public class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
|
public class VolumeMeter : Container
|
||||||
{
|
{
|
||||||
private CircularProgress volumeCircle;
|
private CircularProgress volumeCircle;
|
||||||
private CircularProgress volumeCircleGlow;
|
private CircularProgress volumeCircleGlow;
|
||||||
@ -226,59 +224,27 @@ namespace osu.Game.Overlays.Volume
|
|||||||
|
|
||||||
private const float adjust_step = 0.05f;
|
private const float adjust_step = 0.05f;
|
||||||
|
|
||||||
public void Increase() => adjust(1);
|
public void Increase(double amount = 1, bool isPrecise = false) => adjust(amount, isPrecise);
|
||||||
public void Decrease() => adjust(-1);
|
public void Decrease(double amount = 1, bool isPrecise = false) => adjust(-amount, isPrecise);
|
||||||
|
|
||||||
private void adjust(int direction)
|
|
||||||
{
|
|
||||||
float amount = adjust_step * direction;
|
|
||||||
|
|
||||||
// handle the case where the OnPressed action was actually a mouse wheel.
|
|
||||||
// this allows for precise wheel handling.
|
|
||||||
var state = GetContainingInputManager().CurrentState;
|
|
||||||
if (state.Mouse?.ScrollDelta.Y != 0)
|
|
||||||
{
|
|
||||||
OnScroll(state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Volume += amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OnPressed(GlobalAction action)
|
|
||||||
{
|
|
||||||
if (!IsHovered) return false;
|
|
||||||
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case GlobalAction.DecreaseVolume:
|
|
||||||
Decrease();
|
|
||||||
return true;
|
|
||||||
case GlobalAction.IncreaseVolume:
|
|
||||||
Increase();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// because volume precision is set to 0.01, this local is required to keep track of more precise adjustments and only apply when possible.
|
// because volume precision is set to 0.01, this local is required to keep track of more precise adjustments and only apply when possible.
|
||||||
private double scrollAmount;
|
private double adjustAccumulator;
|
||||||
|
|
||||||
|
private void adjust(double delta, bool isPrecise)
|
||||||
|
{
|
||||||
|
adjustAccumulator += delta * adjust_step * (isPrecise ? 0.1 : 1);
|
||||||
|
if (Math.Abs(adjustAccumulator) < Bindable.Precision)
|
||||||
|
return;
|
||||||
|
Volume += adjustAccumulator;
|
||||||
|
adjustAccumulator = 0;
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnScroll(InputState state)
|
protected override bool OnScroll(InputState state)
|
||||||
{
|
{
|
||||||
scrollAmount += adjust_step * state.Mouse.ScrollDelta.Y * (state.Mouse.HasPreciseScroll ? 0.1f : 1);
|
adjust(state.Mouse.ScrollDelta.Y, state.Mouse.HasPreciseScroll);
|
||||||
|
|
||||||
if (Math.Abs(scrollAmount) < Bindable.Precision)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
Volume += scrollAmount;
|
|
||||||
scrollAmount = 0;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnReleased(GlobalAction action) => false;
|
|
||||||
|
|
||||||
private const float transition_length = 500;
|
private const float transition_length = 500;
|
||||||
|
|
||||||
protected override bool OnHover(InputState state)
|
protected override bool OnHover(InputState state)
|
||||||
|
@ -93,7 +93,7 @@ namespace osu.Game.Overlays
|
|||||||
muteButton.Current.ValueChanged += _ => Show();
|
muteButton.Current.ValueChanged += _ => Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Adjust(GlobalAction action)
|
public bool Adjust(GlobalAction action, float amount = 1, bool isPrecise = false)
|
||||||
{
|
{
|
||||||
if (!IsLoaded) return false;
|
if (!IsLoaded) return false;
|
||||||
|
|
||||||
@ -103,13 +103,13 @@ namespace osu.Game.Overlays
|
|||||||
if (State == Visibility.Hidden)
|
if (State == Visibility.Hidden)
|
||||||
Show();
|
Show();
|
||||||
else
|
else
|
||||||
volumeMeterMaster.Decrease();
|
volumeMeterMaster.Decrease(amount, isPrecise);
|
||||||
return true;
|
return true;
|
||||||
case GlobalAction.IncreaseVolume:
|
case GlobalAction.IncreaseVolume:
|
||||||
if (State == Visibility.Hidden)
|
if (State == Visibility.Hidden)
|
||||||
Show();
|
Show();
|
||||||
else
|
else
|
||||||
volumeMeterMaster.Increase();
|
volumeMeterMaster.Increase(amount, isPrecise);
|
||||||
return true;
|
return true;
|
||||||
case GlobalAction.ToggleMute:
|
case GlobalAction.ToggleMute:
|
||||||
Show();
|
Show();
|
||||||
|
@ -89,13 +89,12 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
if (HitObject.SampleControlPoint == null)
|
if (HitObject.SampleControlPoint == null)
|
||||||
throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
|
throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
|
||||||
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
|
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
|
||||||
AddInternal(Samples = new SkinnableSound(samples.Select(s => new SampleInfo
|
|
||||||
{
|
samples = samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).ToArray();
|
||||||
Bank = s.Bank ?? HitObject.SampleControlPoint.SampleBank,
|
foreach (var s in samples)
|
||||||
Name = s.Name,
|
s.Namespace = SampleNamespace;
|
||||||
Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume,
|
|
||||||
Namespace = SampleNamespace
|
AddInternal(Samples = new SkinnableSound(samples));
|
||||||
}).ToArray()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ using osu.Game.Rulesets.Objects.Types;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -196,9 +197,6 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]);
|
var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]);
|
||||||
var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]);
|
var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]);
|
||||||
|
|
||||||
// Let's not implement this for now, because this doesn't fit nicely into the bank structure
|
|
||||||
//string sampleFile = split2.Length > 4 ? split2[4] : string.Empty;
|
|
||||||
|
|
||||||
string stringBank = bank.ToString().ToLower();
|
string stringBank = bank.ToString().ToLower();
|
||||||
if (stringBank == @"none")
|
if (stringBank == @"none")
|
||||||
stringBank = null;
|
stringBank = null;
|
||||||
@ -211,6 +209,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
if (split.Length > 3)
|
if (split.Length > 3)
|
||||||
bankInfo.Volume = int.Parse(split[3]);
|
bankInfo.Volume = int.Parse(split[3]);
|
||||||
|
|
||||||
|
bankInfo.Filename = split.Length > 4 ? split[4] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -252,6 +252,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
private List<SampleInfo> convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
|
private List<SampleInfo> convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
|
||||||
{
|
{
|
||||||
|
// 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 } };
|
||||||
|
|
||||||
var soundTypes = new List<SampleInfo>
|
var soundTypes = new List<SampleInfo>
|
||||||
{
|
{
|
||||||
new SampleInfo
|
new SampleInfo
|
||||||
@ -297,14 +301,24 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
private class SampleBankInfo
|
private class SampleBankInfo
|
||||||
{
|
{
|
||||||
|
public string Filename;
|
||||||
|
|
||||||
public string Normal;
|
public string Normal;
|
||||||
public string Add;
|
public string Add;
|
||||||
public int Volume;
|
public int Volume;
|
||||||
|
|
||||||
public SampleBankInfo Clone()
|
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FileSampleInfo : SampleInfo
|
||||||
|
{
|
||||||
|
public string Filename;
|
||||||
|
|
||||||
|
public override IEnumerable<string> LookupNames => new[]
|
||||||
{
|
{
|
||||||
return (SampleBankInfo)MemberwiseClone();
|
Filename,
|
||||||
}
|
Path.ChangeExtension(Filename, null)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
|
@ -35,6 +35,12 @@ namespace osu.Game.Screens.Menu
|
|||||||
private readonly Box boxHoverLayer;
|
private readonly Box boxHoverLayer;
|
||||||
private readonly SpriteIcon icon;
|
private readonly SpriteIcon icon;
|
||||||
private readonly string sampleName;
|
private readonly string sampleName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The menu state for which we are visible for.
|
||||||
|
/// </summary>
|
||||||
|
public ButtonSystemState VisibleState = ButtonSystemState.TopLevel;
|
||||||
|
|
||||||
private readonly Action clickAction;
|
private readonly Action clickAction;
|
||||||
private readonly Key triggerKey;
|
private readonly Key triggerKey;
|
||||||
private SampleChannel sampleClick;
|
private SampleChannel sampleClick;
|
||||||
@ -51,7 +57,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Alpha = 0;
|
Alpha = 0;
|
||||||
|
|
||||||
Vector2 boxSize = new Vector2(ButtonSystem.BUTTON_WIDTH + Math.Abs(extraWidth), ButtonSystem.BUTTON_AREA_HEIGHT);
|
Vector2 boxSize = new Vector2(ButtonSystem.BUTTON_WIDTH + Math.Abs(extraWidth), ButtonArea.BUTTON_AREA_HEIGHT);
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -260,6 +266,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
this.FadeOut(800);
|
this.FadeOut(800);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ButtonState.Expanded:
|
case ButtonState.Expanded:
|
||||||
const int expand_duration = 500;
|
const int expand_duration = 500;
|
||||||
@ -276,6 +283,33 @@ namespace osu.Game.Screens.Menu
|
|||||||
StateChanged?.Invoke(State);
|
StateChanged?.Invoke(State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ButtonSystemState ButtonSystemState
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
ContractStyle = 0;
|
||||||
|
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case ButtonSystemState.Initial:
|
||||||
|
State = ButtonState.Contracted;
|
||||||
|
break;
|
||||||
|
case ButtonSystemState.EnteringMode:
|
||||||
|
ContractStyle = 1;
|
||||||
|
State = ButtonState.Contracted;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (value == VisibleState)
|
||||||
|
State = ButtonState.Expanded;
|
||||||
|
else if (value < VisibleState)
|
||||||
|
State = ButtonState.Contracted;
|
||||||
|
else
|
||||||
|
State = ButtonState.Exploded;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ButtonState
|
public enum ButtonState
|
||||||
|
148
osu.Game/Screens/Menu/ButtonArea.cs
Normal file
148
osu.Game/Screens/Menu/ButtonArea.cs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// 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;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Menu
|
||||||
|
{
|
||||||
|
public class ButtonArea : Container, IStateful<Visibility>
|
||||||
|
{
|
||||||
|
public FlowContainerWithOrigin Flow;
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => Flow;
|
||||||
|
|
||||||
|
private readonly ButtonAreaBackground buttonAreaBackground;
|
||||||
|
private Visibility state;
|
||||||
|
|
||||||
|
public const float BUTTON_AREA_HEIGHT = 100;
|
||||||
|
|
||||||
|
public ButtonArea()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
InternalChild = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Size = new Vector2(1, BUTTON_AREA_HEIGHT),
|
||||||
|
Alpha = 0,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
buttonAreaBackground = new ButtonAreaBackground(),
|
||||||
|
Flow = new FlowContainerWithOrigin
|
||||||
|
{
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(-ButtonSystem.WEDGE_WIDTH, 0),
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public ButtonSystemState ButtonSystemState
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case ButtonSystemState.Exit:
|
||||||
|
case ButtonSystemState.Initial:
|
||||||
|
case ButtonSystemState.EnteringMode:
|
||||||
|
State = Visibility.Hidden;
|
||||||
|
break;
|
||||||
|
case ButtonSystemState.TopLevel:
|
||||||
|
case ButtonSystemState.Play:
|
||||||
|
State = Visibility.Visible;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonAreaBackground.ButtonSystemState = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Visibility State
|
||||||
|
{
|
||||||
|
get => state;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == state) return;
|
||||||
|
|
||||||
|
state = value;
|
||||||
|
InternalChild.FadeTo(state == Visibility.Hidden ? 0 : 1, 300);
|
||||||
|
StateChanged?.Invoke(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<Visibility> StateChanged;
|
||||||
|
|
||||||
|
private class ButtonAreaBackground : Box, IStateful<ButtonAreaBackgroundState>
|
||||||
|
{
|
||||||
|
private ButtonAreaBackgroundState state;
|
||||||
|
|
||||||
|
public ButtonAreaBackground()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Size = new Vector2(2, 1);
|
||||||
|
Colour = OsuColour.Gray(50);
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ButtonAreaBackgroundState State
|
||||||
|
{
|
||||||
|
get => state;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == state) return;
|
||||||
|
|
||||||
|
state = value;
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case ButtonAreaBackgroundState.Flat:
|
||||||
|
this.ScaleTo(new Vector2(2, 0), 300, Easing.InSine);
|
||||||
|
break;
|
||||||
|
case ButtonAreaBackgroundState.Normal:
|
||||||
|
this.ScaleTo(Vector2.One, 400, Easing.OutQuint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
StateChanged?.Invoke(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ButtonSystemState ButtonSystemState
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
State = ButtonAreaBackgroundState.Normal;
|
||||||
|
break;
|
||||||
|
case ButtonSystemState.Initial:
|
||||||
|
case ButtonSystemState.Exit:
|
||||||
|
case ButtonSystemState.EnteringMode:
|
||||||
|
State = ButtonAreaBackgroundState.Flat;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<ButtonAreaBackgroundState> StateChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ButtonAreaBackgroundState
|
||||||
|
{
|
||||||
|
Normal,
|
||||||
|
Flat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,9 +10,8 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
@ -23,9 +22,9 @@ using OpenTK.Input;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Menu
|
namespace osu.Game.Screens.Menu
|
||||||
{
|
{
|
||||||
public class ButtonSystem : Container, IStateful<MenuState>, IKeyBindingHandler<GlobalAction>
|
public class ButtonSystem : Container, IStateful<ButtonSystemState>, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
public event Action<MenuState> StateChanged;
|
public event Action<ButtonSystemState> StateChanged;
|
||||||
|
|
||||||
public Action OnEdit;
|
public Action OnEdit;
|
||||||
public Action OnExit;
|
public Action OnExit;
|
||||||
@ -34,12 +33,6 @@ namespace osu.Game.Screens.Menu
|
|||||||
public Action OnSettings;
|
public Action OnSettings;
|
||||||
public Action OnMulti;
|
public Action OnMulti;
|
||||||
public Action OnChart;
|
public Action OnChart;
|
||||||
public Action OnTest;
|
|
||||||
|
|
||||||
private readonly FlowContainerWithOrigin buttonFlow;
|
|
||||||
|
|
||||||
//todo: make these non-internal somehow.
|
|
||||||
public const float BUTTON_AREA_HEIGHT = 100;
|
|
||||||
|
|
||||||
public const float BUTTON_WIDTH = 140f;
|
public const float BUTTON_WIDTH = 140f;
|
||||||
public const float WEDGE_WIDTH = 20;
|
public const float WEDGE_WIDTH = 20;
|
||||||
@ -55,18 +48,16 @@ namespace osu.Game.Screens.Menu
|
|||||||
this.logo.Action = onOsuLogo;
|
this.logo.Action = onOsuLogo;
|
||||||
|
|
||||||
// osuLogo.SizeForFlow relies on loading to be complete.
|
// osuLogo.SizeForFlow relies on loading to be complete.
|
||||||
buttonFlow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0);
|
buttonArea.Flow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0);
|
||||||
|
|
||||||
updateLogoState();
|
updateLogoState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Drawable iconFacade;
|
private readonly Drawable iconFacade;
|
||||||
private readonly Container buttonArea;
|
private readonly ButtonArea buttonArea;
|
||||||
private readonly Box buttonAreaBackground;
|
|
||||||
|
|
||||||
private readonly Button backButton;
|
private readonly Button backButton;
|
||||||
private readonly Button settingsButton;
|
|
||||||
|
|
||||||
private readonly List<Button> buttonsTopLevel = new List<Button>();
|
private readonly List<Button> buttonsTopLevel = new List<Button>();
|
||||||
private readonly List<Button> buttonsPlay = new List<Button>();
|
private readonly List<Button> buttonsPlay = new List<Button>();
|
||||||
@ -77,57 +68,35 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Child = buttonArea = new ButtonArea();
|
||||||
|
|
||||||
|
buttonArea.AddRange(new[]
|
||||||
{
|
{
|
||||||
buttonArea = new Container
|
new Button(@"settings", string.Empty, FontAwesome.fa_gear, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O),
|
||||||
|
backButton = new Button(@"back", @"button-back-select", FontAwesome.fa_osu_left_o, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, -WEDGE_WIDTH)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
VisibleState = ButtonSystemState.Play,
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Size = new Vector2(1, BUTTON_AREA_HEIGHT),
|
|
||||||
Alpha = 0,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
buttonAreaBackground = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Size = new Vector2(2, 1),
|
|
||||||
Colour = OsuColour.Gray(50),
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
},
|
|
||||||
buttonFlow = new FlowContainerWithOrigin
|
|
||||||
{
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Spacing = new Vector2(-WEDGE_WIDTH, 0),
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
settingsButton = new Button(@"settings", string.Empty, FontAwesome.fa_gear, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O),
|
|
||||||
backButton = new Button(@"back", string.Empty, FontAwesome.fa_osu_left_o, new Color4(51, 58, 94, 255), onBack, -WEDGE_WIDTH),
|
|
||||||
iconFacade = new Container //need a container to make the osu! icon flow properly.
|
|
||||||
{
|
|
||||||
Size = new Vector2(0, BUTTON_AREA_HEIGHT)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
CentreTarget = iconFacade
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
iconFacade = new Container //need a container to make the osu! icon flow properly.
|
||||||
|
{
|
||||||
|
Size = new Vector2(0, ButtonArea.BUTTON_AREA_HEIGHT)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
buttonArea.Flow.CentreTarget = iconFacade;
|
||||||
|
|
||||||
buttonsPlay.Add(new Button(@"solo", @"button-solo-select", FontAwesome.fa_user, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P));
|
buttonsPlay.Add(new Button(@"solo", @"button-solo-select", FontAwesome.fa_user, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P));
|
||||||
buttonsPlay.Add(new Button(@"multi", @"button-generic-select", FontAwesome.fa_users, new Color4(94, 63, 186, 255), () => OnMulti?.Invoke(), 0, Key.M));
|
buttonsPlay.Add(new Button(@"multi", @"button-generic-select", FontAwesome.fa_users, new Color4(94, 63, 186, 255), () => OnMulti?.Invoke(), 0, Key.M));
|
||||||
buttonsPlay.Add(new Button(@"chart", @"button-generic-select", FontAwesome.fa_osu_charts, new Color4(80, 53, 160, 255), () => OnChart?.Invoke()));
|
buttonsPlay.Add(new Button(@"chart", @"button-generic-select", FontAwesome.fa_osu_charts, new Color4(80, 53, 160, 255), () => OnChart?.Invoke()));
|
||||||
|
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
|
||||||
|
|
||||||
buttonsTopLevel.Add(new Button(@"play", @"button-play-select", FontAwesome.fa_osu_logo, new Color4(102, 68, 204, 255), onPlay, WEDGE_WIDTH, Key.P));
|
buttonsTopLevel.Add(new Button(@"play", @"button-play-select", FontAwesome.fa_osu_logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P));
|
||||||
buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", FontAwesome.fa_osu_edit_o, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E));
|
buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", FontAwesome.fa_osu_edit_o, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E));
|
||||||
buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", FontAwesome.fa_osu_chevron_down_o, new Color4(165, 204, 0, 255), () => OnDirect?.Invoke(), 0, Key.D));
|
buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", FontAwesome.fa_osu_chevron_down_o, new Color4(165, 204, 0, 255), () => OnDirect?.Invoke(), 0, Key.D));
|
||||||
buttonsTopLevel.Add(new Button(@"exit", string.Empty, FontAwesome.fa_osu_cross_o, new Color4(238, 51, 153, 255), onExit, 0, Key.Q));
|
buttonsTopLevel.Add(new Button(@"exit", string.Empty, FontAwesome.fa_osu_cross_o, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q));
|
||||||
|
|
||||||
buttonFlow.AddRange(buttonsPlay);
|
buttonArea.AddRange(buttonsPlay);
|
||||||
buttonFlow.AddRange(buttonsTopLevel);
|
buttonArea.AddRange(buttonsTopLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private OsuGame game;
|
private OsuGame game;
|
||||||
@ -139,40 +108,14 @@ namespace osu.Game.Screens.Menu
|
|||||||
sampleBack = audio.Sample.Get(@"Menu/button-back-select");
|
sampleBack = audio.Sample.Get(@"Menu/button-back-select");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
|
||||||
{
|
|
||||||
if (args.Repeat) return false;
|
|
||||||
|
|
||||||
switch (args.Key)
|
|
||||||
{
|
|
||||||
case Key.Space:
|
|
||||||
logo?.TriggerOnClick(state);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OnPressed(GlobalAction action)
|
public bool OnPressed(GlobalAction action)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case GlobalAction.Back:
|
case GlobalAction.Back:
|
||||||
return goBack();
|
return goBack();
|
||||||
default:
|
case GlobalAction.Select:
|
||||||
return false;
|
logo?.TriggerOnClick();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool goBack()
|
|
||||||
{
|
|
||||||
switch (State)
|
|
||||||
{
|
|
||||||
case MenuState.TopLevel:
|
|
||||||
State = MenuState.Initial;
|
|
||||||
return true;
|
|
||||||
case MenuState.Play:
|
|
||||||
backButton.TriggerOnClick();
|
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
@ -181,20 +124,20 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
public bool OnReleased(GlobalAction action) => false;
|
public bool OnReleased(GlobalAction action) => false;
|
||||||
|
|
||||||
private void onPlay()
|
private bool goBack()
|
||||||
{
|
{
|
||||||
State = MenuState.Play;
|
switch (State)
|
||||||
}
|
{
|
||||||
|
case ButtonSystemState.TopLevel:
|
||||||
private void onExit()
|
State = ButtonSystemState.Initial;
|
||||||
{
|
sampleBack?.Play();
|
||||||
OnExit?.Invoke();
|
return true;
|
||||||
}
|
case ButtonSystemState.Play:
|
||||||
|
backButton.TriggerOnClick();
|
||||||
private void onBack()
|
return true;
|
||||||
{
|
default:
|
||||||
sampleBack?.Play();
|
return false;
|
||||||
State = MenuState.TopLevel;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool onOsuLogo()
|
private bool onOsuLogo()
|
||||||
@ -203,24 +146,24 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
case MenuState.Initial:
|
case ButtonSystemState.Initial:
|
||||||
State = MenuState.TopLevel;
|
State = ButtonSystemState.TopLevel;
|
||||||
return true;
|
return true;
|
||||||
case MenuState.TopLevel:
|
case ButtonSystemState.TopLevel:
|
||||||
buttonsTopLevel.First().TriggerOnClick();
|
buttonsTopLevel.First().TriggerOnClick();
|
||||||
return false;
|
return false;
|
||||||
case MenuState.Play:
|
case ButtonSystemState.Play:
|
||||||
buttonsPlay.First().TriggerOnClick();
|
buttonsPlay.First().TriggerOnClick();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MenuState state;
|
private ButtonSystemState state = ButtonSystemState.Initial;
|
||||||
|
|
||||||
public override bool HandleKeyboardInput => state != MenuState.Exit;
|
public override bool HandleKeyboardInput => state != ButtonSystemState.Exit;
|
||||||
public override bool HandleMouseInput => state != MenuState.Exit;
|
public override bool HandleMouseInput => state != ButtonSystemState.Exit;
|
||||||
|
|
||||||
public MenuState State
|
public ButtonSystemState State
|
||||||
{
|
{
|
||||||
get { return state; }
|
get { return state; }
|
||||||
|
|
||||||
@ -228,71 +171,19 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
if (state == value) return;
|
if (state == value) return;
|
||||||
|
|
||||||
MenuState lastState = state;
|
ButtonSystemState lastState = state;
|
||||||
state = value;
|
state = value;
|
||||||
|
|
||||||
//todo: figure a more elegant way of doing this.
|
|
||||||
buttonsTopLevel.ForEach(b => b.ContractStyle = 0);
|
|
||||||
buttonsPlay.ForEach(b => b.ContractStyle = 0);
|
|
||||||
backButton.ContractStyle = 0;
|
|
||||||
settingsButton.ContractStyle = 0;
|
|
||||||
|
|
||||||
updateLogoState(lastState);
|
updateLogoState(lastState);
|
||||||
|
|
||||||
using (buttonArea.BeginDelayedSequence(lastState == MenuState.Initial ? 150 : 0, true))
|
Logger.Log($"{nameof(ButtonSystem)}'s state changed from {lastState} to {state}");
|
||||||
|
|
||||||
|
using (buttonArea.BeginDelayedSequence(lastState == ButtonSystemState.Initial ? 150 : 0, true))
|
||||||
{
|
{
|
||||||
switch (state)
|
buttonArea.ButtonSystemState = state;
|
||||||
{
|
|
||||||
case MenuState.Exit:
|
|
||||||
case MenuState.Initial:
|
|
||||||
buttonAreaBackground.ScaleTo(Vector2.One, 500, Easing.Out);
|
|
||||||
buttonArea.FadeOut(300);
|
|
||||||
|
|
||||||
foreach (Button b in buttonsTopLevel)
|
foreach (var b in buttonArea.Children.OfType<Button>())
|
||||||
b.State = ButtonState.Contracted;
|
b.ButtonSystemState = state;
|
||||||
|
|
||||||
foreach (Button b in buttonsPlay)
|
|
||||||
b.State = ButtonState.Contracted;
|
|
||||||
|
|
||||||
if (state != MenuState.Exit && lastState == MenuState.TopLevel)
|
|
||||||
sampleBack?.Play();
|
|
||||||
break;
|
|
||||||
case MenuState.TopLevel:
|
|
||||||
buttonAreaBackground.ScaleTo(Vector2.One, 200, Easing.Out);
|
|
||||||
|
|
||||||
buttonArea.FadeIn(300);
|
|
||||||
|
|
||||||
foreach (Button b in buttonsTopLevel)
|
|
||||||
b.State = ButtonState.Expanded;
|
|
||||||
|
|
||||||
foreach (Button b in buttonsPlay)
|
|
||||||
b.State = ButtonState.Contracted;
|
|
||||||
break;
|
|
||||||
case MenuState.Play:
|
|
||||||
foreach (Button b in buttonsTopLevel)
|
|
||||||
b.State = ButtonState.Exploded;
|
|
||||||
|
|
||||||
foreach (Button b in buttonsPlay)
|
|
||||||
b.State = ButtonState.Expanded;
|
|
||||||
break;
|
|
||||||
case MenuState.EnteringMode:
|
|
||||||
buttonAreaBackground.ScaleTo(new Vector2(2, 0), 300, Easing.InSine);
|
|
||||||
|
|
||||||
buttonsTopLevel.ForEach(b => b.ContractStyle = 1);
|
|
||||||
buttonsPlay.ForEach(b => b.ContractStyle = 1);
|
|
||||||
backButton.ContractStyle = 1;
|
|
||||||
settingsButton.ContractStyle = 1;
|
|
||||||
|
|
||||||
foreach (Button b in buttonsTopLevel)
|
|
||||||
b.State = ButtonState.Contracted;
|
|
||||||
|
|
||||||
foreach (Button b in buttonsPlay)
|
|
||||||
b.State = ButtonState.Contracted;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
backButton.State = state == MenuState.Play ? ButtonState.Expanded : ButtonState.Contracted;
|
|
||||||
settingsButton.State = state == MenuState.TopLevel ? ButtonState.Expanded : ButtonState.Contracted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StateChanged?.Invoke(State);
|
StateChanged?.Invoke(State);
|
||||||
@ -301,14 +192,14 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
private ScheduledDelegate logoDelayedAction;
|
private ScheduledDelegate logoDelayedAction;
|
||||||
|
|
||||||
private void updateLogoState(MenuState lastState = MenuState.Initial)
|
private void updateLogoState(ButtonSystemState lastState = ButtonSystemState.Initial)
|
||||||
{
|
{
|
||||||
if (logo == null) return;
|
if (logo == null) return;
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case MenuState.Exit:
|
case ButtonSystemState.Exit:
|
||||||
case MenuState.Initial:
|
case ButtonSystemState.Initial:
|
||||||
logoDelayedAction?.Cancel();
|
logoDelayedAction?.Cancel();
|
||||||
logoDelayedAction = Scheduler.AddDelayed(() =>
|
logoDelayedAction = Scheduler.AddDelayed(() =>
|
||||||
{
|
{
|
||||||
@ -316,7 +207,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
if (game != null)
|
if (game != null)
|
||||||
{
|
{
|
||||||
game.OverlayActivationMode.Value = state == MenuState.Exit ? OverlayActivation.Disabled : OverlayActivation.All;
|
game.OverlayActivationMode.Value = state == ButtonSystemState.Exit ? OverlayActivation.Disabled : OverlayActivation.All;
|
||||||
game.Toolbar.Hide();
|
game.Toolbar.Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,22 +218,22 @@ namespace osu.Game.Screens.Menu
|
|||||||
logo.ScaleTo(1, 800, Easing.OutExpo);
|
logo.ScaleTo(1, 800, Easing.OutExpo);
|
||||||
}, buttonArea.Alpha * 150);
|
}, buttonArea.Alpha * 150);
|
||||||
break;
|
break;
|
||||||
case MenuState.TopLevel:
|
case ButtonSystemState.TopLevel:
|
||||||
case MenuState.Play:
|
case ButtonSystemState.Play:
|
||||||
switch (lastState)
|
switch (lastState)
|
||||||
{
|
{
|
||||||
case MenuState.TopLevel: // coming from toplevel to play
|
case ButtonSystemState.TopLevel: // coming from toplevel to play
|
||||||
break;
|
break;
|
||||||
case MenuState.Initial:
|
case ButtonSystemState.Initial:
|
||||||
logo.ClearTransforms(targetMember: nameof(Position));
|
logo.ClearTransforms(targetMember: nameof(Position));
|
||||||
logo.RelativePositionAxes = Axes.None;
|
logo.RelativePositionAxes = Axes.None;
|
||||||
|
|
||||||
bool impact = logo.Scale.X > 0.6f;
|
bool impact = logo.Scale.X > 0.6f;
|
||||||
|
|
||||||
if (lastState == MenuState.Initial)
|
if (lastState == ButtonSystemState.Initial)
|
||||||
logo.ScaleTo(0.5f, 200, Easing.In);
|
logo.ScaleTo(0.5f, 200, Easing.In);
|
||||||
|
|
||||||
logo.MoveTo(logoTrackingPosition, lastState == MenuState.EnteringMode ? 0 : 200, Easing.In);
|
logo.MoveTo(logoTrackingPosition, lastState == ButtonSystemState.EnteringMode ? 0 : 200, Easing.In);
|
||||||
|
|
||||||
logoDelayedAction?.Cancel();
|
logoDelayedAction?.Cancel();
|
||||||
logoDelayedAction = Scheduler.AddDelayed(() =>
|
logoDelayedAction = Scheduler.AddDelayed(() =>
|
||||||
@ -368,7 +259,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case MenuState.EnteringMode:
|
case ButtonSystemState.EnteringMode:
|
||||||
logoTracking = true;
|
logoTracking = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -387,7 +278,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
if (logo != null)
|
if (logo != null)
|
||||||
{
|
{
|
||||||
if (logoTracking)
|
if (logoTracking && iconFacade.IsLoaded)
|
||||||
logo.Position = logoTrackingPosition;
|
logo.Position = logoTrackingPosition;
|
||||||
|
|
||||||
iconFacade.Width = logo.SizeForFlow * 0.5f;
|
iconFacade.Width = logo.SizeForFlow * 0.5f;
|
||||||
@ -395,12 +286,12 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MenuState
|
public enum ButtonSystemState
|
||||||
{
|
{
|
||||||
|
Exit,
|
||||||
Initial,
|
Initial,
|
||||||
TopLevel,
|
TopLevel,
|
||||||
Play,
|
Play,
|
||||||
EnteringMode,
|
EnteringMode,
|
||||||
Exit,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,9 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
private readonly ButtonSystem buttons;
|
private readonly ButtonSystem buttons;
|
||||||
|
|
||||||
protected override bool HideOverlaysOnEnter => buttons.State == MenuState.Initial;
|
protected override bool HideOverlaysOnEnter => buttons.State == ButtonSystemState.Initial;
|
||||||
|
|
||||||
protected override bool AllowBackButton => buttons.State != MenuState.Initial;
|
protected override bool AllowBackButton => buttons.State != ButtonSystemState.Initial;
|
||||||
|
|
||||||
private readonly BackgroundScreenDefault background;
|
private readonly BackgroundScreenDefault background;
|
||||||
private Screen songSelect;
|
private Screen songSelect;
|
||||||
@ -123,7 +123,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
if (resuming)
|
if (resuming)
|
||||||
{
|
{
|
||||||
buttons.State = MenuState.TopLevel;
|
buttons.State = ButtonSystemState.TopLevel;
|
||||||
|
|
||||||
const float length = 300;
|
const float length = 300;
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
const float length = 400;
|
const float length = 400;
|
||||||
|
|
||||||
buttons.State = MenuState.EnteringMode;
|
buttons.State = ButtonSystemState.EnteringMode;
|
||||||
|
|
||||||
Content.FadeOut(length, Easing.InSine);
|
Content.FadeOut(length, Easing.InSine);
|
||||||
Content.MoveTo(new Vector2(-800, 0), length, Easing.InSine);
|
Content.MoveTo(new Vector2(-800, 0), length, Easing.InSine);
|
||||||
@ -175,7 +175,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
protected override bool OnExiting(Screen next)
|
protected override bool OnExiting(Screen next)
|
||||||
{
|
{
|
||||||
buttons.State = MenuState.Exit;
|
buttons.State = ButtonSystemState.Exit;
|
||||||
Content.FadeOut(3000);
|
Content.FadeOut(3000);
|
||||||
return base.OnExiting(next);
|
return base.OnExiting(next);
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ namespace osu.Game.Screens
|
|||||||
sampleExit = audio.Sample.Get(@"UI/screen-back");
|
sampleExit = audio.Sample.Get(@"UI/screen-back");
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnPressed(GlobalAction action)
|
public virtual bool OnPressed(GlobalAction action)
|
||||||
{
|
{
|
||||||
if (!IsCurrentScreen) return false;
|
if (!IsCurrentScreen) return false;
|
||||||
|
|
||||||
|
@ -55,6 +55,8 @@ namespace osu.Game.Screens.Select
|
|||||||
private readonly Box box;
|
private readonly Box box;
|
||||||
private readonly Box light;
|
private readonly Box light;
|
||||||
|
|
||||||
|
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => box.ReceiveMouseInputAt(screenSpacePos);
|
||||||
|
|
||||||
public FooterButton()
|
public FooterButton()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
|
@ -46,7 +46,9 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
public void UpdateRank(ScoreRank newRank)
|
public void UpdateRank(ScoreRank newRank)
|
||||||
{
|
{
|
||||||
Rank = newRank;
|
Rank = newRank;
|
||||||
updateTexture();
|
|
||||||
|
if (IsLoaded)
|
||||||
|
updateTexture();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ using osu.Framework.Threading;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Screens.Backgrounds;
|
using osu.Game.Screens.Backgrounds;
|
||||||
@ -67,6 +68,7 @@ namespace osu.Game.Screens.Select
|
|||||||
protected new readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
protected new readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||||
|
|
||||||
private DependencyContainer dependencies;
|
private DependencyContainer dependencies;
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||||
=> dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
=> dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||||
|
|
||||||
@ -136,7 +138,11 @@ namespace osu.Game.Screens.Select
|
|||||||
Height = filter_height,
|
Height = filter_height,
|
||||||
FilterChanged = c => Carousel.Filter(c),
|
FilterChanged = c => Carousel.Filter(c),
|
||||||
Background = { Width = 2 },
|
Background = { Width = 2 },
|
||||||
Exit = Exit,
|
Exit = () =>
|
||||||
|
{
|
||||||
|
if (IsCurrentScreen)
|
||||||
|
Exit();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -187,8 +193,6 @@ namespace osu.Game.Screens.Select
|
|||||||
dependencies.CacheAs(Ruleset);
|
dependencies.CacheAs(Ruleset);
|
||||||
dependencies.CacheAs<IBindable<RulesetInfo>>(Ruleset);
|
dependencies.CacheAs<IBindable<RulesetInfo>>(Ruleset);
|
||||||
|
|
||||||
base.Ruleset.ValueChanged += r => updateSelectedBeatmap(beatmapNoDebounce);
|
|
||||||
|
|
||||||
if (Footer != null)
|
if (Footer != null)
|
||||||
{
|
{
|
||||||
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
|
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
|
||||||
@ -216,6 +220,12 @@ namespace osu.Game.Screens.Select
|
|||||||
Beatmap.BindValueChanged(workingBeatmapChanged);
|
Beatmap.BindValueChanged(workingBeatmapChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
base.Ruleset.ValueChanged += r => updateSelectedBeatmap(beatmapNoDebounce);
|
||||||
|
}
|
||||||
|
|
||||||
public void Edit(BeatmapInfo beatmap)
|
public void Edit(BeatmapInfo beatmap)
|
||||||
{
|
{
|
||||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
|
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
|
||||||
@ -229,6 +239,10 @@ namespace osu.Game.Screens.Select
|
|||||||
/// <param name="performStartAction">Whether to trigger <see cref="OnStart"/>.</param>
|
/// <param name="performStartAction">Whether to trigger <see cref="OnStart"/>.</param>
|
||||||
public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true)
|
public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true)
|
||||||
{
|
{
|
||||||
|
// avoid attempting to continue before a selection has been obtained.
|
||||||
|
// this could happen via a user interaction while the carousel is still in a loading state.
|
||||||
|
if (Carousel.SelectedBeatmap == null) return;
|
||||||
|
|
||||||
// if we have a pending filter operation, we want to run it now.
|
// if we have a pending filter operation, we want to run it now.
|
||||||
// it could change selection (ie. if the ruleset has been changed).
|
// it could change selection (ie. if the ruleset has been changed).
|
||||||
Carousel.FlushPendingFilterOperations();
|
Carousel.FlushPendingFilterOperations();
|
||||||
@ -464,7 +478,8 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
private void carouselBeatmapsLoaded()
|
private void carouselBeatmapsLoaded()
|
||||||
{
|
{
|
||||||
if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false && Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false))
|
if (!Beatmap.IsDefault && Beatmap.Value.BeatmapSetInfo?.DeletePending == false && Beatmap.Value.BeatmapSetInfo?.Protected == false
|
||||||
|
&& Carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Carousel.SelectedBeatmapSet == null && !Carousel.SelectNextRandom())
|
if (Carousel.SelectedBeatmapSet == null && !Carousel.SelectNextRandom())
|
||||||
@ -481,16 +496,26 @@ namespace osu.Game.Screens.Select
|
|||||||
dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap));
|
dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool OnPressed(GlobalAction action)
|
||||||
|
{
|
||||||
|
if (!IsCurrentScreen) return false;
|
||||||
|
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case GlobalAction.Select:
|
||||||
|
FinaliseSelection();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnPressed(action);
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||||
{
|
{
|
||||||
if (args.Repeat) return false;
|
if (args.Repeat) return false;
|
||||||
|
|
||||||
switch (args.Key)
|
switch (args.Key)
|
||||||
{
|
{
|
||||||
case Key.KeypadEnter:
|
|
||||||
case Key.Enter:
|
|
||||||
FinaliseSelection();
|
|
||||||
return true;
|
|
||||||
case Key.Delete:
|
case Key.Delete:
|
||||||
if (state.Keyboard.ShiftPressed)
|
if (state.Keyboard.ShiftPressed)
|
||||||
{
|
{
|
||||||
|
@ -44,19 +44,17 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
private SampleChannel loadChannel(SampleInfo info, Func<string, SampleChannel> getSampleFunction)
|
private SampleChannel loadChannel(SampleInfo info, Func<string, SampleChannel> getSampleFunction)
|
||||||
{
|
{
|
||||||
SampleChannel ch = null;
|
foreach (var lookup in info.LookupNames)
|
||||||
|
{
|
||||||
|
var ch = getSampleFunction($"Gameplay/{lookup}");
|
||||||
|
if (ch == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (info.Namespace != null)
|
|
||||||
ch = getSampleFunction($"Gameplay/{info.Namespace}/{info.Bank}-{info.Name}");
|
|
||||||
|
|
||||||
// try without namespace as a fallback.
|
|
||||||
if (ch == null)
|
|
||||||
ch = getSampleFunction($"Gameplay/{info.Bank}-{info.Name}");
|
|
||||||
|
|
||||||
if (ch != null)
|
|
||||||
ch.Volume.Value = info.Volume / 100.0;
|
ch.Volume.Value = info.Volume / 100.0;
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
return ch;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
|
|
||||||
protected abstract string ResourceAssembly { get; }
|
protected abstract string ResourceAssembly { get; }
|
||||||
|
|
||||||
|
protected IBeatmapConverter Converter { get; private set; }
|
||||||
|
|
||||||
protected void Test(string name)
|
protected void Test(string name)
|
||||||
{
|
{
|
||||||
var ourResult = convert(name);
|
var ourResult = convert(name);
|
||||||
@ -41,14 +43,22 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
Assert.Fail($"A conversion did not generate any hitobjects, but should have, for hitobject at time: {expectedResult.Mappings[mappingCounter].StartTime}\n");
|
Assert.Fail($"A conversion did not generate any hitobjects, but should have, for hitobject at time: {expectedResult.Mappings[mappingCounter].StartTime}\n");
|
||||||
else if (mappingCounter >= expectedResult.Mappings.Count)
|
else if (mappingCounter >= expectedResult.Mappings.Count)
|
||||||
Assert.Fail($"A conversion generated hitobjects, but should not have, for hitobject at time: {ourResult.Mappings[mappingCounter].StartTime}\n");
|
Assert.Fail($"A conversion generated hitobjects, but should not have, for hitobject at time: {ourResult.Mappings[mappingCounter].StartTime}\n");
|
||||||
|
else if (!expectedResult.Mappings[mappingCounter].Equals(ourResult.Mappings[mappingCounter]))
|
||||||
|
{
|
||||||
|
var expectedMapping = expectedResult.Mappings[mappingCounter];
|
||||||
|
var ourMapping = ourResult.Mappings[mappingCounter];
|
||||||
|
|
||||||
|
Assert.Fail($"The conversion mapping differed for object at time {expectedMapping.StartTime}:\n"
|
||||||
|
+ $"Expected {JsonConvert.SerializeObject(expectedMapping)}\n"
|
||||||
|
+ $"Received: {JsonConvert.SerializeObject(ourMapping)}\n");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var counter = mappingCounter;
|
var ourMapping = ourResult.Mappings[mappingCounter];
|
||||||
|
var expectedMapping = expectedResult.Mappings[mappingCounter];
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
var ourMapping = ourResult.Mappings[counter];
|
|
||||||
var expectedMapping = expectedResult.Mappings[counter];
|
|
||||||
|
|
||||||
int objectCounter = 0;
|
int objectCounter = 0;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@ -60,10 +70,6 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
else if (objectCounter >= expectedMapping.Objects.Count)
|
else if (objectCounter >= expectedMapping.Objects.Count)
|
||||||
Assert.Fail($"The conversion generated a hitobject, but should not have, for hitobject at time: {ourMapping.StartTime}:\n"
|
Assert.Fail($"The conversion generated a hitobject, but should not have, for hitobject at time: {ourMapping.StartTime}:\n"
|
||||||
+ $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
|
+ $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
|
||||||
else if (!expectedMapping.Equals(ourMapping))
|
|
||||||
Assert.Fail($"The conversion mapping differed for object at time {expectedMapping.StartTime}:\n"
|
|
||||||
+ $"Expected {JsonConvert.SerializeObject(expectedMapping)}\n"
|
|
||||||
+ $"Received: {JsonConvert.SerializeObject(ourMapping)}\n");
|
|
||||||
else if (!expectedMapping.Objects[objectCounter].Equals(ourMapping.Objects[objectCounter]))
|
else if (!expectedMapping.Objects[objectCounter].Equals(ourMapping.Objects[objectCounter]))
|
||||||
{
|
{
|
||||||
Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}:\n"
|
Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}:\n"
|
||||||
@ -88,10 +94,11 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
var rulesetInstance = CreateRuleset();
|
var rulesetInstance = CreateRuleset();
|
||||||
beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo();
|
beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo();
|
||||||
|
|
||||||
var result = new ConvertResult();
|
Converter = rulesetInstance.CreateBeatmapConverter(beatmap);
|
||||||
var converter = rulesetInstance.CreateBeatmapConverter(beatmap);
|
|
||||||
|
|
||||||
converter.ObjectConverted += (orig, converted) =>
|
var result = new ConvertResult();
|
||||||
|
|
||||||
|
Converter.ObjectConverted += (orig, converted) =>
|
||||||
{
|
{
|
||||||
converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty));
|
converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty));
|
||||||
|
|
||||||
@ -103,7 +110,7 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
result.Mappings.Add(mapping);
|
result.Mappings.Add(mapping);
|
||||||
};
|
};
|
||||||
|
|
||||||
IBeatmap convertedBeatmap = converter.Convert();
|
IBeatmap convertedBeatmap = Converter.Convert();
|
||||||
rulesetInstance.CreateBeatmapProcessor(convertedBeatmap)?.PostProcess();
|
rulesetInstance.CreateBeatmapProcessor(convertedBeatmap)?.PostProcess();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -42,7 +42,9 @@ namespace osu.Game.Users
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
country = value;
|
country = value;
|
||||||
sprite.Texture = getFlagTexture();
|
|
||||||
|
if (IsLoaded)
|
||||||
|
sprite.Texture = getFlagTexture();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,9 +23,6 @@ namespace osu.Game.Users
|
|||||||
|
|
||||||
public Bindable<UserStatus> Status = new Bindable<UserStatus>();
|
public Bindable<UserStatus> Status = new Bindable<UserStatus>();
|
||||||
|
|
||||||
[JsonProperty(@"age")]
|
|
||||||
public int? Age;
|
|
||||||
|
|
||||||
//public Team Team;
|
//public Team Team;
|
||||||
|
|
||||||
[JsonProperty(@"profile_colour")]
|
[JsonProperty(@"profile_colour")]
|
||||||
|
@ -14,11 +14,11 @@
|
|||||||
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" />
|
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Humanizer" Version="2.3.3" />
|
<PackageReference Include="Humanizer" Version="2.4.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2018.629.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2018.710.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.17.1" />
|
<PackageReference Include="SharpCompress" Version="0.17.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
|
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassWithVirtualMembersNeverInherited_002EGlobal/@EntryIndexedValue">HINT</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ClassWithVirtualMembersNeverInherited_002EGlobal/@EntryIndexedValue">HINT</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CollectionNeverQueried_002EGlobal/@EntryIndexedValue">SUGGESTION</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CollectionNeverQueried_002EGlobal/@EntryIndexedValue">SUGGESTION</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CollectionNeverQueried_002ELocal/@EntryIndexedValue">HINT</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CollectionNeverQueried_002ELocal/@EntryIndexedValue">HINT</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CommentTypo/@EntryIndexedValue">HINT</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CompareOfFloatsByEqualityOperator/@EntryIndexedValue">HINT</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CompareOfFloatsByEqualityOperator/@EntryIndexedValue">HINT</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertClosureToMethodGroup/@EntryIndexedValue">WARNING</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertClosureToMethodGroup/@EntryIndexedValue">WARNING</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfDoToWhile/@EntryIndexedValue">WARNING</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConvertIfDoToWhile/@EntryIndexedValue">WARNING</s:String>
|
||||||
@ -43,6 +44,7 @@
|
|||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=FieldCanBeMadeReadOnly_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=FieldCanBeMadeReadOnly_002EGlobal/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=FieldCanBeMadeReadOnly_002ELocal/@EntryIndexedValue">WARNING</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=FieldCanBeMadeReadOnly_002ELocal/@EntryIndexedValue">WARNING</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ForCanBeConvertedToForeach/@EntryIndexedValue">WARNING</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ForCanBeConvertedToForeach/@EntryIndexedValue">WARNING</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=IdentifierTypo/@EntryIndexedValue">HINT</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ImpureMethodCallOnReadonlyValueField/@EntryIndexedValue">HINT</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ImpureMethodCallOnReadonlyValueField/@EntryIndexedValue">HINT</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InconsistentNaming/@EntryIndexedValue">ERROR</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InconsistentNaming/@EntryIndexedValue">ERROR</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InheritdocConsiderUsage/@EntryIndexedValue">HINT</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=InheritdocConsiderUsage/@EntryIndexedValue">HINT</s:String>
|
||||||
@ -133,6 +135,7 @@
|
|||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleOrDefault_002E2/@EntryIndexedValue">WARNING</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleOrDefault_002E2/@EntryIndexedValue">WARNING</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleOrDefault_002E3/@EntryIndexedValue">WARNING</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleOrDefault_002E3/@EntryIndexedValue">WARNING</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleOrDefault_002E4/@EntryIndexedValue">WARNING</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleOrDefault_002E4/@EntryIndexedValue">WARNING</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringLiteralTypo/@EntryIndexedValue">HINT</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FBuiltInTypes/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FBuiltInTypes/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FSimpleTypes/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FSimpleTypes/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SwitchStatementMissingSomeCases/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SwitchStatementMissingSomeCases/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||||
@ -666,4 +669,22 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
|||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Beatmap/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=beatmaps/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=beatmap_0027s/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=bindable/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Catmull/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Drawables/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=gameplay/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=hitobjects/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=keymods/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kiai/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Leaderboard/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Leaderboards/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Playfield/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=resampler/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=ruleset/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=rulesets/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Taiko/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unranked/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
|
Loading…
Reference in New Issue
Block a user