1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-15 12:42:48 +08:00

Compare commits

...

3371 Commits

1141 changed files with 47489 additions and 14843 deletions
+42 -35
View File
@@ -2,63 +2,70 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"command": "msbuild",
"type": "shell",
"suppressTaskName": true,
"args": [
"/property:GenerateFullPaths=true",
"/property:DebugType=portable",
"/verbosity:minimal",
"/m" //parallel compiling support.
],
"tasks": [{
"taskName": "Build (Debug)",
"label": "Build (Debug)",
"type": "shell",
"command": "msbuild",
"args": [
"/p:GenerateFullPaths=true",
"/p:DebugType=portable",
"/m",
"/v:m"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": [
"$msCompile"
]
"problemMatcher": "$msCompile"
},
{
"taskName": "Build (Release)",
"label": "Build (Release)",
"type": "shell",
"command": "msbuild",
"args": [
"/p:Configuration=Release",
"/p:DebugType=portable",
"/p:GenerateFullPaths=true",
"/m",
"/v:m"
],
"group": "build",
"args": [
"/property:Configuration=Release"
],
"problemMatcher": [
"$msCompile"
]
"problemMatcher": "$msCompile"
},
{
"taskName": "Clean (Debug)",
"label": "Clean (Debug)",
"type": "shell",
"command": "msbuild",
"args": [
"/target:Clean"
"/p:DebugType=portable",
"/p:GenerateFullPaths=true",
"/m",
"/t:Clean",
"/v:m"
],
"problemMatcher": [
"$msCompile"
]
"problemMatcher": "$msCompile"
},
{
"taskName": "Clean (Release)",
"label": "Clean (Release)",
"type": "shell",
"command": "msbuild",
"args": [
"/target:Clean",
"/property:Configuration=Release"
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/p:DebugType=portable",
"/m",
"/t:Clean",
"/v:m"
],
"problemMatcher": [
"$msCompile"
]
"problemMatcher": "$msCompile"
},
{
"taskName": "Clean All",
"label": "Clean All",
"dependsOn": [
"Clean (Debug)",
"Clean (Release)"
],
"problemMatcher": [
"$msCompile"
]
"problemMatcher": "$msCompile"
}
]
}
+36
View File
@@ -0,0 +1,36 @@
# Linux
### 1. Requirements:
Mono >= 5.4.0 (>= 5.8.0 recommended)
Please check [here](http://www.mono-project.com/download/) for stable or [here](http://www.mono-project.com/download/alpha/) for an alpha release.
NuGet >= 4.4.0
msbuild
git
### 2. Cloning project
Clone the entire repository with submodules using
```
git clone https://github.com/ppy/osu --recursive
```
Then restore NuGet packages from the repository
```
nuget restore
```
### 3. Compiling
Simply run `msbuild` where `osu.sln` is located, this will create all binaries in `osu/osu.Desktop/bin/Debug`.
### 4. Optimizing
If you want additional performance you can change build type to Release with
```
msbuild -p:Configuration=Release
```
Additionally, mono provides an AOT utility which attempts to precompile binaries. You can utilize that by running
```
mono --aot ./osu\!.exe
```
### 5. Troubleshooting
You may run into trouble with NuGet versioning, as the one in packaging system is almost always out of date. Simply run
```
nuget
sudo nuget update -self
```
**Warning** NuGet creates few config files when it's run for the first time.
Do not run NuGet as root on the first run or you might run into very peculiar issues.
+7 -5
View File
@@ -1,9 +1,11 @@
osu!lazer is currently in early stages of development and is not yet ready for end users. Please avoid creating issues or bugs if you do not personally intend to fix them. Some acceptable topics include:
osu!lazer is currently still under heavy development!
Please ensure that you are making an issue for one of the following:
- A bug with currently implemented features (not features that don't exist)
- A feature you are considering adding, so we can collaborate on feedback and design.
- Discussions about technical design decisions
- Bugs that you have found and are personally willing and able to fix
- TODO lists of smaller tasks around larger features
Basically, issues are not a place for you to get help. They are a place for developers to collaborate on the game.
If your issue qualifies, replace this text with a detailed description of your issue with as much relevant information as you can provide.
Screenshots and log files are highly welcomed.
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="opentk-develop" value="https://www.myget.org/F/opentk-develop" />
</packageSources>
</configuration>
+8 -3
View File
@@ -8,14 +8,19 @@ This is still heavily under development and is not intended for end-user use. Th
# Requirements
- A desktop platform which can compile .NET 4.5 (tested on macOS, linux and windows). We recommend using [Visual Studio Code](https://code.visualstudio.com/) (all platforms) or [Visual Studio Community Edition](https://www.visualstudio.com/) (windows only), both of which are free.
- Make sure you initialise and keep submodules up-to-date.
- A desktop platform that can compile .NET 4.6.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here.
# Getting Started
- Clone the repository including submodules (`git clone --recurse-submodules https://github.com/ppy/osu`)
- Build in your IDE of choice (recommended IDEs automatically restore nuget packages; if you are using an alternative make sure to `nuget restore`)
# Contributing
We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted.
Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu-framework/issues).
Please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**.
Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues).
Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible.
+8 -4
View File
@@ -9,17 +9,21 @@ cache:
- inspectcode -> appveyor.yml
- packages -> **\packages.config
install:
- cmd: git submodule update --init --recursive
- 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.3/CodeFileSanity.exe
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.4/CodeFileSanity.exe
before_build:
- cmd: CodeFileSanity.exe
- cmd: nuget restore
- cmd: nuget restore -verbosity quiet
build:
project: osu.sln
parallel: true
verbosity: minimal
test:
assemblies:
only:
- 'osu.Desktop\**\*.dll'
after_build:
- cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
+2 -2
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<configuration>
@@ -13,7 +13,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<add key="ProjectName" value="osu.Desktop" />
<add key="NuSpecName" value="osu.Desktop\osu.nuspec" />
<add key="SolutionName" value="osu" />
<add key="TargetName" value="Client\osu.Desktop" />
<add key="TargetName" value="osu.Desktop" />
<add key="PackageName" value="osulazer" />
<add key="IconName" value="lazer.ico" />
<add key="CodeSigningCertificate" value="" />
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
@@ -13,4 +13,4 @@ namespace osu.Desktop.Deploy
[JsonProperty(@"name")]
public string Name;
}
}
}
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
@@ -25,4 +25,4 @@ namespace osu.Desktop.Deploy
[JsonProperty(@"upload_url")]
public string UploadUrl;
}
}
}
+11 -3
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
@@ -145,6 +145,8 @@ namespace osu.Desktop.Deploy
/// </summary>
private static void checkReleaseFiles()
{
if (!canGitHub) return;
var releaseLines = getReleaseLines();
//ensure we have all files necessary
@@ -157,6 +159,8 @@ namespace osu.Desktop.Deploy
private static void pruneReleases()
{
if (!canGitHub) return;
write("Pruning RELEASES...");
var releaseLines = getReleaseLines().ToList();
@@ -190,7 +194,7 @@ namespace osu.Desktop.Deploy
private static void uploadBuild(string version)
{
if (string.IsNullOrEmpty(GitHubAccessToken) || string.IsNullOrEmpty(codeSigningCertPath))
if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate))
return;
write("Publishing to GitHub...");
@@ -228,8 +232,12 @@ namespace osu.Desktop.Deploy
private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage);
private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken);
private static void checkGitHubReleases()
{
if (!canGitHub) return;
write("Checking GitHub releases...");
var req = new JsonWebRequest<List<GitHubRelease>>($"{GitHubApiEndpoint}");
req.AuthenticatedBlockingPerform();
@@ -390,7 +398,7 @@ namespace osu.Desktop.Deploy
public static void AuthenticatedBlockingPerform(this WebRequest r)
{
r.Headers.Add("Authorization", $"token {GitHubAccessToken}");
r.AddHeader("Authorization", $"token {GitHubAccessToken}");
r.Perform();
}
}
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Reflection;
+1 -5
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="..\osu.Game.props" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -22,7 +22,6 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -102,9 +101,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="..\osu.licenseheader">
<Link>osu.licenseheader</Link>
</None>
<None Include="App.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<packages>
+17 -16
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
@@ -18,9 +18,12 @@ namespace osu.Desktop
{
internal class OsuGameDesktop : OsuGame
{
private readonly bool noVersionOverlay;
public OsuGameDesktop(string[] args = null)
: base(args)
{
noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false;
}
public override Storage GetStorageForStableInstall()
@@ -42,7 +45,7 @@ namespace osu.Desktop
{
protected override string LocateBasePath()
{
Func<string, bool> checkExists = p => Directory.Exists(Path.Combine(p, "Songs"));
bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
string stableInstallPath;
@@ -79,11 +82,14 @@ namespace osu.Desktop
{
base.LoadComplete();
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
if (!noVersionOverlay)
{
Add(v);
v.State = Visibility.Visible;
});
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
{
Add(v);
v.State = Visibility.Visible;
});
}
}
public override void SetHost(GameHost host)
@@ -105,16 +111,11 @@ namespace osu.Desktop
{
var filePaths = new [] { e.FileName };
if (filePaths.All(f => Path.GetExtension(f) == @".osz"))
Task.Run(() => BeatmapManager.Import(filePaths));
else if (filePaths.All(f => Path.GetExtension(f) == @".osr"))
Task.Run(() =>
{
var score = ScoreStore.ReadReplayFile(filePaths.First());
Schedule(() => LoadScore(score));
});
}
var firstExtension = Path.GetExtension(filePaths.First());
private static readonly string[] allowed_extensions = { @".osz", @".osr" };
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
}
}
}
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Platform;
@@ -27,4 +27,4 @@ namespace osu.Desktop
host.Window.CursorState |= CursorState.Hidden;
}
}
}
}
+8 -8
View File
@@ -1,9 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Diagnostics;
using System.Net.Http;
using osu.Framework.Allocation;
using osu.Framework.Development;
using osu.Framework.Graphics;
@@ -32,7 +31,8 @@ namespace osu.Desktop.Overlays
private OsuConfigManager config;
private OsuGameBase game;
public override bool HandleInput => false;
public override bool HandleKeyboardInput => false;
public override bool HandleMouseInput => false;
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
@@ -111,7 +111,7 @@ namespace osu.Desktop.Overlays
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
if (!string.IsNullOrEmpty(lastVersion))
Scheduler.AddDelayed(() => notificationOverlay.Post(new UpdateCompleteNotification(version)), 5000);
notificationOverlay.Post(new UpdateCompleteNotification(version));
}
}
@@ -198,10 +198,9 @@ namespace osu.Desktop.Overlays
}
}
}
catch (HttpRequestException)
catch (Exception)
{
//likely have no internet connection.
//we'll ignore this and retry later.
// we'll ignore this and retry later. can be triggered by no internet connection or thread abortion.
}
finally
{
@@ -233,7 +232,8 @@ namespace osu.Desktop.Overlays
Text = @"Update ready to install. Click to restart!",
Activated = () =>
{
UpdateManager.RestartAppWhenExited();
// Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here
UpdateManager.RestartAppWhenExited().Wait();
game.GracefullyExit();
return true;
}
+14 -3
View File
@@ -1,9 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.IO;
using System.Linq;
using System.Runtime;
using osu.Framework;
using osu.Framework.Platform;
using osu.Game.IPC;
@@ -15,6 +16,9 @@ namespace osu.Desktop
[STAThread]
public static int Main(string[] args)
{
if (!RuntimeInfo.IsMono)
useMulticoreJit();
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;
@@ -22,7 +26,7 @@ namespace osu.Desktop
{
if (!host.IsPrimaryInstance)
{
var importer = new BeatmapIPCChannel(host);
var importer = new ArchiveImportIPCChannel(host);
// Restore the cwd so relative paths given at the command line work correctly
Directory.SetCurrentDirectory(cwd);
foreach (var file in args)
@@ -43,10 +47,17 @@ namespace osu.Desktop
host.Run(new OsuGameDesktop(args));
break;
}
}
return 0;
}
}
private static void useMulticoreJit()
{
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
ProfileOptimization.SetProfileRoot(directory.FullName);
ProfileOptimization.StartProfile("Startup.Profile");
}
}
}
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Reflection;
@@ -12,7 +12,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("ppy Pty Ltd")]
[assembly: AssemblyProduct("osu!lazer")]
[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2017")]
[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
+4 -8
View File
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<Import Project="..\osu.Game.props" />
<PropertyGroup>
<ProjectGuid>{419659FD-72EA-4678-9EB8-B22A746CED70}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -62,7 +63,6 @@
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<Commandlineparameters>
</Commandlineparameters>
<LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
@@ -98,7 +98,6 @@
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<LangVersion>6</LangVersion>
<ErrorReport>prompt</ErrorReport>
<StartArguments>--tests</StartArguments>
</PropertyGroup>
@@ -136,8 +135,8 @@
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SharpCompress, Version=0.18.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
@@ -174,9 +173,6 @@
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<None Include="..\osu.licenseheader">
<Link>osu.licenseheader</Link>
</None>
<None Include="app.config" />
<None Include="OpenTK.dll.config" />
<None Include="osu!.res" />
+4 -6
View File
@@ -12,15 +12,13 @@
<description>click the circles. to the beat.</description>
<summary>click the circles.</summary>
<releaseNotes>testing</releaseNotes>
<copyright>Copyright ppy Pty Ltd 2007-2017</copyright>
<copyright>Copyright ppy Pty Ltd 2007-2018</copyright>
<language>en-AU</language>
</metadata>
<files>
<file src="*.exe" target="lib\net45\" exclude="**vshost**"/>
<file src="*.dll" target="lib\net45\"/>
<file src="*.config" target="lib\net45\"/>
<file src="x86\*.dll" target="lib\net45\x86\"/>
<file src="x64\*.dll" target="lib\net45\x64\"/>
<file src="**.exe" target="lib\net45\" exclude="**vshost**"/>
<file src="**.dll" target="lib\net45\"/>
<file src="**.config" target="lib\net45\"/>
</files>
</package>
+2 -2
View File
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<packages>
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
<package id="Splat" version="2.0.0" targetFramework="net45" />
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
@@ -11,15 +11,16 @@ using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Catch.Beatmaps
{
internal class CatchBeatmapConverter : BeatmapConverter<CatchBaseHit>
public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
{
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
protected override IEnumerable<CatchBaseHit> ConvertHitObject(HitObject obj, Beatmap beatmap)
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap)
{
var curveData = obj as IHasCurve;
var positionData = obj as IHasPosition;
var positionData = obj as IHasXPosition;
var comboData = obj as IHasCombo;
var endTime = obj as IHasEndTime;
if (positionData == null)
yield break;
@@ -42,6 +43,19 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
yield break;
}
if (endTime != null)
{
yield return new BananaShower
{
StartTime = obj.StartTime,
Samples = obj.Samples,
Duration = endTime.Duration,
NewCombo = comboData?.NewCombo ?? false
};
yield break;
}
yield return new Fruit
{
StartTime = obj.StartTime,
@@ -1,37 +1,71 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
namespace osu.Game.Rulesets.Catch.Beatmaps
{
internal class CatchBeatmapProcessor : BeatmapProcessor<CatchBaseHit>
public class CatchBeatmapProcessor : BeatmapProcessor<CatchHitObject>
{
public override void PostProcess(Beatmap<CatchBaseHit> beatmap)
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
{
if (beatmap.ComboColors.Count == 0)
return;
initialiseHyperDash(beatmap.HitObjects);
int comboIndex = 0;
int colourIndex = 0;
CatchBaseHit lastObj = null;
base.PostProcess(beatmap);
int index = 0;
foreach (var obj in beatmap.HitObjects)
{
if (obj.NewCombo)
{
if (lastObj != null) lastObj.LastInCombo = true;
obj.IndexInBeatmap = index++;
}
comboIndex = 0;
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
private void initialiseHyperDash(List<CatchHitObject> objects)
{
// todo: add difficulty adjust.
double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2;
int lastDirection = 0;
double lastExcess = halfCatcherWidth;
int objCount = objects.Count;
for (int i = 0; i < objCount - 1; i++)
{
CatchHitObject currentObject = objects[i];
// not needed?
// if (currentObject is TinyDroplet) continue;
CatchHitObject nextObject = objects[i + 1];
// while (nextObject is TinyDroplet)
// {
// if (++i == objCount - 1) break;
// nextObject = objects[i + 1];
// }
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4;
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext)
{
currentObject.HyperDashTarget = nextObject;
lastExcess = halfCatcherWidth;
}
else
{
//currentObject.DistanceToHyperDash = timeToNext - distanceToNext;
lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth);
}
obj.ComboIndex = comboIndex++;
obj.ComboColour = beatmap.ComboColors[colourIndex];
lastObj = obj;
lastDirection = thisDirection;
}
}
}
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
@@ -8,17 +8,14 @@ using System.Collections.Generic;
namespace osu.Game.Rulesets.Catch
{
public class CatchDifficultyCalculator : DifficultyCalculator<CatchBaseHit>
public class CatchDifficultyCalculator : DifficultyCalculator<CatchHitObject>
{
public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap)
{
}
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
{
return 0;
}
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
}
}
}
+2 -2
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
@@ -22,6 +22,6 @@ namespace osu.Game.Rulesets.Catch
[Description("Move right")]
MoveRight,
[Description("Engage dash")]
Dash
Dash,
}
}
+11 -5
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
@@ -10,6 +10,8 @@ using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Replays.Types;
namespace osu.Game.Rulesets.Catch
{
@@ -80,7 +82,7 @@ namespace osu.Game.Rulesets.Catch
{
Mods = new Mod[]
{
new ModAutoplay(),
new CatchModAutoplay(),
new ModCinema(),
},
},
@@ -93,13 +95,17 @@ namespace osu.Game.Rulesets.Catch
public override string Description => "osu!catch";
public override string ShortName => "fruits";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new CatchDifficultyCalculator(beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
public override int LegacyID => 2;
public override int? LegacyID => 2;
public CatchRuleset(RulesetInfo rulesetInfo)
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
public CatchRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo)
{
}
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
-69
View File
@@ -1,69 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModNoFail : ModNoFail
{
}
public class CatchModEasy : ModEasy
{
}
public class CatchModHidden : ModHidden
{
public override string Description => @"Play with fading notes for a slight score advantage.";
public override double ScoreMultiplier => 1.06;
}
public class CatchModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1.12;
public override bool Ranked => true;
}
public class CatchModSuddenDeath : ModSuddenDeath
{
}
public class CatchModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.5;
}
public class CatchModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.06;
}
public class CatchModRelax : ModRelax
{
public override string Description => @"Use the mouse to control the catcher.";
}
public class CatchModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.5;
}
public class CatchModNightcore : ModNightcore
{
public override double ScoreMultiplier => 1.06;
}
public class CatchModFlashlight : ModFlashlight
{
public override double ScoreMultiplier => 1.12;
}
public class CatchModPerfect : ModPerfect
{
}
}
@@ -0,0 +1,24 @@
// 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.Catch.Objects;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Users;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModAutoplay : ModAutoplay<CatchHitObject>
{
protected override Score CreateReplayScore(Beatmap<CatchHitObject> beatmap)
{
return new Score
{
User = new User { Username = "osu!salad!" },
Replay = new CatchAutoGenerator(beatmap).Generate(),
};
}
}
}
@@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModDaycore : ModDaycore
{
public override double ScoreMultiplier => 0.3;
}
}
@@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModDoubleTime : ModDoubleTime
{
public override double ScoreMultiplier => 1.06;
}
}
@@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModEasy : ModEasy
{
public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!";
}
}
@@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModFlashlight : ModFlashlight
{
public override double ScoreMultiplier => 1.12;
}
}
@@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModHalfTime : ModHalfTime
{
public override double ScoreMultiplier => 0.3;
}
}
@@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1.12;
}
}
@@ -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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModHidden : ModHidden
{
public override string Description => @"Play with fading fruits.";
public override double ScoreMultiplier => 1.06;
}
}
@@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModNightcore : ModNightcore
{
public override double ScoreMultiplier => 1.06;
}
}
@@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModNoFail : ModNoFail
{
}
}
@@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModPerfect : ModPerfect
{
}
}
@@ -0,0 +1,12 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModRelax : ModRelax
{
public override string Description => @"Use the mouse to control the catcher.";
}
}
@@ -0,0 +1,11 @@
// 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.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModSuddenDeath : ModSuddenDeath
{
}
}
@@ -0,0 +1,48 @@
// 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.MathUtils;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Catch.Objects
{
public class BananaShower : CatchHitObject, IHasEndTime
{
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
public override bool LastInCombo => true;
protected override void CreateNestedHitObjects()
{
base.CreateNestedHitObjects();
createBananas();
}
private void createBananas()
{
double spacing = Duration;
while (spacing > 100)
spacing /= 2;
if (spacing <= 0)
return;
for (double i = StartTime; i <= EndTime; i += spacing)
AddNested(new Banana
{
Samples = Samples,
StartTime = i,
X = RNG.NextSingle()
});
}
public double EndTime => StartTime + Duration;
public double Duration { get; set; }
public class Banana : Fruit
{
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
}
}
}
@@ -1,24 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects
{
public abstract class CatchBaseHit : HitObject, IHasXPosition, IHasCombo
{
public float X { get; set; }
public Color4 ComboColour { get; set; } = Color4.Gray;
public int ComboIndex { get; set; }
public virtual bool NewCombo { get; set; }
/// <summary>
/// The next fruit starts a new combo. Used for explodey.
/// </summary>
public virtual bool LastInCombo { get; set; }
}
}
@@ -0,0 +1,60 @@
// 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.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Catch.Objects
{
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation
{
public const double OBJECT_RADIUS = 44;
public float X { get; set; }
public int IndexInBeatmap { get; set; }
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
public virtual bool NewCombo { get; set; }
public int IndexInCurrentCombo { get; set; }
public int ComboIndex { get; set; }
/// <summary>
/// The next fruit starts a new combo. Used for explodey.
/// </summary>
public virtual bool LastInCombo { get; set; }
public float Scale { get; set; } = 1;
/// <summary>
/// Whether this fruit can initiate a hyperdash.
/// </summary>
public bool HyperDash => HyperDashTarget != null;
/// <summary>
/// The target fruit if we are to initiate a hyperdash.
/// </summary>
public CatchHitObject HyperDashTarget;
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
}
}
public enum FruitVisualRepresentation
{
Pear,
Grape,
Raspberry,
Pineapple,
Banana // banananananannaanana
}
}
@@ -0,0 +1,44 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableBananaShower : DrawableCatchHitObject<BananaShower>
{
private readonly Container bananaContainer;
public DrawableBananaShower(BananaShower s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
: base(s)
{
RelativeSizeAxes = Axes.X;
Origin = Anchor.BottomLeft;
X = 0;
InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
foreach (var b in s.NestedHitObjects.Cast<BananaShower.Banana>())
AddNested(getVisualRepresentation?.Invoke(b));
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
AddJudgement(new Judgement { Result = NestedHitObjects.Cast<DrawableCatchHitObject>().Any(n => n.Judgements.Any(j => j.IsHit)) ? HitResult.Perfect : HitResult.Miss });
}
protected override void AddNested(DrawableHitObject h)
{
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
bananaContainer.Add(h);
base.AddNested(h);
}
}
}
@@ -1,15 +1,32 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public abstract class PalpableCatchHitObject<TObject> : DrawableCatchHitObject<TObject>
where TObject : CatchHitObject
{
public override bool CanBePlated => true;
protected PalpableCatchHitObject(TObject hitObject)
: base(hitObject)
{
Scale = new Vector2(HitObject.Scale);
}
}
public abstract class DrawableCatchHitObject<TObject> : DrawableCatchHitObject
where TObject : CatchBaseHit
where TObject : CatchHitObject
{
public new TObject HitObject;
@@ -17,25 +34,37 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
: base(hitObject)
{
HitObject = hitObject;
Anchor = Anchor.BottomLeft;
}
}
public abstract class DrawableCatchHitObject : DrawableScrollingHitObject<CatchBaseHit>
public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
{
protected DrawableCatchHitObject(CatchBaseHit hitObject)
public virtual bool CanBePlated => false;
protected DrawableCatchHitObject(CatchHitObject hitObject)
: base(hitObject)
{
RelativePositionAxes = Axes.Both;
RelativePositionAxes = Axes.X;
X = hitObject.X;
Y = (float)HitObject.StartTime;
}
public Func<CatchBaseHit, bool> CheckPosition;
public Func<CatchHitObject, bool> CheckPosition;
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (timeOffset > 0)
AddJudgement(new Judgement { Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Perfect : HitResult.Miss });
if (CheckPosition == null) return;
if (timeOffset >= 0)
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
private const float preempt = 1000;
@@ -43,17 +72,21 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
protected override void UpdateState(ArmedState state)
{
using (BeginAbsoluteSequence(HitObject.StartTime - preempt))
{
// animation
this.FadeIn(200);
}
switch (state)
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
using (BeginAbsoluteSequence(endTime, true))
{
case ArmedState.Miss:
using (BeginAbsoluteSequence(HitObject.StartTime, true))
this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out);
break;
switch (state)
{
case ArmedState.Miss:
this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire();
break;
case ArmedState.Hit:
this.FadeOut().Expire();
break;
}
}
}
}
@@ -1,34 +1,43 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableDroplet : DrawableCatchHitObject<Droplet>
public class DrawableDroplet : PalpableCatchHitObject<Droplet>
{
private Pulp pulp;
public DrawableDroplet(Droplet h)
: base(h)
{
Origin = Anchor.Centre;
Size = new Vector2(Pulp.PULP_SIZE);
AccentColour = h.ComboColour;
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
Masking = false;
}
[BackgroundDependencyLoader]
private void load()
{
Child = new Pulp
InternalChild = pulp = new Pulp
{
AccentColour = AccentColour,
Scale = new Vector2(0.8f),
Size = Size
};
}
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
base.AccentColour = value;
pulp.AccentColour = AccentColour;
}
}
}
}
@@ -1,24 +1,29 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableFruit : DrawableCatchHitObject<Fruit>
public class DrawableFruit : PalpableCatchHitObject<Fruit>
{
private Circle border;
public DrawableFruit(Fruit h)
: base(h)
{
Origin = Anchor.Centre;
Size = new Vector2(Pulp.PULP_SIZE * 2.2f, Pulp.PULP_SIZE * 2.8f);
AccentColour = HitObject.ComboColour;
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS);
Masking = false;
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
@@ -27,49 +32,274 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
[BackgroundDependencyLoader]
private void load()
{
Children = new Framework.Graphics.Drawable[]
// todo: this should come from the skin.
AccentColour = colourForRrepesentation(HitObject.VisualRepresentation);
InternalChildren = new[]
{
//todo: share this more
new BufferedContainer
createPulp(HitObject.VisualRepresentation),
border = new Circle
{
RelativeSizeAxes = Axes.Both,
CacheDrawnFrameBuffer = true,
EdgeEffect = new EdgeEffectParameters
{
Hollow = !HitObject.HyperDash,
Type = EdgeEffectType.Glow,
Radius = 4,
Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Darken(1).Opacity(0.6f)
},
Size = new Vector2(Height * 1.5f),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BorderColour = Color4.White,
BorderThickness = 4f,
Children = new Framework.Graphics.Drawable[]
{
new Pulp
new Box
{
RelativePositionAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AccentColour = AccentColour,
Scale = new Vector2(0.6f),
},
new Pulp
{
RelativePositionAxes = Axes.Both,
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AccentColour = AccentColour,
Y = -0.08f
},
new Pulp
{
RelativePositionAxes = Axes.Both,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
AccentColour = AccentColour,
Y = -0.08f
},
new Pulp
{
RelativePositionAxes = Axes.Both,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
AccentColour = AccentColour,
},
AlwaysPresent = true,
Colour = AccentColour,
Alpha = 0,
RelativeSizeAxes = Axes.Both
}
}
}
},
};
if (HitObject.HyperDash)
{
AddInternal(new Pulp
{
RelativePositionAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AccentColour = Color4.Red,
Blending = BlendingMode.Additive,
Alpha = 0.5f,
Scale = new Vector2(1.333f)
});
}
}
private Framework.Graphics.Drawable createPulp(FruitVisualRepresentation representation)
{
const float large_pulp_3 = 13f;
const float distance_from_centre_3 = 0.23f;
const float large_pulp_4 = large_pulp_3 * 0.925f;
const float distance_from_centre_4 = distance_from_centre_3 / 0.925f;
const float small_pulp = large_pulp_3 / 2;
Vector2 positionAt(float angle, float distance) => new Vector2(
distance * (float)Math.Sin(angle * Math.PI / 180),
distance * (float)Math.Cos(angle * Math.PI / 180));
switch (representation)
{
default:
return new Container();
case FruitVisualRepresentation.Raspberry:
return new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Framework.Graphics.Drawable[]
{
new Pulp
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
AccentColour = AccentColour,
Size = new Vector2(small_pulp),
Y = 0.05f,
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_4),
Position = positionAt(0, distance_from_centre_4),
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_4),
Position = positionAt(90, distance_from_centre_4),
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_4),
Position = positionAt(180, distance_from_centre_4),
},
new Pulp
{
Size = new Vector2(large_pulp_4),
AccentColour = AccentColour,
Position = positionAt(270, distance_from_centre_4),
},
}
};
case FruitVisualRepresentation.Pineapple:
return new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Framework.Graphics.Drawable[]
{
new Pulp
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
AccentColour = AccentColour,
Size = new Vector2(small_pulp),
Y = 0.1f,
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_4),
Position = positionAt(45, distance_from_centre_4),
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_4),
Position = positionAt(135, distance_from_centre_4),
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_4),
Position = positionAt(225, distance_from_centre_4),
},
new Pulp
{
Size = new Vector2(large_pulp_4),
AccentColour = AccentColour,
Position = positionAt(315, distance_from_centre_4),
},
}
};
case FruitVisualRepresentation.Pear:
return new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Framework.Graphics.Drawable[]
{
new Pulp
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AccentColour = AccentColour,
Size = new Vector2(small_pulp),
Y = -0.1f,
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_3),
Position = positionAt(60, distance_from_centre_3),
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_3),
Position = positionAt(180, distance_from_centre_3),
},
new Pulp
{
Size = new Vector2(large_pulp_3),
AccentColour = AccentColour,
Position = positionAt(300, distance_from_centre_3),
},
}
};
case FruitVisualRepresentation.Grape:
return new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Framework.Graphics.Drawable[]
{
new Pulp
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AccentColour = AccentColour,
Size = new Vector2(small_pulp),
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_3),
Position = positionAt(0, distance_from_centre_3),
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_3),
Position = positionAt(120, distance_from_centre_3),
},
new Pulp
{
Size = new Vector2(large_pulp_3),
AccentColour = AccentColour,
Position = positionAt(240, distance_from_centre_3),
},
}
};
case FruitVisualRepresentation.Banana:
return new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Framework.Graphics.Drawable[]
{
new Pulp
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AccentColour = AccentColour,
Size = new Vector2(small_pulp),
Y = -0.15f
},
new Pulp
{
AccentColour = AccentColour,
Size = new Vector2(large_pulp_4 * 1.2f, large_pulp_4 * 3),
},
}
};
}
}
protected override void Update()
{
base.Update();
border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
}
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
{
switch (representation)
{
default:
case FruitVisualRepresentation.Pear:
return new Color4(17, 136, 170, 255);
case FruitVisualRepresentation.Grape:
return new Color4(204, 102, 0, 255);
case FruitVisualRepresentation.Raspberry:
return new Color4(121, 9, 13, 255);
case FruitVisualRepresentation.Pineapple:
return new Color4(102, 136, 0, 255);
case FruitVisualRepresentation.Banana:
switch (RNG.Next(0, 3))
{
default:
return new Color4(255, 240, 0, 255);
case 1:
return new Color4(255, 192, 0, 255);
case 2:
return new Color4(214, 221, 28, 255);
}
}
}
}
}
@@ -1,9 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
@@ -12,41 +13,27 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
private readonly Container dropletContainer;
public DrawableJuiceStream(JuiceStream s) : base(s)
public DrawableJuiceStream(JuiceStream s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
: base(s)
{
RelativeSizeAxes = Axes.Both;
Height = (float)HitObject.Duration;
Origin = Anchor.BottomLeft;
X = 0;
Child = dropletContainer = new Container
{
RelativeSizeAxes = Axes.Both,
RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
};
InternalChild = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
foreach (CatchBaseHit tick in s.Ticks)
{
TinyDroplet tiny = tick as TinyDroplet;
if (tiny != null)
{
AddNested(new DrawableDroplet(tiny) { Scale = new Vector2(0.5f) });
continue;
}
Droplet droplet = tick as Droplet;
if (droplet != null)
AddNested(new DrawableDroplet(droplet));
Fruit fruit = tick as Fruit;
if (fruit != null)
AddNested(new DrawableFruit(fruit));
}
foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>())
AddNested(getVisualRepresentation?.Invoke(o));
}
protected override void AddNested(DrawableHitObject<CatchBaseHit> h)
protected override bool ProvidesJudgement => false;
protected override void AddNested(DrawableHitObject h)
{
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
var catchObject = (DrawableCatchHitObject)h;
catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
dropletContainer.Add(h);
base.AddNested(h);
}
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
@@ -6,18 +6,17 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
{
public class Pulp : Circle, IHasAccentColour
{
public const float PULP_SIZE = 20;
public Pulp()
{
Size = new Vector2(PULP_SIZE);
RelativePositionAxes = Axes.Both;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Blending = BlendingMode.Additive;
Colour = Color4.White.Opacity(0.9f);
@@ -34,8 +33,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Lighten(100),
Radius = 8,
Colour = accentColour.Darken(0.2f).Opacity(0.75f)
};
}
}
+2 -2
View File
@@ -1,9 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Catch.Objects
{
public class Droplet : CatchBaseHit
public class Droplet : CatchHitObject
{
}
}
+2 -2
View File
@@ -1,9 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Catch.Objects
{
public class Fruit : CatchBaseHit
public class Fruit : CatchHitObject
{
}
}
+69 -81
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
@@ -11,27 +11,24 @@ using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
using osu.Framework.Lists;
namespace osu.Game.Rulesets.Catch.Objects
{
public class JuiceStream : CatchBaseHit, IHasCurve
public class JuiceStream : CatchHitObject, IHasCurve
{
/// <summary>
/// Positional distance that results in a duration of one second, before any speed adjustments.
/// </summary>
private const float base_scoring_distance = 100;
public readonly SliderCurve Curve = new SliderCurve();
public int RepeatCount { get; set; } = 1;
public int RepeatCount { get; set; }
public double Velocity;
public double TickDistance;
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(controlPointInfo, difficulty);
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
@@ -42,71 +39,58 @@ namespace osu.Game.Rulesets.Catch.Objects
TickDistance = scoringDistance / difficulty.SliderTickRate;
}
public IEnumerable<CatchBaseHit> Ticks
protected override void CreateNestedHitObjects()
{
get
base.CreateNestedHitObjects();
createTicks();
}
private void createTicks()
{
if (TickDistance == 0)
return;
var length = Curve.Distance;
var tickDistance = Math.Min(TickDistance, length);
var spanDuration = length / Velocity;
var minDistanceFromEnd = Velocity * 0.01;
AddNested(new Fruit
{
SortedList<CatchBaseHit> ticks = new SortedList<CatchBaseHit>((a, b) => a.StartTime.CompareTo(b.StartTime));
Samples = Samples,
StartTime = StartTime,
X = X
});
if (TickDistance == 0)
return ticks;
double lastDropletTime = StartTime;
var length = Curve.Distance;
var tickDistance = Math.Min(TickDistance, length);
var repeatDuration = length / Velocity;
for (int span = 0; span < this.SpanCount(); span++)
{
var spanStartTime = StartTime + span * spanDuration;
var reversed = span % 2 == 1;
var minDistanceFromEnd = Velocity * 0.01;
ticks.Add(new Fruit
for (double d = 0; d <= length; d += tickDistance)
{
Samples = Samples,
ComboColour = ComboColour,
StartTime = StartTime,
X = X
});
var timeProgress = d / length;
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
for (var repeat = 0; repeat < RepeatCount; repeat++)
{
var repeatStartTime = StartTime + repeat * repeatDuration;
var reversed = repeat % 2 == 1;
double time = spanStartTime + timeProgress * spanDuration;
for (var d = tickDistance; d <= length; d += tickDistance)
{
if (d > length - minDistanceFromEnd)
break;
var timeProgress = d / length;
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
var lastTickTime = repeatStartTime + timeProgress * repeatDuration;
ticks.Add(new Droplet
{
StartTime = lastTickTime,
ComboColour = ComboColour,
X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
Volume = s.Volume
}))
});
}
double tinyTickInterval = tickDistance / length * repeatDuration;
double tinyTickInterval = time - lastDropletTime;
while (tinyTickInterval > 100)
tinyTickInterval /= 2;
for (double t = 0; t < repeatDuration; t += tinyTickInterval)
for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
{
double progress = reversed ? 1 - t / repeatDuration : t / repeatDuration;
double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
ticks.Add(new TinyDroplet
AddNested(new TinyDroplet
{
StartTime = repeatStartTime + t,
ComboColour = ComboColour,
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
StartTime = t,
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
@@ -115,22 +99,36 @@ namespace osu.Game.Rulesets.Catch.Objects
});
}
ticks.Add(new Fruit
if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
{
Samples = Samples,
ComboColour = ComboColour,
StartTime = repeatStartTime + repeatDuration,
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
});
AddNested(new Droplet
{
StartTime = time,
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
Volume = s.Volume
}))
});
}
lastDropletTime = time;
}
return ticks;
AddNested(new Fruit
{
Samples = Samples,
StartTime = spanStartTime + spanDuration,
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
});
}
}
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
public float EndX => Curve.PositionAt(ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH;
public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
public double Duration => EndTime - StartTime;
@@ -140,30 +138,20 @@ namespace osu.Game.Rulesets.Catch.Objects
set { Curve.Distance = value; }
}
public SliderCurve Curve { get; } = new SliderCurve();
public List<Vector2> ControlPoints
{
get { return Curve.ControlPoints; }
set { Curve.ControlPoints = value; }
}
public List<SampleInfoList> RepeatSamples { get; set; } = new List<SampleInfoList>();
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
public CurveType CurveType
{
get { return Curve.CurveType; }
set { Curve.CurveType = value; }
}
public Vector2 PositionAt(double progress) => Curve.PositionAt(ProgressAt(progress));
public double ProgressAt(double progress)
{
double p = progress * RepeatCount % 1;
if (RepeatAt(progress) % 2 == 1)
p = 1 - p;
return p;
}
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
}
}
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Catch.Objects
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Reflection;
@@ -12,7 +12,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("ppy Pty Ltd")]
[assembly: AssemblyProduct("osu.Game.Rulesets.Catch")]
[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2017")]
[assembly: AssemblyCopyright("ppy Pty Ltd 2007-2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -0,0 +1,120 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Replays;
using osu.Game.Users;
namespace osu.Game.Rulesets.Catch.Replays
{
internal class CatchAutoGenerator : AutoGenerator<CatchHitObject>
{
public const double RELEASE_DELAY = 20;
public CatchAutoGenerator(Beatmap<CatchHitObject> beatmap)
: base(beatmap)
{
Replay = new Replay { User = new User { Username = @"Autoplay" } };
}
protected Replay Replay;
public override Replay Generate()
{
// todo: add support for HT DT
const double dash_speed = CatcherArea.Catcher.BASE_SPEED;
const double movement_speed = dash_speed / 2;
float lastPosition = 0.5f;
double lastTime = 0;
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
Replay.Frames.Add(new CatchReplayFrame(-100000, lastPosition));
void moveToNext(CatchHitObject h)
{
float positionChange = Math.Abs(lastPosition - h.X);
double timeAvailable = h.StartTime - lastTime;
//So we can either make it there without a dash or not.
double speedRequired = positionChange / timeAvailable;
bool dashRequired = speedRequired > movement_speed && h.StartTime != 0;
// todo: get correct catcher size, based on difficulty CS.
const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f;
if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X)
{
//we are already in the correct range.
lastTime = h.StartTime;
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition));
return;
}
if (h is BananaShower.Banana)
{
// auto bananas unrealistically warp to catch 100% combo.
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
}
else if (h.HyperDash)
{
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
}
else if (dashRequired)
{
//we do a movement in two parts - the dash part then the normal part...
double timeAtNormalSpeed = positionChange / movement_speed;
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
double timeAtDashSpeed = timeWeNeedToSave / 2;
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
//dash movement
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, true));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + timeAtDashSpeed, midPosition));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
}
else
{
double timeBefore = positionChange / movement_speed;
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition));
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
}
lastTime = h.StartTime;
lastPosition = h.X;
}
foreach (var obj in Beatmap.HitObjects)
{
switch (obj)
{
case Fruit _:
moveToNext(obj);
break;
}
foreach (var nestedObj in obj.NestedHitObjects.Cast<CatchHitObject>())
{
switch (nestedObj)
{
case BananaShower.Banana _:
case TinyDroplet _:
case Droplet _:
moveToNext(nestedObj);
break;
}
}
}
return Replay;
}
}
}
@@ -0,0 +1,60 @@
// 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 osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Catch.Replays
{
public class CatchFramedReplayInputHandler : FramedReplayInputHandler<CatchReplayFrame>
{
public CatchFramedReplayInputHandler(Replay replay)
: base(replay)
{
}
protected override bool IsImportant(CatchReplayFrame frame) => frame.Position > 0;
protected float? Position
{
get
{
if (!HasFrames)
return null;
return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
}
}
public override List<InputState> GetPendingStates()
{
if (!Position.HasValue) return new List<InputState>();
var actions = new List<CatchAction>();
if (CurrentFrame.Dashing)
actions.Add(CatchAction.Dash);
if (Position.Value > CurrentFrame.Position)
actions.Add(CatchAction.MoveRight);
else if (Position.Value < CurrentFrame.Position)
actions.Add(CatchAction.MoveLeft);
return new List<InputState>
{
new CatchReplayState
{
PressedActions = actions,
CatcherX = Position.Value
},
};
}
public class CatchReplayState : ReplayState<CatchAction>
{
public float? CatcherX { get; set; }
}
}
}
@@ -0,0 +1,34 @@
// 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.Catch.UI;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Replays.Legacy;
using osu.Game.Rulesets.Replays.Types;
namespace osu.Game.Rulesets.Catch.Replays
{
public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame
{
public float Position;
public bool Dashing;
public CatchReplayFrame()
{
}
public CatchReplayFrame(double time, float? position = null, bool dashing = false)
: base(time)
{
Position = position ?? -1;
Dashing = dashing;
}
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap)
{
Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH;
Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1;
}
}
}
@@ -0,0 +1,957 @@
{
"Mappings": [{
"StartTime": 500.0,
"Objects": [{
"StartTime": 500.0,
"Position": 96.0
}, {
"StartTime": 562.0,
"Position": 100.84
}, {
"StartTime": 625.0,
"Position": 125.0
}, {
"StartTime": 687.0,
"Position": 152.84
}, {
"StartTime": 750.0,
"Position": 191.0
}, {
"StartTime": 812.0,
"Position": 212.84
}, {
"StartTime": 875.0,
"Position": 217.0
}, {
"StartTime": 937.0,
"Position": 234.84
}, {
"StartTime": 1000.0,
"Position": 256.0
}, {
"StartTime": 1062.0,
"Position": 267.84
}, {
"StartTime": 1125.0,
"Position": 284.0
}, {
"StartTime": 1187.0,
"Position": 311.84
}, {
"StartTime": 1250.0,
"Position": 350.0
}, {
"StartTime": 1312.0,
"Position": 359.84
}, {
"StartTime": 1375.0,
"Position": 367.0
}, {
"StartTime": 1437.0,
"Position": 400.84
}, {
"StartTime": 1500.0,
"Position": 416.0
}, {
"StartTime": 1562.0,
"Position": 377.159973
}, {
"StartTime": 1625.0,
"Position": 367.0
}, {
"StartTime": 1687.0,
"Position": 374.159973
}, {
"StartTime": 1750.0,
"Position": 353.0
}, {
"StartTime": 1812.0,
"Position": 329.159973
}, {
"StartTime": 1875.0,
"Position": 288.0
}, {
"StartTime": 1937.0,
"Position": 259.159973
}, {
"StartTime": 2000.0,
"Position": 256.0
}, {
"StartTime": 2058.0,
"Position": 232.44
}, {
"StartTime": 2116.0,
"Position": 222.879974
}, {
"StartTime": 2174.0,
"Position": 185.319992
}, {
"StartTime": 2232.0,
"Position": 177.76001
}, {
"StartTime": 2290.0,
"Position": 162.200012
}, {
"StartTime": 2348.0,
"Position": 158.639984
}, {
"StartTime": 2406.0,
"Position": 111.079994
}, {
"StartTime": 2500.0,
"Position": 96.0
}]
}, {
"StartTime": 3000.0,
"Objects": [{
"StartTime": 3000.0,
"Position": 18.0
}, {
"StartTime": 3062.0,
"Position": 482.0
}, {
"StartTime": 3125.0,
"Position": 243.0
}, {
"StartTime": 3187.0,
"Position": 332.0
}, {
"StartTime": 3250.0,
"Position": 477.0
}, {
"StartTime": 3312.0,
"Position": 376.0
}, {
"StartTime": 3375.0,
"Position": 104.0
}, {
"StartTime": 3437.0,
"Position": 156.0
}, {
"StartTime": 3500.0,
"Position": 135.0
}, {
"StartTime": 3562.0,
"Position": 256.0
}, {
"StartTime": 3625.0,
"Position": 360.0
}, {
"StartTime": 3687.0,
"Position": 199.0
}, {
"StartTime": 3750.0,
"Position": 239.0
}, {
"StartTime": 3812.0,
"Position": 326.0
}, {
"StartTime": 3875.0,
"Position": 393.0
}, {
"StartTime": 3937.0,
"Position": 470.0
}, {
"StartTime": 4000.0,
"Position": 136.0
}]
}, {
"StartTime": 4500.0,
"Objects": [{
"StartTime": 4500.0,
"Position": 317.0
}, {
"StartTime": 4562.0,
"Position": 354.0
}, {
"StartTime": 4625.0,
"Position": 414.0
}, {
"StartTime": 4687.0,
"Position": 39.0
}, {
"StartTime": 4750.0,
"Position": 172.0
}, {
"StartTime": 4812.0,
"Position": 479.0
}, {
"StartTime": 4875.0,
"Position": 18.0
}, {
"StartTime": 4937.0,
"Position": 151.0
}, {
"StartTime": 5000.0,
"Position": 342.0
}, {
"StartTime": 5062.0,
"Position": 400.0
}, {
"StartTime": 5125.0,
"Position": 420.0
}, {
"StartTime": 5187.0,
"Position": 90.0
}, {
"StartTime": 5250.0,
"Position": 220.0
}, {
"StartTime": 5312.0,
"Position": 80.0
}, {
"StartTime": 5375.0,
"Position": 421.0
}, {
"StartTime": 5437.0,
"Position": 473.0
}, {
"StartTime": 5500.0,
"Position": 97.0
}]
}, {
"StartTime": 6000.0,
"Objects": [{
"StartTime": 6000.0,
"Position": 105.0
}, {
"StartTime": 6062.0,
"Position": 249.0
}, {
"StartTime": 6125.0,
"Position": 163.0
}, {
"StartTime": 6187.0,
"Position": 194.0
}, {
"StartTime": 6250.0,
"Position": 106.0
}, {
"StartTime": 6312.0,
"Position": 212.0
}, {
"StartTime": 6375.0,
"Position": 257.0
}, {
"StartTime": 6437.0,
"Position": 461.0
}, {
"StartTime": 6500.0,
"Position": 79.0
}]
}, {
"StartTime": 7000.0,
"Objects": [{
"StartTime": 7000.0,
"Position": 256.0
}, {
"StartTime": 7062.0,
"Position": 294.84
}, {
"StartTime": 7125.0,
"Position": 279.0
}, {
"StartTime": 7187.0,
"Position": 309.84
}, {
"StartTime": 7250.0,
"Position": 336.0
}, {
"StartTime": 7312.0,
"Position": 322.16
}, {
"StartTime": 7375.0,
"Position": 308.0
}, {
"StartTime": 7437.0,
"Position": 263.16
}, {
"StartTime": 7500.0,
"Position": 256.0
}, {
"StartTime": 7562.0,
"Position": 261.84
}, {
"StartTime": 7625.0,
"Position": 277.0
}, {
"StartTime": 7687.0,
"Position": 318.84
}, {
"StartTime": 7750.0,
"Position": 336.0
}, {
"StartTime": 7803.0,
"Position": 305.04
}, {
"StartTime": 7857.0,
"Position": 307.76
}, {
"StartTime": 7910.0,
"Position": 297.8
}, {
"StartTime": 8000.0,
"Position": 256.0
}]
}, {
"StartTime": 8500.0,
"Objects": [{
"StartTime": 8500.0,
"Position": 32.0
}, {
"StartTime": 8562.0,
"Position": 22.8515015
}, {
"StartTime": 8625.0,
"Position": 28.5659637
}, {
"StartTime": 8687.0,
"Position": 50.3433228
}, {
"StartTime": 8750.0,
"Position": 56.58974
}, {
"StartTime": 8812.0,
"Position": 64.23422
}, {
"StartTime": 8875.0,
"Position": 67.7117844
}, {
"StartTime": 8937.0,
"Position": 90.52607
}, {
"StartTime": 9000.0,
"Position": 101.81015
}, {
"StartTime": 9062.0,
"Position": 113.478188
}, {
"StartTime": 9125.0,
"Position": 159.414444
}, {
"StartTime": 9187.0,
"Position": 155.1861
}, {
"StartTime": 9250.0,
"Position": 179.600418
}, {
"StartTime": 9312.0,
"Position": 212.293015
}, {
"StartTime": 9375.0,
"Position": 197.2076
}, {
"StartTime": 9437.0,
"Position": 243.438324
}, {
"StartTime": 9500.0,
"Position": 237.2304
}, {
"StartTime": 9562.0,
"Position": 241.253983
}, {
"StartTime": 9625.0,
"Position": 258.950623
}, {
"StartTime": 9687.0,
"Position": 253.3786
}, {
"StartTime": 9750.0,
"Position": 270.8865
}, {
"StartTime": 9812.0,
"Position": 244.38974
}, {
"StartTime": 9875.0,
"Position": 242.701874
}, {
"StartTime": 9937.0,
"Position": 256.2331
}, {
"StartTime": 10000.0,
"Position": 270.339874
}, {
"StartTime": 10062.0,
"Position": 275.9349
}, {
"StartTime": 10125.0,
"Position": 297.2969
}, {
"StartTime": 10187.0,
"Position": 307.834137
}, {
"StartTime": 10250.0,
"Position": 321.6449
}, {
"StartTime": 10312.0,
"Position": 357.746338
}, {
"StartTime": 10375.0,
"Position": 358.21875
}, {
"StartTime": 10437.0,
"Position": 394.943
}, {
"StartTime": 10500.0,
"Position": 401.0588
}, {
"StartTime": 10558.0,
"Position": 418.21347
}, {
"StartTime": 10616.0,
"Position": 424.6034
}, {
"StartTime": 10674.0,
"Position": 455.835754
}, {
"StartTime": 10732.0,
"Position": 477.5042
}, {
"StartTime": 10790.0,
"Position": 476.290955
}, {
"StartTime": 10848.0,
"Position": 470.943237
}, {
"StartTime": 10906.0,
"Position": 503.3372
}, {
"StartTime": 10999.0,
"Position": 508.166229
}]
}, {
"StartTime": 11500.0,
"Objects": [{
"StartTime": 11500.0,
"Position": 321.0
}, {
"StartTime": 11562.0,
"Position": 17.0
}, {
"StartTime": 11625.0,
"Position": 173.0
}, {
"StartTime": 11687.0,
"Position": 170.0
}, {
"StartTime": 11750.0,
"Position": 447.0
}, {
"StartTime": 11812.0,
"Position": 218.0
}, {
"StartTime": 11875.0,
"Position": 394.0
}, {
"StartTime": 11937.0,
"Position": 46.0
}, {
"StartTime": 12000.0,
"Position": 480.0
}]
}, {
"StartTime": 12500.0,
"Objects": [{
"StartTime": 12500.0,
"Position": 512.0
}, {
"StartTime": 12562.0,
"Position": 491.3132
}, {
"StartTime": 12625.0,
"Position": 484.3089
}, {
"StartTime": 12687.0,
"Position": 454.6221
}, {
"StartTime": 12750.0,
"Position": 433.617767
}, {
"StartTime": 12812.0,
"Position": 399.930969
}, {
"StartTime": 12875.0,
"Position": 395.926666
}, {
"StartTime": 12937.0,
"Position": 361.239868
}, {
"StartTime": 13000.0,
"Position": 353.235535
}, {
"StartTime": 13062.0,
"Position": 314.548767
}, {
"StartTime": 13125.0,
"Position": 315.544434
}, {
"StartTime": 13187.0,
"Position": 288.857635
}, {
"StartTime": 13250.0,
"Position": 254.853333
}, {
"StartTime": 13312.0,
"Position": 239.166534
}, {
"StartTime": 13375.0,
"Position": 240.1622
}, {
"StartTime": 13437.0,
"Position": 212.4754
}, {
"StartTime": 13500.0,
"Position": 194.471069
}, {
"StartTime": 13562.0,
"Position": 161.784271
}, {
"StartTime": 13625.0,
"Position": 145.779968
}, {
"StartTime": 13687.0,
"Position": 129.09314
}, {
"StartTime": 13750.0,
"Position": 104.088837
}, {
"StartTime": 13812.0,
"Position": 95.40204
}, {
"StartTime": 13875.0,
"Position": 61.3977356
}, {
"StartTime": 13937.0,
"Position": 56.710907
}, {
"StartTime": 14000.0,
"Position": 35.7066345
}, {
"StartTime": 14062.0,
"Position": 5.019806
}, {
"StartTime": 14125.0,
"Position": 0.0
}, {
"StartTime": 14187.0,
"Position": 39.7696266
}, {
"StartTime": 14250.0,
"Position": 23.0119171
}, {
"StartTime": 14312.0,
"Position": 75.94882
}, {
"StartTime": 14375.0,
"Position": 98.19112
}, {
"StartTime": 14437.0,
"Position": 82.12803
}, {
"StartTime": 14500.0,
"Position": 118.370323
}, {
"StartTime": 14562.0,
"Position": 149.307236
}, {
"StartTime": 14625.0,
"Position": 168.549515
}, {
"StartTime": 14687.0,
"Position": 190.486435
}, {
"StartTime": 14750.0,
"Position": 186.728714
}, {
"StartTime": 14812.0,
"Position": 199.665634
}, {
"StartTime": 14875.0,
"Position": 228.907928
}, {
"StartTime": 14937.0,
"Position": 264.844849
}, {
"StartTime": 15000.0,
"Position": 271.087128
}, {
"StartTime": 15062.0,
"Position": 290.024017
}, {
"StartTime": 15125.0,
"Position": 302.266327
}, {
"StartTime": 15187.0,
"Position": 344.203247
}, {
"StartTime": 15250.0,
"Position": 356.445526
}, {
"StartTime": 15312.0,
"Position": 359.382446
}, {
"StartTime": 15375.0,
"Position": 401.624725
}, {
"StartTime": 15437.0,
"Position": 388.561646
}, {
"StartTime": 15500.0,
"Position": 423.803925
}, {
"StartTime": 15562.0,
"Position": 425.740845
}, {
"StartTime": 15625.0,
"Position": 449.983124
}, {
"StartTime": 15687.0,
"Position": 468.920044
}, {
"StartTime": 15750.0,
"Position": 492.162323
}, {
"StartTime": 15812.0,
"Position": 506.784332
}, {
"StartTime": 15875.0,
"Position": 474.226227
}, {
"StartTime": 15937.0,
"Position": 482.978638
}, {
"StartTime": 16000.0,
"Position": 446.420532
}, {
"StartTime": 16058.0,
"Position": 418.4146
}, {
"StartTime": 16116.0,
"Position": 425.408844
}, {
"StartTime": 16174.0,
"Position": 383.402924
}, {
"StartTime": 16232.0,
"Position": 363.397156
}, {
"StartTime": 16290.0,
"Position": 343.391235
}, {
"StartTime": 16348.0,
"Position": 328.385468
}, {
"StartTime": 16406.0,
"Position": 322.3797
}, {
"StartTime": 16500.0,
"Position": 291.1977
}]
}, {
"StartTime": 17000.0,
"Objects": [{
"StartTime": 17000.0,
"Position": 256.0
}, {
"StartTime": 17062.0,
"Position": 228.16
}, {
"StartTime": 17125.0,
"Position": 234.0
}, {
"StartTime": 17187.0,
"Position": 202.16
}, {
"StartTime": 17250.0,
"Position": 176.0
}, {
"StartTime": 17312.0,
"Position": 210.84
}, {
"StartTime": 17375.0,
"Position": 221.0
}, {
"StartTime": 17437.0,
"Position": 219.84
}, {
"StartTime": 17500.0,
"Position": 256.0
}, {
"StartTime": 17562.0,
"Position": 219.16
}, {
"StartTime": 17625.0,
"Position": 228.0
}, {
"StartTime": 17687.0,
"Position": 203.16
}, {
"StartTime": 17750.0,
"Position": 176.0
}, {
"StartTime": 17803.0,
"Position": 174.959991
}, {
"StartTime": 17857.0,
"Position": 214.23999
}, {
"StartTime": 17910.0,
"Position": 228.200012
}, {
"StartTime": 18000.0,
"Position": 256.0
}]
}, {
"StartTime": 18500.0,
"Objects": [{
"StartTime": 18500.0,
"Position": 362.0
}, {
"StartTime": 18559.0,
"Position": 249.0
}, {
"StartTime": 18618.0,
"Position": 357.0
}, {
"StartTime": 18678.0,
"Position": 167.0
}, {
"StartTime": 18737.0,
"Position": 477.0
}, {
"StartTime": 18796.0,
"Position": 411.0
}, {
"StartTime": 18856.0,
"Position": 254.0
}, {
"StartTime": 18915.0,
"Position": 308.0
}, {
"StartTime": 18975.0,
"Position": 399.0
}, {
"StartTime": 19034.0,
"Position": 176.0
}, {
"StartTime": 19093.0,
"Position": 14.0
}, {
"StartTime": 19153.0,
"Position": 258.0
}, {
"StartTime": 19212.0,
"Position": 221.0
}, {
"StartTime": 19271.0,
"Position": 481.0
}, {
"StartTime": 19331.0,
"Position": 92.0
}, {
"StartTime": 19390.0,
"Position": 211.0
}, {
"StartTime": 19450.0,
"Position": 135.0
}]
}, {
"StartTime": 19875.0,
"Objects": [{
"StartTime": 19875.0,
"Position": 216.0
}, {
"StartTime": 19937.0,
"Position": 215.307053
}, {
"StartTime": 20000.0,
"Position": 236.036865
}, {
"StartTime": 20062.0,
"Position": 236.312088
}, {
"StartTime": 20125.0,
"Position": 235.838928
}, {
"StartTime": 20187.0,
"Position": 269.9743
}, {
"StartTime": 20250.0,
"Position": 285.999146
}, {
"StartTime": 20312.0,
"Position": 283.669067
}, {
"StartTime": 20375.0,
"Position": 317.446747
}, {
"StartTime": 20437.0,
"Position": 330.750275
}, {
"StartTime": 20500.0,
"Position": 344.0156
}, {
"StartTime": 20562.0,
"Position": 318.472168
}, {
"StartTime": 20625.0,
"Position": 309.165466
}, {
"StartTime": 20687.0,
"Position": 317.044617
}, {
"StartTime": 20750.0,
"Position": 280.457367
}, {
"StartTime": 20812.0,
"Position": 272.220581
}, {
"StartTime": 20875.0,
"Position": 270.3294
}, {
"StartTime": 20937.0,
"Position": 262.57605
}, {
"StartTime": 21000.0,
"Position": 244.803329
}, {
"StartTime": 21062.0,
"Position": 215.958359
}, {
"StartTime": 21125.0,
"Position": 177.79332
}, {
"StartTime": 21187.0,
"Position": 190.948349
}, {
"StartTime": 21250.0,
"Position": 158.78334
}, {
"StartTime": 21312.0,
"Position": 136.93837
}, {
"StartTime": 21375.0,
"Position": 119.121056
}, {
"StartTime": 21437.0,
"Position": 132.387573
}, {
"StartTime": 21500.0,
"Position": 124.503014
}, {
"StartTime": 21562.0,
"Position": 118.749374
}, {
"StartTime": 21625.0,
"Position": 123.165535
}, {
"StartTime": 21687.0,
"Position": 96.02999
}, {
"StartTime": 21750.0,
"Position": 118.547928
}, {
"StartTime": 21812.0,
"Position": 128.856232
}, {
"StartTime": 21875.0,
"Position": 124.28746
}, {
"StartTime": 21937.0,
"Position": 150.754929
}, {
"StartTime": 22000.0,
"Position": 149.528732
}, {
"StartTime": 22062.0,
"Position": 145.1691
}, {
"StartTime": 22125.0,
"Position": 182.802155
}, {
"StartTime": 22187.0,
"Position": 178.6452
}, {
"StartTime": 22250.0,
"Position": 213.892181
}, {
"StartTime": 22312.0,
"Position": 218.713028
}, {
"StartTime": 22375.0,
"Position": 240.4715
}, {
"StartTime": 22437.0,
"Position": 239.371887
}, {
"StartTime": 22500.0,
"Position": 261.907257
}, {
"StartTime": 22562.0,
"Position": 314.353119
}, {
"StartTime": 22625.0,
"Position": 299.273376
}, {
"StartTime": 22687.0,
"Position": 356.98288
}, {
"StartTime": 22750.0,
"Position": 339.078552
}, {
"StartTime": 22812.0,
"Position": 377.8958
}, {
"StartTime": 22875.0,
"Position": 398.054047
}, {
"StartTime": 22937.0,
"Position": 398.739441
}, {
"StartTime": 23000.0,
"Position": 407.178467
}, {
"StartTime": 23062.0,
"Position": 444.8687
}, {
"StartTime": 23125.0,
"Position": 417.069977
}, {
"StartTime": 23187.0,
"Position": 454.688477
}, {
"StartTime": 23250.0,
"Position": 428.9612
}, {
"StartTime": 23312.0,
"Position": 441.92807
}, {
"StartTime": 23375.0,
"Position": 439.749878
}, {
"StartTime": 23433.0,
"Position": 455.644684
}, {
"StartTime": 23491.0,
"Position": 440.7359
}, {
"StartTime": 23549.0,
"Position": 430.0944
}, {
"StartTime": 23607.0,
"Position": 420.796173
}, {
"StartTime": 23665.0,
"Position": 435.897461
}, {
"StartTime": 23723.0,
"Position": 418.462555
}, {
"StartTime": 23781.0,
"Position": 405.53775
}, {
"StartTime": 23874.0,
"Position": 408.720825
}]
}]
}
@@ -0,0 +1,27 @@
osu file format v14
[Difficulty]
HPDrainRate:6
CircleSize:4
OverallDifficulty:7
ApproachRate:8.3
SliderMultiplier:1.6
SliderTickRate:1
[TimingPoints]
500,500,4,2,1,50,1,0
13426,-100,4,3,1,45,0,0
14884,-100,4,2,1,50,0,0
[HitObjects]
96,192,500,6,0,L|416:192,2,320
256,192,3000,12,0,4000,0:0:0:0:
256,192,4500,12,0,5500,0:0:0:0:
256,192,6000,12,0,6500,0:0:0:0:
256,128,7000,6,0,L|352:128,4,80
32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800
256,192,11500,12,0,12000,0:0:0:0:
512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280
256,256,17000,6,0,L|160:256,4,80
256,192,18500,12,0,19450,0:0:0:0:
216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280
@@ -1,43 +1,41 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.Scoring
{
internal class CatchScoreProcessor : ScoreProcessor<CatchBaseHit>
public class CatchScoreProcessor : ScoreProcessor<CatchHitObject>
{
public CatchScoreProcessor(RulesetContainer<CatchBaseHit> rulesetContainer)
public CatchScoreProcessor(RulesetContainer<CatchHitObject> rulesetContainer)
: base(rulesetContainer)
{
}
protected override void SimulateAutoplay(Beatmap<CatchBaseHit> beatmap)
protected override void SimulateAutoplay(Beatmap<CatchHitObject> beatmap)
{
foreach (var obj in beatmap.HitObjects)
{
var stream = obj as JuiceStream;
if (stream != null)
switch (obj)
{
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
foreach (var unused in stream.Ticks)
case JuiceStream stream:
foreach (var _ in stream.NestedHitObjects.Cast<CatchHitObject>())
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
break;
case BananaShower shower:
foreach (var _ in shower.NestedHitObjects.Cast<CatchHitObject>())
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
continue;
break;
case Fruit _:
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
break;
}
var fruit = obj as Fruit;
if (fruit != null)
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
}
base.SimulateAutoplay(beatmap);
@@ -0,0 +1,67 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Catch.Tests
{
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")]
public new void Test(string name)
{
base.Test(name);
}
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
{
if (hitObject is JuiceStream stream)
{
foreach (var nested in stream.NestedHitObjects)
{
yield return new ConvertValue
{
StartTime = nested.StartTime,
Position = ((CatchHitObject)nested).X * CatchPlayfield.BASE_WIDTH
};
}
}
else
{
yield return new ConvertValue
{
StartTime = hitObject.StartTime,
Position = ((CatchHitObject)hitObject).X * CatchPlayfield.BASE_WIDTH
};
}
}
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new CatchBeatmapConverter();
}
public struct ConvertValue : IEquatable<ConvertValue>
{
/// <summary>
/// A sane value to account for osu!stable using ints everwhere.
/// </summary>
private const float conversion_lenience = 2;
public double StartTime;
public float Position;
public bool Equals(ConvertValue other)
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
&& Precision.AlmostEquals(Position, other.Position, conversion_lenience);
}
}
@@ -0,0 +1,47 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.UI;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(BananaShower),
typeof(DrawableBananaShower),
typeof(CatchRuleset),
typeof(CatchRulesetContainer),
};
public TestCaseBananaShower()
: base(new CatchRuleset())
{
}
protected override Beatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset.RulesetInfo
}
};
beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 5000, NewCombo = true });
return beatmap;
}
}
}
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
@@ -6,10 +6,9 @@ using NUnit.Framework;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
[Ignore("getting CI working")]
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
{
public TestCaseCatchPlayer() : base(typeof(CatchRuleset))
public TestCaseCatchPlayer() : base(new CatchRuleset())
{
}
}
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
@@ -8,19 +8,27 @@ using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
[Ignore("getting CI working")]
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
{
public TestCaseCatchStacker() : base(typeof(CatchRuleset))
public TestCaseCatchStacker()
: base(new CatchRuleset())
{
}
protected override Beatmap CreateBeatmap()
protected override Beatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap();
var beatmap = new Beatmap
{
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
Ruleset = ruleset.RulesetInfo
}
};
for (int i = 0; i < 256; i++)
beatmap.HitObjects.Add(new Fruit { X = 0.5f, StartTime = i * 100, NewCombo = i % 8 == 0 });
for (int i = 0; i < 512; i++)
beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 });
return beatmap;
}
@@ -1,44 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Tests.Visual;
using OpenTK;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
[Ignore("getting CI working")]
internal class TestCaseCatcher : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Catcher),
};
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
Children = new Drawable[]
{
new CatchInputManager(rulesets.GetRuleset(2))
{
RelativeSizeAxes = Axes.Both,
Child = new Catcher
{
RelativePositionAxes = Axes.Both,
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Size = new Vector2(1, 0.2f),
}
},
};
}
}
}
@@ -0,0 +1,61 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestCaseCatcherArea : OsuTestCase
{
private RulesetInfo catchRuleset;
private TestCatcherArea catcherArea;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(CatcherArea),
};
public TestCaseCatcherArea()
{
AddSliderStep<float>("CircleSize", 0, 8, 5, createCatcher);
AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t));
}
private void createCatcher(float size)
{
Child = new CatchInputManager(catchRuleset)
{
RelativeSizeAxes = Axes.Both,
Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomLeft
},
};
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
catchRuleset = rulesets.GetRuleset(2);
}
private class TestCatcherArea : CatcherArea
{
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
: base(beatmapDifficulty)
{
}
public void ToggleHyperDash(bool status) => MovableCatcher.HyperDashModifier = status ? 2 : 1;
}
}
}
@@ -0,0 +1,74 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using osu.Game.Tests.Visual;
using OpenTK;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestCaseFruitObjects : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(CatchHitObject),
typeof(Fruit),
typeof(Droplet),
typeof(DrawableCatchHitObject),
typeof(DrawableFruit),
typeof(DrawableDroplet),
typeof(Pulp),
};
public TestCaseFruitObjects()
{
Add(new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
createDrawable(0),
createDrawable(1),
createDrawable(2),
},
new Drawable[]
{
createDrawable(3),
createDrawable(4),
createDrawable(5),
},
}
});
}
private DrawableFruit createDrawable(int index)
{
var fruit = new Fruit
{
StartTime = 1000000000000,
IndexInBeatmap = index,
Scale = 1.5f,
};
return new DrawableFruit(fruit)
{
Anchor = Anchor.Centre,
RelativePositionAxes = Axes.Both,
Position = Vector2.Zero,
Alpha = 1,
LifetimeStart = double.NegativeInfinity,
LifetimeEnd = double.PositiveInfinity,
};
}
}
}
@@ -0,0 +1,30 @@
// 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.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
{
public TestCaseHyperdash()
: base(new CatchRuleset())
{
}
protected override Beatmap CreateBeatmap(Ruleset ruleset)
{
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
for (int i = 0; i < 512; i++)
if (i % 5 < 3)
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });
return beatmap;
}
}
}
@@ -0,0 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
namespace osu.Game.Rulesets.Catch.Tests
{
[TestFixture]
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
{
public TestCasePerformancePoints()
: base(new CatchRuleset())
{
}
}
}
+23 -47
View File
@@ -1,76 +1,64 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.UI;
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Catch.UI
{
public class CatchPlayfield : ScrollingPlayfield
{
public static readonly float BASE_WIDTH = 512;
public const float BASE_WIDTH = 512;
protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content;
private readonly Container catcherContainer;
private readonly Catcher catcher;
private readonly CatcherArea catcherArea;
public CatchPlayfield()
: base(Axes.Y)
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
: base(ScrollingDirection.Down, BASE_WIDTH)
{
Container explodingFruitContainer;
Reversed.Value = true;
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
InternalChildren = new Drawable[]
ScaledContent.Anchor = Anchor.BottomLeft;
ScaledContent.Origin = Anchor.BottomLeft;
ScaledContent.AddRange(new Drawable[]
{
content = new Container<Drawable>
{
RelativeSizeAxes = Axes.Both,
},
explodingFruitContainer = new Container
{
RelativeSizeAxes = Axes.Both,
},
catcherContainer = new Container
catcherArea = new CatcherArea(difficulty)
{
RelativeSizeAxes = Axes.X,
GetVisualRepresentation = getVisualRepresentation,
ExplodingFruitTarget = explodingFruitContainer,
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft,
Height = 180,
Child = catcher = new Catcher
{
ExplodingFruitTarget = explodingFruitContainer,
RelativePositionAxes = Axes.Both,
Origin = Anchor.TopCentre,
X = 0.5f,
}
}
};
},
content = new Container<Drawable>
{
RelativeSizeAxes = Axes.Both,
},
});
}
protected override void Update()
{
base.Update();
catcher.Size = new Vector2(catcherContainer.DrawSize.Y);
}
public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X / DrawSize.X / 2;
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
public override void Add(DrawableHitObject h)
{
h.Depth = (float)h.HitObject.StartTime;
h.OnJudgement += onJudgement;
base.Add(h);
@@ -78,18 +66,6 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.CheckPosition = CheckIfWeCanCatch;
}
public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
if (judgement.IsHit)
{
Vector2 screenPosition = judgedObject.ScreenSpaceDrawQuad.Centre;
// todo: don't do this
(judgedObject.Parent as Container<DrawableHitObject>)?.Remove(judgedObject);
(judgedObject.Parent as Container)?.Remove(judgedObject);
catcher.Add(judgedObject, screenPosition);
}
}
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement);
}
}
@@ -1,19 +1,24 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Input.Handlers;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using OpenTK;
namespace osu.Game.Rulesets.Catch.UI
{
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchBaseHit>
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchHitObject>
{
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
@@ -22,23 +27,31 @@ namespace osu.Game.Rulesets.Catch.UI
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
protected override BeatmapProcessor<CatchBaseHit> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
protected override Playfield CreatePlayfield() => new CatchPlayfield();
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter() => new CatchBeatmapConverter();
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation);
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
protected override DrawableHitObject<CatchBaseHit> GetVisualRepresentation(CatchBaseHit h)
protected override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h)
{
var fruit = h as Fruit;
if (fruit != null)
return new DrawableFruit(fruit);
var stream = h as JuiceStream;
if (stream != null)
return new DrawableJuiceStream(stream);
switch (h)
{
case Fruit fruit:
return new DrawableFruit(fruit);
case JuiceStream stream:
return new DrawableJuiceStream(stream, GetVisualRepresentation);
case BananaShower banana:
return new DrawableBananaShower(banana, GetVisualRepresentation);
case TinyDroplet tiny:
return new DrawableDroplet(tiny) { Scale = new Vector2(0.5f) };
case Droplet droplet:
return new DrawableDroplet(droplet);
}
return null;
}
-193
View File
@@ -1,193 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
namespace osu.Game.Rulesets.Catch.UI
{
public class Catcher : Container, IKeyBindingHandler<CatchAction>
{
private Texture texture;
private Container<DrawableHitObject> caughtFruit;
public Container ExplodingFruitTarget;
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
Children = new Drawable[]
{
createCatcherSprite(),
caughtFruit = new Container<DrawableHitObject>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
}
};
}
private int currentDirection;
private bool dashing;
protected bool Dashing
{
get { return dashing; }
set
{
if (value == dashing) return;
dashing = value;
if (dashing)
Schedule(addAdditiveSprite);
}
}
private void addAdditiveSprite()
{
if (!dashing) return;
var additive = createCatcherSprite();
additive.RelativePositionAxes = Axes.Both;
additive.Blending = BlendingMode.Additive;
additive.Position = Position;
additive.Scale = Scale;
((Container)Parent).Add(additive);
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
Scheduler.AddDelayed(addAdditiveSprite, 50);
}
private Sprite createCatcherSprite() => new Sprite
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Texture = texture,
OriginPosition = new Vector2(DrawWidth / 2, 10) //temporary until the sprite is aligned correctly.
};
public bool OnPressed(CatchAction action)
{
switch (action)
{
case CatchAction.MoveLeft:
currentDirection--;
return true;
case CatchAction.MoveRight:
currentDirection++;
return true;
case CatchAction.Dash:
Dashing = true;
return true;
}
return false;
}
public bool OnReleased(CatchAction action)
{
switch (action)
{
case CatchAction.MoveLeft:
currentDirection++;
return true;
case CatchAction.MoveRight:
currentDirection--;
return true;
case CatchAction.Dash:
Dashing = false;
return true;
}
return false;
}
/// <summary>
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
/// </summary>
private const double base_speed = 1.0 / 512;
protected override void Update()
{
base.Update();
if (currentDirection == 0) return;
double dashModifier = Dashing ? 1 : 0.5;
Scale = new Vector2(Math.Sign(currentDirection), 1);
X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * base_speed * dashModifier, 0, 1);
}
public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
{
fruit.RelativePositionAxes = Axes.None;
fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0);
fruit.Anchor = Anchor.TopCentre;
fruit.Origin = Anchor.BottomCentre;
fruit.Scale *= 0.7f;
fruit.LifetimeEnd = double.MaxValue;
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
{
fruit.X += RNG.Next(-5, 5);
fruit.Y -= RNG.Next(0, 5);
}
caughtFruit.Add(fruit);
if (((CatchBaseHit)fruit.HitObject).LastInCombo)
explode();
}
private void explode()
{
var fruit = caughtFruit.ToArray();
foreach (var f in fruit)
{
var originalX = f.X * Scale.X;
if (ExplodingFruitTarget != null)
{
f.Anchor = Anchor.TopLeft;
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
caughtFruit.Remove(f);
ExplodingFruitTarget.Add(f);
}
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
.Then()
.MoveToY(f.Y + 50, 500, Easing.InSine);
f.MoveToX(f.X + originalX * 6, 1000);
f.FadeOut(750);
f.Expire();
}
}
}
}
+416
View File
@@ -0,0 +1,416 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.UI
{
public class CatcherArea : Container
{
public const float CATCHER_SIZE = 172;
protected readonly Catcher MovableCatcher;
public Func<CatchHitObject, DrawableHitObject<CatchHitObject>> GetVisualRepresentation;
public Container ExplodingFruitTarget
{
set { MovableCatcher.ExplodingFruitTarget = value; }
}
public CatcherArea(BeatmapDifficulty difficulty = null)
{
RelativeSizeAxes = Axes.X;
Height = CATCHER_SIZE;
Child = MovableCatcher = new Catcher(difficulty)
{
AdditiveTarget = this,
};
}
private DrawableCatchHitObject lastPlateableFruit;
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
{
if (judgement.IsHit && fruit.CanBePlated)
{
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
if (caughtFruit == null) return;
caughtFruit.RelativePositionAxes = Axes.None;
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
caughtFruit.Anchor = Anchor.TopCentre;
caughtFruit.Origin = Anchor.Centre;
caughtFruit.Scale *= 0.7f;
caughtFruit.LifetimeEnd = double.MaxValue;
MovableCatcher.Add(caughtFruit);
lastPlateableFruit = caughtFruit;
}
if (fruit.HitObject.LastInCombo)
{
if (judgement.IsHit)
{
// 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)
MovableCatcher.Explode();
else
lastPlateableFruit.OnLoadComplete = _ => { MovableCatcher.Explode(); };
}
else
MovableCatcher.Drop();
}
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
var state = GetContainingInputManager().CurrentState as CatchFramedReplayInputHandler.CatchReplayState;
if (state?.CatcherX != null)
MovableCatcher.X = state.CatcherX.Value;
}
public bool OnReleased(CatchAction action) => false;
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
public class Catcher : Container, IKeyBindingHandler<CatchAction>
{
private Texture texture;
private Container<DrawableHitObject> caughtFruit;
public Container ExplodingFruitTarget;
public Container AdditiveTarget;
public Catcher(BeatmapDifficulty difficulty = null)
{
RelativePositionAxes = Axes.X;
X = 0.5f;
Origin = Anchor.TopCentre;
Anchor = Anchor.TopLeft;
Size = new Vector2(CATCHER_SIZE);
if (difficulty != null)
Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
Children = new Drawable[]
{
caughtFruit = new Container<DrawableHitObject>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.BottomCentre,
},
createCatcherSprite(),
};
}
private int currentDirection;
private bool dashing;
protected bool Dashing
{
get { return dashing; }
set
{
if (value == dashing) return;
dashing = value;
Trail |= dashing;
}
}
private bool trail;
/// <summary>
/// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
/// </summary>
protected bool Trail
{
get { return trail; }
set
{
if (value == trail) return;
trail = value;
if (Trail)
beginTrail();
}
}
private void beginTrail()
{
Trail &= dashing || HyperDashing;
Trail &= AdditiveTarget != null;
if (!Trail) return;
var additive = createCatcherSprite();
additive.Anchor = Anchor;
additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly.
additive.Position = Position;
additive.Scale = Scale;
additive.Colour = HyperDashing ? Color4.Red : Color4.White;
additive.RelativePositionAxes = RelativePositionAxes;
additive.Blending = BlendingMode.Additive;
AdditiveTarget.Add(additive);
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
}
private Sprite createCatcherSprite() => new Sprite
{
Size = new Vector2(CATCHER_SIZE),
FillMode = FillMode.Fill,
Texture = texture,
OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly.
};
/// <summary>
/// Add a caught fruit to the catcher's stack.
/// </summary>
/// <param name="fruit">The fruit that was caught.</param>
public void Add(DrawableHitObject fruit)
{
float ourRadius = fruit.DrawSize.X / 2 * fruit.Scale.X;
float theirRadius = 0;
const float allowance = 6;
while (caughtFruit.Any(f =>
f.LifetimeEnd == double.MaxValue &&
Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2)))
{
float diff = (ourRadius + theirRadius) / allowance;
fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff;
fruit.Y -= RNG.NextSingle() * diff;
}
fruit.X = MathHelper.Clamp(fruit.X, -CATCHER_SIZE / 2, CATCHER_SIZE / 2);
caughtFruit.Add(fruit);
}
/// <summary>
/// Let the catcher attempt to catch a fruit.
/// </summary>
/// <param name="fruit">The fruit to catch.</param>
/// <returns>Whether the catch is possible.</returns>
public bool AttemptCatch(CatchHitObject fruit)
{
double halfCatcherWidth = CATCHER_SIZE * Math.Abs(Scale.X) * 0.5f;
// this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
var validCatch =
catchObjectPosition >= catcherPosition - halfCatcherWidth &&
catchObjectPosition <= catcherPosition + halfCatcherWidth;
if (validCatch && fruit.HyperDash)
{
HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED;
HyperDashDirection = fruit.HyperDashTarget.X - fruit.X;
}
else
HyperDashModifier = 1;
return validCatch;
}
/// <summary>
/// Whether we are hypderdashing or not.
/// </summary>
public bool HyperDashing => hyperDashModifier != 1;
private double hyperDashModifier = 1;
/// <summary>
/// The direction in which hyperdash is allowed. 0 allows both directions.
/// </summary>
public double HyperDashDirection;
/// <summary>
/// The speed modifier resultant from hyperdash. Will trigger hyperdash when not equal to 1.
/// </summary>
public double HyperDashModifier
{
get { return hyperDashModifier; }
set
{
if (value == hyperDashModifier) return;
hyperDashModifier = value;
const float transition_length = 180;
if (HyperDashing)
{
this.FadeColour(Color4.OrangeRed, transition_length, Easing.OutQuint);
this.FadeTo(0.2f, transition_length, Easing.OutQuint);
Trail = true;
}
else
{
HyperDashDirection = 0;
this.FadeColour(Color4.White, transition_length, Easing.OutQuint);
this.FadeTo(1, transition_length, Easing.OutQuint);
}
}
}
public bool OnPressed(CatchAction action)
{
switch (action)
{
case CatchAction.MoveLeft:
currentDirection--;
return true;
case CatchAction.MoveRight:
currentDirection++;
return true;
case CatchAction.Dash:
Dashing = true;
return true;
}
return false;
}
public bool OnReleased(CatchAction action)
{
switch (action)
{
case CatchAction.MoveLeft:
currentDirection++;
return true;
case CatchAction.MoveRight:
currentDirection--;
return true;
case CatchAction.Dash:
Dashing = false;
return true;
}
return false;
}
/// <summary>
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
/// </summary>
public const double BASE_SPEED = 1.0 / 512;
protected override void Update()
{
base.Update();
if (currentDirection == 0) return;
var direction = Math.Sign(currentDirection);
double dashModifier = Dashing ? 1 : 0.5;
if (hyperDashModifier != 1 && (HyperDashDirection == 0 || direction == Math.Sign(HyperDashDirection)))
dashModifier = hyperDashModifier;
Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y);
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
}
/// <summary>
/// Drop any fruit off the plate.
/// </summary>
public void Drop()
{
var fruit = caughtFruit.ToArray();
foreach (var f in fruit)
{
if (ExplodingFruitTarget != null)
{
f.Anchor = Anchor.TopLeft;
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
caughtFruit.Remove(f);
ExplodingFruitTarget.Add(f);
}
f.MoveToY(f.Y + 75, 750, Easing.InSine);
f.FadeOut(750);
f.Expire();
}
}
/// <summary>
/// Explode any fruit off the plate.
/// </summary>
public void Explode()
{
var fruit = caughtFruit.ToArray();
foreach (var f in fruit)
{
var originalX = f.X * Scale.X;
if (ExplodingFruitTarget != null)
{
f.Anchor = Anchor.TopLeft;
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
caughtFruit.Remove(f);
ExplodingFruitTarget.Add(f);
}
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
.Then()
.MoveToY(f.Y + 50, 500, Easing.InSine);
f.MoveToX(f.X + originalX * 6, 1000);
f.FadeOut(750);
f.Expire();
}
}
}
}
}
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="..\osu.Game.props" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@@ -21,7 +21,6 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -33,14 +32,30 @@
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Reference Include="JetBrains.Annotations, Version=11.1.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0.13\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.bundle_green.1.1.8\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.core.1.1.8\lib\net45\SQLitePCLRaw.core.dll</HintPath>
</Reference>
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.8\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Collections" />
<Reference Include="System.Core" />
@@ -50,32 +65,51 @@
<Compile Include="Beatmaps\CatchBeatmapProcessor.cs" />
<Compile Include="CatchDifficultyCalculator.cs" />
<Compile Include="CatchInputManager.cs" />
<Compile Include="Mods\CatchModDaycore.cs" />
<Compile Include="Mods\CatchModDoubleTime.cs" />
<Compile Include="Mods\CatchModEasy.cs" />
<Compile Include="Mods\CatchModFlashlight.cs" />
<Compile Include="Mods\CatchModHalfTime.cs" />
<Compile Include="Mods\CatchModHardRock.cs" />
<Compile Include="Mods\CatchModHidden.cs" />
<Compile Include="Mods\CatchModNightcore.cs" />
<Compile Include="Mods\CatchModPerfect.cs" />
<Compile Include="Mods\CatchModRelax.cs" />
<Compile Include="Mods\CatchModSuddenDeath.cs" />
<Compile Include="Mods\CatchModAutoplay.cs" />
<Compile Include="Objects\BananaShower.cs" />
<Compile Include="Objects\Drawable\DrawableBananaShower.cs" />
<Compile Include="Objects\Drawable\DrawableCatchHitObject.cs" />
<Compile Include="Objects\Drawable\DrawableDroplet.cs" />
<Compile Include="Objects\Drawable\DrawableJuiceStream.cs" />
<Compile Include="Objects\Drawable\Pieces\Pulp.cs" />
<Compile Include="Objects\JuiceStream.cs" />
<Compile Include="Replays\CatchAutoGenerator.cs" />
<Compile Include="Replays\CatchFramedReplayInputHandler.cs" />
<Compile Include="Replays\CatchReplayFrame.cs" />
<Compile Include="Scoring\CatchScoreProcessor.cs" />
<Compile Include="Judgements\CatchJudgement.cs" />
<Compile Include="Objects\CatchBaseHit.cs" />
<Compile Include="Objects\CatchHitObject.cs" />
<Compile Include="Objects\Drawable\DrawableFruit.cs" />
<Compile Include="Objects\Droplet.cs" />
<Compile Include="Objects\Fruit.cs" />
<Compile Include="Objects\TinyDroplet.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Tests\TestCaseCatcher.cs" />
<Compile Include="Tests\CatchBeatmapConversionTest.cs" />
<Compile Include="Tests\TestCaseBananaShower.cs" />
<Compile Include="Tests\TestCaseCatcherArea.cs" />
<Compile Include="Tests\TestCaseCatchStacker.cs" />
<Compile Include="Tests\TestCaseFruitObjects.cs" />
<Compile Include="Tests\TestCasePerformancePoints.cs" />
<Compile Include="Tests\TestCaseCatchPlayer.cs" />
<Compile Include="UI\Catcher.cs" />
<Compile Include="Tests\TestCaseHyperdash.cs" />
<Compile Include="UI\CatcherArea.cs" />
<Compile Include="UI\CatchRulesetContainer.cs" />
<Compile Include="UI\CatchPlayfield.cs" />
<Compile Include="CatchRuleset.cs" />
<Compile Include="Mods\CatchMod.cs" />
<Compile Include="Mods\CatchModNoFail.cs" />
</ItemGroup>
<ItemGroup>
<None Include="..\osu.licenseheader">
<Link>osu.licenseheader</Link>
</None>
<None Include="app.config" />
<None Include="OpenTK.dll.config" />
<None Include="packages.config" />
@@ -92,12 +126,23 @@
<Private>True</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic-expected-conversion.json" />
<EmbeddedResource Include="Resources\Testing\Beatmaps\basic.osu" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" />
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" />
<Error Condition="!Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" />
</Target>
<Target Name="AfterBuild">
</Target>
-->
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
</Project>
+8 -1
View File
@@ -1,5 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="11.1.0" targetFramework="net461" />
<package id="NUnit" version="3.8.1" targetFramework="net461" />
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
<package id="ppy.OpenTK" version="3.0.13" targetFramework="net461" />
<package id="SQLitePCLRaw.bundle_green" version="1.1.8" targetFramework="net461" />
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net461" />
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.8" targetFramework="net461" />
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.8" targetFramework="net461" />
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.8" targetFramework="net461" />
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.8" targetFramework="net461" />
</packages>
@@ -0,0 +1,33 @@
// 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 osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Beatmaps
{
public class ManiaBeatmap : Beatmap<ManiaHitObject>
{
/// <summary>
/// The definitions for each stage in a <see cref="ManiaPlayfield"/>.
/// </summary>
public List<StageDefinition> Stages = new List<StageDefinition>();
/// <summary>
/// Total number of columns represented by all stages in this <see cref="ManiaBeatmap"/>.
/// </summary>
public int TotalColumns => Stages.Sum(g => g.Columns);
/// <summary>
/// Creates a new <see cref="ManiaBeatmap"/>.
/// </summary>
/// <param name="defaultStage">The initial stages.</param>
public ManiaBeatmap(StageDefinition defaultStage)
{
Stages.Add(defaultStage);
}
}
}
@@ -1,8 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Mania.Objects;
using System;
using System.Linq;
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
@@ -24,25 +25,39 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
public int TargetColumns;
public readonly bool IsForCurrentRuleset;
private Pattern lastPattern = new Pattern();
private FastRandom random;
private Beatmap beatmap;
private readonly int availableColumns;
private readonly bool isForCurrentRuleset;
private ManiaBeatmap beatmap;
public ManiaBeatmapConverter(bool isForCurrentRuleset, int availableColumns)
public ManiaBeatmapConverter(bool isForCurrentRuleset, Beatmap original)
{
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
IsForCurrentRuleset = isForCurrentRuleset;
this.isForCurrentRuleset = isForCurrentRuleset;
this.availableColumns = availableColumns;
var roundedCircleSize = Math.Round(original.BeatmapInfo.BaseDifficulty.CircleSize);
var roundedOverallDifficulty = Math.Round(original.BeatmapInfo.BaseDifficulty.OverallDifficulty);
if (isForCurrentRuleset)
TargetColumns = (int)Math.Max(1, roundedCircleSize);
else
{
float percentSliderOrSpinner = (float)original.HitObjects.Count(h => h is IHasEndTime) / original.HitObjects.Count;
if (percentSliderOrSpinner < 0.2)
TargetColumns = 7;
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
TargetColumns = roundedOverallDifficulty > 5 ? 7 : 6;
else if (percentSliderOrSpinner > 0.6)
TargetColumns = roundedOverallDifficulty > 4 ? 5 : 4;
else
TargetColumns = Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7));
}
}
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original)
{
beatmap = original;
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
@@ -51,6 +66,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
return base.ConvertBeatmap(original);
}
protected override Beatmap<ManiaHitObject> CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns });
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
{
var maniaOriginal = original as ManiaHitObject;
@@ -60,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
yield break;
}
var objects = isForCurrentRuleset ? generateSpecific(original) : generateConverted(original);
var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap);
if (objects == null)
yield break;
@@ -93,10 +110,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// Method that generates hit objects for osu!mania specific beatmaps.
/// </summary>
/// <param name="original">The original hit object.</param>
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
/// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original)
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, Beatmap originalBeatmap)
{
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
Pattern newPattern = generator.Generate();
lastPattern = newPattern;
@@ -108,26 +126,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// Method that generates hit objects for non-osu!mania beatmaps.
/// </summary>
/// <param name="original">The original hit object.</param>
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
/// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateConverted(HitObject original)
private IEnumerable<ManiaHitObject> generateConverted(HitObject original, Beatmap originalBeatmap)
{
var endTimeData = original as IHasEndTime;
var distanceData = original as IHasDistance;
var positionData = original as IHasPosition;
// Following lines currently commented out to appease resharper
Patterns.PatternGenerator conversion = null;
if (distanceData != null)
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
else if (endTimeData != null)
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, availableColumns);
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap);
else if (positionData != null)
{
computeDensity(original.StartTime);
conversion = new HitObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern, lastTime, lastPosition, density, lastStair);
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
recordNote(original.StartTime, positionData.Position);
}
@@ -136,10 +153,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
return null;
Pattern newPattern = conversion.Generate();
lastPattern = newPattern;
var stairPatternGenerator = (HitObjectPatternGenerator)conversion;
lastStair = stairPatternGenerator.StairType;
lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
return newPattern.HitObjects;
}
@@ -149,8 +165,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// </summary>
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
: base(random, hitObject, beatmap, availableColumns, previousPattern)
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
}
@@ -192,14 +208,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// </summary>
/// <param name="time">The time to retrieve the sample info list from.</param>
/// <returns></returns>
private SampleInfoList sampleInfoListAt(double time)
private List<SampleInfo> sampleInfoListAt(double time)
{
var curveData = HitObject as IHasCurve;
if (curveData == null)
return HitObject.Samples;
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.RepeatCount;
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
return curveData.RepeatSamples[index];
@@ -1,7 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Audio;
using osu.Game.Beatmaps;
@@ -25,39 +26,39 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly double endTime;
private readonly double segmentDuration;
private readonly int repeatCount;
private readonly int spanCount;
private PatternType convertType;
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
: base(random, hitObject, beatmap, availableColumns, previousPattern)
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
convertType = PatternType.None;
if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
convertType = PatternType.LowProbability;
var distanceData = hitObject as IHasDistance;
var repeatsData = hitObject as IHasRepeats;
repeatCount = repeatsData?.RepeatCount ?? 1;
spanCount = repeatsData?.SpanCount() ?? 1;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime);
// The true distance, accounting for any repeats
double distance = (distanceData?.Distance ?? 0) * repeatCount;
double distance = (distanceData?.Distance ?? 0) * spanCount;
// The velocity of the osu! hit object - calculated as the velocity of a slider
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
// The duration of the osu! hit object
double osuDuration = distance / osuVelocity;
endTime = hitObject.StartTime + osuDuration;
segmentDuration = (endTime - HitObject.StartTime) / repeatCount;
segmentDuration = (endTime - HitObject.StartTime) / spanCount;
}
public override Pattern Generate()
{
if (repeatCount > 1)
if (spanCount > 1)
{
if (segmentDuration <= 90)
return generateRandomHoldNotes(HitObject.StartTime, 1);
@@ -65,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (segmentDuration <= 120)
{
convertType |= PatternType.ForceNotStack;
return generateRandomNotes(HitObject.StartTime, repeatCount + 1);
return generateRandomNotes(HitObject.StartTime, spanCount + 1);
}
if (segmentDuration <= 160)
@@ -78,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (duration >= 4000)
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
if (segmentDuration > 400 && repeatCount < AvailableColumns - 1 - RandomStart)
if (segmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
return generateTiledHoldNotes(HitObject.StartTime);
return generateHoldAndNormalNotes(HitObject.StartTime);
@@ -86,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (segmentDuration <= 110)
{
if (PreviousPattern.ColumnWithObjects < AvailableColumns)
if (PreviousPattern.ColumnWithObjects < TotalColumns)
convertType |= PatternType.ForceNotStack;
else
convertType &= ~PatternType.ForceNotStack;
@@ -134,12 +135,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern();
int usableColumns = AvailableColumns - RandomStart - PreviousPattern.ColumnWithObjects;
int nextColumn = Random.Next(RandomStart, AvailableColumns);
int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects;
int nextColumn = Random.Next(RandomStart, TotalColumns);
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
{
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
nextColumn = Random.Next(RandomStart, AvailableColumns);
nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(pattern, nextColumn, startTime, endTime);
}
@@ -147,7 +148,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
for (int i = 0; i < noteCount - usableColumns; i++)
{
while (pattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, AvailableColumns);
nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(pattern, nextColumn, startTime, endTime);
}
@@ -171,10 +172,10 @@ 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 < AvailableColumns)
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
{
while (PreviousPattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, AvailableColumns);
nextColumn = Random.Next(RandomStart, TotalColumns);
}
int lastColumn = nextColumn;
@@ -182,7 +183,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
addToPattern(pattern, nextColumn, startTime, startTime);
while (nextColumn == lastColumn)
nextColumn = Random.Next(RandomStart, AvailableColumns);
nextColumn = Random.Next(RandomStart, TotalColumns);
lastColumn = nextColumn;
startTime += segmentDuration;
@@ -212,7 +213,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
bool increasing = Random.NextDouble() > 0.5;
for (int i = 0; i <= repeatCount; i++)
for (int i = 0; i <= spanCount; i++)
{
addToPattern(pattern, column, startTime, startTime);
startTime += segmentDuration;
@@ -220,7 +221,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Check if we're at the borders of the stage, and invert the pattern if so
if (increasing)
{
if (column >= AvailableColumns - 1)
if (column >= TotalColumns - 1)
{
increasing = false;
column--;
@@ -258,24 +259,24 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern();
bool legacy = AvailableColumns >= 4 && AvailableColumns <= 8;
int interval = Random.Next(1, AvailableColumns - (legacy ? 1 : 0));
bool legacy = TotalColumns >= 4 && TotalColumns <= 8;
int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0));
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i <= repeatCount; i++)
for (int i = 0; i <= spanCount; i++)
{
addToPattern(pattern, nextColumn, startTime, startTime);
nextColumn += interval;
if (nextColumn >= AvailableColumns - RandomStart)
nextColumn = nextColumn - AvailableColumns - RandomStart + (legacy ? 1 : 0);
if (nextColumn >= TotalColumns - RandomStart)
nextColumn = nextColumn - TotalColumns - RandomStart + (legacy ? 1 : 0);
nextColumn += RandomStart;
// If we're in 2K, let's not add many consecutive doubles
if (AvailableColumns > 2)
if (TotalColumns > 2)
addToPattern(pattern, nextColumn, startTime, startTime);
nextColumn = Random.Next(RandomStart, AvailableColumns);
nextColumn = Random.Next(RandomStart, TotalColumns);
startTime += segmentDuration;
}
@@ -297,7 +298,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// □ - □ □
// ■ - ■ ■
switch (AvailableColumns)
switch (TotalColumns)
{
case 2:
p2 = 0;
@@ -305,23 +306,23 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p4 = 0;
break;
case 3:
p2 = Math.Max(p2, 0.1);
p2 = Math.Min(p2, 0.1);
p3 = 0;
p4 = 0;
break;
case 4:
p2 = Math.Max(p2, 0.3);
p3 = Math.Max(p3, 0.04);
p2 = Math.Min(p2, 0.3);
p3 = Math.Min(p3, 0.04);
p4 = 0;
break;
case 5:
p2 = Math.Max(p2, 0.34);
p3 = Math.Max(p3, 0.1);
p4 = Math.Max(p4, 0.03);
p2 = Math.Min(p2, 0.34);
p3 = Math.Min(p3, 0.1);
p4 = Math.Min(p4, 0.03);
break;
}
Func<SampleInfo, bool> isDoubleSample = sample => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH;
bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH;
bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0;
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
@@ -350,19 +351,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern();
int columnRepeat = Math.Min(repeatCount, AvailableColumns);
int columnRepeat = Math.Min(spanCount, TotalColumns);
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < AvailableColumns)
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
{
while (PreviousPattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, AvailableColumns);
nextColumn = Random.Next(RandomStart, TotalColumns);
}
for (int i = 0; i < columnRepeat; i++)
{
while (pattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, AvailableColumns);
nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(pattern, nextColumn, startTime, endTime);
startTime += segmentDuration;
@@ -387,36 +388,38 @@ 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 < AvailableColumns)
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
{
while (PreviousPattern.ColumnHasObject(holdColumn))
holdColumn = Random.Next(RandomStart, AvailableColumns);
holdColumn = Random.Next(RandomStart, TotalColumns);
}
// Create the hold note
addToPattern(pattern, holdColumn, startTime, endTime);
int noteCount = 1;
int nextColumn = Random.Next(RandomStart, TotalColumns);
int noteCount;
if (ConversionDifficulty > 6.5)
noteCount = GetRandomNoteCount(0.63, 0);
else if (ConversionDifficulty > 4)
noteCount = GetRandomNoteCount(AvailableColumns < 6 ? 0.12 : 0.45, 0);
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0.12 : 0.45, 0);
else if (ConversionDifficulty > 2.5)
noteCount = GetRandomNoteCount(AvailableColumns < 6 ? 0 : 0.24, 0);
noteCount = Math.Min(AvailableColumns - 1, noteCount);
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0 : 0.24, 0);
else
noteCount = 0;
noteCount = Math.Min(TotalColumns - 1, noteCount);
bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP);
int nextColumn = Random.Next(RandomStart, AvailableColumns);
var rowPattern = new Pattern();
for (int i = 0; i <= repeatCount; i++)
for (int i = 0; i <= spanCount; i++)
{
if (!(ignoreHead && startTime == HitObject.StartTime))
{
for (int j = 0; j < noteCount; j++)
{
while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn)
nextColumn = Random.Next(RandomStart, AvailableColumns);
nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(rowPattern, nextColumn, startTime, startTime);
}
}
@@ -435,14 +438,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// </summary>
/// <param name="time">The time to retrieve the sample info list from.</param>
/// <returns></returns>
private SampleInfoList sampleInfoListAt(double time)
private List<SampleInfo> sampleInfoListAt(double time)
{
var curveData = HitObject as IHasCurve;
if (curveData == null)
return HitObject.Samples;
double segmentTime = (endTime - HitObject.StartTime) / repeatCount;
double segmentTime = (endTime - HitObject.StartTime) / spanCount;
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
return curveData.RepeatSamples[index];
@@ -1,12 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using System.Collections.Generic;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using System.Linq;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
@@ -15,8 +16,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
private readonly double endTime;
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns)
: base(random, hitObject, beatmap, availableColumns, new Pattern())
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
{
var endtimeData = HitObject as IHasEndTime;
@@ -29,14 +30,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
bool generateHold = endTime - HitObject.StartTime >= 100;
if (AvailableColumns == 8)
if (TotalColumns == 8)
{
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000)
addToPattern(pattern, 0, generateHold);
else
addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold);
}
else if (AvailableColumns > 0)
else if (TotalColumns > 0)
addToPattern(pattern, getNextRandomColumn(0), generateHold);
return pattern;
@@ -49,10 +50,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <returns>A random column after <paramref name="start"/>.</returns>
private int getNextRandomColumn(int start)
{
int nextColumn = Random.Next(start, AvailableColumns);
int nextColumn = Random.Next(start, TotalColumns);
while (PreviousPattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(start, AvailableColumns);
nextColumn = Random.Next(start, TotalColumns);
return nextColumn;
}
@@ -76,10 +77,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
Duration = endTime - HitObject.StartTime
};
hold.Head.Samples.Add(new SampleInfo
{
Name = SampleInfo.HIT_NORMAL
});
if (hold.Head.Samples == null)
hold.Head.Samples = new List<SampleInfo>();
hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL });
hold.Tail.Samples = HitObject.Samples;
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
@@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType;
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
: base(random, hitObject, beatmap, availableColumns, previousPattern)
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, Beatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
@@ -88,23 +88,23 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Generate a new pattern by copying the last hit objects in reverse-column order
var pattern = new Pattern();
for (int i = RandomStart; i < AvailableColumns; i++)
for (int i = RandomStart; i < TotalColumns; i++)
if (PreviousPattern.ColumnHasObject(i))
addToPattern(pattern, RandomStart + AvailableColumns - i - 1);
addToPattern(pattern, RandomStart + TotalColumns - i - 1);
return pattern;
}
if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1
// If we convert to 7K + 1, let's not overload the special key
&& (AvailableColumns != 8 || lastColumn != 0)
&& (TotalColumns != 8 || lastColumn != 0)
// Make sure the last column was not the centre column
&& (AvailableColumns % 2 == 0 || lastColumn != AvailableColumns / 2))
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
{
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
var pattern = new Pattern();
int column = RandomStart + AvailableColumns - lastColumn - 1;
int column = RandomStart + TotalColumns - lastColumn - 1;
addToPattern(pattern, column);
return pattern;
@@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Generate a new pattern by placing on the already filled columns
var pattern = new Pattern();
for (int i = RandomStart; i < AvailableColumns; i++)
for (int i = RandomStart; i < TotalColumns; i++)
if (PreviousPattern.ColumnHasObject(i))
addToPattern(pattern, i);
@@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern();
int targetColumn = lastColumn + 1;
if (targetColumn == AvailableColumns)
if (targetColumn == TotalColumns)
{
targetColumn = RandomStart;
StairType = PatternType.ReverseStair;
@@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int targetColumn = lastColumn - 1;
if (targetColumn == RandomStart - 1)
{
targetColumn = AvailableColumns - 1;
targetColumn = TotalColumns - 1;
StairType = PatternType.Stair;
}
@@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
bool allowStacking = (convertType & PatternType.ForceNotStack) == 0;
if (!allowStacking)
noteCount = Math.Min(noteCount, AvailableColumns - RandomStart - PreviousPattern.ColumnWithObjects);
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i < noteCount; i++)
@@ -216,11 +216,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if ((convertType & PatternType.Gathered) > 0)
{
nextColumn++;
if (nextColumn == AvailableColumns)
if (nextColumn == TotalColumns)
nextColumn = RandomStart;
}
else
nextColumn = Random.Next(RandomStart, AvailableColumns);
nextColumn = Random.Next(RandomStart, TotalColumns);
}
addToPattern(pattern, nextColumn);
@@ -268,7 +268,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
bool addToCentre;
int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre);
int columnLimit = (AvailableColumns % 2 == 0 ? AvailableColumns : AvailableColumns - 1) / 2;
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
int nextColumn = Random.Next(RandomStart, columnLimit);
for (int i = 0; i < noteCount; i++)
{
@@ -278,11 +278,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Add normal note
addToPattern(pattern, nextColumn);
// Add mirrored note
addToPattern(pattern, RandomStart + AvailableColumns - nextColumn - 1);
addToPattern(pattern, RandomStart + TotalColumns - nextColumn - 1);
}
if (addToCentre)
addToPattern(pattern, AvailableColumns / 2);
addToPattern(pattern, TotalColumns / 2);
if (RandomStart > 0 && hasSpecialColumn)
addToPattern(pattern, 0);
@@ -300,7 +300,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <returns>The amount of notes to be generated.</returns>
private int getRandomNoteCount(double p2, double p3, double p4, double p5)
{
switch (AvailableColumns)
switch (TotalColumns)
{
case 2:
p2 = 0;
@@ -309,20 +309,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p5 = 0;
break;
case 3:
p2 = Math.Max(p2, 0.1);
p2 = Math.Min(p2, 0.1);
p3 = 0;
p4 = 0;
p5 = 0;
break;
case 4:
p2 = Math.Max(p2, 0.23);
p3 = Math.Max(p3, 0.04);
p2 = Math.Min(p2, 0.23);
p3 = Math.Min(p3, 0.04);
p4 = 0;
p5 = 0;
break;
case 5:
p3 = Math.Max(p3, 0.15);
p4 = Math.Max(p4, 0.03);
p3 = Math.Min(p3, 0.15);
p4 = Math.Min(p4, 0.03);
p5 = 0;
break;
}
@@ -348,7 +348,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if ((convertType & PatternType.ForceNotStack) > 0)
return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3);
switch (AvailableColumns)
switch (TotalColumns)
{
case 2:
centreProbability = 0;
@@ -356,30 +356,30 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p3 = 0;
break;
case 3:
centreProbability = Math.Max(centreProbability, 0.03);
p2 = Math.Max(p2, 0.1);
centreProbability = Math.Min(centreProbability, 0.03);
p2 = 0;
p3 = 0;
break;
case 4:
centreProbability = 0;
p2 = Math.Max(p2 * 2, 0.2);
p2 = Math.Min(p2 * 2, 0.2);
p3 = 0;
break;
case 5:
centreProbability = Math.Max(centreProbability, 0.03);
centreProbability = Math.Min(centreProbability, 0.03);
p3 = 0;
break;
case 6:
centreProbability = 0;
p2 = Math.Max(p2 * 2, 0.5);
p3 = Math.Max(p3 * 2, 0.15);
p2 = Math.Min(p2 * 2, 0.5);
p3 = Math.Min(p3 * 2, 0.15);
break;
}
double centreVal = Random.NextDouble();
int noteCount = GetRandomNoteCount(p2, p3);
addToCentre = AvailableColumns % 2 != 0 && noteCount != 3 && centreVal > 1 - centreProbability;
addToCentre = TotalColumns % 2 != 0 && noteCount != 3 && centreVal > 1 - centreProbability;
return noteCount;
}
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
@@ -25,16 +25,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// </summary>
protected readonly FastRandom Random;
protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
: base(hitObject, beatmap, availableColumns, previousPattern)
/// <summary>
/// The beatmap which <see cref="HitObject"/> is being converted from.
/// </summary>
protected readonly Beatmap OriginalBeatmap;
protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
: base(hitObject, beatmap, previousPattern)
{
if (random == null) throw new ArgumentNullException(nameof(random));
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap));
Random = random;
RandomStart = AvailableColumns == 8 ? 1 : 0;
OriginalBeatmap = originalBeatmap;
RandomStart = TotalColumns == 8 ? 1 : 0;
}
/// <summary>
@@ -45,14 +50,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <returns>The column.</returns>
protected int GetColumn(float position, bool allowSpecial = false)
{
if (allowSpecial && AvailableColumns == 8)
if (allowSpecial && TotalColumns == 8)
{
const float local_x_divisor = 512f / 7;
return MathHelper.Clamp((int)Math.Floor(position / local_x_divisor), 0, 6) + 1;
}
float localXDivisor = 512f / AvailableColumns;
return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, AvailableColumns - 1);
float localXDivisor = 512f / TotalColumns;
return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, TotalColumns - 1);
}
/// <summary>
@@ -95,17 +100,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (conversionDifficulty != null)
return conversionDifficulty.Value;
HitObject lastObject = Beatmap.HitObjects.LastOrDefault();
HitObject firstObject = Beatmap.HitObjects.FirstOrDefault();
HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault();
HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault();
double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0);
drainTime -= Beatmap.TotalBreakTime;
drainTime -= OriginalBeatmap.TotalBreakTime;
if (drainTime == 0)
drainTime = 10000;
drainTime = 10000000;
BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.BaseDifficulty;
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
// We need this in seconds
drainTime /= 1000;
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
return conversionDifficulty.Value;
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
@@ -1,8 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
@@ -12,11 +11,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
/// </summary>
internal abstract class PatternGenerator
{
/// <summary>
/// The number of columns available to create the pattern.
/// </summary>
protected readonly int AvailableColumns;
/// <summary>
/// The last pattern.
/// </summary>
@@ -30,19 +24,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
/// <summary>
/// The beatmap which <see cref="HitObject"/> is a part of.
/// </summary>
protected readonly Beatmap Beatmap;
protected readonly ManiaBeatmap Beatmap;
protected PatternGenerator(HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
protected readonly int TotalColumns;
protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
{
if (hitObject == null) throw new ArgumentNullException(nameof(hitObject));
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
HitObject = hitObject;
Beatmap = beatmap;
AvailableColumns = availableColumns;
PreviousPattern = previousPattern;
TotalColumns = Beatmap.TotalColumns;
}
/// <summary>
@@ -0,0 +1,25 @@
// 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.UI;
namespace osu.Game.Rulesets.Mania.Beatmaps
{
/// <summary>
/// Defines properties for each stage in a <see cref="ManiaPlayfield"/>.
/// </summary>
public struct StageDefinition
{
/// <summary>
/// The number of <see cref="Column"/>s which this stage contains.
/// </summary>
public int Columns;
/// <summary>
/// Whether the column index is a special column for this stage.
/// </summary>
/// <param name="column">The 0-based column index.</param>
/// <returns>Whether the column is a special column.</returns>
public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2;
}
}
@@ -0,0 +1,34 @@
// 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.Tracking;
using osu.Game.Configuration;
using osu.Game.Rulesets.Configuration;
namespace osu.Game.Rulesets.Mania.Configuration
{
public class ManiaConfigManager : RulesetConfigManager<ManiaSetting>
{
public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant)
: base(settings, ruleset, variant)
{
}
protected override void InitialiseDefaults()
{
base.InitialiseDefaults();
Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0);
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
{
new TrackedSetting<double>(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
};
}
public enum ManiaSetting
{
ScrollTime
}
}
@@ -1,200 +0,0 @@
// Copyright (c) 2007-2017 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.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Judgements
{
public class HitWindows
{
#region Constants
/// <summary>
/// PERFECT hit window at OD = 10.
/// </summary>
private const double perfect_min = 27.8;
/// <summary>
/// PERFECT hit window at OD = 5.
/// </summary>
private const double perfect_mid = 38.8;
/// <summary>
/// PERFECT hit window at OD = 0.
/// </summary>
private const double perfect_max = 44.8;
/// <summary>
/// GREAT hit window at OD = 10.
/// </summary>
private const double great_min = 68;
/// <summary>
/// GREAT hit window at OD = 5.
/// </summary>
private const double great_mid = 98;
/// <summary>
/// GREAT hit window at OD = 0.
/// </summary>
private const double great_max = 128;
/// <summary>
/// GOOD hit window at OD = 10.
/// </summary>
private const double good_min = 134;
/// <summary>
/// GOOD hit window at OD = 5.
/// </summary>
private const double good_mid = 164;
/// <summary>
/// GOOD hit window at OD = 0.
/// </summary>
private const double good_max = 194;
/// <summary>
/// OK hit window at OD = 10.
/// </summary>
private const double ok_min = 194;
/// <summary>
/// OK hit window at OD = 5.
/// </summary>
private const double ok_mid = 224;
/// <summary>
/// OK hit window at OD = 0.
/// </summary>
private const double ok_max = 254;
/// <summary>
/// BAD hit window at OD = 10.
/// </summary>
private const double bad_min = 242;
/// <summary>
/// BAD hit window at OD = 5.
/// </summary>
private const double bad_mid = 272;
/// <summary>
/// BAD hit window at OD = 0.
/// </summary>
private const double bad_max = 302;
/// <summary>
/// MISS hit window at OD = 10.
/// </summary>
private const double miss_min = 316;
/// <summary>
/// MISS hit window at OD = 5.
/// </summary>
private const double miss_mid = 346;
/// <summary>
/// MISS hit window at OD = 0.
/// </summary>
private const double miss_max = 376;
#endregion
/// <summary>
/// Hit window for a PERFECT hit.
/// </summary>
public double Perfect = perfect_mid;
/// <summary>
/// Hit window for a GREAT hit.
/// </summary>
public double Great = great_mid;
/// <summary>
/// Hit window for a GOOD hit.
/// </summary>
public double Good = good_mid;
/// <summary>
/// Hit window for an OK hit.
/// </summary>
public double Ok = ok_mid;
/// <summary>
/// Hit window for a BAD hit.
/// </summary>
public double Bad = bad_mid;
/// <summary>
/// Hit window for a MISS hit.
/// </summary>
public double Miss = miss_mid;
/// <summary>
/// Constructs default hit windows.
/// </summary>
public HitWindows()
{
}
/// <summary>
/// Constructs hit windows by fitting a parameter to a 2-part piecewise linear function for each hit window.
/// </summary>
/// <param name="difficulty">The parameter.</param>
public HitWindows(double difficulty)
{
Perfect = BeatmapDifficulty.DifficultyRange(difficulty, perfect_max, perfect_mid, perfect_min);
Great = BeatmapDifficulty.DifficultyRange(difficulty, great_max, great_mid, great_min);
Good = BeatmapDifficulty.DifficultyRange(difficulty, good_max, good_mid, good_min);
Ok = BeatmapDifficulty.DifficultyRange(difficulty, ok_max, ok_mid, ok_min);
Bad = BeatmapDifficulty.DifficultyRange(difficulty, bad_max, bad_mid, bad_min);
Miss = BeatmapDifficulty.DifficultyRange(difficulty, miss_max, miss_mid, miss_min);
}
/// <summary>
/// Retrieves the hit result for a time offset.
/// </summary>
/// <param name="hitOffset">The time offset.</param>
/// <returns>The hit result, or null if the time offset results in a miss.</returns>
public HitResult? ResultFor(double hitOffset)
{
if (hitOffset <= Perfect / 2)
return HitResult.Perfect;
if (hitOffset <= Great / 2)
return HitResult.Great;
if (hitOffset <= Good / 2)
return HitResult.Good;
if (hitOffset <= Ok / 2)
return HitResult.Ok;
if (hitOffset <= Bad / 2)
return HitResult.Meh;
return null;
}
/// <summary>
/// Constructs new hit windows which have been multiplied by a value.
/// </summary>
/// <param name="windows">The original hit windows.</param>
/// <param name="value">The value to multiply each hit window by.</param>
public static HitWindows operator *(HitWindows windows, double value)
{
return new HitWindows
{
Perfect = windows.Perfect * value,
Great = windows.Great * value,
Good = windows.Good * value,
Ok = windows.Ok * value,
Bad = windows.Bad * value,
Miss = windows.Miss * value
};
}
/// <summary>
/// Constructs new hit windows which have been divided by a value.
/// </summary>
/// <param name="windows">The original hit windows.</param>
/// <param name="value">The value to divide each hit window by.</param>
public static HitWindows operator /(HitWindows windows, double value)
{
return new HitWindows
{
Perfect = windows.Perfect / value,
Great = windows.Great / value,
Good = windows.Good / value,
Ok = windows.Ok / value,
Bad = windows.Bad / value,
Miss = windows.Miss / value
};
}
}
}
@@ -1,7 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
@@ -24,4 +24,4 @@ namespace osu.Game.Rulesets.Mania.Judgements
}
}
}
}
}
@@ -1,7 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
@@ -11,4 +11,4 @@ namespace osu.Game.Rulesets.Mania.Judgements
protected override int NumericResultFor(HitResult result) => 20;
}
}
}
@@ -1,8 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
@@ -1,26 +1,146 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using System.Collections.Generic;
using osu.Game.Rulesets.Mods;
using System;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Mania
{
public class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject>
internal class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject>
{
private const double star_scaling_factor = 0.018;
/// <summary>
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size strain_step.
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
/// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
/// </summary>
private const double strain_step = 400;
/// <summary>
/// The weighting of each strain value decays to this number * it's previous value
/// </summary>
private const double decay_weight = 0.9;
/// <summary>
/// HitObjects are stored as a member variable.
/// </summary>
private readonly List<ManiaHitObjectDifficulty> difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
public ManiaDifficultyCalculator(Beatmap beatmap)
: base(beatmap)
{
}
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
public ManiaDifficultyCalculator(Beatmap beatmap, Mod[] mods)
: base(beatmap, mods)
{
return 0;
}
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.BaseDifficulty.CircleSize)));
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
{
// Fill our custom DifficultyHitObject class, that carries additional information
difficultyHitObjects.Clear();
int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7;
foreach (var hitObject in Beatmap.HitObjects)
difficultyHitObjects.Add(new ManiaHitObjectDifficulty(hitObject, columnCount));
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
if (!calculateStrainValues())
return 0;
double starRating = calculateDifficulty() * star_scaling_factor;
categoryDifficulty?.Add("Strain", starRating);
return starRating;
}
private bool calculateStrainValues()
{
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
using (List<ManiaHitObjectDifficulty>.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator())
{
if (!hitObjectsEnumerator.MoveNext())
return false;
ManiaHitObjectDifficulty current = hitObjectsEnumerator.Current;
// First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
while (hitObjectsEnumerator.MoveNext())
{
var next = hitObjectsEnumerator.Current;
next?.CalculateStrains(current, TimeRate);
current = next;
}
return true;
}
}
private double calculateDifficulty()
{
double actualStrainStep = strain_step * TimeRate;
// Find the highest strain value within each strain step
List<double> highestStrains = new List<double>();
double intervalEndTime = actualStrainStep;
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
ManiaHitObjectDifficulty previousHitObject = null;
foreach (var hitObject in difficultyHitObjects)
{
// While we are beyond the current interval push the currently available maximum to our strain list
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
{
highestStrains.Add(maximumStrain);
// The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
// until the beginning of the next interval.
if (previousHitObject == null)
{
maximumStrain = 0;
}
else
{
double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay;
}
// Go to the next time interval
intervalEndTime += actualStrainStep;
}
// Obtain maximum strain
double strain = hitObject.IndividualStrain + hitObject.OverallStrain;
maximumStrain = Math.Max(strain, maximumStrain);
previousHitObject = hitObject;
}
// Build the weighted sum over the highest strains for each interval
double difficulty = 0;
double weight = 1;
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
foreach (double strain in highestStrains)
{
difficulty += weight * strain;
weight *= decay_weight;
}
return difficulty;
}
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap);
}
}
+27 -4
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
@@ -17,8 +17,13 @@ namespace osu.Game.Rulesets.Mania
public enum ManiaAction
{
[Description("Special")]
Special,
[Description("Special 1")]
Special1 = 1,
[Description("Special 2")]
Special2,
// This offsets the start value of normal keys in-case we add more special keys
// above at a later time, without breaking replays/configs.
[Description("Key 1")]
Key1 = 10,
[Description("Key 2")]
@@ -36,6 +41,24 @@ namespace osu.Game.Rulesets.Mania
[Description("Key 8")]
Key8,
[Description("Key 9")]
Key9
Key9,
[Description("Key 10")]
Key10,
[Description("Key 11")]
Key11,
[Description("Key 12")]
Key12,
[Description("Key 13")]
Key13,
[Description("Key 14")]
Key14,
[Description("Key 15")]
Key15,
[Description("Key 16")]
Key16,
[Description("Key 17")]
Key17,
[Description("Key 18")]
Key18,
}
}
+188 -33
View File
@@ -1,15 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays.Types;
namespace osu.Game.Rulesets.Mania
{
@@ -86,7 +90,8 @@ namespace osu.Game.Rulesets.Mania
},
},
new ManiaModRandom(),
new ManiaModKeyCoop(),
new ManiaModDualStages(),
new ManiaModMirror(),
new MultiMod
{
Mods = new Mod[]
@@ -95,7 +100,6 @@ namespace osu.Game.Rulesets.Mania
new ModCinema(),
},
},
new ManiaModGravity()
};
default:
@@ -105,53 +109,204 @@ namespace osu.Game.Rulesets.Mania
public override string Description => "osu!mania";
public override string ShortName => "mania";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods);
public override int LegacyID => 3;
public override int? LegacyID => 3;
public ManiaRuleset(RulesetInfo rulesetInfo)
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
public ManiaRuleset(RulesetInfo rulesetInfo = null)
: base(rulesetInfo)
{
}
public override IEnumerable<int> AvailableVariants => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
public override IEnumerable<int> AvailableVariants
{
get
{
for (int i = 1; i <= 9; i++)
yield return (int)PlayfieldType.Single + i;
for (int i = 2; i <= 18; i += 2)
yield return (int)PlayfieldType.Dual + i;
}
}
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
{
var leftKeys = new[]
switch (getPlayfieldType(variant))
{
InputKey.A,
InputKey.S,
InputKey.D,
InputKey.F
};
case PlayfieldType.Single:
return new VariantMappingGenerator
{
LeftKeys = new[]
{
InputKey.A,
InputKey.S,
InputKey.D,
InputKey.F
},
RightKeys = new[]
{
InputKey.J,
InputKey.K,
InputKey.L,
InputKey.Semicolon
},
SpecialKey = InputKey.Space,
SpecialAction = ManiaAction.Special1,
NormalActionStart = ManiaAction.Key1,
}.GenerateKeyBindingsFor(variant, out _);
case PlayfieldType.Dual:
int keys = getDualStageKeyCount(variant);
var rightKeys = new[]
{
InputKey.J,
InputKey.K,
InputKey.L,
InputKey.Semicolon
};
var stage1Bindings = new VariantMappingGenerator
{
LeftKeys = new[]
{
InputKey.Number1,
InputKey.Number2,
InputKey.Number3,
InputKey.Number4,
},
RightKeys = new[]
{
InputKey.Z,
InputKey.X,
InputKey.C,
InputKey.V
},
SpecialKey = InputKey.Tilde,
SpecialAction = ManiaAction.Special1,
NormalActionStart = ManiaAction.Key1
}.GenerateKeyBindingsFor(keys, out var nextNormal);
ManiaAction currentKey = ManiaAction.Key1;
var stage2Bindings = new VariantMappingGenerator
{
LeftKeys = new[]
{
InputKey.Number7,
InputKey.Number8,
InputKey.Number9,
InputKey.Number0
},
RightKeys = new[]
{
InputKey.O,
InputKey.P,
InputKey.BracketLeft,
InputKey.BracketRight
},
SpecialKey = InputKey.BackSlash,
SpecialAction = ManiaAction.Special2,
NormalActionStart = nextNormal
}.GenerateKeyBindingsFor(keys, out _);
var bindings = new List<KeyBinding>();
return stage1Bindings.Concat(stage2Bindings);
}
for (int i = leftKeys.Length - variant / 2; i < leftKeys.Length; i++)
bindings.Add(new KeyBinding(leftKeys[i], currentKey++));
for (int i = 0; i < variant / 2; i++)
bindings.Add(new KeyBinding(rightKeys[i], currentKey++));
if (variant % 2 == 1)
bindings.Add(new KeyBinding(InputKey.Space, ManiaAction.Special));
return bindings;
return new KeyBinding[0];
}
public override string GetVariantName(int variant) => $"{variant}K";
public override string GetVariantName(int variant)
{
switch (getPlayfieldType(variant))
{
default:
return $"{variant}K";
case PlayfieldType.Dual:
{
var keys = getDualStageKeyCount(variant);
return $"{keys}K + {keys}K";
}
}
}
/// <summary>
/// Finds the number of keys for each stage in a <see cref="PlayfieldType.Dual"/> variant.
/// </summary>
/// <param name="variant">The variant.</param>
private int getDualStageKeyCount(int variant) => (variant - (int)PlayfieldType.Dual) / 2;
/// <summary>
/// Finds the <see cref="PlayfieldType"/> that corresponds to a variant value.
/// </summary>
/// <param name="variant">The variant value.</param>
/// <returns>The <see cref="PlayfieldType"/> that corresponds to <paramref name="variant"/>.</returns>
private PlayfieldType getPlayfieldType(int variant)
{
return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast<int>().OrderByDescending(i => i).First(v => variant >= v);
}
private class VariantMappingGenerator
{
/// <summary>
/// All the <see cref="InputKey"/>s available to the left hand.
/// </summary>
public InputKey[] LeftKeys;
/// <summary>
/// All the <see cref="InputKey"/>s available to the right hand.
/// </summary>
public InputKey[] RightKeys;
/// <summary>
/// The <see cref="InputKey"/> for the special key.
/// </summary>
public InputKey SpecialKey;
/// <summary>
/// The <see cref="ManiaAction"/> at which the normal columns should begin.
/// </summary>
public ManiaAction NormalActionStart;
/// <summary>
/// The <see cref="ManiaAction"/> for the special column.
/// </summary>
public ManiaAction SpecialAction;
/// <summary>
/// Generates a list of <see cref="KeyBinding"/>s for a specific number of columns.
/// </summary>
/// <param name="columns">The number of columns that need to be bound.</param>
/// <param name="nextNormalAction">The next <see cref="ManiaAction"/> to use for normal columns.</param>
/// <returns>The keybindings.</returns>
public IEnumerable<KeyBinding> GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction)
{
ManiaAction currentNormalAction = NormalActionStart;
var bindings = new List<KeyBinding>();
for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++)
bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++));
for (int i = 0; i < columns / 2; i++)
bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++));
if (columns % 2 == 1)
bindings.Add(new KeyBinding(SpecialKey, SpecialAction));
nextNormalAction = currentNormalAction;
return bindings;
}
}
}
public enum PlayfieldType
{
/// <summary>
/// Columns are grouped into a single stage.
/// Number of columns in this stage lies at (item - Single).
/// </summary>
Single = 0,
/// <summary>
/// Columns are grouped into two stages.
/// Overall number of columns lies at (item - Dual), further computation is required for
/// number of columns in each individual stage.
/// </summary>
Dual = 1000,
}
}
@@ -1,4 +1,4 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
@@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
/// </summary>
internal class FastRandom
{
private const double uint_to_real = 1.0 / (uint.MaxValue + 1.0);
private const double int_to_real = 1.0 / (int.MaxValue + 1.0);
private const uint int_mask = 0x7FFFFFFF;
private const uint y = 842502087;
private const uint z = 3579807591;
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
/// Generates a random double value within the range [0, 1).
/// </summary>
/// <returns>The random value.</returns>
public double NextDouble() => uint_to_real * NextUInt();
public double NextDouble() => int_to_real * Next();
private uint bitBuffer;
private int bitIndex = 32;
@@ -1,23 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Timing;
namespace osu.Game.Rulesets.Mania.Mods
{
/// <summary>
/// A type of mod which generates speed adjustments that scroll the hit objects and bar lines.
/// </summary>
internal interface IGenerateSpeedAdjustments
{
/// <summary>
/// Applies this mod to a hit renderer.
/// </summary>
/// <param name="rulesetContainer">The hit renderer to apply to.</param>
/// <param name="hitObjectTimingChanges">The per-column list of speed adjustments for hit objects.</param>
/// <param name="barlineTimingChanges">The list of speed adjustments for bar lines.</param>
void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List<SpeedAdjustmentContainer>[] hitObjectTimingChanges, ref List<SpeedAdjustmentContainer> barlineTimingChanges);
}
}
@@ -0,0 +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.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public interface IPlayfieldTypeMod : IApplicableMod
{
/// <summary>
/// The <see cref="PlayfieldType"/> which this <see cref="IPlayfieldTypeMod"/> requires.
/// </summary>
PlayfieldType PlayfieldType { get; }
}
}

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