mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 18:27:26 +08:00
Merge remote-tracking branch 'upstream/master' into select_resolution_in_fullscreen_mode
This commit is contained in:
commit
46b6601cbf
@ -24,9 +24,9 @@ Clone the repository including submodules
|
||||
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`
|
||||
- From command line using `dotnet run --project osu.Desktop`
|
||||
|
||||
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`).
|
||||
If you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`). Visual Studio Code users must run `Restore` task from debug tab before attempt to build.
|
||||
|
||||
# Contributing
|
||||
|
||||
|
11
appveyor.yml
11
appveyor.yml
@ -2,20 +2,21 @@ clone_depth: 1
|
||||
version: '{branch}-{build}'
|
||||
image: Visual Studio 2017
|
||||
configuration: Debug
|
||||
cache:
|
||||
- C:\ProgramData\chocolatey\bin -> appveyor.yml
|
||||
- C:\ProgramData\chocolatey\lib -> appveyor.yml
|
||||
install:
|
||||
- cmd: git submodule update --init --recursive --depth=5
|
||||
- cmd: choco install resharper-clt -y
|
||||
- cmd: choco install nvika -y
|
||||
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.5/CodeFileSanity.exe
|
||||
- cmd: dotnet tool install CodeFileSanity --version 0.0.16 --global
|
||||
before_build:
|
||||
- cmd: CodeFileSanity.exe
|
||||
- cmd: CodeFileSanity
|
||||
- cmd: nuget restore -verbosity quiet
|
||||
environment:
|
||||
TargetFramework: net471
|
||||
build:
|
||||
project: osu.sln
|
||||
parallel: true
|
||||
verbosity: minimal
|
||||
after_build:
|
||||
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
|
||||
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
|
||||
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
|
||||
|
@ -16,17 +16,15 @@ build_script:
|
||||
- cd osu-deploy
|
||||
- nuget restore -verbosity quiet
|
||||
- msbuild osu.Desktop.Deploy.csproj
|
||||
- cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\net471\osu.Desktop.Deploy.exe.config
|
||||
- cd bin\Debug\net471\
|
||||
- osu.Desktop.Deploy.exe %code_signing_password% %APPVEYOR_REPO_TAG_NAME%
|
||||
- cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\netcoreapp2.1\osu.Desktop.Deploy.dll.config
|
||||
- dotnet bin/Debug/netcoreapp2.1/osu.Desktop.Deploy.dll %code_signing_password% %APPVEYOR_REPO_TAG_NAME%
|
||||
environment:
|
||||
TargetFramework: net471
|
||||
decode_secret:
|
||||
secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY=
|
||||
code_signing_password:
|
||||
secure: 34tLNqvjmmZEi97MLKfrnQ==
|
||||
artifacts:
|
||||
- path: 'Releases\*'
|
||||
- path: 'osu-deploy/releases/*'
|
||||
deploy:
|
||||
- provider: Environment
|
||||
name: github
|
@ -12,6 +12,8 @@ using osu.Framework.Platform;
|
||||
using osu.Game;
|
||||
using OpenTK.Input;
|
||||
using Microsoft.Win32;
|
||||
using osu.Desktop.Updater;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Platform.Windows;
|
||||
|
||||
namespace osu.Desktop
|
||||
@ -38,6 +40,51 @@ 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 (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
|
||||
Add(new SquirrelUpdateManager());
|
||||
else
|
||||
Add(new SimpleUpdateManager());
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
/// A method of accessing an osu-stable install in a controlled fashion.
|
||||
/// </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()
|
||||
|
@ -4,12 +4,13 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Development;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.IPC;
|
||||
#if NET_FRAMEWORK
|
||||
using System.Runtime;
|
||||
#endif
|
||||
|
||||
namespace osu.Desktop
|
||||
{
|
||||
@ -18,16 +19,13 @@ namespace osu.Desktop
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
// required to initialise native SQLite libraries on some platforms.
|
||||
|
||||
if (!RuntimeInfo.IsMono)
|
||||
useMulticoreJit();
|
||||
|
||||
// Back up the cwd before DesktopGameHost changes it
|
||||
var cwd = Environment.CurrentDirectory;
|
||||
|
||||
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
|
||||
{
|
||||
host.ExceptionThrown += handleException;
|
||||
|
||||
if (!host.IsPrimaryInstance)
|
||||
{
|
||||
var importer = new ArchiveImportIPCChannel(host);
|
||||
@ -54,13 +52,23 @@ namespace osu.Desktop
|
||||
}
|
||||
}
|
||||
|
||||
private static void useMulticoreJit()
|
||||
private static int allowableExceptions = DebugUtils.IsDebugBuild ? 0 : 1;
|
||||
|
||||
/// <summary>
|
||||
/// Allow a maximum of one unhandled exception, per second of execution.
|
||||
/// </summary>
|
||||
/// <param name="arg"></param>
|
||||
/// <returns></returns>
|
||||
private static bool handleException(Exception arg)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
|
||||
ProfileOptimization.SetProfileRoot(directory.FullName);
|
||||
ProfileOptimization.StartProfile("Startup.Profile");
|
||||
#endif
|
||||
bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0;
|
||||
|
||||
Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {allowableExceptions} more allowable exceptions" : "denied")} .");
|
||||
|
||||
// restore the stock of allowable exceptions after a short delay.
|
||||
Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions));
|
||||
|
||||
return continueExecution;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
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(".app.zip"));
|
||||
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; }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
@ -16,7 +16,7 @@ using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using Squirrel;
|
||||
|
||||
namespace osu.Desktop.Overlays
|
||||
namespace osu.Desktop.Updater
|
||||
{
|
||||
public class SquirrelUpdateManager : Component
|
||||
{
|
||||
@ -35,7 +35,7 @@ namespace osu.Desktop.Overlays
|
||||
notificationOverlay = notification;
|
||||
|
||||
if (game.IsDeployedBuild)
|
||||
Schedule(() => checkForUpdateAsync());
|
||||
Schedule(() => Task.Run(() => checkForUpdateAsync()));
|
||||
}
|
||||
|
||||
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
|
||||
@ -161,4 +161,3 @@ namespace osu.Desktop.Overlays
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.Game.props" />
|
||||
<PropertyGroup Label="Project">
|
||||
<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@ -13,9 +13,6 @@
|
||||
<Version>0.0.0</Version>
|
||||
<FileVersion>0.0.0</FileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Defines">
|
||||
<DefineConstants Condition="'$(TargetFramework)' == 'net471'">$(DefineConstants);NET_FRAMEWORK</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>osu.Desktop.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
@ -29,11 +26,12 @@
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.1" />
|
||||
<PackageReference Include="squirrel.windows" Version="1.8.0" Condition="'$(TargetFramework)' == 'net471'" />
|
||||
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
|
||||
<PackageReference Include="ppy.squirrel.windows" Version="1.8.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Resources">
|
||||
<EmbeddedResource Include="lazer.ico" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<metadata>
|
||||
<id>osulazer</id>
|
||||
<version>0.0.0</version>
|
||||
<title>osulazer</title>
|
||||
<title>osu!lazer</title>
|
||||
<authors>ppy Pty Ltd</authors>
|
||||
<owners>Dean Herbert</owners>
|
||||
<projectUrl>https://osu.ppy.sh/</projectUrl>
|
||||
|
@ -2,35 +2,7 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "VisualTests (Debug, net471)",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
"type": "mono",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Debug, msbuild)",
|
||||
"runtimeExecutable": null,
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "VisualTests (Release, net471)",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
"type": "mono",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Catch.Tests.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Release, msbuild)",
|
||||
"runtimeExecutable": null,
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "VisualTests (Debug, netcoreapp2.1)",
|
||||
"name": "VisualTests (Debug)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "dotnet",
|
||||
@ -38,12 +10,12 @@
|
||||
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Debug, dotnet)",
|
||||
"preLaunchTask": "Build (Debug)",
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "VisualTests (Release, netcoreapp2.1)",
|
||||
"name": "VisualTests (Release)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "dotnet",
|
||||
@ -51,7 +23,7 @@
|
||||
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Release, dotnet)",
|
||||
"preLaunchTask": "Build (Release)",
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
}
|
||||
|
46
osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json
vendored
46
osu.Game.Rulesets.Catch.Tests/.vscode/tasks.json
vendored
@ -4,43 +4,13 @@
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build (Debug, msbuild)",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"osu.Game.Rulesets.Catch.Tests.csproj",
|
||||
"/p:TargetFramework=net471",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
"/verbosity:m"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Build (Release, msbuild)",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"osu.Game.Rulesets.Catch.Tests.csproj",
|
||||
"/p:Configuration=Release",
|
||||
"/p:TargetFramework=net471",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
"/verbosity:m"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Build (Debug, dotnet)",
|
||||
"label": "Build (Debug)",
|
||||
"type": "shell",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"build",
|
||||
"--no-restore",
|
||||
"osu.Game.Rulesets.Catch.Tests.csproj",
|
||||
"/p:TargetFramework=netcoreapp2.1",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
"/verbosity:m"
|
||||
@ -49,14 +19,13 @@
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Build (Release, dotnet)",
|
||||
"label": "Build (Release)",
|
||||
"type": "shell",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"build",
|
||||
"--no-restore",
|
||||
"osu.Game.Rulesets.Catch.Tests.csproj",
|
||||
"/p:TargetFramework=netcoreapp2.1",
|
||||
"/p:Configuration=Release",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
@ -66,16 +35,7 @@
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Restore (net471)",
|
||||
"type": "shell",
|
||||
"command": "nuget",
|
||||
"args": [
|
||||
"restore"
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Restore (netcoreapp2.1)",
|
||||
"label": "Restore",
|
||||
"type": "shell",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
|
@ -28,18 +28,20 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
if (hitObject is JuiceStream stream)
|
||||
switch (hitObject)
|
||||
{
|
||||
foreach (var nested in stream.NestedHitObjects)
|
||||
yield return new ConvertValue((CatchHitObject)nested);
|
||||
case JuiceStream stream:
|
||||
foreach (var nested in stream.NestedHitObjects)
|
||||
yield return new ConvertValue((CatchHitObject)nested);
|
||||
break;
|
||||
case BananaShower shower:
|
||||
foreach (var nested in shower.NestedHitObjects)
|
||||
yield return new ConvertValue((CatchHitObject)nested);
|
||||
break;
|
||||
default:
|
||||
yield return new ConvertValue((CatchHitObject)hitObject);
|
||||
break;
|
||||
}
|
||||
else if (hitObject is BananaShower shower)
|
||||
{
|
||||
foreach (var nested in shower.NestedHitObjects)
|
||||
yield return new ConvertValue((CatchHitObject)nested);
|
||||
}
|
||||
else
|
||||
yield return new ConvertValue((CatchHitObject)hitObject);
|
||||
}
|
||||
|
||||
protected override Ruleset CreateRuleset() => new CatchRuleset();
|
||||
|
@ -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
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
|
||||
public class TestCaseHyperDash : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseHyperdash()
|
||||
public TestCaseHyperDash()
|
||||
: base(new CatchRuleset())
|
||||
{
|
||||
}
|
@ -1,8 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFrameworks>netcoreapp2.1;net471</TargetFrameworks>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
||||
|
@ -41,6 +41,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
RepeatCount = curveData.RepeatCount,
|
||||
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset ?? 0
|
||||
};
|
||||
}
|
||||
@ -51,7 +52,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
StartTime = obj.StartTime,
|
||||
Samples = obj.Samples,
|
||||
Duration = endTime.Duration,
|
||||
NewCombo = comboData?.NewCombo ?? false
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||
};
|
||||
}
|
||||
else
|
||||
@ -61,6 +63,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
StartTime = obj.StartTime,
|
||||
Samples = obj.Samples,
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH
|
||||
};
|
||||
}
|
||||
|
@ -93,13 +93,11 @@ namespace osu.Game.Rulesets.Catch
|
||||
new CatchModHidden(),
|
||||
new CatchModFlashlight(),
|
||||
};
|
||||
case ModType.Special:
|
||||
case ModType.Automation:
|
||||
return new Mod[]
|
||||
{
|
||||
new CatchModRelax(),
|
||||
null,
|
||||
null,
|
||||
new MultiMod(new CatchModAutoplay(), new ModCinema()),
|
||||
new CatchModRelax(),
|
||||
};
|
||||
default:
|
||||
return new Mod[] { };
|
||||
|
@ -46,13 +46,16 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
|
||||
foreach (var hitObject in beatmap.HitObjects)
|
||||
{
|
||||
// We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
|
||||
if (hitObject is Fruit)
|
||||
switch (hitObject)
|
||||
{
|
||||
difficultyHitObjects.Add(new CatchDifficultyHitObject((CatchHitObject)hitObject, halfCatchWidth));
|
||||
// We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
|
||||
case Fruit fruit:
|
||||
difficultyHitObjects.Add(new CatchDifficultyHitObject(fruit, halfCatchWidth));
|
||||
break;
|
||||
case JuiceStream _:
|
||||
difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType<CatchHitObject>().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth)));
|
||||
break;
|
||||
}
|
||||
if (hitObject is JuiceStream)
|
||||
difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType<CatchHitObject>().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth)));
|
||||
}
|
||||
|
||||
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
|
||||
@ -61,12 +64,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
return new CatchDifficultyAttributes(mods, 0);
|
||||
|
||||
// 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;
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Judgements
|
||||
@ -9,8 +10,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
|
||||
{
|
||||
public override bool AffectsCombo => false;
|
||||
|
||||
public override bool ShouldExplode => true;
|
||||
|
||||
protected override int NumericResultFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
@ -32,5 +31,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ShouldExplodeFor(JudgementResult result) => true;
|
||||
}
|
||||
}
|
||||
|
@ -23,21 +23,10 @@ namespace osu.Game.Rulesets.Catch.Judgements
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The base health increase for the result achieved.
|
||||
/// Retrieves the numeric health increase of a <see cref="HitResult"/>.
|
||||
/// </summary>
|
||||
public float HealthIncrease => HealthIncreaseFor(Result);
|
||||
|
||||
/// <summary>
|
||||
/// Whether fruit on the platter should explode or drop.
|
||||
/// Note that this is only checked if the owning object is also <see cref="IHasComboInformation.LastInCombo" />
|
||||
/// </summary>
|
||||
public virtual bool ShouldExplode => IsHit;
|
||||
|
||||
/// <summary>
|
||||
/// Convert a <see cref="HitResult"/> to a base health increase.
|
||||
/// </summary>
|
||||
/// <param name="result">The value to convert.</param>
|
||||
/// <returns>The base health increase.</returns>
|
||||
/// <param name="result">The <see cref="HitResult"/> to find the numeric health increase for.</param>
|
||||
/// <returns>The numeric health increase of <paramref name="result"/>.</returns>
|
||||
protected virtual float HealthIncreaseFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
@ -48,5 +37,18 @@ namespace osu.Game.Rulesets.Catch.Judgements
|
||||
return 10.2f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the numeric health increase of a <see cref="JudgementResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="result">The <see cref="JudgementResult"/> to find the numeric health increase for.</param>
|
||||
/// <returns>The numeric health increase of <paramref name="result"/>.</returns>
|
||||
public float HealthIncreaseFor(JudgementResult result) => HealthIncreaseFor(result.Type);
|
||||
|
||||
/// <summary>
|
||||
/// Whether fruit on the platter should explode or drop.
|
||||
/// Note that this is only checked if the owning object is also <see cref="IHasComboInformation.LastInCombo" />
|
||||
/// </summary>
|
||||
public virtual bool ShouldExplodeFor(JudgementResult result) => result.IsHit;
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,15 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public class Banana : Fruit
|
||||
{
|
||||
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
|
||||
|
||||
public override Judgement CreateJudgement() => new CatchBananaJudgement();
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
public virtual bool NewCombo { get; set; }
|
||||
|
||||
public int ComboOffset { get; set; }
|
||||
|
||||
public int IndexInCurrentCombo { get; set; }
|
||||
|
||||
public int ComboIndex { get; set; }
|
||||
|
@ -1,8 +1,6 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
public class DrawableBanana : DrawableFruit
|
||||
@ -11,7 +9,5 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
: base(h)
|
||||
{
|
||||
}
|
||||
|
||||
protected override CatchJudgement CreateJudgement() => new CatchBananaJudgement();
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
AddNested(getVisualRepresentation?.Invoke(b));
|
||||
}
|
||||
|
||||
protected override bool ProvidesJudgement => false;
|
||||
|
||||
protected override void AddNested(DrawableHitObject h)
|
||||
{
|
||||
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
||||
|
@ -5,7 +5,6 @@ using System;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -53,20 +52,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
|
||||
public Func<CatchHitObject, bool> CheckPosition;
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (CheckPosition == null) return;
|
||||
|
||||
if (timeOffset >= 0)
|
||||
{
|
||||
var judgement = CreateJudgement();
|
||||
judgement.Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss;
|
||||
AddJudgement(judgement);
|
||||
}
|
||||
if (timeOffset >= 0 && Result != null)
|
||||
ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss);
|
||||
}
|
||||
|
||||
protected virtual CatchJudgement CreateJudgement() => new CatchJudgement();
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
|
@ -6,7 +6,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
@ -24,8 +23,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
Masking = false;
|
||||
}
|
||||
|
||||
protected override CatchJudgement CreateJudgement() => new CatchDropletJudgement();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
|
@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
AddNested(getVisualRepresentation?.Invoke(o));
|
||||
}
|
||||
|
||||
protected override bool ProvidesJudgement => false;
|
||||
|
||||
protected override void AddNested(DrawableHitObject h)
|
||||
{
|
||||
var catchObject = (DrawableCatchHitObject)h;
|
||||
|
@ -2,18 +2,15 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
public class DrawableTinyDroplet : DrawableDroplet
|
||||
{
|
||||
public DrawableTinyDroplet(Droplet h)
|
||||
public DrawableTinyDroplet(TinyDroplet h)
|
||||
: base(h)
|
||||
{
|
||||
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 8;
|
||||
}
|
||||
|
||||
protected override CatchJudgement CreateJudgement() => new CatchTinyDropletJudgement();
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public class Droplet : CatchHitObject
|
||||
{
|
||||
public override Judgement CreateJudgement() => new CatchDropletJudgement();
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public class Fruit : CatchHitObject
|
||||
{
|
||||
public override Judgement CreateJudgement() => new CatchJudgement();
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public class TinyDroplet : Droplet
|
||||
{
|
||||
public override Judgement CreateJudgement() => new CatchTinyDropletJudgement();
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.StateChanges;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
@ -21,55 +20,28 @@ namespace osu.Game.Rulesets.Catch.Scoring
|
||||
|
||||
private float hpDrainRate;
|
||||
|
||||
protected override void SimulateAutoplay(Beatmap<CatchHitObject> beatmap)
|
||||
protected override void ApplyBeatmap(Beatmap<CatchHitObject> beatmap)
|
||||
{
|
||||
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
|
||||
base.ApplyBeatmap(beatmap);
|
||||
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
switch (obj)
|
||||
{
|
||||
case JuiceStream stream:
|
||||
foreach (var nestedObject in stream.NestedHitObjects)
|
||||
switch (nestedObject)
|
||||
{
|
||||
case TinyDroplet _:
|
||||
AddJudgement(new CatchTinyDropletJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
case Droplet _:
|
||||
AddJudgement(new CatchDropletJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
case Fruit _:
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case BananaShower shower:
|
||||
foreach (var _ in shower.NestedHitObjects.Cast<CatchHitObject>())
|
||||
AddJudgement(new CatchBananaJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
case Fruit _:
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
break;
|
||||
}
|
||||
}
|
||||
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
|
||||
}
|
||||
|
||||
private const double harshness = 0.01;
|
||||
|
||||
protected override void OnNewJudgement(Judgement judgement)
|
||||
protected override void ApplyResult(JudgementResult result)
|
||||
{
|
||||
base.OnNewJudgement(judgement);
|
||||
base.ApplyResult(result);
|
||||
|
||||
if (judgement.Result == HitResult.Miss)
|
||||
if (result.Type == HitResult.Miss)
|
||||
{
|
||||
if (!judgement.IsBonus)
|
||||
if (!result.Judgement.IsBonus)
|
||||
Health.Value -= hpDrainRate * (harshness * 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (judgement is CatchJudgement catchJudgement)
|
||||
Health.Value += Math.Max(catchJudgement.HealthIncrease - hpDrainRate, 0) * harshness;
|
||||
if (result.Judgement is CatchJudgement catchJudgement)
|
||||
Health.Value += Math.Max(catchJudgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
private readonly CatcherArea catcherArea;
|
||||
|
||||
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
|
||||
: base(ScrollingDirection.Down, BASE_WIDTH)
|
||||
: base(BASE_WIDTH)
|
||||
{
|
||||
Direction.Value = ScrollingDirection.Down;
|
||||
|
||||
Container explodingFruitContainer;
|
||||
|
||||
Anchor = Anchor.TopCentre;
|
||||
@ -57,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
public override void Add(DrawableHitObject h)
|
||||
{
|
||||
h.OnJudgement += onJudgement;
|
||||
h.OnNewResult += onNewResult;
|
||||
|
||||
base.Add(h);
|
||||
|
||||
@ -65,6 +67,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
fruit.CheckPosition = CheckIfWeCanCatch;
|
||||
}
|
||||
|
||||
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement);
|
||||
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||
=> catcherArea.OnResult((DrawableCatchHitObject)judgedObject, result);
|
||||
}
|
||||
}
|
||||
|
@ -48,10 +48,13 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
private DrawableCatchHitObject lastPlateableFruit;
|
||||
|
||||
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
|
||||
public void OnResult(DrawableCatchHitObject fruit, JudgementResult result)
|
||||
{
|
||||
void runAfterLoaded(Action action)
|
||||
{
|
||||
if (lastPlateableFruit == null)
|
||||
return;
|
||||
|
||||
// this is required to make this run after the last caught fruit runs UpdateState at least once.
|
||||
// TODO: find a better alternative
|
||||
if (lastPlateableFruit.IsLoaded)
|
||||
@ -60,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
lastPlateableFruit.OnLoadComplete = _ => action();
|
||||
}
|
||||
|
||||
if (judgement.IsHit && fruit.CanBePlated)
|
||||
if (result.IsHit && fruit.CanBePlated)
|
||||
{
|
||||
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
|
||||
|
||||
@ -83,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
if (fruit.HitObject.LastInCombo)
|
||||
{
|
||||
if (((CatchJudgement)judgement).ShouldExplode)
|
||||
if (((CatchJudgement)result.Judgement).ShouldExplodeFor(result))
|
||||
runAfterLoaded(() => MovableCatcher.Explode());
|
||||
else
|
||||
MovableCatcher.Drop();
|
||||
@ -255,11 +258,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
double positionDifference = target.X * CatchPlayfield.BASE_WIDTH - catcherPosition;
|
||||
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
|
||||
{
|
||||
SetHyperdashState();
|
||||
SetHyperDashState();
|
||||
}
|
||||
|
||||
return validCatch;
|
||||
@ -270,18 +273,18 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
private float hyperDashTargetPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we are hypderdashing or not.
|
||||
/// Whether we are hyper-dashing or not.
|
||||
/// </summary>
|
||||
public bool HyperDashing => hyperDashModifier != 1;
|
||||
|
||||
/// <summary>
|
||||
/// Set hyperdash state.
|
||||
/// Set hyper-dash state.
|
||||
/// </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="targetPosition">When this catcher crosses this position, this catcher ends hyperdashing.</param>
|
||||
public void SetHyperdashState(double modifier = 1, float targetPosition = -1)
|
||||
/// <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 hyper-dashing.</param>
|
||||
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;
|
||||
if (modifier <= 1 || X == targetPosition)
|
||||
@ -291,8 +294,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
if (previouslyHyperDashing)
|
||||
{
|
||||
this.FadeColour(Color4.White, hyperdash_transition_length, Easing.OutQuint);
|
||||
this.FadeTo(1, hyperdash_transition_length, Easing.OutQuint);
|
||||
this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint);
|
||||
this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -303,8 +306,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
if (!previouslyHyperDashing)
|
||||
{
|
||||
this.FadeColour(Color4.OrangeRed, hyperdash_transition_length, Easing.OutQuint);
|
||||
this.FadeTo(0.2f, hyperdash_transition_length, Easing.OutQuint);
|
||||
this.FadeColour(Color4.OrangeRed, hyper_dash_transition_length, Easing.OutQuint);
|
||||
this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint);
|
||||
Trail = true;
|
||||
}
|
||||
}
|
||||
@ -370,7 +373,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
hyperDashDirection < 0 && hyperDashTargetPosition > X)
|
||||
{
|
||||
X = hyperDashTargetPosition;
|
||||
SetHyperdashState();
|
||||
SetHyperDashState();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,35 +2,7 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "VisualTests (Debug, net471)",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
"type": "mono",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Debug, msbuild)",
|
||||
"runtimeExecutable": null,
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "VisualTests (Release, net471)",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
"type": "mono",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Mania.Tests.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Release, msbuild)",
|
||||
"runtimeExecutable": null,
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "VisualTests (Debug, netcoreapp2.1)",
|
||||
"name": "VisualTests (Debug)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "dotnet",
|
||||
@ -38,12 +10,12 @@
|
||||
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Debug, dotnet)",
|
||||
"preLaunchTask": "Build (Debug)",
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "VisualTests (Release, netcoreapp2.1)",
|
||||
"name": "VisualTests (Release)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "dotnet",
|
||||
@ -51,7 +23,7 @@
|
||||
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Release, dotnet)",
|
||||
"preLaunchTask": "Build (Release)",
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
}
|
||||
|
46
osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json
vendored
46
osu.Game.Rulesets.Mania.Tests/.vscode/tasks.json
vendored
@ -4,43 +4,13 @@
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build (Debug, msbuild)",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"osu.Game.Rulesets.Mania.Tests.csproj",
|
||||
"/p:TargetFramework=net471",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
"/verbosity:m"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Build (Release, msbuild)",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"osu.Game.Rulesets.Mania.Tests.csproj",
|
||||
"/p:Configuration=Release",
|
||||
"/p:TargetFramework=net471",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
"/verbosity:m"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Build (Debug, dotnet)",
|
||||
"label": "Build (Debug)",
|
||||
"type": "shell",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"build",
|
||||
"--no-restore",
|
||||
"osu.Game.Rulesets.Mania.Tests.csproj",
|
||||
"/p:TargetFramework=netcoreapp2.1",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
"/verbosity:m"
|
||||
@ -49,14 +19,13 @@
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Build (Release, dotnet)",
|
||||
"label": "Build (Release)",
|
||||
"type": "shell",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"build",
|
||||
"--no-restore",
|
||||
"osu.Game.Rulesets.Mania.Tests.csproj",
|
||||
"/p:TargetFramework=netcoreapp2.1",
|
||||
"/p:Configuration=Release",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
@ -66,16 +35,7 @@
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Restore (net471)",
|
||||
"type": "shell",
|
||||
"command": "nuget",
|
||||
"args": [
|
||||
"restore"
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Restore (netcoreapp2.1)",
|
||||
"label": "Restore",
|
||||
"type": "shell",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
|
@ -14,24 +14,20 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
/// </summary>
|
||||
public class ScrollingTestContainer : Container
|
||||
{
|
||||
private readonly ScrollingDirection direction;
|
||||
[Cached(Type = typeof(IScrollingInfo))]
|
||||
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
|
||||
|
||||
public ScrollingTestContainer(ScrollingDirection direction)
|
||||
{
|
||||
this.direction = direction;
|
||||
scrollingInfo.Direction.Value = direction;
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
dependencies.CacheAs<IScrollingInfo>(new ScrollingInfo { Direction = { Value = direction }});
|
||||
return dependencies;
|
||||
}
|
||||
public void Flip() => scrollingInfo.Direction.Value = scrollingInfo.Direction.Value == ScrollingDirection.Up ? ScrollingDirection.Down : ScrollingDirection.Up;
|
||||
}
|
||||
|
||||
private class ScrollingInfo : IScrollingInfo
|
||||
{
|
||||
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||
}
|
||||
public class TestScrollingInfo : IScrollingInfo
|
||||
{
|
||||
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
private Drawable createColumn(ScrollingDirection direction, ManiaAction action)
|
||||
{
|
||||
var column = new Column(direction)
|
||||
var column = new Column
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
32
osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs
Normal file
32
osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCaseEditor : EditorTestCase
|
||||
{
|
||||
private readonly Bindable<ManiaScrollingDirection> direction = new Bindable<ManiaScrollingDirection>();
|
||||
|
||||
public TestCaseEditor()
|
||||
: base(new ManiaRuleset())
|
||||
{
|
||||
AddStep("upwards scroll", () => direction.Value = ManiaScrollingDirection.Up);
|
||||
AddStep("downwards scroll", () => direction.Value = ManiaScrollingDirection.Down);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetConfigCache configCache)
|
||||
{
|
||||
var config = (ManiaConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
||||
config.BindWith(ManiaSetting.ScrollDirection, direction);
|
||||
}
|
||||
}
|
||||
}
|
@ -46,15 +46,20 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Spacing = new Vector2(20),
|
||||
Children = new[]
|
||||
{
|
||||
createNoteDisplay(ScrollingDirection.Down),
|
||||
createNoteDisplay(ScrollingDirection.Up),
|
||||
createHoldNoteDisplay(ScrollingDirection.Down),
|
||||
createHoldNoteDisplay(ScrollingDirection.Up),
|
||||
createNoteDisplay(ScrollingDirection.Down, 1, out var note1),
|
||||
createNoteDisplay(ScrollingDirection.Up, 2, out var note2),
|
||||
createHoldNoteDisplay(ScrollingDirection.Down, 1, out var holdNote1),
|
||||
createHoldNoteDisplay(ScrollingDirection.Up, 2, out var holdNote2),
|
||||
}
|
||||
};
|
||||
|
||||
AddAssert("note 1 facing downwards", () => verifyAnchors(note1, Anchor.y2));
|
||||
AddAssert("note 2 facing upwards", () => verifyAnchors(note2, Anchor.y0));
|
||||
AddAssert("hold note 1 facing downwards", () => verifyAnchors(holdNote1, Anchor.y2));
|
||||
AddAssert("hold note 2 facing upwards", () => verifyAnchors(holdNote2, Anchor.y0));
|
||||
}
|
||||
|
||||
private Drawable createNoteDisplay(ScrollingDirection direction)
|
||||
private Drawable createNoteDisplay(ScrollingDirection direction, int identifier, out DrawableNote hitObject)
|
||||
{
|
||||
var note = new Note { StartTime = 999999999 };
|
||||
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
@ -62,24 +67,24 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
return new ScrollingTestContainer(direction)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Child = new NoteContainer(direction, $"note, scrolling {direction.ToString().ToLower()}")
|
||||
Child = new NoteContainer(direction, $"note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}")
|
||||
{
|
||||
Child = new DrawableNote(note) { AccentColour = Color4.OrangeRed }
|
||||
Child = hitObject = new DrawableNote(note) { AccentColour = Color4.OrangeRed }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Drawable createHoldNoteDisplay(ScrollingDirection direction)
|
||||
private Drawable createHoldNoteDisplay(ScrollingDirection direction, int identifier, out DrawableHoldNote hitObject)
|
||||
{
|
||||
var note = new HoldNote { StartTime = 999999999, Duration = 1000 };
|
||||
var note = new HoldNote { StartTime = 999999999, Duration = 5000 };
|
||||
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
return new ScrollingTestContainer(direction)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Child = new NoteContainer(direction, $"hold note, scrolling {direction.ToString().ToLower()}")
|
||||
Child = new NoteContainer(direction, $"hold note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}")
|
||||
{
|
||||
Child = new DrawableHoldNote(note)
|
||||
Child = hitObject = new DrawableHoldNote(note)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
AccentColour = Color4.OrangeRed,
|
||||
@ -88,6 +93,12 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
};
|
||||
}
|
||||
|
||||
private bool verifyAnchors(DrawableHitObject hitObject, Anchor expectedAnchor)
|
||||
=> hitObject.Anchor.HasFlag(expectedAnchor) && hitObject.Origin.HasFlag(expectedAnchor);
|
||||
|
||||
private bool verifyAnchors(DrawableHoldNote holdNote, Anchor expectedAnchor)
|
||||
=> verifyAnchors((DrawableHitObject)holdNote, expectedAnchor) && holdNote.NestedHitObjects.All(n => verifyAnchors(n, expectedAnchor));
|
||||
|
||||
private class NoteContainer : Container
|
||||
{
|
||||
private readonly Container content;
|
||||
@ -137,9 +148,9 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
};
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
dependencies.CacheAs<IBindable<ManiaAction>>(new Bindable<ManiaAction>());
|
||||
return dependencies;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
@ -24,6 +25,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
private readonly List<ManiaStage> stages = new List<ManiaStage>();
|
||||
|
||||
private FillFlowContainer<ScrollingTestContainer> fill;
|
||||
|
||||
public TestCaseStage()
|
||||
: base(columns)
|
||||
{
|
||||
@ -32,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
Child = fill = new FillFlowContainer<ScrollingTestContainer>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
@ -54,8 +57,22 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
AddStep("hold note", createHoldNote);
|
||||
AddStep("minor bar line", () => createBarLine(false));
|
||||
AddStep("major bar line", () => createBarLine(true));
|
||||
|
||||
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[0], Anchor.TopCentre));
|
||||
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[1], Anchor.BottomCentre));
|
||||
|
||||
AddStep("flip direction", () =>
|
||||
{
|
||||
foreach (var c in fill.Children)
|
||||
c.Flip();
|
||||
});
|
||||
|
||||
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[0], Anchor.BottomCentre));
|
||||
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[1], Anchor.TopCentre));
|
||||
}
|
||||
|
||||
private bool notesInStageAreAnchored(ManiaStage stage, Anchor anchor) => stage.Columns.SelectMany(c => c.AllHitObjects).All(o => o.Anchor == anchor);
|
||||
|
||||
private void createNote()
|
||||
{
|
||||
foreach (var stage in stages)
|
||||
@ -101,11 +118,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable createStage(ScrollingDirection direction, ManiaAction action)
|
||||
private ScrollingTestContainer createStage(ScrollingDirection direction, ManiaAction action)
|
||||
{
|
||||
var specialAction = ManiaAction.Special1;
|
||||
|
||||
var stage = new ManiaStage(direction, 0, new StageDefinition { Columns = 2 }, ref action, ref specialAction) { VisibleTimeRange = { Value = 2000 } };
|
||||
var stage = new ManiaStage(0, new StageDefinition { Columns = 2 }, ref action, ref specialAction) { VisibleTimeRange = { Value = 2000 } };
|
||||
stages.Add(stage);
|
||||
|
||||
return new ScrollingTestContainer(direction)
|
||||
|
@ -1,8 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFrameworks>netcoreapp2.1;net471</TargetFrameworks>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
||||
|
@ -133,26 +133,26 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
if (ConversionDifficulty > 6.5)
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.78, 0.3, 0);
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.85, 0.36, 0.03);
|
||||
}
|
||||
|
||||
if (ConversionDifficulty > 4)
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.43, 0.08, 0);
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.56, 0.18, 0);
|
||||
}
|
||||
|
||||
if (ConversionDifficulty > 2.5)
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.3, 0, 0);
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.37, 0.08, 0);
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.17, 0, 0);
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.27, 0, 0);
|
||||
}
|
||||
@ -173,19 +173,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
var pattern = new Pattern();
|
||||
|
||||
int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects;
|
||||
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
int nextColumn = GetRandomColumn();
|
||||
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
|
||||
{
|
||||
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
// Find available column
|
||||
nextColumn = FindAvailableColumn(nextColumn, pattern, PreviousPattern);
|
||||
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||
}
|
||||
|
||||
// This is can't be combined with the above loop due to RNG
|
||||
for (int i = 0; i < noteCount - usableColumns; i++)
|
||||
{
|
||||
while (pattern.ColumnHasObject(nextColumn))
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
nextColumn = FindAvailableColumn(nextColumn, pattern);
|
||||
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||
}
|
||||
|
||||
@ -209,19 +208,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
var pattern = new Pattern();
|
||||
|
||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
{
|
||||
while (PreviousPattern.ColumnHasObject(nextColumn))
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
}
|
||||
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
|
||||
|
||||
int lastColumn = nextColumn;
|
||||
for (int i = 0; i < noteCount; i++)
|
||||
{
|
||||
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||
while (nextColumn == lastColumn)
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
|
||||
nextColumn = FindAvailableColumn(nextColumn, validation: c => c != lastColumn);
|
||||
lastColumn = nextColumn;
|
||||
startTime += SegmentDuration;
|
||||
}
|
||||
@ -313,7 +307,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (TotalColumns > 2)
|
||||
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
nextColumn = GetRandomColumn();
|
||||
startTime += SegmentDuration;
|
||||
}
|
||||
|
||||
@ -361,7 +355,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP || sample.Name == SampleInfo.HIT_FINISH;
|
||||
|
||||
bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0;
|
||||
bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
|
||||
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
|
||||
|
||||
if (canGenerateTwoNotes)
|
||||
@ -391,17 +385,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
int columnRepeat = Math.Min(spanCount, TotalColumns);
|
||||
|
||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
{
|
||||
while (PreviousPattern.ColumnHasObject(nextColumn))
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
}
|
||||
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
|
||||
|
||||
for (int i = 0; i < columnRepeat; i++)
|
||||
{
|
||||
while (pattern.ColumnHasObject(nextColumn))
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
|
||||
nextColumn = FindAvailableColumn(nextColumn, pattern);
|
||||
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||
startTime += SegmentDuration;
|
||||
}
|
||||
@ -425,16 +414,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
var pattern = new Pattern();
|
||||
|
||||
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
{
|
||||
while (PreviousPattern.ColumnHasObject(holdColumn))
|
||||
holdColumn = Random.Next(RandomStart, TotalColumns);
|
||||
}
|
||||
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
holdColumn = FindAvailableColumn(holdColumn, PreviousPattern);
|
||||
|
||||
// Create the hold note
|
||||
addToPattern(pattern, holdColumn, startTime, EndTime);
|
||||
|
||||
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
int nextColumn = GetRandomColumn();
|
||||
int noteCount;
|
||||
if (ConversionDifficulty > 6.5)
|
||||
noteCount = GetRandomNoteCount(0.63, 0);
|
||||
@ -455,8 +441,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
for (int j = 0; j < noteCount; j++)
|
||||
{
|
||||
while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn)
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
nextColumn = FindAvailableColumn(nextColumn, validation: c => c != holdColumn, patterns: rowPattern);
|
||||
addToPattern(rowPattern, nextColumn, startTime, startTime);
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap)
|
||||
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
|
||||
{
|
||||
var endtimeData = HitObject as IHasEndTime;
|
||||
|
||||
endTime = endtimeData?.EndTime ?? 0;
|
||||
endTime = (HitObject as IHasEndTime)?.EndTime ?? 0;
|
||||
}
|
||||
|
||||
public override IEnumerable<Pattern> Generate()
|
||||
@ -35,34 +33,23 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
bool generateHold = endTime - HitObject.StartTime >= 100;
|
||||
|
||||
if (TotalColumns == 8)
|
||||
switch (TotalColumns)
|
||||
{
|
||||
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000)
|
||||
case 8 when HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000:
|
||||
addToPattern(pattern, 0, generateHold);
|
||||
else
|
||||
addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold);
|
||||
break;
|
||||
case 8:
|
||||
addToPattern(pattern, FindAvailableColumn(GetRandomColumn(), PreviousPattern), generateHold);
|
||||
break;
|
||||
default:
|
||||
if (TotalColumns > 0)
|
||||
addToPattern(pattern, GetRandomColumn(), generateHold);
|
||||
break;
|
||||
}
|
||||
else if (TotalColumns > 0)
|
||||
addToPattern(pattern, getNextRandomColumn(0), generateHold);
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Picks a random column after a column.
|
||||
/// </summary>
|
||||
/// <param name="start">The starting column.</param>
|
||||
/// <returns>A random column after <paramref name="start"/>.</returns>
|
||||
private int getNextRandomColumn(int start)
|
||||
{
|
||||
int nextColumn = Random.Next(start, TotalColumns);
|
||||
|
||||
while (PreviousPattern.ColumnHasObject(nextColumn))
|
||||
nextColumn = Random.Next(start, TotalColumns);
|
||||
|
||||
return nextColumn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs and adds a note to a pattern.
|
||||
/// </summary>
|
||||
|
@ -21,12 +21,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
private readonly PatternType convertType;
|
||||
|
||||
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, IBeatmap originalBeatmap)
|
||||
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density,
|
||||
PatternType lastStair, IBeatmap originalBeatmap)
|
||||
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
|
||||
{
|
||||
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
|
||||
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
|
||||
|
||||
StairType = lastStair;
|
||||
|
||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
||||
@ -79,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
else
|
||||
convertType |= PatternType.LowProbability;
|
||||
|
||||
if ((convertType & PatternType.KeepSingle) == 0)
|
||||
if (!convertType.HasFlag(PatternType.KeepSingle))
|
||||
{
|
||||
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
|
||||
convertType |= PatternType.Mirror;
|
||||
@ -107,7 +105,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
|
||||
|
||||
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
|
||||
if (convertType.HasFlag(PatternType.Reverse) && PreviousPattern.HitObjects.Any())
|
||||
{
|
||||
// Generate a new pattern by copying the last hit objects in reverse-column order
|
||||
for (int i = RandomStart; i < TotalColumns; i++)
|
||||
@ -117,7 +115,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1
|
||||
if (convertType.HasFlag(PatternType.Cycle) && 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
|
||||
@ -130,7 +128,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any())
|
||||
if (convertType.HasFlag(PatternType.ForceStack) && PreviousPattern.HitObjects.Any())
|
||||
{
|
||||
// Generate a new pattern by placing on the already filled columns
|
||||
for (int i = RandomStart; i < TotalColumns; i++)
|
||||
@ -142,7 +140,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
if (PreviousPattern.HitObjects.Count() == 1)
|
||||
{
|
||||
if ((convertType & PatternType.Stair) > 0)
|
||||
if (convertType.HasFlag(PatternType.Stair))
|
||||
{
|
||||
// Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
|
||||
int targetColumn = lastColumn + 1;
|
||||
@ -153,7 +151,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.ReverseStair) > 0)
|
||||
if (convertType.HasFlag(PatternType.ReverseStair))
|
||||
{
|
||||
// Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
|
||||
int targetColumn = lastColumn - 1;
|
||||
@ -165,10 +163,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
}
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.KeepSingle) > 0)
|
||||
if (convertType.HasFlag(PatternType.KeepSingle))
|
||||
return pattern = generateRandomNotes(1);
|
||||
|
||||
if ((convertType & PatternType.Mirror) > 0)
|
||||
if (convertType.HasFlag(PatternType.Mirror))
|
||||
{
|
||||
if (ConversionDifficulty > 6.5)
|
||||
return pattern = generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
||||
@ -179,21 +177,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
if (ConversionDifficulty > 6.5)
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return pattern = generateRandomPattern(0.78, 0.42, 0, 0);
|
||||
return pattern = generateRandomPattern(1, 0.62, 0, 0);
|
||||
}
|
||||
|
||||
if (ConversionDifficulty > 4)
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return pattern = generateRandomPattern(0.35, 0.08, 0, 0);
|
||||
return pattern = generateRandomPattern(0.52, 0.15, 0, 0);
|
||||
}
|
||||
|
||||
if (ConversionDifficulty > 2)
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return pattern = generateRandomPattern(0.18, 0, 0, 0);
|
||||
return pattern = generateRandomPattern(0.45, 0, 0, 0);
|
||||
}
|
||||
@ -204,9 +202,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
foreach (var obj in pattern.HitObjects)
|
||||
{
|
||||
if ((convertType & PatternType.Stair) > 0 && obj.Column == TotalColumns - 1)
|
||||
if (convertType.HasFlag(PatternType.Stair) && obj.Column == TotalColumns - 1)
|
||||
StairType = PatternType.ReverseStair;
|
||||
if ((convertType & PatternType.ReverseStair) > 0 && obj.Column == RandomStart)
|
||||
if (convertType.HasFlag(PatternType.ReverseStair) && obj.Column == RandomStart)
|
||||
StairType = PatternType.Stair;
|
||||
}
|
||||
}
|
||||
@ -225,7 +223,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
var pattern = new Pattern();
|
||||
|
||||
bool allowStacking = (convertType & PatternType.ForceNotStack) == 0;
|
||||
bool allowStacking = !convertType.HasFlag(PatternType.ForceNotStack);
|
||||
|
||||
if (!allowStacking)
|
||||
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
|
||||
@ -233,22 +231,27 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||
for (int i = 0; i < noteCount; i++)
|
||||
{
|
||||
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking)
|
||||
{
|
||||
if ((convertType & PatternType.Gathered) > 0)
|
||||
{
|
||||
nextColumn++;
|
||||
if (nextColumn == TotalColumns)
|
||||
nextColumn = RandomStart;
|
||||
}
|
||||
else
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
}
|
||||
nextColumn = allowStacking
|
||||
? FindAvailableColumn(nextColumn, nextColumn: getNextColumn, patterns: pattern)
|
||||
: FindAvailableColumn(nextColumn, nextColumn: getNextColumn, patterns: new[] { pattern, PreviousPattern });
|
||||
|
||||
addToPattern(pattern, nextColumn);
|
||||
}
|
||||
|
||||
return pattern;
|
||||
|
||||
int getNextColumn(int last)
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.Gathered))
|
||||
{
|
||||
last++;
|
||||
if (last == TotalColumns)
|
||||
last = RandomStart;
|
||||
}
|
||||
else
|
||||
last = GetRandomColumn();
|
||||
return last;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -285,17 +288,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||
private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3)
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.ForceNotStack))
|
||||
return generateRandomPattern(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
|
||||
|
||||
var pattern = new Pattern();
|
||||
|
||||
bool addToCentre;
|
||||
int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre);
|
||||
|
||||
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
|
||||
int nextColumn = Random.Next(RandomStart, columnLimit);
|
||||
int nextColumn = GetRandomColumn(upperBound: columnLimit);
|
||||
for (int i = 0; i < noteCount; i++)
|
||||
{
|
||||
while (pattern.ColumnHasObject(nextColumn))
|
||||
nextColumn = Random.Next(RandomStart, columnLimit);
|
||||
nextColumn = FindAvailableColumn(nextColumn, upperBound: columnLimit, patterns: pattern);
|
||||
|
||||
// Add normal note
|
||||
addToPattern(pattern, nextColumn);
|
||||
@ -367,9 +372,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
addToCentre = false;
|
||||
|
||||
if ((convertType & PatternType.ForceNotStack) > 0)
|
||||
return getRandomNoteCount(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
|
||||
|
||||
switch (TotalColumns)
|
||||
{
|
||||
case 2:
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.MathUtils;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -90,6 +91,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
}
|
||||
|
||||
private double? conversionDifficulty;
|
||||
|
||||
/// <summary>
|
||||
/// A difficulty factor used for various conversion methods from osu!stable.
|
||||
/// </summary>
|
||||
@ -116,5 +118,82 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
return conversionDifficulty.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a new column in which a <see cref="HitObject"/> can be placed.
|
||||
/// This uses <see cref="GetRandomColumn"/> to pick the next candidate column.
|
||||
/// </summary>
|
||||
/// <param name="initialColumn">The initial column to test. This may be returned if it is already a valid column.</param>
|
||||
/// <param name="patterns">A list of patterns for which the validity of a column should be checked against.
|
||||
/// A column is not a valid candidate if a <see cref="HitObject"/> occupies the same column in any of the patterns.</param>
|
||||
/// <returns>A column for which there are no <see cref="HitObject"/>s in any of <paramref name="patterns"/> occupying the same column.</returns>
|
||||
/// <exception cref="NotEnoughColumnsException">If there are no valid candidate columns.</exception>
|
||||
protected int FindAvailableColumn(int initialColumn, params Pattern[] patterns)
|
||||
=> FindAvailableColumn(initialColumn, null, patterns: patterns);
|
||||
|
||||
/// <summary>
|
||||
/// Finds a new column in which a <see cref="HitObject"/> can be placed.
|
||||
/// </summary>
|
||||
/// <param name="initialColumn">The initial column to test. This may be returned if it is already a valid column.</param>
|
||||
/// <param name="nextColumn">A function to retrieve the next column. If null, a randomisation scheme will be used.</param>
|
||||
/// <param name="validation">A function to perform additional validation checks to determine if a column is a valid candidate for a <see cref="HitObject"/>.</param>
|
||||
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
|
||||
/// <param name="upperBound">The maximum column index. If null, <see cref="PatternGenerator.TotalColumns"/> is used.</param>
|
||||
/// <param name="patterns">A list of patterns for which the validity of a column should be checked against.
|
||||
/// A column is not a valid candidate if a <see cref="HitObject"/> occupies the same column in any of the patterns.</param>
|
||||
/// <returns>A column which has passed the <paramref name="validation"/> check and for which there are no
|
||||
/// <see cref="HitObject"/>s in any of <paramref name="patterns"/> occupying the same column.</returns>
|
||||
/// <exception cref="NotEnoughColumnsException">If there are no valid candidate columns.</exception>
|
||||
protected int FindAvailableColumn(int initialColumn, int? lowerBound = null, int? upperBound = null, Func<int, int> nextColumn = null, [InstantHandle] Func<int, bool> validation = null,
|
||||
params Pattern[] patterns)
|
||||
{
|
||||
lowerBound = lowerBound ?? RandomStart;
|
||||
upperBound = upperBound ?? TotalColumns;
|
||||
nextColumn = nextColumn ?? (_ => GetRandomColumn(lowerBound, upperBound));
|
||||
|
||||
// Check for the initial column
|
||||
if (isValid(initialColumn))
|
||||
return initialColumn;
|
||||
|
||||
// Ensure that we have at least one free column, so that an endless loop is avoided
|
||||
bool hasValidColumns = false;
|
||||
for (int i = lowerBound.Value; i < upperBound.Value; i++)
|
||||
{
|
||||
hasValidColumns = isValid(i);
|
||||
if (hasValidColumns)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasValidColumns)
|
||||
throw new NotEnoughColumnsException();
|
||||
|
||||
// Iterate until a valid column is found. This is a random iteration in the default case.
|
||||
do
|
||||
{
|
||||
initialColumn = nextColumn(initialColumn);
|
||||
} while (!isValid(initialColumn));
|
||||
|
||||
return initialColumn;
|
||||
|
||||
bool isValid(int column) => validation?.Invoke(column) != false && !patterns.Any(p => p.ColumnHasObject(column));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random column index in the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
|
||||
/// </summary>
|
||||
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
|
||||
/// <param name="upperBound">The maximum column index. If null, <see cref="PatternGenerator.TotalColumns"/> is used.</param>
|
||||
protected int GetRandomColumn(int? lowerBound = null, int? upperBound = null) => Random.Next(lowerBound ?? RandomStart, upperBound ?? TotalColumns);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when mania conversion is stuck in an infinite loop unable to find columns to place new hitobjects in.
|
||||
/// </summary>
|
||||
public class NotEnoughColumnsException : Exception
|
||||
{
|
||||
public NotEnoughColumnsException()
|
||||
: base("There were not enough columns to complete conversion.")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
|
||||
{
|
||||
public class HoldNoteMask : HitObjectMask
|
||||
{
|
||||
public new DrawableHoldNote HitObject => (DrawableHoldNote)base.HitObject;
|
||||
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
private readonly BodyPiece body;
|
||||
|
||||
public HoldNoteMask(DrawableHoldNote hold)
|
||||
: base(hold)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new HoldNoteNoteMask(hold.Head),
|
||||
new HoldNoteNoteMask(hold.Tail),
|
||||
body = new BodyPiece
|
||||
{
|
||||
AccentColour = Color4.Transparent
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, IScrollingInfo scrollingInfo)
|
||||
{
|
||||
body.BorderColour = colours.Yellow;
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Size = HitObject.DrawSize + new Vector2(0, HitObject.Tail.DrawHeight);
|
||||
Position = Parent.ToLocalSpace(HitObject.ScreenSpaceDrawQuad.TopLeft);
|
||||
|
||||
// This is a side-effect of not matching the hitobject's anchors/origins, which is kinda hard to do
|
||||
// When scrolling upwards our origin is already at the top of the head note (which is the intended location),
|
||||
// but when scrolling downwards our origin is at the _bottom_ of the tail note (where we need to be at the _top_ of the tail note)
|
||||
if (direction.Value == ScrollingDirection.Down)
|
||||
Y -= HitObject.Tail.DrawHeight;
|
||||
}
|
||||
|
||||
private class HoldNoteNoteMask : NoteMask
|
||||
{
|
||||
public HoldNoteNoteMask(DrawableNote note)
|
||||
: base(note)
|
||||
{
|
||||
Select();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Anchor = HitObject.Anchor;
|
||||
Origin = HitObject.Origin;
|
||||
|
||||
Position = HitObject.DrawPosition;
|
||||
}
|
||||
|
||||
// Todo: This is temporary, since the note masks don't do anything special yet. In the future they will handle input.
|
||||
public override bool HandleMouseInput => false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
|
||||
{
|
||||
public class NoteMask : HitObjectMask
|
||||
{
|
||||
public NoteMask(DrawableNote note)
|
||||
: base(note)
|
||||
{
|
||||
Scale = note.Scale;
|
||||
|
||||
CornerRadius = 5;
|
||||
Masking = true;
|
||||
|
||||
AddInternal(new NotePiece());
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
Colour = colours.Yellow;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Size = HitObject.DrawSize;
|
||||
Position = Parent.ToLocalSpace(HitObject.ScreenSpaceDrawQuad.TopLeft);
|
||||
}
|
||||
}
|
||||
}
|
17
osu.Game.Rulesets.Mania/Edit/ManiaEditPlayfield.cs
Normal file
17
osu.Game.Rulesets.Mania/Edit/ManiaEditPlayfield.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public class ManiaEditPlayfield : ManiaPlayfield
|
||||
{
|
||||
public ManiaEditPlayfield(List<StageDefinition> stages)
|
||||
: base(stages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
27
osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
Normal file
27
osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using OpenTK;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public class ManiaEditRulesetContainer : ManiaRulesetContainer
|
||||
{
|
||||
public ManiaEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Playfield CreatePlayfield() => new ManiaEditPlayfield(Beatmap.Stages)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
|
||||
protected override Vector2 PlayfieldArea => Vector2.One;
|
||||
}
|
||||
}
|
56
osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
Normal file
56
osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
using osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit
|
||||
{
|
||||
public class ManiaHitObjectComposer : HitObjectComposer
|
||||
{
|
||||
protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
|
||||
|
||||
public ManiaHitObjectComposer(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
dependencies.CacheAs<IScrollingInfo>(new ManiaScrollingInfo(Config));
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new ManiaEditRulesetContainer(ruleset, beatmap);
|
||||
|
||||
protected override IReadOnlyList<ICompositionTool> CompositionTools => new ICompositionTool[]
|
||||
{
|
||||
new HitObjectCompositionTool<Note>("Note"),
|
||||
new HitObjectCompositionTool<HoldNote>("Hold"),
|
||||
};
|
||||
|
||||
public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
|
||||
{
|
||||
switch (hitObject)
|
||||
{
|
||||
case DrawableNote note:
|
||||
return new NoteMask(note);
|
||||
case DrawableHoldNote holdNote:
|
||||
return new HoldNoteMask(holdNote);
|
||||
}
|
||||
|
||||
return base.CreateMaskFor(hitObject);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
public class HoldNoteTailJudgement : ManiaJudgement
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the hold note has been released too early and shouldn't give full score for the release.
|
||||
/// </summary>
|
||||
public bool HasBroken;
|
||||
|
||||
protected override int NumericResultFor(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
default:
|
||||
return base.NumericResultFor(result);
|
||||
case HitResult.Great:
|
||||
case HitResult.Perfect:
|
||||
return base.NumericResultFor(HasBroken ? HitResult.Good : result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -19,9 +19,11 @@ using osu.Game.Configuration;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Configuration;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Difficulty;
|
||||
using osu.Game.Rulesets.Mania.Edit;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania
|
||||
@ -32,6 +34,8 @@ namespace osu.Game.Rulesets.Mania
|
||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
|
||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => new ManiaPerformanceCalculator(this, beatmap, score);
|
||||
|
||||
public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this);
|
||||
|
||||
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
|
||||
{
|
||||
if (mods.HasFlag(LegacyMods.Nightcore))
|
||||
@ -120,7 +124,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()),
|
||||
new ManiaModFlashlight(),
|
||||
};
|
||||
case ModType.Special:
|
||||
case ModType.Conversion:
|
||||
return new Mod[]
|
||||
{
|
||||
new MultiMod(new ManiaModKey4(),
|
||||
@ -135,6 +139,10 @@ namespace osu.Game.Rulesets.Mania
|
||||
new ManiaModRandom(),
|
||||
new ManiaModDualStages(),
|
||||
new ManiaModMirror(),
|
||||
};
|
||||
case ModType.Automation:
|
||||
return new Mod[]
|
||||
{
|
||||
new MultiMod(new ManiaModAutoplay(), new ModCinema()),
|
||||
};
|
||||
default:
|
||||
|
@ -19,14 +19,14 @@ namespace osu.Game.Rulesets.Mania
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ManiaConfigManager config)
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsEnumDropdown<ManiaScrollingDirection>
|
||||
{
|
||||
LabelText = "Scrolling direction",
|
||||
Bindable = config.GetBindable<ManiaScrollingDirection>(ManiaSetting.ScrollDirection)
|
||||
Bindable = ((ManiaConfigManager)Config).GetBindable<ManiaScrollingDirection>(ManiaSetting.ScrollDirection)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public override string ShortenedName => Name;
|
||||
public abstract int KeyCount { get; }
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
|
||||
public override bool Ranked => true;
|
||||
|
||||
|
@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override string Name => "Dual Stages";
|
||||
public override string ShortenedName => "DS";
|
||||
public override string Description => @"Double the stages, double the fun!";
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
private bool isForCurrentRuleset;
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public override string Name => "Mirror";
|
||||
public override string ShortenedName => "MR";
|
||||
public override ModType Type => ModType.Special;
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => true;
|
||||
|
||||
|
@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public override string Name => "Random";
|
||||
public override string ShortenedName => "RD";
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
|
||||
public override string Description => @"Shuffle around the keys!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
@ -19,10 +18,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
/// </summary>
|
||||
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>, IKeyBindingHandler<ManiaAction>
|
||||
{
|
||||
public override bool DisplayJudgement => false;
|
||||
public override bool DisplayResult => false;
|
||||
|
||||
private readonly DrawableNote head;
|
||||
private readonly DrawableNote tail;
|
||||
public readonly DrawableNote Head;
|
||||
public readonly DrawableNote Tail;
|
||||
|
||||
private readonly BodyPiece bodyPiece;
|
||||
|
||||
@ -57,12 +56,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
HoldStartTime = () => holdStartTime
|
||||
})
|
||||
},
|
||||
head = new DrawableHeadNote(this)
|
||||
Head = new DrawableHeadNote(this)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
},
|
||||
tail = new DrawableTailNote(this)
|
||||
Tail = new DrawableTailNote(this)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
@ -72,8 +71,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
foreach (var tick in tickContainer)
|
||||
AddNested(tick);
|
||||
|
||||
AddNested(head);
|
||||
AddNested(tail);
|
||||
AddNested(Head);
|
||||
AddNested(Tail);
|
||||
}
|
||||
|
||||
protected override void OnDirectionChanged(ScrollingDirection direction)
|
||||
@ -91,16 +90,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
base.AccentColour = value;
|
||||
|
||||
bodyPiece.AccentColour = value;
|
||||
head.AccentColour = value;
|
||||
tail.AccentColour = value;
|
||||
Head.AccentColour = value;
|
||||
Tail.AccentColour = value;
|
||||
tickContainer.ForEach(t => t.AccentColour = value);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (tail.AllJudged)
|
||||
AddJudgement(new HoldNoteJudgement { Result = HitResult.Perfect });
|
||||
if (Tail.AllJudged)
|
||||
ApplyResult(r => r.Type = HitResult.Perfect);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@ -108,8 +107,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
base.Update();
|
||||
|
||||
// Make the body piece not lie under the head note
|
||||
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * head.Height / 2;
|
||||
bodyPiece.Height = DrawHeight - head.Height / 2 + tail.Height / 2;
|
||||
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * Head.Height / 2;
|
||||
bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
@ -141,7 +140,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
holdStartTime = null;
|
||||
|
||||
// If the key has been released too early, the user should not receive full score for the release
|
||||
if (!tail.IsHit)
|
||||
if (!Tail.IsHit)
|
||||
hasBroken = true;
|
||||
|
||||
return true;
|
||||
@ -166,7 +165,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
return false;
|
||||
|
||||
// If the key has been released too early, the user should not receive full score for the release
|
||||
if (Judgements.Any(j => j.Result == HitResult.Miss))
|
||||
if (Result.Type == HitResult.Miss)
|
||||
holdNote.hasBroken = true;
|
||||
|
||||
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
|
||||
@ -197,7 +196,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
this.holdNote = holdNote;
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
// Factor in the release lenience
|
||||
timeOffset /= release_window_lenience;
|
||||
@ -205,13 +204,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
{
|
||||
AddJudgement(new HoldNoteTailJudgement
|
||||
{
|
||||
Result = HitResult.Miss,
|
||||
HasBroken = holdNote.hasBroken
|
||||
});
|
||||
}
|
||||
ApplyResult(r => r.Type = HitResult.Miss);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -220,10 +213,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (result == HitResult.None)
|
||||
return;
|
||||
|
||||
AddJudgement(new HoldNoteTailJudgement
|
||||
ApplyResult(r =>
|
||||
{
|
||||
Result = result,
|
||||
HasBroken = holdNote.hasBroken
|
||||
if (holdNote.hasBroken && (result == HitResult.Perfect || result == HitResult.Perfect))
|
||||
result = HitResult.Good;
|
||||
|
||||
r.Type = result;
|
||||
});
|
||||
}
|
||||
|
||||
@ -238,7 +233,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (action != Action.Value)
|
||||
return false;
|
||||
|
||||
UpdateJudgement(true);
|
||||
UpdateResult(true);
|
||||
|
||||
// Handled by the hold note, which will set holding = false
|
||||
return false;
|
||||
|
@ -7,7 +7,6 @@ using OpenTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
@ -72,29 +71,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (!userTriggered)
|
||||
return;
|
||||
|
||||
if (Time.Current < HitObject.StartTime)
|
||||
return;
|
||||
|
||||
if (HoldStartTime?.Invoke() > HitObject.StartTime)
|
||||
return;
|
||||
var startTime = HoldStartTime?.Invoke();
|
||||
|
||||
AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
if (AllJudged)
|
||||
return;
|
||||
|
||||
if (HoldStartTime?.Invoke() == null)
|
||||
return;
|
||||
|
||||
UpdateJudgement(true);
|
||||
if (startTime == null || startTime > HitObject.StartTime)
|
||||
ApplyResult(r => r.Type = HitResult.Miss);
|
||||
else
|
||||
ApplyResult(r => r.Type = HitResult.Perfect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
@ -56,12 +55,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||
AddJudgement(new ManiaJudgement { Result = HitResult.Miss });
|
||||
ApplyResult(r => r.Type = HitResult.Miss);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -69,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (result == HitResult.None)
|
||||
return;
|
||||
|
||||
AddJudgement(new ManiaJudgement { Result = result });
|
||||
ApplyResult(r => r.Type = result);
|
||||
}
|
||||
|
||||
public virtual bool OnPressed(ManiaAction action)
|
||||
@ -77,7 +76,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
if (action != Action.Value)
|
||||
return false;
|
||||
|
||||
return UpdateJudgement(true);
|
||||
return UpdateResult(true);
|
||||
}
|
||||
|
||||
public virtual bool OnReleased(ManiaAction action) => false;
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
@ -55,7 +57,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// <summary>
|
||||
/// The tail note of the hold.
|
||||
/// </summary>
|
||||
public readonly Note Tail = new Note();
|
||||
public readonly TailNote Tail = new TailNote();
|
||||
|
||||
/// <summary>
|
||||
/// The time between ticks of this hold.
|
||||
@ -68,9 +70,6 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
|
||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
|
||||
|
||||
Head.ApplyDefaults(controlPointInfo, difficulty);
|
||||
Tail.ApplyDefaults(controlPointInfo, difficulty);
|
||||
}
|
||||
|
||||
protected override void CreateNestedHitObjects()
|
||||
@ -78,6 +77,9 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
base.CreateNestedHitObjects();
|
||||
|
||||
createTicks();
|
||||
|
||||
AddNested(Head);
|
||||
AddNested(Tail);
|
||||
}
|
||||
|
||||
private void createTicks()
|
||||
@ -94,5 +96,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override Judgement CreateJudgement() => new HoldNoteJudgement();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
/// <summary>
|
||||
@ -8,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// </summary>
|
||||
public class HoldNoteTick : ManiaHitObject
|
||||
{
|
||||
public override Judgement CreateJudgement() => new HoldNoteTickJudgement();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
/// <summary>
|
||||
@ -8,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// </summary>
|
||||
public class Note : ManiaHitObject
|
||||
{
|
||||
public override Judgement CreateJudgement() => new ManiaJudgement();
|
||||
}
|
||||
}
|
||||
|
13
osu.Game.Rulesets.Mania/Objects/TailNote.cs
Normal file
13
osu.Game.Rulesets.Mania/Objects/TailNote.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
public class TailNote : Note
|
||||
{
|
||||
public override Judgement CreateJudgement() => new ManiaJudgement();
|
||||
}
|
||||
}
|
@ -56,10 +56,15 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
{
|
||||
foreach (var point in group)
|
||||
{
|
||||
if (point is HitPoint)
|
||||
actions.Add(columnActions[point.Column]);
|
||||
if (point is ReleasePoint)
|
||||
actions.Remove(columnActions[point.Column]);
|
||||
switch (point)
|
||||
{
|
||||
case HitPoint _:
|
||||
actions.Add(columnActions[point.Column]);
|
||||
break;
|
||||
case ReleasePoint _:
|
||||
actions.Remove(columnActions[point.Column]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, actions.ToArray()));
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.StateChanges;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Replays
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
@ -97,31 +96,20 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SimulateAutoplay(Beatmap<ManiaHitObject> beatmap)
|
||||
protected override void ApplyBeatmap(Beatmap<ManiaHitObject> beatmap)
|
||||
{
|
||||
base.ApplyBeatmap(beatmap);
|
||||
|
||||
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty;
|
||||
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
|
||||
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
|
||||
}
|
||||
|
||||
protected override void SimulateAutoplay(Beatmap<ManiaHitObject> beatmap)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
var holdNote = obj as HoldNote;
|
||||
|
||||
if (holdNote != null)
|
||||
{
|
||||
// Head
|
||||
AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
|
||||
|
||||
// Ticks
|
||||
int tickCount = holdNote.NestedHitObjects.OfType<HoldNoteTick>().Count();
|
||||
for (int i = 0; i < tickCount; i++)
|
||||
AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
|
||||
}
|
||||
|
||||
AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
|
||||
}
|
||||
base.SimulateAutoplay(beatmap);
|
||||
|
||||
if (!HasFailed)
|
||||
break;
|
||||
@ -133,20 +121,20 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNewJudgement(Judgement judgement)
|
||||
protected override void ApplyResult(JudgementResult result)
|
||||
{
|
||||
base.OnNewJudgement(judgement);
|
||||
base.ApplyResult(result);
|
||||
|
||||
bool isTick = judgement is HoldNoteTickJudgement;
|
||||
bool isTick = result.Judgement is HoldNoteTickJudgement;
|
||||
|
||||
if (isTick)
|
||||
{
|
||||
if (judgement.IsHit)
|
||||
if (result.IsHit)
|
||||
Health.Value += hpMultiplier * hp_increase_tick;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (judgement.Result)
|
||||
switch (result.Type)
|
||||
{
|
||||
case HitResult.Miss:
|
||||
Health.Value += hpMissMultiplier * hp_increase_miss;
|
||||
|
@ -32,8 +32,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
protected override Container<Drawable> Content => hitObjectArea;
|
||||
|
||||
public Column(ScrollingDirection direction)
|
||||
: base(direction)
|
||||
public Column()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Width = column_width;
|
||||
@ -118,9 +117,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
dependencies.CacheAs<IBindable<ManiaAction>>(Action);
|
||||
return dependencies;
|
||||
}
|
||||
@ -132,14 +131,14 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
public override void Add(DrawableHitObject hitObject)
|
||||
{
|
||||
hitObject.AccentColour = AccentColour;
|
||||
hitObject.OnJudgement += OnJudgement;
|
||||
hitObject.OnNewResult += OnNewResult;
|
||||
|
||||
HitObjects.Add(hitObject);
|
||||
}
|
||||
|
||||
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||
{
|
||||
if (!judgement.IsHit || !judgedObject.DisplayJudgement)
|
||||
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements)
|
||||
return;
|
||||
|
||||
explosionContainer.Add(new HitExplosion(judgedObject)
|
||||
|
@ -8,10 +8,10 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
internal class DrawableManiaJudgement : DrawableJudgement
|
||||
public class DrawableManiaJudgement : DrawableJudgement
|
||||
{
|
||||
public DrawableManiaJudgement(Judgement judgement, DrawableHitObject judgedObject)
|
||||
: base(judgement, judgedObject)
|
||||
public DrawableManiaJudgement(JudgementResult result, DrawableHitObject judgedObject)
|
||||
: base(result, judgedObject)
|
||||
{
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
this.FadeInFromZero(50, Easing.OutQuint);
|
||||
|
||||
if (Judgement.IsHit)
|
||||
if (Result.IsHit)
|
||||
{
|
||||
this.ScaleTo(0.8f);
|
||||
this.ScaleTo(1, 250, Easing.OutElastic);
|
||||
|
@ -1,28 +1,23 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public class ManiaPlayfield : ManiaScrollingPlayfield
|
||||
{
|
||||
public List<Column> Columns => stages.SelectMany(x => x.Columns).ToList();
|
||||
private readonly List<ManiaStage> stages = new List<ManiaStage>();
|
||||
|
||||
public ManiaPlayfield(ScrollingDirection direction, List<StageDefinition> stageDefinitions)
|
||||
: base(direction)
|
||||
public ManiaPlayfield(List<StageDefinition> stageDefinitions)
|
||||
{
|
||||
if (stageDefinitions == null)
|
||||
throw new ArgumentNullException(nameof(stageDefinitions));
|
||||
@ -31,18 +26,18 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
throw new ArgumentException("Can't have zero or fewer stages.");
|
||||
|
||||
GridContainer playfieldGrid;
|
||||
InternalChild = playfieldGrid = new GridContainer
|
||||
AddInternal(playfieldGrid = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[] { new Drawable[stageDefinitions.Count] }
|
||||
};
|
||||
});
|
||||
|
||||
var normalColumnAction = ManiaAction.Key1;
|
||||
var specialColumnAction = ManiaAction.Special1;
|
||||
int firstColumnIndex = 0;
|
||||
for (int i = 0; i < stageDefinitions.Count; i++)
|
||||
{
|
||||
var newStage = new ManiaStage(direction, firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
|
||||
var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
|
||||
newStage.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||
|
||||
playfieldGrid.Content[0][i] = newStage;
|
||||
@ -76,10 +71,5 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
maniaConfig.BindWith(ManiaSetting.ScrollTime, VisibleTimeRange);
|
||||
}
|
||||
|
||||
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
{
|
||||
getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
@ -35,8 +34,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public IEnumerable<BarLine> BarLines;
|
||||
|
||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||
private ScrollingInfo scrollingInfo;
|
||||
protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
|
||||
|
||||
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
@ -70,24 +68,24 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ManiaConfigManager config)
|
||||
private void load()
|
||||
{
|
||||
BarLines.ForEach(Playfield.Add);
|
||||
|
||||
config.BindWith(ManiaSetting.ScrollDirection, configDirection);
|
||||
configDirection.BindValueChanged(d => scrollingInfo.Direction.Value = (ScrollingDirection)d, true);
|
||||
}
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||
dependencies.CacheAs<IScrollingInfo>(scrollingInfo = new ScrollingInfo());
|
||||
dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
|
||||
if (dependencies.Get<ManiaScrollingInfo>() == null)
|
||||
dependencies.CacheAs<IScrollingInfo>(new ManiaScrollingInfo(Config));
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(scrollingInfo.Direction, Beatmap.Stages)
|
||||
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -115,11 +113,5 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
|
||||
|
||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
|
||||
|
||||
private class ScrollingInfo : IScrollingInfo
|
||||
{
|
||||
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
23
osu.Game.Rulesets.Mania/UI/ManiaScrollingInfo.cs
Normal file
23
osu.Game.Rulesets.Mania/UI/ManiaScrollingInfo.cs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
public class ManiaScrollingInfo : IScrollingInfo
|
||||
{
|
||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||
|
||||
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||
|
||||
public ManiaScrollingInfo(ManiaConfigManager config)
|
||||
{
|
||||
config.BindWith(ManiaSetting.ScrollDirection, configDirection);
|
||||
configDirection.BindValueChanged(v => Direction.Value = (ScrollingDirection)v, true);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,11 +11,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
public ManiaScrollingPlayfield(ScrollingDirection direction)
|
||||
: base(direction)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
/// <summary>
|
||||
/// A collection of <see cref="Column"/>s.
|
||||
/// </summary>
|
||||
internal class ManiaStage : ManiaScrollingPlayfield
|
||||
public class ManiaStage : ManiaScrollingPlayfield
|
||||
{
|
||||
public const float HIT_TARGET_POSITION = 50;
|
||||
|
||||
@ -43,8 +43,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
private readonly int firstColumnIndex;
|
||||
|
||||
public ManiaStage(ScrollingDirection direction, int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
|
||||
: base(direction)
|
||||
public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
|
||||
{
|
||||
this.firstColumnIndex = firstColumnIndex;
|
||||
|
||||
@ -124,7 +123,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
for (int i = 0; i < definition.Columns; i++)
|
||||
{
|
||||
var isSpecial = definition.IsSpecialColumn(i);
|
||||
var column = new Column(direction)
|
||||
var column = new Column
|
||||
{
|
||||
IsSpecial = isSpecial,
|
||||
Action = { Value = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++ }
|
||||
@ -157,18 +156,18 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
var maniaObject = (ManiaHitObject)h.HitObject;
|
||||
int columnIndex = maniaObject.Column - firstColumnIndex;
|
||||
Columns.ElementAt(columnIndex).Add(h);
|
||||
h.OnJudgement += OnJudgement;
|
||||
h.OnNewResult += OnNewResult;
|
||||
}
|
||||
|
||||
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));
|
||||
|
||||
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||
{
|
||||
if (!judgedObject.DisplayJudgement)
|
||||
if (!judgedObject.DisplayResult || !DisplayJudgements)
|
||||
return;
|
||||
|
||||
judgements.Clear();
|
||||
judgements.Add(new DrawableManiaJudgement(judgement, judgedObject)
|
||||
judgements.Add(new DrawableManiaJudgement(result, judgedObject)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
36
osu.Game.Rulesets.Osu.Tests/.vscode/launch.json
vendored
36
osu.Game.Rulesets.Osu.Tests/.vscode/launch.json
vendored
@ -2,35 +2,7 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "VisualTests (Debug, net471)",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
"type": "mono",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Debug, msbuild)",
|
||||
"runtimeExecutable": null,
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "VisualTests (Release, net471)",
|
||||
"windows": {
|
||||
"type": "clr"
|
||||
},
|
||||
"type": "mono",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Osu.Tests.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Release, msbuild)",
|
||||
"runtimeExecutable": null,
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "VisualTests (Debug, netcoreapp2.1)",
|
||||
"name": "VisualTests (Debug)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "dotnet",
|
||||
@ -38,12 +10,12 @@
|
||||
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Debug, dotnet)",
|
||||
"preLaunchTask": "Build (Debug)",
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "VisualTests (Release, netcoreapp2.1)",
|
||||
"name": "VisualTests (Release)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "dotnet",
|
||||
@ -51,7 +23,7 @@
|
||||
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
|
||||
],
|
||||
"cwd": "${workspaceRoot}",
|
||||
"preLaunchTask": "Build (Release, dotnet)",
|
||||
"preLaunchTask": "Build (Release)",
|
||||
"env": {},
|
||||
"console": "internalConsole"
|
||||
}
|
||||
|
46
osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json
vendored
46
osu.Game.Rulesets.Osu.Tests/.vscode/tasks.json
vendored
@ -4,43 +4,13 @@
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build (Debug, msbuild)",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"osu.Game.Rulesets.Osu.Tests.csproj",
|
||||
"/p:TargetFramework=net471",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
"/verbosity:m"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Build (Release, msbuild)",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"osu.Game.Rulesets.Osu.Tests.csproj",
|
||||
"/p:Configuration=Release",
|
||||
"/p:TargetFramework=net471",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
"/verbosity:m"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Build (Debug, dotnet)",
|
||||
"label": "Build (Debug)",
|
||||
"type": "shell",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"build",
|
||||
"--no-restore",
|
||||
"osu.Game.Rulesets.Osu.Tests.csproj",
|
||||
"/p:TargetFramework=netcoreapp2.1",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
"/verbosity:m"
|
||||
@ -49,14 +19,13 @@
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Build (Release, dotnet)",
|
||||
"label": "Build (Release)",
|
||||
"type": "shell",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"build",
|
||||
"--no-restore",
|
||||
"osu.Game.Rulesets.Osu.Tests.csproj",
|
||||
"/p:TargetFramework=netcoreapp2.1",
|
||||
"/p:Configuration=Release",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
@ -66,16 +35,7 @@
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "Restore (net471)",
|
||||
"type": "shell",
|
||||
"command": "nuget",
|
||||
"args": [
|
||||
"restore"
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "Restore (netcoreapp2.1)",
|
||||
"label": "Restore",
|
||||
"type": "shell",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
/// <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>
|
||||
private const double conversion_lenience = 2;
|
||||
|
||||
|
64
osu.Game.Rulesets.Osu.Tests/StackingTest.cs
Normal file
64
osu.Game.Rulesets.Osu.Tests/StackingTest.cs
Normal file
@ -0,0 +1,64 @@
|
||||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class StackingTest
|
||||
{
|
||||
[Test]
|
||||
public void TestStacking()
|
||||
{
|
||||
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(beatmap_data)))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo);
|
||||
|
||||
var objects = converted.HitObjects.ToList();
|
||||
|
||||
// The last hitobject triggers the stacking
|
||||
for (int i = 0; i < objects.Count - 1; i++)
|
||||
Assert.AreEqual(0, ((OsuHitObject)objects[i]).StackHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private const string beatmap_data = @"
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.2
|
||||
|
||||
[Difficulty]
|
||||
ApproachRate:9.2
|
||||
SliderMultiplier:1
|
||||
SliderTickRate:0.5
|
||||
|
||||
[TimingPoints]
|
||||
217871,6400,4,2,1,20,1,0
|
||||
217871,-800,4,2,1,20,0,0
|
||||
218071,-787.5,4,2,1,20,0,0
|
||||
218271,-775,4,2,1,20,0,0
|
||||
218471,-762.5,4,2,1,20,0,0
|
||||
218671,-750,4,2,1,20,0,0
|
||||
240271,-10,4,2,0,5,0,0
|
||||
|
||||
[HitObjects]
|
||||
311,185,217871,6,0,L|318:158,1,25
|
||||
311,185,218071,2,0,L|335:170,1,25
|
||||
311,185,218271,2,0,L|338:192,1,25
|
||||
311,185,218471,2,0,L|325:209,1,25
|
||||
311,185,218671,2,0,L|304:212,1,25
|
||||
311,185,240271,5,0,0:0:0:0:
|
||||
";
|
||||
}
|
||||
}
|
@ -5,12 +5,10 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Tests.Visual;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -87,7 +85,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDrawableHitCircle : DrawableHitCircle
|
||||
protected class TestDrawableHitCircle : DrawableHitCircle
|
||||
{
|
||||
private readonly bool auto;
|
||||
|
||||
@ -96,19 +94,17 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
this.auto = auto;
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
public void TriggerJudgement() => UpdateResult(true);
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (auto && !userTriggered && timeOffset > 0)
|
||||
{
|
||||
// force success
|
||||
AddJudgement(new OsuJudgement
|
||||
{
|
||||
Result = HitResult.Great
|
||||
});
|
||||
State.Value = ArmedState.Hit;
|
||||
ApplyResult(r => r.Type = HitResult.Great);
|
||||
}
|
||||
else
|
||||
base.CheckForJudgements(userTriggered, timeOffset);
|
||||
base.CheckForResult(userTriggered, timeOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
23
osu.Game.Rulesets.Osu.Tests/TestCaseShaking.cs
Normal file
23
osu.Game.Rulesets.Osu.Tests/TestCaseShaking.cs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class TestCaseShaking : TestCaseHitCircle
|
||||
{
|
||||
public override void Add(Drawable drawable)
|
||||
{
|
||||
base.Add(drawable);
|
||||
|
||||
if (drawable is TestDrawableHitCircle hitObject)
|
||||
{
|
||||
Scheduler.AddDelayed(() => hitObject.TriggerJudgement(),
|
||||
hitObject.HitObject.StartTime - (hitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -304,13 +304,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>())
|
||||
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
||||
|
||||
drawable.OnJudgement += onJudgement;
|
||||
drawable.OnNewResult += onNewResult;
|
||||
|
||||
Add(drawable);
|
||||
}
|
||||
|
||||
private float judgementOffsetDirection = 1;
|
||||
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
|
||||
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||
{
|
||||
var osuObject = judgedObject as DrawableOsuHitObject;
|
||||
if (osuObject == null)
|
||||
@ -321,8 +321,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = judgement.IsHit ? "Hit!" : "Miss!",
|
||||
Colour = judgement.IsHit ? Color4.Green : Color4.Red,
|
||||
Text = result.IsHit ? "Hit!" : "Miss!",
|
||||
Colour = result.IsHit ? Color4.Green : Color4.Red,
|
||||
TextSize = 30,
|
||||
Position = osuObject.HitObject.StackedEndPosition + judgementOffsetDirection * new Vector2(0, 45)
|
||||
});
|
||||
|
@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
this.auto = auto;
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1)
|
||||
{
|
||||
@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
auto = false;
|
||||
}
|
||||
|
||||
base.CheckForJudgements(userTriggered, timeOffset);
|
||||
base.CheckForResult(userTriggered, timeOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Project">
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFrameworks>netcoreapp2.1;net471</TargetFrameworks>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Project References">
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
|
||||
|
@ -12,7 +12,7 @@ using osu.Game.Rulesets.Osu.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
{
|
||||
internal class OsuBeatmapConverter : BeatmapConverter<OsuHitObject>
|
||||
public class OsuBeatmapConverter : BeatmapConverter<OsuHitObject>
|
||||
{
|
||||
public OsuBeatmapConverter(IBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
RepeatCount = curveData.RepeatCount,
|
||||
Position = positionData?.Position ?? Vector2.Zero,
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset
|
||||
};
|
||||
}
|
||||
@ -52,7 +53,9 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
StartTime = original.StartTime,
|
||||
Samples = original.Samples,
|
||||
EndTime = endTimeData.EndTime,
|
||||
Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2
|
||||
Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2,
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||
};
|
||||
}
|
||||
else
|
||||
@ -62,7 +65,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
StartTime = original.StartTime,
|
||||
Samples = original.Samples,
|
||||
Position = positionData?.Position ?? Vector2.Zero,
|
||||
NewCombo = comboData?.NewCombo ?? false
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -8,16 +8,16 @@ using osu.Game.Rulesets.Osu.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
{
|
||||
internal class OsuBeatmapProcessor : BeatmapProcessor
|
||||
public class OsuBeatmapProcessor : BeatmapProcessor
|
||||
{
|
||||
public OsuBeatmapProcessor(IBeatmap beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
public override void PreProcess()
|
||||
public override void PostProcess()
|
||||
{
|
||||
base.PreProcess();
|
||||
base.PostProcess();
|
||||
applyStacking((Beatmap<OsuHitObject>)Beatmap);
|
||||
}
|
||||
|
||||
|
@ -61,19 +61,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
||||
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 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();
|
||||
// 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);
|
||||
|
||||
return new OsuDifficultyAttributes(mods, starRating)
|
||||
{
|
||||
AimStrain = aimRating,
|
||||
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,
|
||||
MaxCombo = maxCombo
|
||||
};
|
||||
|
@ -1,12 +0,0 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public class OsuEditPlayfield : OsuPlayfield
|
||||
{
|
||||
protected override bool DisplayJudgements => false;
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
@ -16,8 +15,6 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
}
|
||||
|
||||
protected override Playfield CreatePlayfield() => new OsuEditPlayfield();
|
||||
|
||||
protected override Vector2 PlayfieldArea => Vector2.One;
|
||||
|
||||
protected override CursorContainer CreateCursor() => null;
|
||||
|
17
osu.Game.Rulesets.Osu/Judgements/ComboResult.cs
Normal file
17
osu.Game.Rulesets.Osu/Judgements/ComboResult.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Judgements
|
||||
{
|
||||
public enum ComboResult
|
||||
{
|
||||
[Description(@"")]
|
||||
None,
|
||||
[Description(@"Good")]
|
||||
Good,
|
||||
[Description(@"Amazing")]
|
||||
Perfect
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Judgements
|
||||
@ -25,7 +24,5 @@ namespace osu.Game.Rulesets.Osu.Judgements
|
||||
return 300;
|
||||
}
|
||||
}
|
||||
|
||||
public ComboResult Combo;
|
||||
}
|
||||
}
|
||||
|
17
osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs
Normal file
17
osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Judgements
|
||||
{
|
||||
public class OsuJudgementResult : JudgementResult
|
||||
{
|
||||
public ComboResult ComboType;
|
||||
|
||||
public OsuJudgementResult(Judgement judgement)
|
||||
: base(judgement)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Name => "Autopilot";
|
||||
public override string ShortenedName => "AP";
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot;
|
||||
public override ModType Type => ModType.Automation;
|
||||
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) };
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
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>())
|
||||
{
|
||||
@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
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;
|
||||
|
||||
// new duration from completed fade in to end (before fading out)
|
||||
|
@ -2,14 +2,89 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModRelax : ModRelax
|
||||
public class OsuModRelax : ModRelax, IApplicableFailOverride, IUpdatableByPlayfield, IApplicableToRulesetContainer<OsuHitObject>
|
||||
{
|
||||
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
|
||||
|
||||
public bool AllowFail => false;
|
||||
|
||||
public void Update(Playfield playfield)
|
||||
{
|
||||
bool requiresHold = false;
|
||||
bool requiresHit = false;
|
||||
|
||||
const float relax_leniency = 3;
|
||||
|
||||
foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
|
||||
{
|
||||
if (!(drawable is DrawableOsuHitObject osuHit))
|
||||
continue;
|
||||
|
||||
double time = osuHit.Clock.CurrentTime;
|
||||
double relativetime = time - osuHit.HitObject.StartTime;
|
||||
|
||||
if (time < osuHit.HitObject.StartTime - relax_leniency) continue;
|
||||
|
||||
if (osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime || osuHit.IsHit)
|
||||
continue;
|
||||
|
||||
requiresHit |= osuHit is DrawableHitCircle && osuHit.IsHovered && osuHit.HitObject.HitWindows.CanBeHit(relativetime);
|
||||
requiresHold |= osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered) || osuHit is DrawableSpinner;
|
||||
}
|
||||
|
||||
if (requiresHit)
|
||||
{
|
||||
addAction(false);
|
||||
addAction(true);
|
||||
}
|
||||
|
||||
addAction(requiresHold);
|
||||
}
|
||||
|
||||
private bool wasHit;
|
||||
private bool wasLeft;
|
||||
|
||||
private OsuInputManager osuInputManager;
|
||||
|
||||
private void addAction(bool hitting)
|
||||
{
|
||||
if (wasHit == hitting)
|
||||
return;
|
||||
|
||||
wasHit = hitting;
|
||||
|
||||
var state = new ReplayState<OsuAction>
|
||||
{
|
||||
PressedActions = new List<OsuAction>()
|
||||
};
|
||||
|
||||
if (hitting)
|
||||
{
|
||||
state.PressedActions.Add(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
|
||||
wasLeft = !wasLeft;
|
||||
}
|
||||
|
||||
osuInputManager.HandleCustomInput(new InputState(), state);
|
||||
}
|
||||
|
||||
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)
|
||||
{
|
||||
// grab the input manager for future use.
|
||||
osuInputManager = (OsuInputManager)rulesetContainer.KeyBindingInputManager;
|
||||
osuInputManager.AllowUserPresses = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override string Name => "Spun Out";
|
||||
public override string ShortenedName => "SO";
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
|
||||
public override ModType Type => ModType.DifficultyReduction;
|
||||
public override string Description => @"Spinners will be automatically completed.";
|
||||
public override double ScoreMultiplier => 0.9;
|
||||
public override bool Ranked => true;
|
||||
|
@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Name => "Target";
|
||||
public override string ShortenedName => "TP";
|
||||
public override ModType Type => ModType.Conversion;
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
|
||||
public override string Description => @"Practice keeping up with the beat of the song.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
{
|
||||
@ -20,27 +21,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Masking = true;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
CornerRadius = width / 2;
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
Child = new SkinnableDrawable("Play/osu/followpoint", _ => new Container
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Color4.White.Opacity(0.2f),
|
||||
Radius = 4,
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
Masking = true,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
CornerRadius = width / 2,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Color4.White.Opacity(0.2f),
|
||||
Radius = 4,
|
||||
},
|
||||
Child = new Box
|
||||
{
|
||||
Size = new Vector2(width),
|
||||
Blending = BlendingMode.Additive,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
};
|
||||
}
|
||||
}, restrictSize: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user