mirror of
https://github.com/ppy/osu.git
synced 2024-11-06 09:07:25 +08:00
Merge remote-tracking branch 'upstream/master' into tgi74-ctb_hr_mangling
This commit is contained in:
commit
d307b8dc2e
42
.gitattributes
vendored
42
.gitattributes
vendored
@ -1,19 +1,23 @@
|
|||||||
# This won't normalise line endings, but it will ensure that merge drivers use CRLF
|
# Autodetect text files and ensure that we normalise their
|
||||||
* -text eol=crlf
|
# line endings to lf internally. When checked out they may
|
||||||
|
# use different line endings.
|
||||||
# Currently in-use binary file extensions
|
* text=auto
|
||||||
*.blend binary
|
|
||||||
*.bmp binary
|
# Check out with crlf (Windows) line endings
|
||||||
*.dll binary
|
*.sln text eol=crlf
|
||||||
*.exe binary
|
*.csproj text eol=crlf
|
||||||
*.icns binary
|
*.cs text diff=csharp eol=crlf
|
||||||
*.ico binary
|
*.resx text eol=crlf
|
||||||
*.jpg binary
|
*.vsixmanifest text eol=crlf
|
||||||
*.osz2 binary
|
packages.config text eol=crlf
|
||||||
*.pdn binary
|
App.config text eol=crlf
|
||||||
*.psd binary
|
*.bat text eol=crlf
|
||||||
*.PSD binary
|
*.cmd text eol=crlf
|
||||||
*.tga binary
|
*.snippet text eol=crlf
|
||||||
*.ttf binary
|
|
||||||
*.wav binary
|
# Check out with lf (UNIX) line endings
|
||||||
*.xnb binary
|
*.sh text eol=lf
|
||||||
|
.gitignore text eol=lf
|
||||||
|
.gitattributes text eol=lf
|
||||||
|
*.md text eol=lf
|
||||||
|
.travis.yml text eol=lf
|
518
.gitignore
vendored
518
.gitignore
vendored
@ -1,259 +1,259 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.suo
|
*.suo
|
||||||
*.user
|
*.user
|
||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
|
||||||
# Build results
|
# Build results
|
||||||
bin/[Dd]ebug/
|
bin/[Dd]ebug/
|
||||||
[Dd]ebugPublic/
|
[Dd]ebugPublic/
|
||||||
[Rr]elease/
|
[Rr]elease/
|
||||||
[Rr]eleases/
|
[Rr]eleases/
|
||||||
bld/
|
bld/
|
||||||
[Bb]in/
|
[Bb]in/
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
[Ll]og/
|
[Ll]og/
|
||||||
|
|
||||||
# Visual Studio 2015 cache/options directory
|
# Visual Studio 2015 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
#wwwroot/
|
#wwwroot/
|
||||||
|
|
||||||
# MSTest test Results
|
# MSTest test Results
|
||||||
[Tt]est[Rr]esult*/
|
[Tt]est[Rr]esult*/
|
||||||
[Bb]uild[Ll]og.*
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
# NUNIT
|
# NUNIT
|
||||||
*.VisualState.xml
|
*.VisualState.xml
|
||||||
TestResult.xml
|
TestResult.xml
|
||||||
|
|
||||||
# Build Results of an ATL Project
|
# Build Results of an ATL Project
|
||||||
[Dd]ebugPS/
|
[Dd]ebugPS/
|
||||||
[Rr]eleasePS/
|
[Rr]eleasePS/
|
||||||
dlldata.c
|
dlldata.c
|
||||||
|
|
||||||
# DNX
|
# DNX
|
||||||
project.lock.json
|
project.lock.json
|
||||||
project.fragment.lock.json
|
project.fragment.lock.json
|
||||||
artifacts/
|
artifacts/
|
||||||
|
|
||||||
*_i.c
|
*_i.c
|
||||||
*_p.c
|
*_p.c
|
||||||
*_i.h
|
*_i.h
|
||||||
*.ilk
|
*.ilk
|
||||||
*.meta
|
*.meta
|
||||||
*.obj
|
*.obj
|
||||||
*.pch
|
*.pch
|
||||||
*.pdb
|
*.pdb
|
||||||
*.pgc
|
*.pgc
|
||||||
*.pgd
|
*.pgd
|
||||||
*.rsp
|
*.rsp
|
||||||
*.sbr
|
*.sbr
|
||||||
*.tlb
|
*.tlb
|
||||||
*.tli
|
*.tli
|
||||||
*.tlh
|
*.tlh
|
||||||
*.tmp
|
*.tmp
|
||||||
*.tmp_proj
|
*.tmp_proj
|
||||||
*.log
|
*.log
|
||||||
*.vspscc
|
*.vspscc
|
||||||
*.vssscc
|
*.vssscc
|
||||||
.builds
|
.builds
|
||||||
*.pidb
|
*.pidb
|
||||||
*.svclog
|
*.svclog
|
||||||
*.scc
|
*.scc
|
||||||
|
|
||||||
# Chutzpah Test files
|
# Chutzpah Test files
|
||||||
_Chutzpah*
|
_Chutzpah*
|
||||||
|
|
||||||
# Visual C++ cache files
|
# Visual C++ cache files
|
||||||
ipch/
|
ipch/
|
||||||
*.aps
|
*.aps
|
||||||
*.ncb
|
*.ncb
|
||||||
*.opendb
|
*.opendb
|
||||||
*.opensdf
|
*.opensdf
|
||||||
*.sdf
|
*.sdf
|
||||||
*.cachefile
|
*.cachefile
|
||||||
*.VC.db
|
*.VC.db
|
||||||
*.VC.VC.opendb
|
*.VC.VC.opendb
|
||||||
|
|
||||||
# Visual Studio profiler
|
# Visual Studio profiler
|
||||||
*.psess
|
*.psess
|
||||||
*.vsp
|
*.vsp
|
||||||
*.vspx
|
*.vspx
|
||||||
*.sap
|
*.sap
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
# TFS 2012 Local Workspace
|
||||||
$tf/
|
$tf/
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
# Guidance Automation Toolkit
|
||||||
*.gpState
|
*.gpState
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
# ReSharper is a .NET coding add-in
|
||||||
_ReSharper*/
|
_ReSharper*/
|
||||||
*.[Rr]e[Ss]harper
|
*.[Rr]e[Ss]harper
|
||||||
*.DotSettings.user
|
*.DotSettings.user
|
||||||
|
|
||||||
# JustCode is a .NET coding add-in
|
# JustCode is a .NET coding add-in
|
||||||
.JustCode
|
.JustCode
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
# TeamCity is a build add-in
|
||||||
_TeamCity*
|
_TeamCity*
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
# DotCover is a Code Coverage Tool
|
||||||
*.dotCover
|
*.dotCover
|
||||||
|
|
||||||
# NCrunch
|
# NCrunch
|
||||||
_NCrunch_*
|
_NCrunch_*
|
||||||
.*crunch*.local.xml
|
.*crunch*.local.xml
|
||||||
nCrunchTemp_*
|
nCrunchTemp_*
|
||||||
|
|
||||||
# MightyMoose
|
# MightyMoose
|
||||||
*.mm.*
|
*.mm.*
|
||||||
AutoTest.Net/
|
AutoTest.Net/
|
||||||
|
|
||||||
# Web workbench (sass)
|
# Web workbench (sass)
|
||||||
.sass-cache/
|
.sass-cache/
|
||||||
|
|
||||||
# Installshield output folder
|
# Installshield output folder
|
||||||
[Ee]xpress/
|
[Ee]xpress/
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
# DocProject is a documentation generator add-in
|
||||||
DocProject/buildhelp/
|
DocProject/buildhelp/
|
||||||
DocProject/Help/*.HxT
|
DocProject/Help/*.HxT
|
||||||
DocProject/Help/*.HxC
|
DocProject/Help/*.HxC
|
||||||
DocProject/Help/*.hhc
|
DocProject/Help/*.hhc
|
||||||
DocProject/Help/*.hhk
|
DocProject/Help/*.hhk
|
||||||
DocProject/Help/*.hhp
|
DocProject/Help/*.hhp
|
||||||
DocProject/Help/Html2
|
DocProject/Help/Html2
|
||||||
DocProject/Help/html
|
DocProject/Help/html
|
||||||
|
|
||||||
# Click-Once directory
|
# Click-Once directory
|
||||||
publish/
|
publish/
|
||||||
|
|
||||||
# Publish Web Output
|
# Publish Web Output
|
||||||
*.[Pp]ublish.xml
|
*.[Pp]ublish.xml
|
||||||
*.azurePubxml
|
*.azurePubxml
|
||||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||||
# but database connection strings (with potential passwords) will be unencrypted
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
*.pubxml
|
*.pubxml
|
||||||
*.publishproj
|
*.publishproj
|
||||||
|
|
||||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
# in these scripts will be unencrypted
|
# in these scripts will be unencrypted
|
||||||
PublishScripts/
|
PublishScripts/
|
||||||
|
|
||||||
# NuGet Packages
|
# NuGet Packages
|
||||||
*.nupkg
|
*.nupkg
|
||||||
# The packages folder can be ignored because of Package Restore
|
# The packages folder can be ignored because of Package Restore
|
||||||
**/packages/*
|
**/packages/*
|
||||||
# except build/, which is used as an MSBuild target.
|
# except build/, which is used as an MSBuild target.
|
||||||
!**/packages/build/
|
!**/packages/build/
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
#!**/packages/repositories.config
|
#!**/packages/repositories.config
|
||||||
# NuGet v3's project.json files produces more ignoreable files
|
# NuGet v3's project.json files produces more ignoreable files
|
||||||
*.nuget.props
|
*.nuget.props
|
||||||
*.nuget.targets
|
*.nuget.targets
|
||||||
|
|
||||||
# Microsoft Azure Build Output
|
# Microsoft Azure Build Output
|
||||||
csx/
|
csx/
|
||||||
*.build.csdef
|
*.build.csdef
|
||||||
|
|
||||||
# Microsoft Azure Emulator
|
# Microsoft Azure Emulator
|
||||||
ecf/
|
ecf/
|
||||||
rcf/
|
rcf/
|
||||||
|
|
||||||
# Windows Store app package directories and files
|
# Windows Store app package directories and files
|
||||||
AppPackages/
|
AppPackages/
|
||||||
BundleArtifacts/
|
BundleArtifacts/
|
||||||
Package.StoreAssociation.xml
|
Package.StoreAssociation.xml
|
||||||
_pkginfo.txt
|
_pkginfo.txt
|
||||||
|
|
||||||
# Visual Studio cache files
|
# Visual Studio cache files
|
||||||
# files ending in .cache can be ignored
|
# files ending in .cache can be ignored
|
||||||
*.[Cc]ache
|
*.[Cc]ache
|
||||||
# but keep track of directories ending in .cache
|
# but keep track of directories ending in .cache
|
||||||
!*.[Cc]ache/
|
!*.[Cc]ache/
|
||||||
|
|
||||||
# Others
|
# Others
|
||||||
ClientBin/
|
ClientBin/
|
||||||
~$*
|
~$*
|
||||||
*~
|
*~
|
||||||
*.dbmdl
|
*.dbmdl
|
||||||
*.dbproj.schemaview
|
*.dbproj.schemaview
|
||||||
*.pfx
|
*.pfx
|
||||||
*.publishsettings
|
*.publishsettings
|
||||||
node_modules/
|
node_modules/
|
||||||
orleans.codegen.cs
|
orleans.codegen.cs
|
||||||
|
|
||||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
#bower_components/
|
#bower_components/
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
# RIA/Silverlight projects
|
||||||
Generated_Code/
|
Generated_Code/
|
||||||
|
|
||||||
# Backup & report files from converting an old project file
|
# Backup & report files from converting an old project file
|
||||||
# to a newer Visual Studio version. Backup files are not needed,
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
# because we have git ;-)
|
# because we have git ;-)
|
||||||
_UpgradeReport_Files/
|
_UpgradeReport_Files/
|
||||||
Backup*/
|
Backup*/
|
||||||
UpgradeLog*.XML
|
UpgradeLog*.XML
|
||||||
UpgradeLog*.htm
|
UpgradeLog*.htm
|
||||||
|
|
||||||
# SQL Server files
|
# SQL Server files
|
||||||
*.mdf
|
*.mdf
|
||||||
*.ldf
|
*.ldf
|
||||||
|
|
||||||
# Business Intelligence projects
|
# Business Intelligence projects
|
||||||
*.rdl.data
|
*.rdl.data
|
||||||
*.bim.layout
|
*.bim.layout
|
||||||
*.bim_*.settings
|
*.bim_*.settings
|
||||||
|
|
||||||
# Microsoft Fakes
|
# Microsoft Fakes
|
||||||
FakesAssemblies/
|
FakesAssemblies/
|
||||||
|
|
||||||
# GhostDoc plugin setting file
|
# GhostDoc plugin setting file
|
||||||
*.GhostDoc.xml
|
*.GhostDoc.xml
|
||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
# Node.js Tools for Visual Studio
|
||||||
.ntvs_analysis.dat
|
.ntvs_analysis.dat
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
# Visual Studio 6 build log
|
||||||
*.plg
|
*.plg
|
||||||
|
|
||||||
# Visual Studio 6 workspace options file
|
# Visual Studio 6 workspace options file
|
||||||
*.opt
|
*.opt
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
# Visual Studio LightSwitch build output
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
**/*.DesktopClient/ModelManifest.xml
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
**/*.Server/GeneratedArtifacts
|
**/*.Server/GeneratedArtifacts
|
||||||
**/*.Server/ModelManifest.xml
|
**/*.Server/ModelManifest.xml
|
||||||
_Pvt_Extensions
|
_Pvt_Extensions
|
||||||
|
|
||||||
# Paket dependency manager
|
# Paket dependency manager
|
||||||
.paket/paket.exe
|
.paket/paket.exe
|
||||||
paket-files/
|
paket-files/
|
||||||
|
|
||||||
# FAKE - F# Make
|
# FAKE - F# Make
|
||||||
.fake/
|
.fake/
|
||||||
|
|
||||||
# JetBrains Rider
|
# JetBrains Rider
|
||||||
.idea/
|
.idea/
|
||||||
*.sln.iml
|
*.sln.iml
|
||||||
|
|
||||||
# CodeRush
|
# CodeRush
|
||||||
.cr/
|
.cr/
|
||||||
|
|
||||||
# Python Tools for Visual Studio (PTVS)
|
# Python Tools for Visual Studio (PTVS)
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
Staging/
|
Staging/
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
language: csharp
|
language: csharp
|
||||||
solution: osu.sln
|
solution: osu.sln
|
46
app.manifest
Normal file
46
app.manifest
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="osu!" />
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
<applicationRequestMinimum>
|
||||||
|
<defaultAssemblyRequest permissionSetReference="Custom" />
|
||||||
|
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
|
||||||
|
</applicationRequestMinimum>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<application>
|
||||||
|
<!-- Windows Vista -->
|
||||||
|
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
|
||||||
|
<!-- Windows 7 -->
|
||||||
|
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
|
||||||
|
<!-- Windows 8 -->
|
||||||
|
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
|
||||||
|
<!-- Windows 8.1 -->
|
||||||
|
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
|
||||||
|
<!-- Windows 10 -->
|
||||||
|
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||||
|
</application>
|
||||||
|
</compatibility>
|
||||||
|
<asmv3:application>
|
||||||
|
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||||
|
<dpiAware>true</dpiAware>
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity
|
||||||
|
type="win32"
|
||||||
|
name="Microsoft.Windows.Common-Controls"
|
||||||
|
version="6.0.0.0"
|
||||||
|
processorArchitecture="*"
|
||||||
|
publicKeyToken="6595b64144ccf1df"
|
||||||
|
language="*"
|
||||||
|
/>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
</asmv1:assembly>
|
@ -1 +1 @@
|
|||||||
Subproject commit 02d7a0fa4798d197cd08570ee48951edbb7c7860
|
Subproject commit 16e6a453db9a8f4454238a2911eb5f1444b7ec2a
|
@ -1,21 +1,21 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
-->
|
-->
|
||||||
<configuration>
|
<configuration>
|
||||||
<appSettings>
|
<appSettings>
|
||||||
<add key="StagingFolder" value="Staging" />
|
<add key="StagingFolder" value="Staging" />
|
||||||
<add key="ReleasesFolder" value="Releases" />
|
<add key="ReleasesFolder" value="Releases" />
|
||||||
<add key="GitHubAccessToken" value="" />
|
<add key="GitHubAccessToken" value="" />
|
||||||
<add key="GitHubUsername" value="ppy" />
|
<add key="GitHubUsername" value="ppy" />
|
||||||
<add key="GitHubRepoName" value="osu" />
|
<add key="GitHubRepoName" value="osu" />
|
||||||
<add key="ProjectName" value="osu.Desktop" />
|
<add key="ProjectName" value="osu.Desktop" />
|
||||||
<add key="NuSpecName" value="osu.Desktop\osu.nuspec" />
|
<add key="NuSpecName" value="osu.Desktop\osu.nuspec" />
|
||||||
<add key="SolutionName" value="osu" />
|
<add key="SolutionName" value="osu" />
|
||||||
<add key="TargetName" value="osu.Desktop" />
|
<add key="TargetName" value="osu.Desktop" />
|
||||||
<add key="PackageName" value="osulazer" />
|
<add key="PackageName" value="osulazer" />
|
||||||
<add key="IconName" value="lazer.ico" />
|
<add key="IconName" value="lazer.ico" />
|
||||||
<add key="CodeSigningCertificate" value="" />
|
<add key="CodeSigningCertificate" value="" />
|
||||||
</appSettings>
|
</appSettings>
|
||||||
</configuration>
|
</configuration>
|
@ -1,16 +1,16 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace osu.Desktop.Deploy
|
namespace osu.Desktop.Deploy
|
||||||
{
|
{
|
||||||
public class GitHubObject
|
public class GitHubObject
|
||||||
{
|
{
|
||||||
[JsonProperty(@"id")]
|
[JsonProperty(@"id")]
|
||||||
public int Id;
|
public int Id;
|
||||||
|
|
||||||
[JsonProperty(@"name")]
|
[JsonProperty(@"name")]
|
||||||
public string Name;
|
public string Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace osu.Desktop.Deploy
|
namespace osu.Desktop.Deploy
|
||||||
{
|
{
|
||||||
public class GitHubRelease
|
public class GitHubRelease
|
||||||
{
|
{
|
||||||
[JsonProperty(@"id")]
|
[JsonProperty(@"id")]
|
||||||
public int Id;
|
public int Id;
|
||||||
|
|
||||||
[JsonProperty(@"tag_name")]
|
[JsonProperty(@"tag_name")]
|
||||||
public string TagName => $"v{Name}";
|
public string TagName => $"v{Name}";
|
||||||
|
|
||||||
[JsonProperty(@"name")]
|
[JsonProperty(@"name")]
|
||||||
public string Name;
|
public string Name;
|
||||||
|
|
||||||
[JsonProperty(@"draft")]
|
[JsonProperty(@"draft")]
|
||||||
public bool Draft;
|
public bool Draft;
|
||||||
|
|
||||||
[JsonProperty(@"prerelease")]
|
[JsonProperty(@"prerelease")]
|
||||||
public bool PreRelease;
|
public bool PreRelease;
|
||||||
|
|
||||||
[JsonProperty(@"upload_url")]
|
[JsonProperty(@"upload_url")]
|
||||||
public string UploadUrl;
|
public string UploadUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,438 +1,438 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Configuration;
|
using System.Configuration;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Framework.IO.Network;
|
using osu.Framework.IO.Network;
|
||||||
using FileWebRequest = osu.Framework.IO.Network.FileWebRequest;
|
using FileWebRequest = osu.Framework.IO.Network.FileWebRequest;
|
||||||
using WebRequest = osu.Framework.IO.Network.WebRequest;
|
using WebRequest = osu.Framework.IO.Network.WebRequest;
|
||||||
|
|
||||||
namespace osu.Desktop.Deploy
|
namespace osu.Desktop.Deploy
|
||||||
{
|
{
|
||||||
internal static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
private static string packages => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
|
private static string packages => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
|
||||||
private static string nugetPath => Path.Combine(packages, @"nuget.commandline\4.5.1\tools\NuGet.exe");
|
private static string nugetPath => Path.Combine(packages, @"nuget.commandline\4.5.1\tools\NuGet.exe");
|
||||||
private static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.7.8\tools\Squirrel.exe");
|
private static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.7.8\tools\Squirrel.exe");
|
||||||
private const string msbuild_path = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe";
|
private const string msbuild_path = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe";
|
||||||
|
|
||||||
public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"];
|
public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"];
|
||||||
public static string ReleasesFolder = ConfigurationManager.AppSettings["ReleasesFolder"];
|
public static string ReleasesFolder = ConfigurationManager.AppSettings["ReleasesFolder"];
|
||||||
public static string GitHubAccessToken = ConfigurationManager.AppSettings["GitHubAccessToken"];
|
public static string GitHubAccessToken = ConfigurationManager.AppSettings["GitHubAccessToken"];
|
||||||
public static string GitHubUsername = ConfigurationManager.AppSettings["GitHubUsername"];
|
public static string GitHubUsername = ConfigurationManager.AppSettings["GitHubUsername"];
|
||||||
public static string GitHubRepoName = ConfigurationManager.AppSettings["GitHubRepoName"];
|
public static string GitHubRepoName = ConfigurationManager.AppSettings["GitHubRepoName"];
|
||||||
public static string SolutionName = ConfigurationManager.AppSettings["SolutionName"];
|
public static string SolutionName = ConfigurationManager.AppSettings["SolutionName"];
|
||||||
public static string ProjectName = ConfigurationManager.AppSettings["ProjectName"];
|
public static string ProjectName = ConfigurationManager.AppSettings["ProjectName"];
|
||||||
public static string NuSpecName = ConfigurationManager.AppSettings["NuSpecName"];
|
public static string NuSpecName = ConfigurationManager.AppSettings["NuSpecName"];
|
||||||
public static string TargetNames = ConfigurationManager.AppSettings["TargetName"];
|
public static string TargetNames = ConfigurationManager.AppSettings["TargetName"];
|
||||||
public static string PackageName = ConfigurationManager.AppSettings["PackageName"];
|
public static string PackageName = ConfigurationManager.AppSettings["PackageName"];
|
||||||
public static string IconName = ConfigurationManager.AppSettings["IconName"];
|
public static string IconName = ConfigurationManager.AppSettings["IconName"];
|
||||||
public static string CodeSigningCertificate = ConfigurationManager.AppSettings["CodeSigningCertificate"];
|
public static string CodeSigningCertificate = ConfigurationManager.AppSettings["CodeSigningCertificate"];
|
||||||
|
|
||||||
public static string GitHubApiEndpoint => $"https://api.github.com/repos/{GitHubUsername}/{GitHubRepoName}/releases";
|
public static string GitHubApiEndpoint => $"https://api.github.com/repos/{GitHubUsername}/{GitHubRepoName}/releases";
|
||||||
public static string GitHubReleasePage => $"https://github.com/{GitHubUsername}/{GitHubRepoName}/releases";
|
public static string GitHubReleasePage => $"https://github.com/{GitHubUsername}/{GitHubRepoName}/releases";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How many previous build deltas we want to keep when publishing.
|
/// How many previous build deltas we want to keep when publishing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int keep_delta_count = 4;
|
private const int keep_delta_count = 4;
|
||||||
|
|
||||||
private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\"";
|
private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\"";
|
||||||
|
|
||||||
private static string homeDir => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
private static string homeDir => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||||
private static string codeSigningCertPath => Path.Combine(homeDir, CodeSigningCertificate);
|
private static string codeSigningCertPath => Path.Combine(homeDir, CodeSigningCertificate);
|
||||||
private static string solutionPath => Environment.CurrentDirectory;
|
private static string solutionPath => Environment.CurrentDirectory;
|
||||||
private static string stagingPath => Path.Combine(solutionPath, StagingFolder);
|
private static string stagingPath => Path.Combine(solutionPath, StagingFolder);
|
||||||
private static string iconPath => Path.Combine(solutionPath, ProjectName, IconName);
|
private static string iconPath => Path.Combine(solutionPath, ProjectName, IconName);
|
||||||
|
|
||||||
private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.nupkg";
|
private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.nupkg";
|
||||||
private static string nupkgDistroFilename(string ver) => $"{PackageName}-{ver}-full.nupkg";
|
private static string nupkgDistroFilename(string ver) => $"{PackageName}-{ver}-full.nupkg";
|
||||||
|
|
||||||
private static readonly Stopwatch sw = new Stopwatch();
|
private static readonly Stopwatch sw = new Stopwatch();
|
||||||
|
|
||||||
private static string codeSigningPassword;
|
private static string codeSigningPassword;
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
displayHeader();
|
displayHeader();
|
||||||
|
|
||||||
findSolutionPath();
|
findSolutionPath();
|
||||||
|
|
||||||
if (!Directory.Exists(ReleasesFolder))
|
if (!Directory.Exists(ReleasesFolder))
|
||||||
{
|
{
|
||||||
write("WARNING: No release directory found. Make sure you want this!", ConsoleColor.Yellow);
|
write("WARNING: No release directory found. Make sure you want this!", ConsoleColor.Yellow);
|
||||||
Directory.CreateDirectory(ReleasesFolder);
|
Directory.CreateDirectory(ReleasesFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkGitHubReleases();
|
checkGitHubReleases();
|
||||||
|
|
||||||
refreshDirectory(StagingFolder);
|
refreshDirectory(StagingFolder);
|
||||||
|
|
||||||
//increment build number until we have a unique one.
|
//increment build number until we have a unique one.
|
||||||
string verBase = DateTime.Now.ToString("yyyy.Mdd.");
|
string verBase = DateTime.Now.ToString("yyyy.Mdd.");
|
||||||
int increment = 0;
|
int increment = 0;
|
||||||
while (Directory.GetFiles(ReleasesFolder, $"*{verBase}{increment}*").Any())
|
while (Directory.GetFiles(ReleasesFolder, $"*{verBase}{increment}*").Any())
|
||||||
increment++;
|
increment++;
|
||||||
|
|
||||||
string version = $"{verBase}{increment}";
|
string version = $"{verBase}{increment}";
|
||||||
|
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
Console.ForegroundColor = ConsoleColor.White;
|
||||||
Console.Write($"Ready to deploy {version}: ");
|
Console.Write($"Ready to deploy {version}: ");
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
|
|
||||||
sw.Start();
|
sw.Start();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(CodeSigningCertificate))
|
if (!string.IsNullOrEmpty(CodeSigningCertificate))
|
||||||
{
|
{
|
||||||
Console.Write("Enter code signing password: ");
|
Console.Write("Enter code signing password: ");
|
||||||
codeSigningPassword = readLineMasked();
|
codeSigningPassword = readLineMasked();
|
||||||
}
|
}
|
||||||
|
|
||||||
write("Updating AssemblyInfo...");
|
write("Updating AssemblyInfo...");
|
||||||
updateCsprojVersion(version);
|
updateCsprojVersion(version);
|
||||||
|
|
||||||
write("Running build process...");
|
write("Running build process...");
|
||||||
foreach (string targetName in TargetNames.Split(','))
|
foreach (string targetName in TargetNames.Split(','))
|
||||||
runCommand(msbuild_path, $"/v:quiet /m /t:{targetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln");
|
runCommand(msbuild_path, $"/v:quiet /m /t:{targetName.Replace('.', '_')} /p:OutputPath={stagingPath};Targets=\"Clean;Build\";Configuration=Release {SolutionName}.sln");
|
||||||
|
|
||||||
write("Creating NuGet deployment package...");
|
write("Creating NuGet deployment package...");
|
||||||
runCommand(nugetPath, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}");
|
runCommand(nugetPath, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}");
|
||||||
|
|
||||||
//prune once before checking for files so we can avoid erroring on files which aren't even needed for this build.
|
//prune once before checking for files so we can avoid erroring on files which aren't even needed for this build.
|
||||||
pruneReleases();
|
pruneReleases();
|
||||||
|
|
||||||
checkReleaseFiles();
|
checkReleaseFiles();
|
||||||
|
|
||||||
write("Running squirrel build...");
|
write("Running squirrel build...");
|
||||||
runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi");
|
runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi");
|
||||||
|
|
||||||
//prune again to clean up before upload.
|
//prune again to clean up before upload.
|
||||||
pruneReleases();
|
pruneReleases();
|
||||||
|
|
||||||
//rename setup to install.
|
//rename setup to install.
|
||||||
File.Copy(Path.Combine(ReleasesFolder, "Setup.exe"), Path.Combine(ReleasesFolder, "install.exe"), true);
|
File.Copy(Path.Combine(ReleasesFolder, "Setup.exe"), Path.Combine(ReleasesFolder, "install.exe"), true);
|
||||||
File.Delete(Path.Combine(ReleasesFolder, "Setup.exe"));
|
File.Delete(Path.Combine(ReleasesFolder, "Setup.exe"));
|
||||||
|
|
||||||
uploadBuild(version);
|
uploadBuild(version);
|
||||||
|
|
||||||
//reset assemblyinfo.
|
//reset assemblyinfo.
|
||||||
updateCsprojVersion("0.0.0");
|
updateCsprojVersion("0.0.0");
|
||||||
|
|
||||||
write("Done!", ConsoleColor.White);
|
write("Done!", ConsoleColor.White);
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void displayHeader()
|
private static void displayHeader()
|
||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine(" Please note that OSU! and PPY are registered trademarks and as such covered by trademark law.");
|
Console.WriteLine(" Please note that OSU! and PPY are registered trademarks and as such covered by trademark law.");
|
||||||
Console.WriteLine(" Do not distribute builds of this project publicly that make use of these.");
|
Console.WriteLine(" Do not distribute builds of this project publicly that make use of these.");
|
||||||
Console.ResetColor();
|
Console.ResetColor();
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ensure we have all the files in the release directory which are expected to be there.
|
/// Ensure we have all the files in the release directory which are expected to be there.
|
||||||
/// This should have been accounted for in earlier steps, and just serves as a verification step.
|
/// This should have been accounted for in earlier steps, and just serves as a verification step.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void checkReleaseFiles()
|
private static void checkReleaseFiles()
|
||||||
{
|
{
|
||||||
if (!canGitHub) return;
|
if (!canGitHub) return;
|
||||||
|
|
||||||
var releaseLines = getReleaseLines();
|
var releaseLines = getReleaseLines();
|
||||||
|
|
||||||
//ensure we have all files necessary
|
//ensure we have all files necessary
|
||||||
foreach (var l in releaseLines)
|
foreach (var l in releaseLines)
|
||||||
if (!File.Exists(Path.Combine(ReleasesFolder, l.Filename)))
|
if (!File.Exists(Path.Combine(ReleasesFolder, l.Filename)))
|
||||||
error($"Local file missing {l.Filename}");
|
error($"Local file missing {l.Filename}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<ReleaseLine> getReleaseLines() => File.ReadAllLines(Path.Combine(ReleasesFolder, "RELEASES")).Select(l => new ReleaseLine(l));
|
private static IEnumerable<ReleaseLine> getReleaseLines() => File.ReadAllLines(Path.Combine(ReleasesFolder, "RELEASES")).Select(l => new ReleaseLine(l));
|
||||||
|
|
||||||
private static void pruneReleases()
|
private static void pruneReleases()
|
||||||
{
|
{
|
||||||
if (!canGitHub) return;
|
if (!canGitHub) return;
|
||||||
|
|
||||||
write("Pruning RELEASES...");
|
write("Pruning RELEASES...");
|
||||||
|
|
||||||
var releaseLines = getReleaseLines().ToList();
|
var releaseLines = getReleaseLines().ToList();
|
||||||
|
|
||||||
var fulls = releaseLines.Where(l => l.Filename.Contains("-full")).Reverse().Skip(1);
|
var fulls = releaseLines.Where(l => l.Filename.Contains("-full")).Reverse().Skip(1);
|
||||||
|
|
||||||
//remove any FULL releases (except most recent)
|
//remove any FULL releases (except most recent)
|
||||||
foreach (var l in fulls)
|
foreach (var l in fulls)
|
||||||
{
|
{
|
||||||
write($"- Removing old release {l.Filename}", ConsoleColor.Yellow);
|
write($"- Removing old release {l.Filename}", ConsoleColor.Yellow);
|
||||||
File.Delete(Path.Combine(ReleasesFolder, l.Filename));
|
File.Delete(Path.Combine(ReleasesFolder, l.Filename));
|
||||||
releaseLines.Remove(l);
|
releaseLines.Remove(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
//remove excess deltas
|
//remove excess deltas
|
||||||
var deltas = releaseLines.Where(l => l.Filename.Contains("-delta")).ToArray();
|
var deltas = releaseLines.Where(l => l.Filename.Contains("-delta")).ToArray();
|
||||||
if (deltas.Length > keep_delta_count)
|
if (deltas.Length > keep_delta_count)
|
||||||
{
|
{
|
||||||
foreach (var l in deltas.Take(deltas.Length - keep_delta_count))
|
foreach (var l in deltas.Take(deltas.Length - keep_delta_count))
|
||||||
{
|
{
|
||||||
write($"- Removing old delta {l.Filename}", ConsoleColor.Yellow);
|
write($"- Removing old delta {l.Filename}", ConsoleColor.Yellow);
|
||||||
File.Delete(Path.Combine(ReleasesFolder, l.Filename));
|
File.Delete(Path.Combine(ReleasesFolder, l.Filename));
|
||||||
releaseLines.Remove(l);
|
releaseLines.Remove(l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var lines = new List<string>();
|
var lines = new List<string>();
|
||||||
releaseLines.ForEach(l => lines.Add(l.ToString()));
|
releaseLines.ForEach(l => lines.Add(l.ToString()));
|
||||||
File.WriteAllLines(Path.Combine(ReleasesFolder, "RELEASES"), lines);
|
File.WriteAllLines(Path.Combine(ReleasesFolder, "RELEASES"), lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void uploadBuild(string version)
|
private static void uploadBuild(string version)
|
||||||
{
|
{
|
||||||
if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate))
|
if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
write("Publishing to GitHub...");
|
write("Publishing to GitHub...");
|
||||||
|
|
||||||
write($"- Creating release {version}...", ConsoleColor.Yellow);
|
write($"- Creating release {version}...", ConsoleColor.Yellow);
|
||||||
var req = new JsonWebRequest<GitHubRelease>($"{GitHubApiEndpoint}")
|
var req = new JsonWebRequest<GitHubRelease>($"{GitHubApiEndpoint}")
|
||||||
{
|
{
|
||||||
Method = HttpMethod.POST,
|
Method = HttpMethod.POST,
|
||||||
};
|
};
|
||||||
req.AddRaw(JsonConvert.SerializeObject(new GitHubRelease
|
req.AddRaw(JsonConvert.SerializeObject(new GitHubRelease
|
||||||
{
|
{
|
||||||
Name = version,
|
Name = version,
|
||||||
Draft = true,
|
Draft = true,
|
||||||
PreRelease = true
|
PreRelease = true
|
||||||
}));
|
}));
|
||||||
req.AuthenticatedBlockingPerform();
|
req.AuthenticatedBlockingPerform();
|
||||||
|
|
||||||
var assetUploadUrl = req.ResponseObject.UploadUrl.Replace("{?name,label}", "?name={0}");
|
var assetUploadUrl = req.ResponseObject.UploadUrl.Replace("{?name,label}", "?name={0}");
|
||||||
foreach (var a in Directory.GetFiles(ReleasesFolder).Reverse()) //reverse to upload RELEASES first.
|
foreach (var a in Directory.GetFiles(ReleasesFolder).Reverse()) //reverse to upload RELEASES first.
|
||||||
{
|
{
|
||||||
write($"- Adding asset {a}...", ConsoleColor.Yellow);
|
write($"- Adding asset {a}...", ConsoleColor.Yellow);
|
||||||
var upload = new WebRequest(assetUploadUrl, Path.GetFileName(a))
|
var upload = new WebRequest(assetUploadUrl, Path.GetFileName(a))
|
||||||
{
|
{
|
||||||
Method = HttpMethod.POST,
|
Method = HttpMethod.POST,
|
||||||
Timeout = 240000,
|
Timeout = 240000,
|
||||||
ContentType = "application/octet-stream",
|
ContentType = "application/octet-stream",
|
||||||
};
|
};
|
||||||
|
|
||||||
upload.AddRaw(File.ReadAllBytes(a));
|
upload.AddRaw(File.ReadAllBytes(a));
|
||||||
upload.AuthenticatedBlockingPerform();
|
upload.AuthenticatedBlockingPerform();
|
||||||
}
|
}
|
||||||
|
|
||||||
openGitHubReleasePage();
|
openGitHubReleasePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage);
|
private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage);
|
||||||
|
|
||||||
private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken);
|
private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken);
|
||||||
|
|
||||||
private static void checkGitHubReleases()
|
private static void checkGitHubReleases()
|
||||||
{
|
{
|
||||||
if (!canGitHub) return;
|
if (!canGitHub) return;
|
||||||
|
|
||||||
write("Checking GitHub releases...");
|
write("Checking GitHub releases...");
|
||||||
var req = new JsonWebRequest<List<GitHubRelease>>($"{GitHubApiEndpoint}");
|
var req = new JsonWebRequest<List<GitHubRelease>>($"{GitHubApiEndpoint}");
|
||||||
req.AuthenticatedBlockingPerform();
|
req.AuthenticatedBlockingPerform();
|
||||||
|
|
||||||
var lastRelease = req.ResponseObject.FirstOrDefault();
|
var lastRelease = req.ResponseObject.FirstOrDefault();
|
||||||
|
|
||||||
if (lastRelease == null)
|
if (lastRelease == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (lastRelease.Draft)
|
if (lastRelease.Draft)
|
||||||
{
|
{
|
||||||
openGitHubReleasePage();
|
openGitHubReleasePage();
|
||||||
error("There's a pending draft release! You probably don't want to push a build with this present.");
|
error("There's a pending draft release! You probably don't want to push a build with this present.");
|
||||||
}
|
}
|
||||||
|
|
||||||
//there's a previous release for this project.
|
//there's a previous release for this project.
|
||||||
var assetReq = new JsonWebRequest<List<GitHubObject>>($"{GitHubApiEndpoint}/{lastRelease.Id}/assets");
|
var assetReq = new JsonWebRequest<List<GitHubObject>>($"{GitHubApiEndpoint}/{lastRelease.Id}/assets");
|
||||||
assetReq.AuthenticatedBlockingPerform();
|
assetReq.AuthenticatedBlockingPerform();
|
||||||
var assets = assetReq.ResponseObject;
|
var assets = assetReq.ResponseObject;
|
||||||
|
|
||||||
//make sure our RELEASES file is the same as the last build on the server.
|
//make sure our RELEASES file is the same as the last build on the server.
|
||||||
var releaseAsset = assets.FirstOrDefault(a => a.Name == "RELEASES");
|
var releaseAsset = assets.FirstOrDefault(a => a.Name == "RELEASES");
|
||||||
|
|
||||||
//if we don't have a RELEASES asset then the previous release likely wasn't a Squirrel one.
|
//if we don't have a RELEASES asset then the previous release likely wasn't a Squirrel one.
|
||||||
if (releaseAsset == null) return;
|
if (releaseAsset == null) return;
|
||||||
|
|
||||||
write($"Last GitHub release was {lastRelease.Name}.");
|
write($"Last GitHub release was {lastRelease.Name}.");
|
||||||
|
|
||||||
bool requireDownload = false;
|
bool requireDownload = false;
|
||||||
|
|
||||||
if (!File.Exists(Path.Combine(ReleasesFolder, nupkgDistroFilename(lastRelease.Name))))
|
if (!File.Exists(Path.Combine(ReleasesFolder, nupkgDistroFilename(lastRelease.Name))))
|
||||||
{
|
{
|
||||||
write("Last version's package not found locally.", ConsoleColor.Red);
|
write("Last version's package not found locally.", ConsoleColor.Red);
|
||||||
requireDownload = true;
|
requireDownload = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var lastReleases = new RawFileWebRequest($"{GitHubApiEndpoint}/assets/{releaseAsset.Id}");
|
var lastReleases = new RawFileWebRequest($"{GitHubApiEndpoint}/assets/{releaseAsset.Id}");
|
||||||
lastReleases.AuthenticatedBlockingPerform();
|
lastReleases.AuthenticatedBlockingPerform();
|
||||||
if (File.ReadAllText(Path.Combine(ReleasesFolder, "RELEASES")) != lastReleases.ResponseString)
|
if (File.ReadAllText(Path.Combine(ReleasesFolder, "RELEASES")) != lastReleases.ResponseString)
|
||||||
{
|
{
|
||||||
write("Server's RELEASES differed from ours.", ConsoleColor.Red);
|
write("Server's RELEASES differed from ours.", ConsoleColor.Red);
|
||||||
requireDownload = true;
|
requireDownload = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!requireDownload) return;
|
if (!requireDownload) return;
|
||||||
|
|
||||||
write("Refreshing local releases directory...");
|
write("Refreshing local releases directory...");
|
||||||
refreshDirectory(ReleasesFolder);
|
refreshDirectory(ReleasesFolder);
|
||||||
|
|
||||||
foreach (var a in assets)
|
foreach (var a in assets)
|
||||||
{
|
{
|
||||||
if (a.Name.EndsWith(".exe")) continue;
|
if (a.Name.EndsWith(".exe")) continue;
|
||||||
|
|
||||||
write($"- Downloading {a.Name}...", ConsoleColor.Yellow);
|
write($"- Downloading {a.Name}...", ConsoleColor.Yellow);
|
||||||
new FileWebRequest(Path.Combine(ReleasesFolder, a.Name), $"{GitHubApiEndpoint}/assets/{a.Id}").AuthenticatedBlockingPerform();
|
new FileWebRequest(Path.Combine(ReleasesFolder, a.Name), $"{GitHubApiEndpoint}/assets/{a.Id}").AuthenticatedBlockingPerform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void refreshDirectory(string directory)
|
private static void refreshDirectory(string directory)
|
||||||
{
|
{
|
||||||
if (Directory.Exists(directory))
|
if (Directory.Exists(directory))
|
||||||
Directory.Delete(directory, true);
|
Directory.Delete(directory, true);
|
||||||
Directory.CreateDirectory(directory);
|
Directory.CreateDirectory(directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void updateCsprojVersion(string version)
|
private static void updateCsprojVersion(string version)
|
||||||
{
|
{
|
||||||
var toUpdate = new[] { "<Version>", "<FileVersion>" };
|
var toUpdate = new[] { "<Version>", "<FileVersion>" };
|
||||||
string file = Path.Combine(ProjectName, $"{ProjectName}.csproj");
|
string file = Path.Combine(ProjectName, $"{ProjectName}.csproj");
|
||||||
|
|
||||||
var l1 = File.ReadAllLines(file);
|
var l1 = File.ReadAllLines(file);
|
||||||
List<string> l2 = new List<string>();
|
List<string> l2 = new List<string>();
|
||||||
foreach (var l in l1)
|
foreach (var l in l1)
|
||||||
{
|
{
|
||||||
string line = l;
|
string line = l;
|
||||||
|
|
||||||
foreach (var tag in toUpdate)
|
foreach (var tag in toUpdate)
|
||||||
{
|
{
|
||||||
int startIndex = l.IndexOf(tag, StringComparison.InvariantCulture);
|
int startIndex = l.IndexOf(tag, StringComparison.InvariantCulture);
|
||||||
if (startIndex == -1)
|
if (startIndex == -1)
|
||||||
continue;
|
continue;
|
||||||
startIndex += tag.Length;
|
startIndex += tag.Length;
|
||||||
|
|
||||||
int endIndex = l.IndexOf("<", startIndex, StringComparison.InvariantCulture);
|
int endIndex = l.IndexOf("<", startIndex, StringComparison.InvariantCulture);
|
||||||
line = $"{l.Substring(0, startIndex)}{version}{l.Substring(endIndex)}";
|
line = $"{l.Substring(0, startIndex)}{version}{l.Substring(endIndex)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
l2.Add(line);
|
l2.Add(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
File.WriteAllLines(file, l2);
|
File.WriteAllLines(file, l2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Find the base path of the active solution (git checkout location)
|
/// Find the base path of the active solution (git checkout location)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static void findSolutionPath()
|
private static void findSolutionPath()
|
||||||
{
|
{
|
||||||
string path = Path.GetDirectoryName(Environment.CommandLine.Replace("\"", "").Trim());
|
string path = Path.GetDirectoryName(Environment.CommandLine.Replace("\"", "").Trim());
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(path))
|
if (string.IsNullOrEmpty(path))
|
||||||
path = Environment.CurrentDirectory;
|
path = Environment.CurrentDirectory;
|
||||||
|
|
||||||
while (!File.Exists(Path.Combine(path, $"{SolutionName}.sln")))
|
while (!File.Exists(Path.Combine(path, $"{SolutionName}.sln")))
|
||||||
path = path.Remove(path.LastIndexOf(Path.DirectorySeparatorChar));
|
path = path.Remove(path.LastIndexOf(Path.DirectorySeparatorChar));
|
||||||
path += Path.DirectorySeparatorChar;
|
path += Path.DirectorySeparatorChar;
|
||||||
|
|
||||||
Environment.CurrentDirectory = path;
|
Environment.CurrentDirectory = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool runCommand(string command, string args)
|
private static bool runCommand(string command, string args)
|
||||||
{
|
{
|
||||||
var psi = new ProcessStartInfo(command, args)
|
var psi = new ProcessStartInfo(command, args)
|
||||||
{
|
{
|
||||||
WorkingDirectory = solutionPath,
|
WorkingDirectory = solutionPath,
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
RedirectStandardOutput = true,
|
RedirectStandardOutput = true,
|
||||||
RedirectStandardError = true,
|
RedirectStandardError = true,
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
WindowStyle = ProcessWindowStyle.Hidden
|
WindowStyle = ProcessWindowStyle.Hidden
|
||||||
};
|
};
|
||||||
|
|
||||||
Process p = Process.Start(psi);
|
Process p = Process.Start(psi);
|
||||||
if (p == null) return false;
|
if (p == null) return false;
|
||||||
|
|
||||||
string output = p.StandardOutput.ReadToEnd();
|
string output = p.StandardOutput.ReadToEnd();
|
||||||
output += p.StandardError.ReadToEnd();
|
output += p.StandardError.ReadToEnd();
|
||||||
|
|
||||||
if (p.ExitCode == 0) return true;
|
if (p.ExitCode == 0) return true;
|
||||||
|
|
||||||
write(output);
|
write(output);
|
||||||
error($"Command {command} {args} failed!");
|
error($"Command {command} {args} failed!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string readLineMasked()
|
private static string readLineMasked()
|
||||||
{
|
{
|
||||||
var fg = Console.ForegroundColor;
|
var fg = Console.ForegroundColor;
|
||||||
Console.ForegroundColor = Console.BackgroundColor;
|
Console.ForegroundColor = Console.BackgroundColor;
|
||||||
var ret = Console.ReadLine();
|
var ret = Console.ReadLine();
|
||||||
Console.ForegroundColor = fg;
|
Console.ForegroundColor = fg;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void error(string message)
|
private static void error(string message)
|
||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
Console.WriteLine($"FATAL ERROR: {message}");
|
Console.WriteLine($"FATAL ERROR: {message}");
|
||||||
|
|
||||||
Console.ReadLine();
|
Console.ReadLine();
|
||||||
Environment.Exit(-1);
|
Environment.Exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void write(string message, ConsoleColor col = ConsoleColor.Gray)
|
private static void write(string message, ConsoleColor col = ConsoleColor.Gray)
|
||||||
{
|
{
|
||||||
if (sw.ElapsedMilliseconds > 0)
|
if (sw.ElapsedMilliseconds > 0)
|
||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Green;
|
Console.ForegroundColor = ConsoleColor.Green;
|
||||||
Console.Write(sw.ElapsedMilliseconds.ToString().PadRight(8));
|
Console.Write(sw.ElapsedMilliseconds.ToString().PadRight(8));
|
||||||
}
|
}
|
||||||
Console.ForegroundColor = col;
|
Console.ForegroundColor = col;
|
||||||
Console.WriteLine(message);
|
Console.WriteLine(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AuthenticatedBlockingPerform(this WebRequest r)
|
public static void AuthenticatedBlockingPerform(this WebRequest r)
|
||||||
{
|
{
|
||||||
r.AddHeader("Authorization", $"token {GitHubAccessToken}");
|
r.AddHeader("Authorization", $"token {GitHubAccessToken}");
|
||||||
r.Perform();
|
r.Perform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class RawFileWebRequest : WebRequest
|
internal class RawFileWebRequest : WebRequest
|
||||||
{
|
{
|
||||||
public RawFileWebRequest(string url) : base(url)
|
public RawFileWebRequest(string url) : base(url)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string Accept => "application/octet-stream";
|
protected override string Accept => "application/octet-stream";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class ReleaseLine
|
internal class ReleaseLine
|
||||||
{
|
{
|
||||||
public string Hash;
|
public string Hash;
|
||||||
public string Filename;
|
public string Filename;
|
||||||
public int Filesize;
|
public int Filesize;
|
||||||
|
|
||||||
public ReleaseLine(string line)
|
public ReleaseLine(string line)
|
||||||
{
|
{
|
||||||
var split = line.Split(' ');
|
var split = line.Split(' ');
|
||||||
Hash = split[0];
|
Hash = split[0];
|
||||||
Filename = split[1];
|
Filename = split[1];
|
||||||
Filesize = int.Parse(split[2]);
|
Filesize = int.Parse(split[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => $"{Hash} {Filename} {Filesize}";
|
public override string ToString() => $"{Hash} {Filename} {Filesize}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="NuGet.CommandLine" Version="4.5.1" />
|
<PackageReference Include="NuGet.CommandLine" Version="4.5.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.8.1" />
|
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||||
<PackageReference Include="squirrel.windows" Version="1.7.8" Condition="'$(TargetFramework)' == 'net461'" />
|
<PackageReference Include="squirrel.windows" Version="1.7.8" Condition="'$(TargetFramework)' == 'net461'" />
|
||||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.0" />
|
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -1,120 +1,120 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Desktop.Overlays;
|
using osu.Desktop.Overlays;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace osu.Desktop
|
namespace osu.Desktop
|
||||||
{
|
{
|
||||||
internal class OsuGameDesktop : OsuGame
|
internal class OsuGameDesktop : OsuGame
|
||||||
{
|
{
|
||||||
private readonly bool noVersionOverlay;
|
private readonly bool noVersionOverlay;
|
||||||
|
|
||||||
public OsuGameDesktop(string[] args = null)
|
public OsuGameDesktop(string[] args = null)
|
||||||
: base(args)
|
: base(args)
|
||||||
{
|
{
|
||||||
noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false;
|
noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Storage GetStorageForStableInstall()
|
public override Storage GetStorageForStableInstall()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new StableStorage();
|
return new StableStorage();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A method of accessing an osu-stable install in a controlled fashion.
|
/// A method of accessing an osu-stable install in a controlled fashion.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private class StableStorage : DesktopStorage
|
private class StableStorage : DesktopStorage
|
||||||
{
|
{
|
||||||
protected override string LocateBasePath()
|
protected override string LocateBasePath()
|
||||||
{
|
{
|
||||||
bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
|
bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
|
||||||
|
|
||||||
string stableInstallPath;
|
string stableInstallPath;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
||||||
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(String.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
|
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(String.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
|
||||||
|
|
||||||
if (checkExists(stableInstallPath))
|
if (checkExists(stableInstallPath))
|
||||||
return stableInstallPath;
|
return stableInstallPath;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
|
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
|
||||||
if (checkExists(stableInstallPath))
|
if (checkExists(stableInstallPath))
|
||||||
return stableInstallPath;
|
return stableInstallPath;
|
||||||
|
|
||||||
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
|
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
|
||||||
if (checkExists(stableInstallPath))
|
if (checkExists(stableInstallPath))
|
||||||
return stableInstallPath;
|
return stableInstallPath;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StableStorage()
|
public StableStorage()
|
||||||
: base(string.Empty)
|
: base(string.Empty)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
if (!noVersionOverlay)
|
if (!noVersionOverlay)
|
||||||
{
|
{
|
||||||
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
|
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
|
||||||
{
|
{
|
||||||
Add(v);
|
Add(v);
|
||||||
v.State = Visibility.Visible;
|
v.State = Visibility.Visible;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void SetHost(GameHost host)
|
public override void SetHost(GameHost host)
|
||||||
{
|
{
|
||||||
base.SetHost(host);
|
base.SetHost(host);
|
||||||
var desktopWindow = host.Window as DesktopGameWindow;
|
var desktopWindow = host.Window as DesktopGameWindow;
|
||||||
if (desktopWindow != null)
|
if (desktopWindow != null)
|
||||||
{
|
{
|
||||||
desktopWindow.CursorState |= CursorState.Hidden;
|
desktopWindow.CursorState |= CursorState.Hidden;
|
||||||
|
|
||||||
desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"));
|
desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico"));
|
||||||
desktopWindow.Title = Name;
|
desktopWindow.Title = Name;
|
||||||
|
|
||||||
desktopWindow.FileDrop += fileDrop;
|
desktopWindow.FileDrop += fileDrop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fileDrop(object sender, FileDropEventArgs e)
|
private void fileDrop(object sender, FileDropEventArgs e)
|
||||||
{
|
{
|
||||||
var filePaths = new[] { e.FileName };
|
var filePaths = new[] { e.FileName };
|
||||||
|
|
||||||
var firstExtension = Path.GetExtension(filePaths.First());
|
var firstExtension = Path.GetExtension(filePaths.First());
|
||||||
|
|
||||||
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
|
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
|
||||||
|
|
||||||
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
|
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,164 +1,164 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
#if NET_FRAMEWORK
|
#if NET_FRAMEWORK
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using Squirrel;
|
using Squirrel;
|
||||||
|
|
||||||
namespace osu.Desktop.Overlays
|
namespace osu.Desktop.Overlays
|
||||||
{
|
{
|
||||||
public class SquirrelUpdateManager : Component
|
public class SquirrelUpdateManager : Component
|
||||||
{
|
{
|
||||||
private UpdateManager updateManager;
|
private UpdateManager updateManager;
|
||||||
private NotificationOverlay notificationOverlay;
|
private NotificationOverlay notificationOverlay;
|
||||||
|
|
||||||
public void PrepareUpdate()
|
public void PrepareUpdate()
|
||||||
{
|
{
|
||||||
// Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here
|
// Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here
|
||||||
UpdateManager.RestartAppWhenExited().Wait();
|
UpdateManager.RestartAppWhenExited().Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(NotificationOverlay notification, OsuGameBase game)
|
private void load(NotificationOverlay notification, OsuGameBase game)
|
||||||
{
|
{
|
||||||
notificationOverlay = notification;
|
notificationOverlay = notification;
|
||||||
|
|
||||||
if (game.IsDeployedBuild)
|
if (game.IsDeployedBuild)
|
||||||
Schedule(() => checkForUpdateAsync());
|
Schedule(() => checkForUpdateAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
|
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
|
||||||
{
|
{
|
||||||
//should we schedule a retry on completion of this check?
|
//should we schedule a retry on completion of this check?
|
||||||
bool scheduleRetry = true;
|
bool scheduleRetry = true;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (updateManager == null) updateManager = await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true);
|
if (updateManager == null) updateManager = await UpdateManager.GitHubUpdateManager(@"https://github.com/ppy/osu", @"osulazer", null, null, true);
|
||||||
|
|
||||||
var info = await updateManager.CheckForUpdate(!useDeltaPatching);
|
var info = await updateManager.CheckForUpdate(!useDeltaPatching);
|
||||||
if (info.ReleasesToApply.Count == 0)
|
if (info.ReleasesToApply.Count == 0)
|
||||||
//no updates available. bail and retry later.
|
//no updates available. bail and retry later.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (notification == null)
|
if (notification == null)
|
||||||
{
|
{
|
||||||
notification = new UpdateProgressNotification(this) { State = ProgressNotificationState.Active };
|
notification = new UpdateProgressNotification(this) { State = ProgressNotificationState.Active };
|
||||||
Schedule(() => notificationOverlay.Post(notification));
|
Schedule(() => notificationOverlay.Post(notification));
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.Progress = 0;
|
notification.Progress = 0;
|
||||||
notification.Text = @"Downloading update...";
|
notification.Text = @"Downloading update...";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await updateManager.DownloadReleases(info.ReleasesToApply, p => notification.Progress = p / 100f);
|
await updateManager.DownloadReleases(info.ReleasesToApply, p => notification.Progress = p / 100f);
|
||||||
|
|
||||||
notification.Progress = 0;
|
notification.Progress = 0;
|
||||||
notification.Text = @"Installing update...";
|
notification.Text = @"Installing update...";
|
||||||
|
|
||||||
await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f);
|
await updateManager.ApplyReleases(info, p => notification.Progress = p / 100f);
|
||||||
|
|
||||||
notification.State = ProgressNotificationState.Completed;
|
notification.State = ProgressNotificationState.Completed;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
if (useDeltaPatching)
|
if (useDeltaPatching)
|
||||||
{
|
{
|
||||||
Logger.Error(e, @"delta patching failed!");
|
Logger.Error(e, @"delta patching failed!");
|
||||||
|
|
||||||
//could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959)
|
//could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959)
|
||||||
//try again without deltas.
|
//try again without deltas.
|
||||||
checkForUpdateAsync(false, notification);
|
checkForUpdateAsync(false, notification);
|
||||||
scheduleRetry = false;
|
scheduleRetry = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.Error(e, @"update failed!");
|
Logger.Error(e, @"update failed!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
// we'll ignore this and retry later. can be triggered by no internet connection or thread abortion.
|
// we'll ignore this and retry later. can be triggered by no internet connection or thread abortion.
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (scheduleRetry)
|
if (scheduleRetry)
|
||||||
{
|
{
|
||||||
if (notification != null)
|
if (notification != null)
|
||||||
notification.State = ProgressNotificationState.Cancelled;
|
notification.State = ProgressNotificationState.Cancelled;
|
||||||
|
|
||||||
//check again in 30 minutes.
|
//check again in 30 minutes.
|
||||||
Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30);
|
Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
updateManager?.Dispose();
|
updateManager?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UpdateProgressNotification : ProgressNotification
|
private class UpdateProgressNotification : ProgressNotification
|
||||||
{
|
{
|
||||||
private readonly SquirrelUpdateManager updateManager;
|
private readonly SquirrelUpdateManager updateManager;
|
||||||
private OsuGame game;
|
private OsuGame game;
|
||||||
|
|
||||||
public UpdateProgressNotification(SquirrelUpdateManager updateManager)
|
public UpdateProgressNotification(SquirrelUpdateManager updateManager)
|
||||||
{
|
{
|
||||||
this.updateManager = updateManager;
|
this.updateManager = updateManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Notification CreateCompletionNotification()
|
protected override Notification CreateCompletionNotification()
|
||||||
{
|
{
|
||||||
return new ProgressCompletionNotification
|
return new ProgressCompletionNotification
|
||||||
{
|
{
|
||||||
Text = @"Update ready to install. Click to restart!",
|
Text = @"Update ready to install. Click to restart!",
|
||||||
Activated = () =>
|
Activated = () =>
|
||||||
{
|
{
|
||||||
updateManager.PrepareUpdate();
|
updateManager.PrepareUpdate();
|
||||||
game.GracefullyExit();
|
game.GracefullyExit();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours, OsuGame game)
|
private void load(OsuColour colours, OsuGame game)
|
||||||
{
|
{
|
||||||
this.game = game;
|
this.game = game;
|
||||||
|
|
||||||
IconContent.AddRange(new Drawable[]
|
IconContent.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow)
|
Colour = ColourInfo.GradientVertical(colours.YellowDark, colours.Yellow)
|
||||||
},
|
},
|
||||||
new SpriteIcon
|
new SpriteIcon
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Icon = FontAwesome.fa_upload,
|
Icon = FontAwesome.fa_upload,
|
||||||
Colour = Color4.White,
|
Colour = Color4.White,
|
||||||
Size = new Vector2(20),
|
Size = new Vector2(20),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,142 +1,142 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Development;
|
using osu.Framework.Development;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Desktop.Overlays
|
namespace osu.Desktop.Overlays
|
||||||
{
|
{
|
||||||
public class VersionManager : OverlayContainer
|
public class VersionManager : OverlayContainer
|
||||||
{
|
{
|
||||||
private OsuConfigManager config;
|
private OsuConfigManager config;
|
||||||
private OsuGameBase game;
|
private OsuGameBase game;
|
||||||
private NotificationOverlay notificationOverlay;
|
private NotificationOverlay notificationOverlay;
|
||||||
|
|
||||||
public override bool HandleKeyboardInput => false;
|
public override bool HandleKeyboardInput => false;
|
||||||
public override bool HandleMouseInput => false;
|
public override bool HandleMouseInput => false;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
|
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
|
||||||
{
|
{
|
||||||
notificationOverlay = notification;
|
notificationOverlay = notification;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.game = game;
|
this.game = game;
|
||||||
|
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Anchor = Anchor.BottomCentre;
|
Anchor = Anchor.BottomCentre;
|
||||||
Origin = Anchor.BottomCentre;
|
Origin = Anchor.BottomCentre;
|
||||||
|
|
||||||
Alpha = 0;
|
Alpha = 0;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(5),
|
Spacing = new Vector2(5),
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Font = @"Exo2.0-Bold",
|
Font = @"Exo2.0-Bold",
|
||||||
Text = game.Name
|
Text = game.Name
|
||||||
},
|
},
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Colour = DebugUtils.IsDebug ? colours.Red : Color4.White,
|
Colour = DebugUtils.IsDebug ? colours.Red : Color4.White,
|
||||||
Text = game.Version
|
Text = game.Version
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
TextSize = 12,
|
TextSize = 12,
|
||||||
Colour = colours.Yellow,
|
Colour = colours.Yellow,
|
||||||
Font = @"Venera",
|
Font = @"Venera",
|
||||||
Text = @"Development Build"
|
Text = @"Development Build"
|
||||||
},
|
},
|
||||||
new Sprite
|
new Sprite
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
Texture = textures.Get(@"Menu/dev-build-footer"),
|
Texture = textures.Get(@"Menu/dev-build-footer"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#if NET_FRAMEWORK
|
#if NET_FRAMEWORK
|
||||||
Add(new SquirrelUpdateManager());
|
Add(new SquirrelUpdateManager());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
var version = game.Version;
|
var version = game.Version;
|
||||||
var lastVersion = config.Get<string>(OsuSetting.Version);
|
var lastVersion = config.Get<string>(OsuSetting.Version);
|
||||||
if (game.IsDeployedBuild && version != lastVersion)
|
if (game.IsDeployedBuild && version != lastVersion)
|
||||||
{
|
{
|
||||||
config.Set(OsuSetting.Version, version);
|
config.Set(OsuSetting.Version, version);
|
||||||
|
|
||||||
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
|
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
|
||||||
if (!string.IsNullOrEmpty(lastVersion))
|
if (!string.IsNullOrEmpty(lastVersion))
|
||||||
notificationOverlay.Post(new UpdateCompleteNotification(version));
|
notificationOverlay.Post(new UpdateCompleteNotification(version));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UpdateCompleteNotification : SimpleNotification
|
private class UpdateCompleteNotification : SimpleNotification
|
||||||
{
|
{
|
||||||
public UpdateCompleteNotification(string version)
|
public UpdateCompleteNotification(string version)
|
||||||
{
|
{
|
||||||
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
|
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
|
||||||
Icon = FontAwesome.fa_check_square;
|
Icon = FontAwesome.fa_check_square;
|
||||||
Activated = delegate
|
Activated = delegate
|
||||||
{
|
{
|
||||||
Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}");
|
Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}");
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
IconBackgound.Colour = colours.BlueDark;
|
IconBackgound.Colour = colours.BlueDark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopIn()
|
protected override void PopIn()
|
||||||
{
|
{
|
||||||
this.FadeIn(1000);
|
this.FadeIn(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,66 +1,66 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.IPC;
|
using osu.Game.IPC;
|
||||||
#if NET_FRAMEWORK
|
#if NET_FRAMEWORK
|
||||||
using System.Runtime;
|
using System.Runtime;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace osu.Desktop
|
namespace osu.Desktop
|
||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
[STAThread]
|
[STAThread]
|
||||||
public static int Main(string[] args)
|
public static int Main(string[] args)
|
||||||
{
|
{
|
||||||
// required to initialise native SQLite libraries on some platforms.
|
// required to initialise native SQLite libraries on some platforms.
|
||||||
|
|
||||||
if (!RuntimeInfo.IsMono)
|
if (!RuntimeInfo.IsMono)
|
||||||
useMulticoreJit();
|
useMulticoreJit();
|
||||||
|
|
||||||
// Back up the cwd before DesktopGameHost changes it
|
// Back up the cwd before DesktopGameHost changes it
|
||||||
var cwd = Environment.CurrentDirectory;
|
var cwd = Environment.CurrentDirectory;
|
||||||
|
|
||||||
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
|
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
|
||||||
{
|
{
|
||||||
if (!host.IsPrimaryInstance)
|
if (!host.IsPrimaryInstance)
|
||||||
{
|
{
|
||||||
var importer = new ArchiveImportIPCChannel(host);
|
var importer = new ArchiveImportIPCChannel(host);
|
||||||
// Restore the cwd so relative paths given at the command line work correctly
|
// Restore the cwd so relative paths given at the command line work correctly
|
||||||
Directory.SetCurrentDirectory(cwd);
|
Directory.SetCurrentDirectory(cwd);
|
||||||
foreach (var file in args)
|
foreach (var file in args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(@"Importing {0}", file);
|
Console.WriteLine(@"Importing {0}", file);
|
||||||
if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000))
|
if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000))
|
||||||
throw new TimeoutException(@"IPC took too long to send");
|
throw new TimeoutException(@"IPC took too long to send");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (args.FirstOrDefault() ?? string.Empty)
|
switch (args.FirstOrDefault() ?? string.Empty)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
host.Run(new OsuGameDesktop(args));
|
host.Run(new OsuGameDesktop(args));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void useMulticoreJit()
|
private static void useMulticoreJit()
|
||||||
{
|
{
|
||||||
#if NET_FRAMEWORK
|
#if NET_FRAMEWORK
|
||||||
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
|
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
|
||||||
ProfileOptimization.SetProfileRoot(directory.FullName);
|
ProfileOptimization.SetProfileRoot(directory.FullName);
|
||||||
ProfileOptimization.StartProfile("Startup.Profile");
|
ProfileOptimization.StartProfile("Startup.Profile");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,67 +1,67 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||||
|
|
||||||
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")]
|
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")]
|
||||||
public new void Test(string name)
|
public new void Test(string name)
|
||||||
{
|
{
|
||||||
base.Test(name);
|
base.Test(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||||
{
|
{
|
||||||
if (hitObject is JuiceStream stream)
|
if (hitObject is JuiceStream stream)
|
||||||
{
|
{
|
||||||
foreach (var nested in stream.NestedHitObjects)
|
foreach (var nested in stream.NestedHitObjects)
|
||||||
{
|
{
|
||||||
yield return new ConvertValue
|
yield return new ConvertValue
|
||||||
{
|
{
|
||||||
StartTime = nested.StartTime,
|
StartTime = nested.StartTime,
|
||||||
Position = ((CatchHitObject)nested).X * CatchPlayfield.BASE_WIDTH
|
Position = ((CatchHitObject)nested).X * CatchPlayfield.BASE_WIDTH
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
yield return new ConvertValue
|
yield return new ConvertValue
|
||||||
{
|
{
|
||||||
StartTime = hitObject.StartTime,
|
StartTime = hitObject.StartTime,
|
||||||
Position = ((CatchHitObject)hitObject).X * CatchPlayfield.BASE_WIDTH
|
Position = ((CatchHitObject)hitObject).X * CatchPlayfield.BASE_WIDTH
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new CatchBeatmapConverter();
|
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new CatchBeatmapConverter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ConvertValue : IEquatable<ConvertValue>
|
public struct ConvertValue : IEquatable<ConvertValue>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A sane value to account for osu!stable using ints everwhere.
|
/// A sane value to account for osu!stable using ints everwhere.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float conversion_lenience = 2;
|
private const float conversion_lenience = 2;
|
||||||
|
|
||||||
public double StartTime;
|
public double StartTime;
|
||||||
public float Position;
|
public float Position;
|
||||||
|
|
||||||
public bool Equals(ConvertValue other)
|
public bool Equals(ConvertValue other)
|
||||||
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
|
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
|
||||||
&& Precision.AlmostEquals(Position, other.Position, conversion_lenience);
|
&& Precision.AlmostEquals(Position, other.Position, conversion_lenience);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,62 +1,62 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
public class TestCaseAutoJuiceStream : TestCasePlayer
|
public class TestCaseAutoJuiceStream : TestCasePlayer
|
||||||
{
|
{
|
||||||
public TestCaseAutoJuiceStream()
|
public TestCaseAutoJuiceStream()
|
||||||
: base(new CatchRuleset())
|
: base(new CatchRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Beatmap CreateBeatmap(Ruleset ruleset)
|
protected override Beatmap CreateBeatmap(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap
|
var beatmap = new Beatmap
|
||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 },
|
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 },
|
||||||
Ruleset = ruleset.RulesetInfo
|
Ruleset = ruleset.RulesetInfo
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < 100; i++)
|
for (int i = 0; i < 100; i++)
|
||||||
{
|
{
|
||||||
float width = (i % 10 + 1) / 20f;
|
float width = (i % 10 + 1) / 20f;
|
||||||
|
|
||||||
beatmap.HitObjects.Add(new JuiceStream
|
beatmap.HitObjects.Add(new JuiceStream
|
||||||
{
|
{
|
||||||
X = 0.5f - width / 2,
|
X = 0.5f - width / 2,
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new List<Vector2>
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(width * CatchPlayfield.BASE_WIDTH, 0)
|
new Vector2(width * CatchPlayfield.BASE_WIDTH, 0)
|
||||||
},
|
},
|
||||||
CurveType = CurveType.Linear,
|
CurveType = CurveType.Linear,
|
||||||
Distance = width * CatchPlayfield.BASE_WIDTH,
|
Distance = width * CatchPlayfield.BASE_WIDTH,
|
||||||
StartTime = i * 2000,
|
StartTime = i * 2000,
|
||||||
NewCombo = i % 8 == 0
|
NewCombo = i % 8 == 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
|
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
|
||||||
{
|
{
|
||||||
beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
|
beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
|
||||||
return base.CreatePlayer(beatmap, ruleset);
|
return base.CreatePlayer(beatmap, ruleset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,47 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer
|
public class TestCaseBananaShower : Game.Tests.Visual.TestCasePlayer
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
typeof(BananaShower),
|
typeof(BananaShower),
|
||||||
typeof(DrawableBananaShower),
|
typeof(DrawableBananaShower),
|
||||||
|
|
||||||
typeof(CatchRuleset),
|
typeof(CatchRuleset),
|
||||||
typeof(CatchRulesetContainer),
|
typeof(CatchRulesetContainer),
|
||||||
};
|
};
|
||||||
|
|
||||||
public TestCaseBananaShower()
|
public TestCaseBananaShower()
|
||||||
: base(new CatchRuleset())
|
: base(new CatchRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Beatmap CreateBeatmap(Ruleset ruleset)
|
protected override Beatmap CreateBeatmap(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap
|
var beatmap = new Beatmap
|
||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
|
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
|
||||||
Ruleset = ruleset.RulesetInfo
|
Ruleset = ruleset.RulesetInfo
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 5000, NewCombo = true });
|
beatmap.HitObjects.Add(new BananaShower { StartTime = 200, Duration = 5000, NewCombo = true });
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
|
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
|
||||||
{
|
{
|
||||||
public TestCaseCatchPlayer() : base(new CatchRuleset())
|
public TestCaseCatchPlayer() : base(new CatchRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
|
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
|
||||||
{
|
{
|
||||||
public TestCaseCatchStacker()
|
public TestCaseCatchStacker()
|
||||||
: base(new CatchRuleset())
|
: base(new CatchRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Beatmap CreateBeatmap(Ruleset ruleset)
|
protected override Beatmap CreateBeatmap(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap
|
var beatmap = new Beatmap
|
||||||
{
|
{
|
||||||
BeatmapInfo = new BeatmapInfo
|
BeatmapInfo = new BeatmapInfo
|
||||||
{
|
{
|
||||||
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
|
BaseDifficulty = new BeatmapDifficulty { CircleSize = 6 },
|
||||||
Ruleset = ruleset.RulesetInfo
|
Ruleset = ruleset.RulesetInfo
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < 512; i++)
|
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 });
|
beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 });
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,61 +1,61 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseCatcherArea : OsuTestCase
|
public class TestCaseCatcherArea : OsuTestCase
|
||||||
{
|
{
|
||||||
private RulesetInfo catchRuleset;
|
private RulesetInfo catchRuleset;
|
||||||
private TestCatcherArea catcherArea;
|
private TestCatcherArea catcherArea;
|
||||||
|
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
typeof(CatcherArea),
|
typeof(CatcherArea),
|
||||||
};
|
};
|
||||||
|
|
||||||
public TestCaseCatcherArea()
|
public TestCaseCatcherArea()
|
||||||
{
|
{
|
||||||
AddSliderStep<float>("CircleSize", 0, 8, 5, createCatcher);
|
AddSliderStep<float>("CircleSize", 0, 8, 5, createCatcher);
|
||||||
AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t));
|
AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createCatcher(float size)
|
private void createCatcher(float size)
|
||||||
{
|
{
|
||||||
Child = new CatchInputManager(catchRuleset)
|
Child = new CatchInputManager(catchRuleset)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
|
Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.BottomLeft
|
Origin = Anchor.BottomLeft
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(RulesetStore rulesets)
|
private void load(RulesetStore rulesets)
|
||||||
{
|
{
|
||||||
catchRuleset = rulesets.GetRuleset(2);
|
catchRuleset = rulesets.GetRuleset(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestCatcherArea : CatcherArea
|
private class TestCatcherArea : CatcherArea
|
||||||
{
|
{
|
||||||
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
|
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
|
||||||
: base(beatmapDifficulty)
|
: base(beatmapDifficulty)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToggleHyperDash(bool status) => MovableCatcher.HyperDashModifier = status ? 2 : 1;
|
public void ToggleHyperDash(bool status) => MovableCatcher.HyperDashModifier = status ? 2 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,74 +1,74 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseFruitObjects : OsuTestCase
|
public class TestCaseFruitObjects : OsuTestCase
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
typeof(CatchHitObject),
|
typeof(CatchHitObject),
|
||||||
typeof(Fruit),
|
typeof(Fruit),
|
||||||
typeof(Droplet),
|
typeof(Droplet),
|
||||||
typeof(DrawableCatchHitObject),
|
typeof(DrawableCatchHitObject),
|
||||||
typeof(DrawableFruit),
|
typeof(DrawableFruit),
|
||||||
typeof(DrawableDroplet),
|
typeof(DrawableDroplet),
|
||||||
typeof(Pulp),
|
typeof(Pulp),
|
||||||
};
|
};
|
||||||
|
|
||||||
public TestCaseFruitObjects()
|
public TestCaseFruitObjects()
|
||||||
{
|
{
|
||||||
Add(new GridContainer
|
Add(new GridContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Content = new[]
|
Content = new[]
|
||||||
{
|
{
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
createDrawable(0),
|
createDrawable(0),
|
||||||
createDrawable(1),
|
createDrawable(1),
|
||||||
createDrawable(2),
|
createDrawable(2),
|
||||||
},
|
},
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
createDrawable(3),
|
createDrawable(3),
|
||||||
createDrawable(4),
|
createDrawable(4),
|
||||||
createDrawable(5),
|
createDrawable(5),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private DrawableFruit createDrawable(int index)
|
private DrawableFruit createDrawable(int index)
|
||||||
{
|
{
|
||||||
var fruit = new Fruit
|
var fruit = new Fruit
|
||||||
{
|
{
|
||||||
StartTime = 1000000000000,
|
StartTime = 1000000000000,
|
||||||
IndexInBeatmap = index,
|
IndexInBeatmap = index,
|
||||||
Scale = 1.5f,
|
Scale = 1.5f,
|
||||||
};
|
};
|
||||||
|
|
||||||
return new DrawableFruit(fruit)
|
return new DrawableFruit(fruit)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
RelativePositionAxes = Axes.Both,
|
RelativePositionAxes = Axes.Both,
|
||||||
Position = Vector2.Zero,
|
Position = Vector2.Zero,
|
||||||
Alpha = 1,
|
Alpha = 1,
|
||||||
LifetimeStart = double.NegativeInfinity,
|
LifetimeStart = double.NegativeInfinity,
|
||||||
LifetimeEnd = double.PositiveInfinity,
|
LifetimeEnd = double.PositiveInfinity,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
|
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
|
||||||
{
|
{
|
||||||
public TestCaseHyperdash()
|
public TestCaseHyperdash()
|
||||||
: base(new CatchRuleset())
|
: base(new CatchRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Beatmap CreateBeatmap(Ruleset ruleset)
|
protected override Beatmap CreateBeatmap(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
|
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < 512; i++)
|
for (int i = 0; i < 512; i++)
|
||||||
if (i % 5 < 3)
|
if (i % 5 < 3)
|
||||||
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });
|
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||||
{
|
{
|
||||||
public TestCasePerformancePoints()
|
public TestCasePerformancePoints()
|
||||||
: base(new CatchRuleset())
|
: base(new CatchRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Project References">
|
<ItemGroup Label="Project References">
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -1,68 +1,68 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||||
{
|
{
|
||||||
public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
|
public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
|
||||||
{
|
{
|
||||||
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
|
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
|
||||||
|
|
||||||
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap)
|
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap)
|
||||||
{
|
{
|
||||||
var curveData = obj as IHasCurve;
|
var curveData = obj as IHasCurve;
|
||||||
var positionData = obj as IHasXPosition;
|
var positionData = obj as IHasXPosition;
|
||||||
var comboData = obj as IHasCombo;
|
var comboData = obj as IHasCombo;
|
||||||
var endTime = obj as IHasEndTime;
|
var endTime = obj as IHasEndTime;
|
||||||
|
|
||||||
if (positionData == null)
|
if (positionData == null)
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
if (curveData != null)
|
if (curveData != null)
|
||||||
{
|
{
|
||||||
yield return new JuiceStream
|
yield return new JuiceStream
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Samples = obj.Samples,
|
Samples = obj.Samples,
|
||||||
ControlPoints = curveData.ControlPoints,
|
ControlPoints = curveData.ControlPoints,
|
||||||
CurveType = curveData.CurveType,
|
CurveType = curveData.CurveType,
|
||||||
Distance = curveData.Distance,
|
Distance = curveData.Distance,
|
||||||
RepeatSamples = curveData.RepeatSamples,
|
RepeatSamples = curveData.RepeatSamples,
|
||||||
RepeatCount = curveData.RepeatCount,
|
RepeatCount = curveData.RepeatCount,
|
||||||
X = positionData.X / CatchPlayfield.BASE_WIDTH,
|
X = positionData.X / CatchPlayfield.BASE_WIDTH,
|
||||||
NewCombo = comboData?.NewCombo ?? false
|
NewCombo = comboData?.NewCombo ?? false
|
||||||
};
|
};
|
||||||
|
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endTime != null)
|
if (endTime != null)
|
||||||
{
|
{
|
||||||
yield return new BananaShower
|
yield return new BananaShower
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Samples = obj.Samples,
|
Samples = obj.Samples,
|
||||||
Duration = endTime.Duration,
|
Duration = endTime.Duration,
|
||||||
NewCombo = comboData?.NewCombo ?? false
|
NewCombo = comboData?.NewCombo ?? false
|
||||||
};
|
};
|
||||||
|
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return new Fruit
|
yield return new Fruit
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Samples = obj.Samples,
|
Samples = obj.Samples,
|
||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
X = positionData.X / CatchPlayfield.BASE_WIDTH
|
X = positionData.X / CatchPlayfield.BASE_WIDTH
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,72 +1,72 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||||
{
|
{
|
||||||
public class CatchBeatmapProcessor : BeatmapProcessor<CatchHitObject>
|
public class CatchBeatmapProcessor : BeatmapProcessor<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
|
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
|
||||||
{
|
{
|
||||||
initialiseHyperDash(beatmap.HitObjects);
|
initialiseHyperDash(beatmap.HitObjects);
|
||||||
|
|
||||||
base.PostProcess(beatmap);
|
base.PostProcess(beatmap);
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
foreach (var obj in beatmap.HitObjects)
|
foreach (var obj in beatmap.HitObjects)
|
||||||
obj.IndexInBeatmap = index++;
|
obj.IndexInBeatmap = index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialiseHyperDash(List<CatchHitObject> objects)
|
private void initialiseHyperDash(List<CatchHitObject> objects)
|
||||||
{
|
{
|
||||||
// todo: add difficulty adjust.
|
// todo: add difficulty adjust.
|
||||||
double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2;
|
double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2;
|
||||||
|
|
||||||
int lastDirection = 0;
|
int lastDirection = 0;
|
||||||
double lastExcess = halfCatcherWidth;
|
double lastExcess = halfCatcherWidth;
|
||||||
|
|
||||||
int objCount = objects.Count;
|
int objCount = objects.Count;
|
||||||
|
|
||||||
for (int i = 0; i < objCount - 1; i++)
|
for (int i = 0; i < objCount - 1; i++)
|
||||||
{
|
{
|
||||||
CatchHitObject currentObject = objects[i];
|
CatchHitObject currentObject = objects[i];
|
||||||
|
|
||||||
// not needed?
|
// not needed?
|
||||||
// if (currentObject is TinyDroplet) continue;
|
// if (currentObject is TinyDroplet) continue;
|
||||||
|
|
||||||
CatchHitObject nextObject = objects[i + 1];
|
CatchHitObject nextObject = objects[i + 1];
|
||||||
|
|
||||||
// while (nextObject is TinyDroplet)
|
// while (nextObject is TinyDroplet)
|
||||||
// {
|
// {
|
||||||
// if (++i == objCount - 1) break;
|
// if (++i == objCount - 1) break;
|
||||||
// nextObject = objects[i + 1];
|
// nextObject = objects[i + 1];
|
||||||
// }
|
// }
|
||||||
|
|
||||||
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
|
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
|
||||||
double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4;
|
double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4;
|
||||||
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
||||||
|
|
||||||
if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext)
|
if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext)
|
||||||
{
|
{
|
||||||
currentObject.HyperDashTarget = nextObject;
|
currentObject.HyperDashTarget = nextObject;
|
||||||
lastExcess = halfCatcherWidth;
|
lastExcess = halfCatcherWidth;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//currentObject.DistanceToHyperDash = timeToNext - distanceToNext;
|
//currentObject.DistanceToHyperDash = timeToNext - distanceToNext;
|
||||||
lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth);
|
lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastDirection = thisDirection;
|
lastDirection = thisDirection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch
|
namespace osu.Game.Rulesets.Catch
|
||||||
{
|
{
|
||||||
public class CatchDifficultyCalculator : DifficultyCalculator<CatchHitObject>
|
public class CatchDifficultyCalculator : DifficultyCalculator<CatchHitObject>
|
||||||
{
|
{
|
||||||
public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap)
|
public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
|
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
|
||||||
|
|
||||||
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
|
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch
|
namespace osu.Game.Rulesets.Catch
|
||||||
{
|
{
|
||||||
public class CatchInputManager : RulesetInputManager<CatchAction>
|
public class CatchInputManager : RulesetInputManager<CatchAction>
|
||||||
{
|
{
|
||||||
public CatchInputManager(RulesetInfo ruleset)
|
public CatchInputManager(RulesetInfo ruleset)
|
||||||
: base(ruleset, 0, SimultaneousBindingMode.Unique)
|
: base(ruleset, 0, SimultaneousBindingMode.Unique)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CatchAction
|
public enum CatchAction
|
||||||
{
|
{
|
||||||
[Description("Move left")]
|
[Description("Move left")]
|
||||||
MoveLeft,
|
MoveLeft,
|
||||||
[Description("Move right")]
|
[Description("Move right")]
|
||||||
MoveRight,
|
MoveRight,
|
||||||
[Description("Engage dash")]
|
[Description("Engage dash")]
|
||||||
Dash,
|
Dash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,113 +1,152 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.Mods;
|
using osu.Game.Rulesets.Catch.Mods;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Catch.Replays;
|
using osu.Game.Rulesets.Catch.Replays;
|
||||||
using osu.Game.Rulesets.Replays.Types;
|
using osu.Game.Rulesets.Replays.Types;
|
||||||
|
using osu.Game.Beatmaps.Legacy;
|
||||||
namespace osu.Game.Rulesets.Catch
|
|
||||||
{
|
namespace osu.Game.Rulesets.Catch
|
||||||
public class CatchRuleset : Ruleset
|
{
|
||||||
{
|
public class CatchRuleset : Ruleset
|
||||||
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new CatchRulesetContainer(this, beatmap, isForCurrentRuleset);
|
{
|
||||||
|
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new CatchRulesetContainer(this, beatmap, isForCurrentRuleset);
|
||||||
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
|
|
||||||
{
|
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
|
||||||
new KeyBinding(InputKey.Z, CatchAction.MoveLeft),
|
{
|
||||||
new KeyBinding(InputKey.Left, CatchAction.MoveLeft),
|
new KeyBinding(InputKey.Z, CatchAction.MoveLeft),
|
||||||
new KeyBinding(InputKey.X, CatchAction.MoveRight),
|
new KeyBinding(InputKey.Left, CatchAction.MoveLeft),
|
||||||
new KeyBinding(InputKey.Right, CatchAction.MoveRight),
|
new KeyBinding(InputKey.X, CatchAction.MoveRight),
|
||||||
new KeyBinding(InputKey.Shift, CatchAction.Dash),
|
new KeyBinding(InputKey.Right, CatchAction.MoveRight),
|
||||||
new KeyBinding(InputKey.Shift, CatchAction.Dash),
|
new KeyBinding(InputKey.Shift, CatchAction.Dash),
|
||||||
};
|
new KeyBinding(InputKey.Shift, CatchAction.Dash),
|
||||||
|
};
|
||||||
public override IEnumerable<Mod> GetModsFor(ModType type)
|
|
||||||
{
|
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
|
||||||
switch (type)
|
{
|
||||||
{
|
if (mods.HasFlag(LegacyMods.Nightcore))
|
||||||
case ModType.DifficultyReduction:
|
yield return new CatchModNightcore();
|
||||||
return new Mod[]
|
else if (mods.HasFlag(LegacyMods.DoubleTime))
|
||||||
{
|
yield return new CatchModDoubleTime();
|
||||||
new CatchModEasy(),
|
|
||||||
new CatchModNoFail(),
|
if (mods.HasFlag(LegacyMods.Autoplay))
|
||||||
new MultiMod
|
yield return new CatchModAutoplay();
|
||||||
{
|
|
||||||
Mods = new Mod[]
|
if (mods.HasFlag(LegacyMods.Easy))
|
||||||
{
|
yield return new CatchModEasy();
|
||||||
new CatchModHalfTime(),
|
|
||||||
new CatchModDaycore(),
|
if (mods.HasFlag(LegacyMods.Flashlight))
|
||||||
},
|
yield return new CatchModFlashlight();
|
||||||
},
|
|
||||||
};
|
if (mods.HasFlag(LegacyMods.HalfTime))
|
||||||
|
yield return new CatchModHalfTime();
|
||||||
case ModType.DifficultyIncrease:
|
|
||||||
return new Mod[]
|
if (mods.HasFlag(LegacyMods.HardRock))
|
||||||
{
|
yield return new CatchModHardRock();
|
||||||
new CatchModHardRock(),
|
|
||||||
new MultiMod
|
if (mods.HasFlag(LegacyMods.Hidden))
|
||||||
{
|
yield return new CatchModHidden();
|
||||||
Mods = new Mod[]
|
|
||||||
{
|
if (mods.HasFlag(LegacyMods.NoFail))
|
||||||
new CatchModSuddenDeath(),
|
yield return new CatchModNoFail();
|
||||||
new CatchModPerfect(),
|
|
||||||
},
|
if (mods.HasFlag(LegacyMods.Perfect))
|
||||||
},
|
yield return new CatchModPerfect();
|
||||||
new MultiMod
|
|
||||||
{
|
if (mods.HasFlag(LegacyMods.Relax))
|
||||||
Mods = new Mod[]
|
yield return new CatchModRelax();
|
||||||
{
|
|
||||||
new CatchModDoubleTime(),
|
if (mods.HasFlag(LegacyMods.SuddenDeath))
|
||||||
new CatchModNightcore(),
|
yield return new CatchModSuddenDeath();
|
||||||
},
|
}
|
||||||
},
|
|
||||||
new CatchModHidden(),
|
public override IEnumerable<Mod> GetModsFor(ModType type)
|
||||||
new CatchModFlashlight(),
|
{
|
||||||
};
|
switch (type)
|
||||||
|
{
|
||||||
case ModType.Special:
|
case ModType.DifficultyReduction:
|
||||||
return new Mod[]
|
return new Mod[]
|
||||||
{
|
{
|
||||||
new CatchModRelax(),
|
new CatchModEasy(),
|
||||||
null,
|
new CatchModNoFail(),
|
||||||
null,
|
new MultiMod
|
||||||
new MultiMod
|
{
|
||||||
{
|
Mods = new Mod[]
|
||||||
Mods = new Mod[]
|
{
|
||||||
{
|
new CatchModHalfTime(),
|
||||||
new CatchModAutoplay(),
|
new CatchModDaycore(),
|
||||||
new ModCinema(),
|
},
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
};
|
|
||||||
|
case ModType.DifficultyIncrease:
|
||||||
default:
|
return new Mod[]
|
||||||
return new Mod[] { };
|
{
|
||||||
}
|
new CatchModHardRock(),
|
||||||
}
|
new MultiMod
|
||||||
|
{
|
||||||
public override string Description => "osu!catch";
|
Mods = new Mod[]
|
||||||
|
{
|
||||||
public override string ShortName => "fruits";
|
new CatchModSuddenDeath(),
|
||||||
|
new CatchModPerfect(),
|
||||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
|
},
|
||||||
|
},
|
||||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
|
new MultiMod
|
||||||
|
{
|
||||||
public override int? LegacyID => 2;
|
Mods = new Mod[]
|
||||||
|
{
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
new CatchModDoubleTime(),
|
||||||
|
new CatchModNightcore(),
|
||||||
public CatchRuleset(RulesetInfo rulesetInfo = null)
|
},
|
||||||
: base(rulesetInfo)
|
},
|
||||||
{
|
new CatchModHidden(),
|
||||||
}
|
new CatchModFlashlight(),
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
case ModType.Special:
|
||||||
|
return new Mod[]
|
||||||
|
{
|
||||||
|
new CatchModRelax(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
new MultiMod
|
||||||
|
{
|
||||||
|
Mods = new Mod[]
|
||||||
|
{
|
||||||
|
new CatchModAutoplay(),
|
||||||
|
new ModCinema(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return new Mod[] { };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
|
||||||
|
|
||||||
|
public override int? LegacyID => 2;
|
||||||
|
|
||||||
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
||||||
|
|
||||||
|
public CatchRuleset(RulesetInfo rulesetInfo = null)
|
||||||
|
: base(rulesetInfo)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Judgements
|
namespace osu.Game.Rulesets.Catch.Judgements
|
||||||
{
|
{
|
||||||
public class CatchJudgement : Judgement
|
public class CatchJudgement : Judgement
|
||||||
{
|
{
|
||||||
// todo: wangs
|
// todo: wangs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Replays;
|
using osu.Game.Rulesets.Catch.Replays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModAutoplay : ModAutoplay<CatchHitObject>
|
public class CatchModAutoplay : ModAutoplay<CatchHitObject>
|
||||||
{
|
{
|
||||||
protected override Score CreateReplayScore(Beatmap<CatchHitObject> beatmap)
|
protected override Score CreateReplayScore(Beatmap<CatchHitObject> beatmap)
|
||||||
{
|
{
|
||||||
return new Score
|
return new Score
|
||||||
{
|
{
|
||||||
User = new User { Username = "osu!salad!" },
|
User = new User { Username = "osu!salad!" },
|
||||||
Replay = new CatchAutoGenerator(beatmap).Generate(),
|
Replay = new CatchAutoGenerator(beatmap).Generate(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModDaycore : ModDaycore
|
public class CatchModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
public override double ScoreMultiplier => 0.3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModDoubleTime : ModDoubleTime
|
public class CatchModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => 1.06;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModEasy : ModEasy
|
public class CatchModEasy : ModEasy
|
||||||
{
|
{
|
||||||
public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!";
|
public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModFlashlight : ModFlashlight
|
public class CatchModFlashlight : ModFlashlight
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => 1.12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModHalfTime : ModHalfTime
|
public class CatchModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
public override double ScoreMultiplier => 0.3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,88 +1,88 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModHardRock : ModHardRock, IApplicableToHitObject<CatchHitObject>
|
public class CatchModHardRock : ModHardRock, IApplicableToHitObject<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => 1.12;
|
||||||
public override bool Ranked => true;
|
public override bool Ranked => true;
|
||||||
|
|
||||||
private float lastStartX;
|
private float lastStartX;
|
||||||
private int lastStartTime;
|
private int lastStartTime;
|
||||||
|
|
||||||
public void ApplyToHitObject(CatchHitObject hitObject)
|
public void ApplyToHitObject(CatchHitObject hitObject)
|
||||||
{
|
{
|
||||||
float position = hitObject.X;
|
float position = hitObject.X;
|
||||||
int startTime = (int)hitObject.StartTime;
|
int startTime = (int)hitObject.StartTime;
|
||||||
|
|
||||||
if (lastStartX == 0)
|
if (lastStartX == 0)
|
||||||
{
|
{
|
||||||
lastStartX = position;
|
lastStartX = position;
|
||||||
lastStartTime = startTime;
|
lastStartTime = startTime;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float diff = lastStartX - position;
|
float diff = lastStartX - position;
|
||||||
int timeDiff = startTime - lastStartTime;
|
int timeDiff = startTime - lastStartTime;
|
||||||
|
|
||||||
if (timeDiff > 1000)
|
if (timeDiff > 1000)
|
||||||
{
|
{
|
||||||
lastStartX = position;
|
lastStartX = position;
|
||||||
lastStartTime = startTime;
|
lastStartTime = startTime;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (diff == 0)
|
if (diff == 0)
|
||||||
{
|
{
|
||||||
bool right = RNG.NextBool();
|
bool right = RNG.NextBool();
|
||||||
|
|
||||||
float rand = Math.Min(20, (float)RNG.NextDouble(0, timeDiff / 4d)) / CatchPlayfield.BASE_WIDTH;
|
float rand = Math.Min(20, (float)RNG.NextDouble(0, timeDiff / 4d)) / CatchPlayfield.BASE_WIDTH;
|
||||||
|
|
||||||
if (right)
|
if (right)
|
||||||
{
|
{
|
||||||
if (position + rand <= 1)
|
if (position + rand <= 1)
|
||||||
position += rand;
|
position += rand;
|
||||||
else
|
else
|
||||||
position -= rand;
|
position -= rand;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (position - rand >= 0)
|
if (position - rand >= 0)
|
||||||
position -= rand;
|
position -= rand;
|
||||||
else
|
else
|
||||||
position += rand;
|
position += rand;
|
||||||
}
|
}
|
||||||
|
|
||||||
hitObject.X = position;
|
hitObject.X = position;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Math.Abs(diff) < timeDiff / 3d)
|
if (Math.Abs(diff) < timeDiff / 3d)
|
||||||
{
|
{
|
||||||
if (diff > 0)
|
if (diff > 0)
|
||||||
{
|
{
|
||||||
if (position - diff > 0)
|
if (position - diff > 0)
|
||||||
position -= diff;
|
position -= diff;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (position - diff < 1)
|
if (position - diff < 1)
|
||||||
position -= diff;
|
position -= diff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hitObject.X = position;
|
hitObject.X = position;
|
||||||
|
|
||||||
lastStartX = position;
|
lastStartX = position;
|
||||||
lastStartTime = startTime;
|
lastStartTime = startTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModHidden : ModHidden
|
public class CatchModHidden : ModHidden
|
||||||
{
|
{
|
||||||
public override string Description => @"Play with fading fruits.";
|
public override string Description => @"Play with fading fruits.";
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => 1.06;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModNightcore : ModNightcore
|
public class CatchModNightcore : ModNightcore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => 1.06;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModNoFail : ModNoFail
|
public class CatchModNoFail : ModNoFail
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModPerfect : ModPerfect
|
public class CatchModPerfect : ModPerfect
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModRelax : ModRelax
|
public class CatchModRelax : ModRelax
|
||||||
{
|
{
|
||||||
public override string Description => @"Use the mouse to control the catcher.";
|
public override string Description => @"Use the mouse to control the catcher.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModSuddenDeath : ModSuddenDeath
|
public class CatchModSuddenDeath : ModSuddenDeath
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,48 +1,48 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class BananaShower : CatchHitObject, IHasEndTime
|
public class BananaShower : CatchHitObject, IHasEndTime
|
||||||
{
|
{
|
||||||
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
|
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
|
||||||
|
|
||||||
public override bool LastInCombo => true;
|
public override bool LastInCombo => true;
|
||||||
|
|
||||||
protected override void CreateNestedHitObjects()
|
protected override void CreateNestedHitObjects()
|
||||||
{
|
{
|
||||||
base.CreateNestedHitObjects();
|
base.CreateNestedHitObjects();
|
||||||
createBananas();
|
createBananas();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createBananas()
|
private void createBananas()
|
||||||
{
|
{
|
||||||
double spacing = Duration;
|
double spacing = Duration;
|
||||||
while (spacing > 100)
|
while (spacing > 100)
|
||||||
spacing /= 2;
|
spacing /= 2;
|
||||||
|
|
||||||
if (spacing <= 0)
|
if (spacing <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (double i = StartTime; i <= EndTime; i += spacing)
|
for (double i = StartTime; i <= EndTime; i += spacing)
|
||||||
AddNested(new Banana
|
AddNested(new Banana
|
||||||
{
|
{
|
||||||
Samples = Samples,
|
Samples = Samples,
|
||||||
StartTime = i,
|
StartTime = i,
|
||||||
X = RNG.NextSingle()
|
X = RNG.NextSingle()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public double EndTime => StartTime + Duration;
|
public double EndTime => StartTime + Duration;
|
||||||
|
|
||||||
public double Duration { get; set; }
|
public double Duration { get; set; }
|
||||||
|
|
||||||
public class Banana : Fruit
|
public class Banana : Fruit
|
||||||
{
|
{
|
||||||
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
|
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,60 +1,60 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation
|
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation
|
||||||
{
|
{
|
||||||
public const double OBJECT_RADIUS = 44;
|
public const double OBJECT_RADIUS = 44;
|
||||||
|
|
||||||
public float X { get; set; }
|
public float X { get; set; }
|
||||||
|
|
||||||
public int IndexInBeatmap { get; set; }
|
public int IndexInBeatmap { get; set; }
|
||||||
|
|
||||||
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
|
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
|
||||||
|
|
||||||
public virtual bool NewCombo { get; set; }
|
public virtual bool NewCombo { get; set; }
|
||||||
|
|
||||||
public int IndexInCurrentCombo { get; set; }
|
public int IndexInCurrentCombo { get; set; }
|
||||||
|
|
||||||
public int ComboIndex { get; set; }
|
public int ComboIndex { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The next fruit starts a new combo. Used for explodey.
|
/// The next fruit starts a new combo. Used for explodey.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool LastInCombo { get; set; }
|
public virtual bool LastInCombo { get; set; }
|
||||||
|
|
||||||
public float Scale { get; set; } = 1;
|
public float Scale { get; set; } = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this fruit can initiate a hyperdash.
|
/// Whether this fruit can initiate a hyperdash.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HyperDash => HyperDashTarget != null;
|
public bool HyperDash => HyperDashTarget != null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The target fruit if we are to initiate a hyperdash.
|
/// The target fruit if we are to initiate a hyperdash.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CatchHitObject HyperDashTarget;
|
public CatchHitObject HyperDashTarget;
|
||||||
|
|
||||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
|
Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum FruitVisualRepresentation
|
public enum FruitVisualRepresentation
|
||||||
{
|
{
|
||||||
Pear,
|
Pear,
|
||||||
Grape,
|
Grape,
|
||||||
Raspberry,
|
Raspberry,
|
||||||
Pineapple,
|
Pineapple,
|
||||||
Banana // banananananannaanana
|
Banana // banananananannaanana
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,44 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||||
{
|
{
|
||||||
public class DrawableBananaShower : DrawableCatchHitObject<BananaShower>
|
public class DrawableBananaShower : DrawableCatchHitObject<BananaShower>
|
||||||
{
|
{
|
||||||
private readonly Container bananaContainer;
|
private readonly Container bananaContainer;
|
||||||
|
|
||||||
public DrawableBananaShower(BananaShower s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
|
public DrawableBananaShower(BananaShower s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
|
||||||
: base(s)
|
: base(s)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Origin = Anchor.BottomLeft;
|
Origin = Anchor.BottomLeft;
|
||||||
X = 0;
|
X = 0;
|
||||||
|
|
||||||
InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
|
InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
foreach (var b in s.NestedHitObjects.Cast<BananaShower.Banana>())
|
foreach (var b in s.NestedHitObjects.Cast<BananaShower.Banana>())
|
||||||
AddNested(getVisualRepresentation?.Invoke(b));
|
AddNested(getVisualRepresentation?.Invoke(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
if (timeOffset >= 0)
|
if (timeOffset >= 0)
|
||||||
AddJudgement(new Judgement { Result = NestedHitObjects.Cast<DrawableCatchHitObject>().Any(n => n.Judgements.Any(j => j.IsHit)) ? HitResult.Perfect : HitResult.Miss });
|
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)
|
protected override void AddNested(DrawableHitObject h)
|
||||||
{
|
{
|
||||||
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
||||||
bananaContainer.Add(h);
|
bananaContainer.Add(h);
|
||||||
base.AddNested(h);
|
base.AddNested(h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,93 +1,93 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||||
{
|
{
|
||||||
public abstract class PalpableCatchHitObject<TObject> : DrawableCatchHitObject<TObject>
|
public abstract class PalpableCatchHitObject<TObject> : DrawableCatchHitObject<TObject>
|
||||||
where TObject : CatchHitObject
|
where TObject : CatchHitObject
|
||||||
{
|
{
|
||||||
public override bool CanBePlated => true;
|
public override bool CanBePlated => true;
|
||||||
|
|
||||||
protected PalpableCatchHitObject(TObject hitObject)
|
protected PalpableCatchHitObject(TObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
Scale = new Vector2(HitObject.Scale);
|
Scale = new Vector2(HitObject.Scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class DrawableCatchHitObject<TObject> : DrawableCatchHitObject
|
public abstract class DrawableCatchHitObject<TObject> : DrawableCatchHitObject
|
||||||
where TObject : CatchHitObject
|
where TObject : CatchHitObject
|
||||||
{
|
{
|
||||||
public new TObject HitObject;
|
public new TObject HitObject;
|
||||||
|
|
||||||
protected DrawableCatchHitObject(TObject hitObject)
|
protected DrawableCatchHitObject(TObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
HitObject = hitObject;
|
HitObject = hitObject;
|
||||||
Anchor = Anchor.BottomLeft;
|
Anchor = Anchor.BottomLeft;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
|
public abstract class DrawableCatchHitObject : DrawableHitObject<CatchHitObject>
|
||||||
{
|
{
|
||||||
public virtual bool CanBePlated => false;
|
public virtual bool CanBePlated => false;
|
||||||
|
|
||||||
protected DrawableCatchHitObject(CatchHitObject hitObject)
|
protected DrawableCatchHitObject(CatchHitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
RelativePositionAxes = Axes.X;
|
RelativePositionAxes = Axes.X;
|
||||||
X = hitObject.X;
|
X = hitObject.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Func<CatchHitObject, bool> CheckPosition;
|
public Func<CatchHitObject, bool> CheckPosition;
|
||||||
|
|
||||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
if (CheckPosition == null) return;
|
if (CheckPosition == null) return;
|
||||||
|
|
||||||
if (timeOffset >= 0)
|
if (timeOffset >= 0)
|
||||||
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
|
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
{
|
{
|
||||||
base.SkinChanged(skin, allowFallback);
|
base.SkinChanged(skin, allowFallback);
|
||||||
|
|
||||||
if (HitObject is IHasComboInformation combo)
|
if (HitObject is IHasComboInformation combo)
|
||||||
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
|
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const float preempt = 1000;
|
private const float preempt = 1000;
|
||||||
|
|
||||||
protected override void UpdateState(ArmedState state)
|
protected override void UpdateState(ArmedState state)
|
||||||
{
|
{
|
||||||
using (BeginAbsoluteSequence(HitObject.StartTime - preempt))
|
using (BeginAbsoluteSequence(HitObject.StartTime - preempt))
|
||||||
this.FadeIn(200);
|
this.FadeIn(200);
|
||||||
|
|
||||||
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
|
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(endTime, true))
|
using (BeginAbsoluteSequence(endTime, true))
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Miss:
|
case ArmedState.Miss:
|
||||||
this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire();
|
this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire();
|
||||||
break;
|
break;
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
this.FadeOut().Expire();
|
this.FadeOut().Expire();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,43 +1,43 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||||
{
|
{
|
||||||
public class DrawableDroplet : PalpableCatchHitObject<Droplet>
|
public class DrawableDroplet : PalpableCatchHitObject<Droplet>
|
||||||
{
|
{
|
||||||
private Pulp pulp;
|
private Pulp pulp;
|
||||||
|
|
||||||
public DrawableDroplet(Droplet h)
|
public DrawableDroplet(Droplet h)
|
||||||
: base(h)
|
: base(h)
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
|
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 4;
|
||||||
Masking = false;
|
Masking = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChild = pulp = new Pulp
|
InternalChild = pulp = new Pulp
|
||||||
{
|
{
|
||||||
Size = Size
|
Size = Size
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Color4 AccentColour
|
public override Color4 AccentColour
|
||||||
{
|
{
|
||||||
get { return base.AccentColour; }
|
get { return base.AccentColour; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
base.AccentColour = value;
|
base.AccentColour = value;
|
||||||
pulp.AccentColour = AccentColour;
|
pulp.AccentColour = AccentColour;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,305 +1,305 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||||
{
|
{
|
||||||
public class DrawableFruit : PalpableCatchHitObject<Fruit>
|
public class DrawableFruit : PalpableCatchHitObject<Fruit>
|
||||||
{
|
{
|
||||||
private Circle border;
|
private Circle border;
|
||||||
|
|
||||||
public DrawableFruit(Fruit h)
|
public DrawableFruit(Fruit h)
|
||||||
: base(h)
|
: base(h)
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS);
|
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS);
|
||||||
Masking = false;
|
Masking = false;
|
||||||
|
|
||||||
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
Rotation = (float)(RNG.NextDouble() - 0.5f) * 40;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
// todo: this should come from the skin.
|
// todo: this should come from the skin.
|
||||||
AccentColour = colourForRrepesentation(HitObject.VisualRepresentation);
|
AccentColour = colourForRrepesentation(HitObject.VisualRepresentation);
|
||||||
|
|
||||||
InternalChildren = new[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
createPulp(HitObject.VisualRepresentation),
|
createPulp(HitObject.VisualRepresentation),
|
||||||
border = new Circle
|
border = new Circle
|
||||||
{
|
{
|
||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Hollow = !HitObject.HyperDash,
|
Hollow = !HitObject.HyperDash,
|
||||||
Type = EdgeEffectType.Glow,
|
Type = EdgeEffectType.Glow,
|
||||||
Radius = 4,
|
Radius = 4,
|
||||||
Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Darken(1).Opacity(0.6f)
|
Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Darken(1).Opacity(0.6f)
|
||||||
},
|
},
|
||||||
Size = new Vector2(Height * 1.5f),
|
Size = new Vector2(Height * 1.5f),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
BorderColour = Color4.White,
|
BorderColour = Color4.White,
|
||||||
BorderThickness = 4f,
|
BorderThickness = 4f,
|
||||||
Children = new Framework.Graphics.Drawable[]
|
Children = new Framework.Graphics.Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
AlwaysPresent = true,
|
AlwaysPresent = true,
|
||||||
Colour = AccentColour,
|
Colour = AccentColour,
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (HitObject.HyperDash)
|
if (HitObject.HyperDash)
|
||||||
{
|
{
|
||||||
AddInternal(new Pulp
|
AddInternal(new Pulp
|
||||||
{
|
{
|
||||||
RelativePositionAxes = Axes.Both,
|
RelativePositionAxes = Axes.Both,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
AccentColour = Color4.Red,
|
AccentColour = Color4.Red,
|
||||||
Blending = BlendingMode.Additive,
|
Blending = BlendingMode.Additive,
|
||||||
Alpha = 0.5f,
|
Alpha = 0.5f,
|
||||||
Scale = new Vector2(1.333f)
|
Scale = new Vector2(1.333f)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Framework.Graphics.Drawable createPulp(FruitVisualRepresentation representation)
|
private Framework.Graphics.Drawable createPulp(FruitVisualRepresentation representation)
|
||||||
{
|
{
|
||||||
const float large_pulp_3 = 13f;
|
const float large_pulp_3 = 13f;
|
||||||
const float distance_from_centre_3 = 0.23f;
|
const float distance_from_centre_3 = 0.23f;
|
||||||
|
|
||||||
const float large_pulp_4 = large_pulp_3 * 0.925f;
|
const float large_pulp_4 = large_pulp_3 * 0.925f;
|
||||||
const float distance_from_centre_4 = distance_from_centre_3 / 0.925f;
|
const float distance_from_centre_4 = distance_from_centre_3 / 0.925f;
|
||||||
|
|
||||||
const float small_pulp = large_pulp_3 / 2;
|
const float small_pulp = large_pulp_3 / 2;
|
||||||
|
|
||||||
Vector2 positionAt(float angle, float distance) => new Vector2(
|
Vector2 positionAt(float angle, float distance) => new Vector2(
|
||||||
distance * (float)Math.Sin(angle * Math.PI / 180),
|
distance * (float)Math.Sin(angle * Math.PI / 180),
|
||||||
distance * (float)Math.Cos(angle * Math.PI / 180));
|
distance * (float)Math.Cos(angle * Math.PI / 180));
|
||||||
|
|
||||||
switch (representation)
|
switch (representation)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
return new Container();
|
return new Container();
|
||||||
case FruitVisualRepresentation.Raspberry:
|
case FruitVisualRepresentation.Raspberry:
|
||||||
return new Container
|
return new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Framework.Graphics.Drawable[]
|
Children = new Framework.Graphics.Drawable[]
|
||||||
{
|
{
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(small_pulp),
|
Size = new Vector2(small_pulp),
|
||||||
Y = 0.05f,
|
Y = 0.05f,
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(large_pulp_4),
|
Size = new Vector2(large_pulp_4),
|
||||||
Position = positionAt(0, distance_from_centre_4),
|
Position = positionAt(0, distance_from_centre_4),
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(large_pulp_4),
|
Size = new Vector2(large_pulp_4),
|
||||||
Position = positionAt(90, distance_from_centre_4),
|
Position = positionAt(90, distance_from_centre_4),
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(large_pulp_4),
|
Size = new Vector2(large_pulp_4),
|
||||||
Position = positionAt(180, distance_from_centre_4),
|
Position = positionAt(180, distance_from_centre_4),
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
Size = new Vector2(large_pulp_4),
|
Size = new Vector2(large_pulp_4),
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Position = positionAt(270, distance_from_centre_4),
|
Position = positionAt(270, distance_from_centre_4),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
case FruitVisualRepresentation.Pineapple:
|
case FruitVisualRepresentation.Pineapple:
|
||||||
return new Container
|
return new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Framework.Graphics.Drawable[]
|
Children = new Framework.Graphics.Drawable[]
|
||||||
{
|
{
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(small_pulp),
|
Size = new Vector2(small_pulp),
|
||||||
Y = 0.1f,
|
Y = 0.1f,
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(large_pulp_4),
|
Size = new Vector2(large_pulp_4),
|
||||||
Position = positionAt(45, distance_from_centre_4),
|
Position = positionAt(45, distance_from_centre_4),
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(large_pulp_4),
|
Size = new Vector2(large_pulp_4),
|
||||||
Position = positionAt(135, distance_from_centre_4),
|
Position = positionAt(135, distance_from_centre_4),
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(large_pulp_4),
|
Size = new Vector2(large_pulp_4),
|
||||||
Position = positionAt(225, distance_from_centre_4),
|
Position = positionAt(225, distance_from_centre_4),
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
Size = new Vector2(large_pulp_4),
|
Size = new Vector2(large_pulp_4),
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Position = positionAt(315, distance_from_centre_4),
|
Position = positionAt(315, distance_from_centre_4),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
case FruitVisualRepresentation.Pear:
|
case FruitVisualRepresentation.Pear:
|
||||||
return new Container
|
return new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Framework.Graphics.Drawable[]
|
Children = new Framework.Graphics.Drawable[]
|
||||||
{
|
{
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(small_pulp),
|
Size = new Vector2(small_pulp),
|
||||||
Y = -0.1f,
|
Y = -0.1f,
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(large_pulp_3),
|
Size = new Vector2(large_pulp_3),
|
||||||
Position = positionAt(60, distance_from_centre_3),
|
Position = positionAt(60, distance_from_centre_3),
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(large_pulp_3),
|
Size = new Vector2(large_pulp_3),
|
||||||
Position = positionAt(180, distance_from_centre_3),
|
Position = positionAt(180, distance_from_centre_3),
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
Size = new Vector2(large_pulp_3),
|
Size = new Vector2(large_pulp_3),
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Position = positionAt(300, distance_from_centre_3),
|
Position = positionAt(300, distance_from_centre_3),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
case FruitVisualRepresentation.Grape:
|
case FruitVisualRepresentation.Grape:
|
||||||
return new Container
|
return new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Framework.Graphics.Drawable[]
|
Children = new Framework.Graphics.Drawable[]
|
||||||
{
|
{
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(small_pulp),
|
Size = new Vector2(small_pulp),
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(large_pulp_3),
|
Size = new Vector2(large_pulp_3),
|
||||||
Position = positionAt(0, distance_from_centre_3),
|
Position = positionAt(0, distance_from_centre_3),
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(large_pulp_3),
|
Size = new Vector2(large_pulp_3),
|
||||||
Position = positionAt(120, distance_from_centre_3),
|
Position = positionAt(120, distance_from_centre_3),
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
Size = new Vector2(large_pulp_3),
|
Size = new Vector2(large_pulp_3),
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Position = positionAt(240, distance_from_centre_3),
|
Position = positionAt(240, distance_from_centre_3),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
case FruitVisualRepresentation.Banana:
|
case FruitVisualRepresentation.Banana:
|
||||||
return new Container
|
return new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Framework.Graphics.Drawable[]
|
Children = new Framework.Graphics.Drawable[]
|
||||||
{
|
{
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(small_pulp),
|
Size = new Vector2(small_pulp),
|
||||||
Y = -0.15f
|
Y = -0.15f
|
||||||
},
|
},
|
||||||
new Pulp
|
new Pulp
|
||||||
{
|
{
|
||||||
AccentColour = AccentColour,
|
AccentColour = AccentColour,
|
||||||
Size = new Vector2(large_pulp_4 * 1.2f, large_pulp_4 * 3),
|
Size = new Vector2(large_pulp_4 * 1.2f, large_pulp_4 * 3),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
|
border.Alpha = (float)MathHelper.Clamp((HitObject.StartTime - Time.Current) / 500, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
|
private Color4 colourForRrepesentation(FruitVisualRepresentation representation)
|
||||||
{
|
{
|
||||||
switch (representation)
|
switch (representation)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case FruitVisualRepresentation.Pear:
|
case FruitVisualRepresentation.Pear:
|
||||||
return new Color4(17, 136, 170, 255);
|
return new Color4(17, 136, 170, 255);
|
||||||
case FruitVisualRepresentation.Grape:
|
case FruitVisualRepresentation.Grape:
|
||||||
return new Color4(204, 102, 0, 255);
|
return new Color4(204, 102, 0, 255);
|
||||||
case FruitVisualRepresentation.Raspberry:
|
case FruitVisualRepresentation.Raspberry:
|
||||||
return new Color4(121, 9, 13, 255);
|
return new Color4(121, 9, 13, 255);
|
||||||
case FruitVisualRepresentation.Pineapple:
|
case FruitVisualRepresentation.Pineapple:
|
||||||
return new Color4(102, 136, 0, 255);
|
return new Color4(102, 136, 0, 255);
|
||||||
case FruitVisualRepresentation.Banana:
|
case FruitVisualRepresentation.Banana:
|
||||||
switch (RNG.Next(0, 3))
|
switch (RNG.Next(0, 3))
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
return new Color4(255, 240, 0, 255);
|
return new Color4(255, 240, 0, 255);
|
||||||
case 1:
|
case 1:
|
||||||
return new Color4(255, 192, 0, 255);
|
return new Color4(255, 192, 0, 255);
|
||||||
case 2:
|
case 2:
|
||||||
return new Color4(214, 221, 28, 255);
|
return new Color4(214, 221, 28, 255);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,41 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||||
{
|
{
|
||||||
public class DrawableJuiceStream : DrawableCatchHitObject<JuiceStream>
|
public class DrawableJuiceStream : DrawableCatchHitObject<JuiceStream>
|
||||||
{
|
{
|
||||||
private readonly Container dropletContainer;
|
private readonly Container dropletContainer;
|
||||||
|
|
||||||
public DrawableJuiceStream(JuiceStream s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
|
public DrawableJuiceStream(JuiceStream s, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation = null)
|
||||||
: base(s)
|
: base(s)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
Origin = Anchor.BottomLeft;
|
Origin = Anchor.BottomLeft;
|
||||||
X = 0;
|
X = 0;
|
||||||
|
|
||||||
InternalChild = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
|
InternalChild = dropletContainer = new Container { RelativeSizeAxes = Axes.Both, };
|
||||||
|
|
||||||
foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>())
|
foreach (var o in s.NestedHitObjects.Cast<CatchHitObject>())
|
||||||
AddNested(getVisualRepresentation?.Invoke(o));
|
AddNested(getVisualRepresentation?.Invoke(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool ProvidesJudgement => false;
|
protected override bool ProvidesJudgement => false;
|
||||||
|
|
||||||
protected override void AddNested(DrawableHitObject h)
|
protected override void AddNested(DrawableHitObject h)
|
||||||
{
|
{
|
||||||
var catchObject = (DrawableCatchHitObject)h;
|
var catchObject = (DrawableCatchHitObject)h;
|
||||||
|
|
||||||
catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
catchObject.CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
||||||
|
|
||||||
dropletContainer.Add(h);
|
dropletContainer.Add(h);
|
||||||
base.AddNested(h);
|
base.AddNested(h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,42 +1,42 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
|
namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
|
||||||
{
|
{
|
||||||
public class Pulp : Circle, IHasAccentColour
|
public class Pulp : Circle, IHasAccentColour
|
||||||
{
|
{
|
||||||
public Pulp()
|
public Pulp()
|
||||||
{
|
{
|
||||||
RelativePositionAxes = Axes.Both;
|
RelativePositionAxes = Axes.Both;
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
Blending = BlendingMode.Additive;
|
Blending = BlendingMode.Additive;
|
||||||
Colour = Color4.White.Opacity(0.9f);
|
Colour = Color4.White.Opacity(0.9f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 accentColour;
|
private Color4 accentColour;
|
||||||
public Color4 AccentColour
|
public Color4 AccentColour
|
||||||
{
|
{
|
||||||
get { return accentColour; }
|
get { return accentColour; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
accentColour = value;
|
accentColour = value;
|
||||||
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Glow,
|
Type = EdgeEffectType.Glow,
|
||||||
Radius = 8,
|
Radius = 8,
|
||||||
Colour = accentColour.Darken(0.2f).Opacity(0.75f)
|
Colour = accentColour.Darken(0.2f).Opacity(0.75f)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class Droplet : CatchHitObject
|
public class Droplet : CatchHitObject
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class Fruit : CatchHitObject
|
public class Fruit : CatchHitObject
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,157 +1,157 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class JuiceStream : CatchHitObject, IHasCurve
|
public class JuiceStream : CatchHitObject, IHasCurve
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float base_scoring_distance = 100;
|
private const float base_scoring_distance = 100;
|
||||||
|
|
||||||
public int RepeatCount { get; set; }
|
public int RepeatCount { get; set; }
|
||||||
|
|
||||||
public double Velocity;
|
public double Velocity;
|
||||||
public double TickDistance;
|
public double TickDistance;
|
||||||
|
|
||||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||||
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
|
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
|
||||||
|
|
||||||
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
|
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
|
||||||
|
|
||||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CreateNestedHitObjects()
|
protected override void CreateNestedHitObjects()
|
||||||
{
|
{
|
||||||
base.CreateNestedHitObjects();
|
base.CreateNestedHitObjects();
|
||||||
|
|
||||||
createTicks();
|
createTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTicks()
|
private void createTicks()
|
||||||
{
|
{
|
||||||
if (TickDistance == 0)
|
if (TickDistance == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var length = Curve.Distance;
|
var length = Curve.Distance;
|
||||||
var tickDistance = Math.Min(TickDistance, length);
|
var tickDistance = Math.Min(TickDistance, length);
|
||||||
var spanDuration = length / Velocity;
|
var spanDuration = length / Velocity;
|
||||||
|
|
||||||
var minDistanceFromEnd = Velocity * 0.01;
|
var minDistanceFromEnd = Velocity * 0.01;
|
||||||
|
|
||||||
AddNested(new Fruit
|
AddNested(new Fruit
|
||||||
{
|
{
|
||||||
Samples = Samples,
|
Samples = Samples,
|
||||||
StartTime = StartTime,
|
StartTime = StartTime,
|
||||||
X = X
|
X = X
|
||||||
});
|
});
|
||||||
|
|
||||||
double lastDropletTime = StartTime;
|
double lastDropletTime = StartTime;
|
||||||
|
|
||||||
for (int span = 0; span < this.SpanCount(); span++)
|
for (int span = 0; span < this.SpanCount(); span++)
|
||||||
{
|
{
|
||||||
var spanStartTime = StartTime + span * spanDuration;
|
var spanStartTime = StartTime + span * spanDuration;
|
||||||
var reversed = span % 2 == 1;
|
var reversed = span % 2 == 1;
|
||||||
|
|
||||||
for (double d = 0; d <= length; d += tickDistance)
|
for (double d = 0; d <= length; d += tickDistance)
|
||||||
{
|
{
|
||||||
var timeProgress = d / length;
|
var timeProgress = d / length;
|
||||||
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
||||||
|
|
||||||
double time = spanStartTime + timeProgress * spanDuration;
|
double time = spanStartTime + timeProgress * spanDuration;
|
||||||
|
|
||||||
double tinyTickInterval = time - lastDropletTime;
|
double tinyTickInterval = time - lastDropletTime;
|
||||||
while (tinyTickInterval > 100)
|
while (tinyTickInterval > 100)
|
||||||
tinyTickInterval /= 2;
|
tinyTickInterval /= 2;
|
||||||
|
|
||||||
for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
|
for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
|
||||||
{
|
{
|
||||||
double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
|
double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
|
||||||
|
|
||||||
AddNested(new TinyDroplet
|
AddNested(new TinyDroplet
|
||||||
{
|
{
|
||||||
StartTime = t,
|
StartTime = t,
|
||||||
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||||
{
|
{
|
||||||
Bank = s.Bank,
|
Bank = s.Bank,
|
||||||
Name = @"slidertick",
|
Name = @"slidertick",
|
||||||
Volume = s.Volume
|
Volume = s.Volume
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
|
if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
|
||||||
{
|
{
|
||||||
AddNested(new Droplet
|
AddNested(new Droplet
|
||||||
{
|
{
|
||||||
StartTime = time,
|
StartTime = time,
|
||||||
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||||
{
|
{
|
||||||
Bank = s.Bank,
|
Bank = s.Bank,
|
||||||
Name = @"slidertick",
|
Name = @"slidertick",
|
||||||
Volume = s.Volume
|
Volume = s.Volume
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
lastDropletTime = time;
|
lastDropletTime = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddNested(new Fruit
|
AddNested(new Fruit
|
||||||
{
|
{
|
||||||
Samples = Samples,
|
Samples = Samples,
|
||||||
StartTime = spanStartTime + spanDuration,
|
StartTime = spanStartTime + spanDuration,
|
||||||
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||||
|
|
||||||
public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
|
public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
|
||||||
|
|
||||||
public double Duration => EndTime - StartTime;
|
public double Duration => EndTime - StartTime;
|
||||||
|
|
||||||
public double Distance
|
public double Distance
|
||||||
{
|
{
|
||||||
get { return Curve.Distance; }
|
get { return Curve.Distance; }
|
||||||
set { Curve.Distance = value; }
|
set { Curve.Distance = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public SliderCurve Curve { get; } = new SliderCurve();
|
public SliderCurve Curve { get; } = new SliderCurve();
|
||||||
|
|
||||||
public List<Vector2> ControlPoints
|
public List<Vector2> ControlPoints
|
||||||
{
|
{
|
||||||
get { return Curve.ControlPoints; }
|
get { return Curve.ControlPoints; }
|
||||||
set { Curve.ControlPoints = value; }
|
set { Curve.ControlPoints = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
|
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
|
||||||
|
|
||||||
public CurveType CurveType
|
public CurveType CurveType
|
||||||
{
|
{
|
||||||
get { return Curve.CurveType; }
|
get { return Curve.CurveType; }
|
||||||
set { Curve.CurveType = value; }
|
set { Curve.CurveType = value; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class TinyDroplet : Droplet
|
public class TinyDroplet : Droplet
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
// We publish our internal attributes to other sub-projects of the framework.
|
// We publish our internal attributes to other sub-projects of the framework.
|
||||||
// Note, that we omit visual tests as they are meant to test the framework
|
// Note, that we omit visual tests as they are meant to test the framework
|
||||||
// behavior "in the wild".
|
// behavior "in the wild".
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests")]
|
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests")]
|
||||||
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.Dynamic")]
|
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.Dynamic")]
|
||||||
|
@ -1,121 +1,121 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Replays
|
namespace osu.Game.Rulesets.Catch.Replays
|
||||||
{
|
{
|
||||||
internal class CatchAutoGenerator : AutoGenerator<CatchHitObject>
|
internal class CatchAutoGenerator : AutoGenerator<CatchHitObject>
|
||||||
{
|
{
|
||||||
public const double RELEASE_DELAY = 20;
|
public const double RELEASE_DELAY = 20;
|
||||||
|
|
||||||
public CatchAutoGenerator(Beatmap<CatchHitObject> beatmap)
|
public CatchAutoGenerator(Beatmap<CatchHitObject> beatmap)
|
||||||
: base(beatmap)
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
Replay = new Replay { User = new User { Username = @"Autoplay" } };
|
Replay = new Replay { User = new User { Username = @"Autoplay" } };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Replay Replay;
|
protected Replay Replay;
|
||||||
|
|
||||||
public override Replay Generate()
|
public override Replay Generate()
|
||||||
{
|
{
|
||||||
// todo: add support for HT DT
|
// todo: add support for HT DT
|
||||||
const double dash_speed = CatcherArea.Catcher.BASE_SPEED;
|
const double dash_speed = CatcherArea.Catcher.BASE_SPEED;
|
||||||
const double movement_speed = dash_speed / 2;
|
const double movement_speed = dash_speed / 2;
|
||||||
float lastPosition = 0.5f;
|
float lastPosition = 0.5f;
|
||||||
double lastTime = 0;
|
double lastTime = 0;
|
||||||
|
|
||||||
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
|
// 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));
|
Replay.Frames.Add(new CatchReplayFrame(-100000, lastPosition));
|
||||||
|
|
||||||
void moveToNext(CatchHitObject h)
|
void moveToNext(CatchHitObject h)
|
||||||
{
|
{
|
||||||
float positionChange = Math.Abs(lastPosition - h.X);
|
float positionChange = Math.Abs(lastPosition - h.X);
|
||||||
double timeAvailable = h.StartTime - lastTime;
|
double timeAvailable = h.StartTime - lastTime;
|
||||||
|
|
||||||
//So we can either make it there without a dash or not.
|
//So we can either make it there without a dash or not.
|
||||||
double speedRequired = positionChange / timeAvailable;
|
double speedRequired = positionChange / timeAvailable;
|
||||||
|
|
||||||
bool dashRequired = speedRequired > movement_speed && h.StartTime != 0;
|
bool dashRequired = speedRequired > movement_speed && h.StartTime != 0;
|
||||||
|
|
||||||
// todo: get correct catcher size, based on difficulty CS.
|
// todo: get correct catcher size, based on difficulty CS.
|
||||||
const float catcher_width_half = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * 0.3f * 0.5f;
|
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)
|
if (lastPosition - catcher_width_half < h.X && lastPosition + catcher_width_half > h.X)
|
||||||
{
|
{
|
||||||
//we are already in the correct range.
|
//we are already in the correct range.
|
||||||
lastTime = h.StartTime;
|
lastTime = h.StartTime;
|
||||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition));
|
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, lastPosition));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (h is BananaShower.Banana)
|
if (h is BananaShower.Banana)
|
||||||
{
|
{
|
||||||
// auto bananas unrealistically warp to catch 100% combo.
|
// auto bananas unrealistically warp to catch 100% combo.
|
||||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||||
}
|
}
|
||||||
else if (h.HyperDash)
|
else if (h.HyperDash)
|
||||||
{
|
{
|
||||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition));
|
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable, lastPosition));
|
||||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||||
}
|
}
|
||||||
else if (dashRequired)
|
else if (dashRequired)
|
||||||
{
|
{
|
||||||
//we do a movement in two parts - the dash part then the normal part...
|
//we do a movement in two parts - the dash part then the normal part...
|
||||||
double timeAtNormalSpeed = positionChange / movement_speed;
|
double timeAtNormalSpeed = positionChange / movement_speed;
|
||||||
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
|
double timeWeNeedToSave = timeAtNormalSpeed - timeAvailable;
|
||||||
double timeAtDashSpeed = timeWeNeedToSave / 2;
|
double timeAtDashSpeed = timeWeNeedToSave / 2;
|
||||||
|
|
||||||
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
|
float midPosition = (float)Interpolation.Lerp(lastPosition, h.X, (float)timeAtDashSpeed / timeAvailable);
|
||||||
|
|
||||||
//dash movement
|
//dash movement
|
||||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeAvailable + 1, lastPosition, true));
|
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 - timeAvailable + timeAtDashSpeed, midPosition));
|
||||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
double timeBefore = positionChange / movement_speed;
|
double timeBefore = positionChange / movement_speed;
|
||||||
|
|
||||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition));
|
Replay.Frames.Add(new CatchReplayFrame(h.StartTime - timeBefore, lastPosition));
|
||||||
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X));
|
||||||
}
|
}
|
||||||
|
|
||||||
lastTime = h.StartTime;
|
lastTime = h.StartTime;
|
||||||
lastPosition = h.X;
|
lastPosition = h.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var obj in Beatmap.HitObjects)
|
foreach (var obj in Beatmap.HitObjects)
|
||||||
{
|
{
|
||||||
switch (obj)
|
switch (obj)
|
||||||
{
|
{
|
||||||
case Fruit _:
|
case Fruit _:
|
||||||
moveToNext(obj);
|
moveToNext(obj);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var nestedObj in obj.NestedHitObjects.Cast<CatchHitObject>())
|
foreach (var nestedObj in obj.NestedHitObjects.Cast<CatchHitObject>())
|
||||||
{
|
{
|
||||||
switch (nestedObj)
|
switch (nestedObj)
|
||||||
{
|
{
|
||||||
case BananaShower.Banana _:
|
case BananaShower.Banana _:
|
||||||
case TinyDroplet _:
|
case TinyDroplet _:
|
||||||
case Droplet _:
|
case Droplet _:
|
||||||
case Fruit _:
|
case Fruit _:
|
||||||
moveToNext(nestedObj);
|
moveToNext(nestedObj);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Replay;
|
return Replay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,60 +1,60 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Replays
|
namespace osu.Game.Rulesets.Catch.Replays
|
||||||
{
|
{
|
||||||
public class CatchFramedReplayInputHandler : FramedReplayInputHandler<CatchReplayFrame>
|
public class CatchFramedReplayInputHandler : FramedReplayInputHandler<CatchReplayFrame>
|
||||||
{
|
{
|
||||||
public CatchFramedReplayInputHandler(Replay replay)
|
public CatchFramedReplayInputHandler(Replay replay)
|
||||||
: base(replay)
|
: base(replay)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool IsImportant(CatchReplayFrame frame) => frame.Position > 0;
|
protected override bool IsImportant(CatchReplayFrame frame) => frame.Position > 0;
|
||||||
|
|
||||||
protected float? Position
|
protected float? Position
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!HasFrames)
|
if (!HasFrames)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
|
return Interpolation.ValueAt(CurrentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override List<InputState> GetPendingStates()
|
public override List<InputState> GetPendingStates()
|
||||||
{
|
{
|
||||||
if (!Position.HasValue) return new List<InputState>();
|
if (!Position.HasValue) return new List<InputState>();
|
||||||
|
|
||||||
var actions = new List<CatchAction>();
|
var actions = new List<CatchAction>();
|
||||||
|
|
||||||
if (CurrentFrame.Dashing)
|
if (CurrentFrame.Dashing)
|
||||||
actions.Add(CatchAction.Dash);
|
actions.Add(CatchAction.Dash);
|
||||||
|
|
||||||
if (Position.Value > CurrentFrame.Position)
|
if (Position.Value > CurrentFrame.Position)
|
||||||
actions.Add(CatchAction.MoveRight);
|
actions.Add(CatchAction.MoveRight);
|
||||||
else if (Position.Value < CurrentFrame.Position)
|
else if (Position.Value < CurrentFrame.Position)
|
||||||
actions.Add(CatchAction.MoveLeft);
|
actions.Add(CatchAction.MoveLeft);
|
||||||
|
|
||||||
return new List<InputState>
|
return new List<InputState>
|
||||||
{
|
{
|
||||||
new CatchReplayState
|
new CatchReplayState
|
||||||
{
|
{
|
||||||
PressedActions = actions,
|
PressedActions = actions,
|
||||||
CatcherX = Position.Value
|
CatcherX = Position.Value
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CatchReplayState : ReplayState<CatchAction>
|
public class CatchReplayState : ReplayState<CatchAction>
|
||||||
{
|
{
|
||||||
public float? CatcherX { get; set; }
|
public float? CatcherX { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.Replays.Legacy;
|
using osu.Game.Rulesets.Replays.Legacy;
|
||||||
using osu.Game.Rulesets.Replays.Types;
|
using osu.Game.Rulesets.Replays.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Replays
|
namespace osu.Game.Rulesets.Catch.Replays
|
||||||
{
|
{
|
||||||
public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame
|
public class CatchReplayFrame : ReplayFrame, IConvertibleReplayFrame
|
||||||
{
|
{
|
||||||
public float Position;
|
public float Position;
|
||||||
public bool Dashing;
|
public bool Dashing;
|
||||||
|
|
||||||
public CatchReplayFrame()
|
public CatchReplayFrame()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public CatchReplayFrame(double time, float? position = null, bool dashing = false)
|
public CatchReplayFrame(double time, float? position = null, bool dashing = false)
|
||||||
: base(time)
|
: base(time)
|
||||||
{
|
{
|
||||||
Position = position ?? -1;
|
Position = position ?? -1;
|
||||||
Dashing = dashing;
|
Dashing = dashing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap)
|
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap)
|
||||||
{
|
{
|
||||||
Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH;
|
Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH;
|
||||||
Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1;
|
Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,44 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Judgements;
|
using osu.Game.Rulesets.Catch.Judgements;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Scoring
|
namespace osu.Game.Rulesets.Catch.Scoring
|
||||||
{
|
{
|
||||||
public class CatchScoreProcessor : ScoreProcessor<CatchHitObject>
|
public class CatchScoreProcessor : ScoreProcessor<CatchHitObject>
|
||||||
{
|
{
|
||||||
public CatchScoreProcessor(RulesetContainer<CatchHitObject> rulesetContainer)
|
public CatchScoreProcessor(RulesetContainer<CatchHitObject> rulesetContainer)
|
||||||
: base(rulesetContainer)
|
: base(rulesetContainer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SimulateAutoplay(Beatmap<CatchHitObject> beatmap)
|
protected override void SimulateAutoplay(Beatmap<CatchHitObject> beatmap)
|
||||||
{
|
{
|
||||||
foreach (var obj in beatmap.HitObjects)
|
foreach (var obj in beatmap.HitObjects)
|
||||||
{
|
{
|
||||||
switch (obj)
|
switch (obj)
|
||||||
{
|
{
|
||||||
case JuiceStream stream:
|
case JuiceStream stream:
|
||||||
foreach (var _ in stream.NestedHitObjects.Cast<CatchHitObject>())
|
foreach (var _ in stream.NestedHitObjects.Cast<CatchHitObject>())
|
||||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||||
break;
|
break;
|
||||||
case BananaShower shower:
|
case BananaShower shower:
|
||||||
foreach (var _ in shower.NestedHitObjects.Cast<CatchHitObject>())
|
foreach (var _ in shower.NestedHitObjects.Cast<CatchHitObject>())
|
||||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||||
break;
|
break;
|
||||||
case Fruit _:
|
case Fruit _:
|
||||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
base.SimulateAutoplay(beatmap);
|
base.SimulateAutoplay(beatmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,70 +1,70 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
public class CatchPlayfield : ScrollingPlayfield
|
public class CatchPlayfield : ScrollingPlayfield
|
||||||
{
|
{
|
||||||
public const float BASE_WIDTH = 512;
|
public const float BASE_WIDTH = 512;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
private readonly Container<Drawable> content;
|
private readonly Container<Drawable> content;
|
||||||
|
|
||||||
private readonly CatcherArea catcherArea;
|
private readonly CatcherArea catcherArea;
|
||||||
|
|
||||||
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
|
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
|
||||||
: base(ScrollingDirection.Down, BASE_WIDTH)
|
: base(ScrollingDirection.Down, BASE_WIDTH)
|
||||||
{
|
{
|
||||||
Container explodingFruitContainer;
|
Container explodingFruitContainer;
|
||||||
|
|
||||||
Anchor = Anchor.TopCentre;
|
Anchor = Anchor.TopCentre;
|
||||||
Origin = Anchor.TopCentre;
|
Origin = Anchor.TopCentre;
|
||||||
|
|
||||||
base.Content.Anchor = Anchor.BottomLeft;
|
base.Content.Anchor = Anchor.BottomLeft;
|
||||||
base.Content.Origin = Anchor.BottomLeft;
|
base.Content.Origin = Anchor.BottomLeft;
|
||||||
|
|
||||||
base.Content.AddRange(new Drawable[]
|
base.Content.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
explodingFruitContainer = new Container
|
explodingFruitContainer = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
catcherArea = new CatcherArea(difficulty)
|
catcherArea = new CatcherArea(difficulty)
|
||||||
{
|
{
|
||||||
GetVisualRepresentation = getVisualRepresentation,
|
GetVisualRepresentation = getVisualRepresentation,
|
||||||
ExplodingFruitTarget = explodingFruitContainer,
|
ExplodingFruitTarget = explodingFruitContainer,
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
},
|
},
|
||||||
content = new Container<Drawable>
|
content = new Container<Drawable>
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
|
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
|
||||||
|
|
||||||
public override void Add(DrawableHitObject h)
|
public override void Add(DrawableHitObject h)
|
||||||
{
|
{
|
||||||
h.OnJudgement += onJudgement;
|
h.OnJudgement += onJudgement;
|
||||||
|
|
||||||
base.Add(h);
|
base.Add(h);
|
||||||
|
|
||||||
var fruit = (DrawableCatchHitObject)h;
|
var fruit = (DrawableCatchHitObject)h;
|
||||||
fruit.CheckPosition = CheckIfWeCanCatch;
|
fruit.CheckPosition = CheckIfWeCanCatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement);
|
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,59 +1,59 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||||
using osu.Game.Rulesets.Catch.Replays;
|
using osu.Game.Rulesets.Catch.Replays;
|
||||||
using osu.Game.Rulesets.Catch.Scoring;
|
using osu.Game.Rulesets.Catch.Scoring;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchHitObject>
|
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchHitObject>
|
||||||
{
|
{
|
||||||
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
||||||
|
|
||||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
||||||
|
|
||||||
protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
|
protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
|
||||||
|
|
||||||
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter() => new CatchBeatmapConverter();
|
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter() => new CatchBeatmapConverter();
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation);
|
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation);
|
||||||
|
|
||||||
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
|
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
|
||||||
|
|
||||||
protected override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h)
|
protected override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h)
|
||||||
{
|
{
|
||||||
switch (h)
|
switch (h)
|
||||||
{
|
{
|
||||||
case Fruit fruit:
|
case Fruit fruit:
|
||||||
return new DrawableFruit(fruit);
|
return new DrawableFruit(fruit);
|
||||||
case JuiceStream stream:
|
case JuiceStream stream:
|
||||||
return new DrawableJuiceStream(stream, GetVisualRepresentation);
|
return new DrawableJuiceStream(stream, GetVisualRepresentation);
|
||||||
case BananaShower banana:
|
case BananaShower banana:
|
||||||
return new DrawableBananaShower(banana, GetVisualRepresentation);
|
return new DrawableBananaShower(banana, GetVisualRepresentation);
|
||||||
case TinyDroplet tiny:
|
case TinyDroplet tiny:
|
||||||
return new DrawableDroplet(tiny) { Scale = new Vector2(0.5f) };
|
return new DrawableDroplet(tiny) { Scale = new Vector2(0.5f) };
|
||||||
case Droplet droplet:
|
case Droplet droplet:
|
||||||
return new DrawableDroplet(droplet);
|
return new DrawableDroplet(droplet);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,416 +1,416 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||||
using osu.Game.Rulesets.Catch.Replays;
|
using osu.Game.Rulesets.Catch.Replays;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
public class CatcherArea : Container
|
public class CatcherArea : Container
|
||||||
{
|
{
|
||||||
public const float CATCHER_SIZE = 172;
|
public const float CATCHER_SIZE = 172;
|
||||||
|
|
||||||
protected readonly Catcher MovableCatcher;
|
protected readonly Catcher MovableCatcher;
|
||||||
|
|
||||||
public Func<CatchHitObject, DrawableHitObject<CatchHitObject>> GetVisualRepresentation;
|
public Func<CatchHitObject, DrawableHitObject<CatchHitObject>> GetVisualRepresentation;
|
||||||
|
|
||||||
public Container ExplodingFruitTarget
|
public Container ExplodingFruitTarget
|
||||||
{
|
{
|
||||||
set { MovableCatcher.ExplodingFruitTarget = value; }
|
set { MovableCatcher.ExplodingFruitTarget = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public CatcherArea(BeatmapDifficulty difficulty = null)
|
public CatcherArea(BeatmapDifficulty difficulty = null)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = CATCHER_SIZE;
|
Height = CATCHER_SIZE;
|
||||||
Child = MovableCatcher = new Catcher(difficulty)
|
Child = MovableCatcher = new Catcher(difficulty)
|
||||||
{
|
{
|
||||||
AdditiveTarget = this,
|
AdditiveTarget = this,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private DrawableCatchHitObject lastPlateableFruit;
|
private DrawableCatchHitObject lastPlateableFruit;
|
||||||
|
|
||||||
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
|
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
|
||||||
{
|
{
|
||||||
if (judgement.IsHit && fruit.CanBePlated)
|
if (judgement.IsHit && fruit.CanBePlated)
|
||||||
{
|
{
|
||||||
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
|
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
|
||||||
|
|
||||||
if (caughtFruit == null) return;
|
if (caughtFruit == null) return;
|
||||||
|
|
||||||
caughtFruit.RelativePositionAxes = Axes.None;
|
caughtFruit.RelativePositionAxes = Axes.None;
|
||||||
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
|
caughtFruit.Position = new Vector2(MovableCatcher.ToLocalSpace(fruit.ScreenSpaceDrawQuad.Centre).X - MovableCatcher.DrawSize.X / 2, 0);
|
||||||
|
|
||||||
caughtFruit.Anchor = Anchor.TopCentre;
|
caughtFruit.Anchor = Anchor.TopCentre;
|
||||||
caughtFruit.Origin = Anchor.Centre;
|
caughtFruit.Origin = Anchor.Centre;
|
||||||
caughtFruit.Scale *= 0.7f;
|
caughtFruit.Scale *= 0.7f;
|
||||||
caughtFruit.LifetimeEnd = double.MaxValue;
|
caughtFruit.LifetimeEnd = double.MaxValue;
|
||||||
|
|
||||||
MovableCatcher.Add(caughtFruit);
|
MovableCatcher.Add(caughtFruit);
|
||||||
|
|
||||||
lastPlateableFruit = caughtFruit;
|
lastPlateableFruit = caughtFruit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fruit.HitObject.LastInCombo)
|
if (fruit.HitObject.LastInCombo)
|
||||||
{
|
{
|
||||||
if (judgement.IsHit)
|
if (judgement.IsHit)
|
||||||
{
|
{
|
||||||
// this is required to make this run after the last caught fruit runs UpdateState at least once.
|
// this is required to make this run after the last caught fruit runs UpdateState at least once.
|
||||||
// TODO: find a better alternative
|
// TODO: find a better alternative
|
||||||
if (lastPlateableFruit.IsLoaded)
|
if (lastPlateableFruit.IsLoaded)
|
||||||
MovableCatcher.Explode();
|
MovableCatcher.Explode();
|
||||||
else
|
else
|
||||||
lastPlateableFruit.OnLoadComplete = _ => { MovableCatcher.Explode(); };
|
lastPlateableFruit.OnLoadComplete = _ => { MovableCatcher.Explode(); };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
MovableCatcher.Drop();
|
MovableCatcher.Drop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateAfterChildren()
|
protected override void UpdateAfterChildren()
|
||||||
{
|
{
|
||||||
base.UpdateAfterChildren();
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
var state = GetContainingInputManager().CurrentState as CatchFramedReplayInputHandler.CatchReplayState;
|
var state = GetContainingInputManager().CurrentState as CatchFramedReplayInputHandler.CatchReplayState;
|
||||||
|
|
||||||
if (state?.CatcherX != null)
|
if (state?.CatcherX != null)
|
||||||
MovableCatcher.X = state.CatcherX.Value;
|
MovableCatcher.X = state.CatcherX.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnReleased(CatchAction action) => false;
|
public bool OnReleased(CatchAction action) => false;
|
||||||
|
|
||||||
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
|
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
|
||||||
|
|
||||||
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
||||||
{
|
{
|
||||||
private Texture texture;
|
private Texture texture;
|
||||||
|
|
||||||
private Container<DrawableHitObject> caughtFruit;
|
private Container<DrawableHitObject> caughtFruit;
|
||||||
|
|
||||||
public Container ExplodingFruitTarget;
|
public Container ExplodingFruitTarget;
|
||||||
|
|
||||||
public Container AdditiveTarget;
|
public Container AdditiveTarget;
|
||||||
|
|
||||||
public Catcher(BeatmapDifficulty difficulty = null)
|
public Catcher(BeatmapDifficulty difficulty = null)
|
||||||
{
|
{
|
||||||
RelativePositionAxes = Axes.X;
|
RelativePositionAxes = Axes.X;
|
||||||
X = 0.5f;
|
X = 0.5f;
|
||||||
|
|
||||||
Origin = Anchor.TopCentre;
|
Origin = Anchor.TopCentre;
|
||||||
Anchor = Anchor.TopLeft;
|
Anchor = Anchor.TopLeft;
|
||||||
|
|
||||||
Size = new Vector2(CATCHER_SIZE);
|
Size = new Vector2(CATCHER_SIZE);
|
||||||
if (difficulty != null)
|
if (difficulty != null)
|
||||||
Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(TextureStore textures)
|
private void load(TextureStore textures)
|
||||||
{
|
{
|
||||||
texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
|
texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
caughtFruit = new Container<DrawableHitObject>
|
caughtFruit = new Container<DrawableHitObject>
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
},
|
},
|
||||||
createCatcherSprite(),
|
createCatcherSprite(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private int currentDirection;
|
private int currentDirection;
|
||||||
|
|
||||||
private bool dashing;
|
private bool dashing;
|
||||||
|
|
||||||
protected bool Dashing
|
protected bool Dashing
|
||||||
{
|
{
|
||||||
get { return dashing; }
|
get { return dashing; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value == dashing) return;
|
if (value == dashing) return;
|
||||||
|
|
||||||
dashing = value;
|
dashing = value;
|
||||||
|
|
||||||
Trail |= dashing;
|
Trail |= dashing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool trail;
|
private bool trail;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
|
/// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool Trail
|
protected bool Trail
|
||||||
{
|
{
|
||||||
get { return trail; }
|
get { return trail; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value == trail) return;
|
if (value == trail) return;
|
||||||
|
|
||||||
trail = value;
|
trail = value;
|
||||||
|
|
||||||
if (Trail)
|
if (Trail)
|
||||||
beginTrail();
|
beginTrail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void beginTrail()
|
private void beginTrail()
|
||||||
{
|
{
|
||||||
Trail &= dashing || HyperDashing;
|
Trail &= dashing || HyperDashing;
|
||||||
Trail &= AdditiveTarget != null;
|
Trail &= AdditiveTarget != null;
|
||||||
|
|
||||||
if (!Trail) return;
|
if (!Trail) return;
|
||||||
|
|
||||||
var additive = createCatcherSprite();
|
var additive = createCatcherSprite();
|
||||||
|
|
||||||
additive.Anchor = Anchor;
|
additive.Anchor = Anchor;
|
||||||
additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly.
|
additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly.
|
||||||
additive.Position = Position;
|
additive.Position = Position;
|
||||||
additive.Scale = Scale;
|
additive.Scale = Scale;
|
||||||
additive.Colour = HyperDashing ? Color4.Red : Color4.White;
|
additive.Colour = HyperDashing ? Color4.Red : Color4.White;
|
||||||
additive.RelativePositionAxes = RelativePositionAxes;
|
additive.RelativePositionAxes = RelativePositionAxes;
|
||||||
additive.Blending = BlendingMode.Additive;
|
additive.Blending = BlendingMode.Additive;
|
||||||
|
|
||||||
AdditiveTarget.Add(additive);
|
AdditiveTarget.Add(additive);
|
||||||
|
|
||||||
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
|
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
|
||||||
|
|
||||||
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
|
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Sprite createCatcherSprite() => new Sprite
|
private Sprite createCatcherSprite() => new Sprite
|
||||||
{
|
{
|
||||||
Size = new Vector2(CATCHER_SIZE),
|
Size = new Vector2(CATCHER_SIZE),
|
||||||
FillMode = FillMode.Fill,
|
FillMode = FillMode.Fill,
|
||||||
Texture = texture,
|
Texture = texture,
|
||||||
OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly.
|
OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly.
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a caught fruit to the catcher's stack.
|
/// Add a caught fruit to the catcher's stack.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fruit">The fruit that was caught.</param>
|
/// <param name="fruit">The fruit that was caught.</param>
|
||||||
public void Add(DrawableHitObject fruit)
|
public void Add(DrawableHitObject fruit)
|
||||||
{
|
{
|
||||||
float ourRadius = fruit.DrawSize.X / 2 * fruit.Scale.X;
|
float ourRadius = fruit.DrawSize.X / 2 * fruit.Scale.X;
|
||||||
float theirRadius = 0;
|
float theirRadius = 0;
|
||||||
|
|
||||||
const float allowance = 6;
|
const float allowance = 6;
|
||||||
|
|
||||||
while (caughtFruit.Any(f =>
|
while (caughtFruit.Any(f =>
|
||||||
f.LifetimeEnd == double.MaxValue &&
|
f.LifetimeEnd == double.MaxValue &&
|
||||||
Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2)))
|
Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2)))
|
||||||
{
|
{
|
||||||
float diff = (ourRadius + theirRadius) / allowance;
|
float diff = (ourRadius + theirRadius) / allowance;
|
||||||
fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff;
|
fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff;
|
||||||
fruit.Y -= RNG.NextSingle() * diff;
|
fruit.Y -= RNG.NextSingle() * diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
fruit.X = MathHelper.Clamp(fruit.X, -CATCHER_SIZE / 2, CATCHER_SIZE / 2);
|
fruit.X = MathHelper.Clamp(fruit.X, -CATCHER_SIZE / 2, CATCHER_SIZE / 2);
|
||||||
|
|
||||||
caughtFruit.Add(fruit);
|
caughtFruit.Add(fruit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Let the catcher attempt to catch a fruit.
|
/// Let the catcher attempt to catch a fruit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fruit">The fruit to catch.</param>
|
/// <param name="fruit">The fruit to catch.</param>
|
||||||
/// <returns>Whether the catch is possible.</returns>
|
/// <returns>Whether the catch is possible.</returns>
|
||||||
public bool AttemptCatch(CatchHitObject fruit)
|
public bool AttemptCatch(CatchHitObject fruit)
|
||||||
{
|
{
|
||||||
double halfCatcherWidth = CATCHER_SIZE * Math.Abs(Scale.X) * 0.5f;
|
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.
|
// this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
|
||||||
var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
|
var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
|
||||||
var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
|
var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
|
||||||
|
|
||||||
var validCatch =
|
var validCatch =
|
||||||
catchObjectPosition >= catcherPosition - halfCatcherWidth &&
|
catchObjectPosition >= catcherPosition - halfCatcherWidth &&
|
||||||
catchObjectPosition <= catcherPosition + halfCatcherWidth;
|
catchObjectPosition <= catcherPosition + halfCatcherWidth;
|
||||||
|
|
||||||
if (validCatch && fruit.HyperDash)
|
if (validCatch && fruit.HyperDash)
|
||||||
{
|
{
|
||||||
HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED;
|
HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED;
|
||||||
HyperDashDirection = fruit.HyperDashTarget.X - fruit.X;
|
HyperDashDirection = fruit.HyperDashTarget.X - fruit.X;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
HyperDashModifier = 1;
|
HyperDashModifier = 1;
|
||||||
|
|
||||||
return validCatch;
|
return validCatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether we are hypderdashing or not.
|
/// Whether we are hypderdashing or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HyperDashing => hyperDashModifier != 1;
|
public bool HyperDashing => hyperDashModifier != 1;
|
||||||
|
|
||||||
private double hyperDashModifier = 1;
|
private double hyperDashModifier = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The direction in which hyperdash is allowed. 0 allows both directions.
|
/// The direction in which hyperdash is allowed. 0 allows both directions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double HyperDashDirection;
|
public double HyperDashDirection;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The speed modifier resultant from hyperdash. Will trigger hyperdash when not equal to 1.
|
/// The speed modifier resultant from hyperdash. Will trigger hyperdash when not equal to 1.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double HyperDashModifier
|
public double HyperDashModifier
|
||||||
{
|
{
|
||||||
get { return hyperDashModifier; }
|
get { return hyperDashModifier; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value == hyperDashModifier) return;
|
if (value == hyperDashModifier) return;
|
||||||
hyperDashModifier = value;
|
hyperDashModifier = value;
|
||||||
|
|
||||||
const float transition_length = 180;
|
const float transition_length = 180;
|
||||||
|
|
||||||
if (HyperDashing)
|
if (HyperDashing)
|
||||||
{
|
{
|
||||||
this.FadeColour(Color4.OrangeRed, transition_length, Easing.OutQuint);
|
this.FadeColour(Color4.OrangeRed, transition_length, Easing.OutQuint);
|
||||||
this.FadeTo(0.2f, transition_length, Easing.OutQuint);
|
this.FadeTo(0.2f, transition_length, Easing.OutQuint);
|
||||||
Trail = true;
|
Trail = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
HyperDashDirection = 0;
|
HyperDashDirection = 0;
|
||||||
this.FadeColour(Color4.White, transition_length, Easing.OutQuint);
|
this.FadeColour(Color4.White, transition_length, Easing.OutQuint);
|
||||||
this.FadeTo(1, transition_length, Easing.OutQuint);
|
this.FadeTo(1, transition_length, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnPressed(CatchAction action)
|
public bool OnPressed(CatchAction action)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case CatchAction.MoveLeft:
|
case CatchAction.MoveLeft:
|
||||||
currentDirection--;
|
currentDirection--;
|
||||||
return true;
|
return true;
|
||||||
case CatchAction.MoveRight:
|
case CatchAction.MoveRight:
|
||||||
currentDirection++;
|
currentDirection++;
|
||||||
return true;
|
return true;
|
||||||
case CatchAction.Dash:
|
case CatchAction.Dash:
|
||||||
Dashing = true;
|
Dashing = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnReleased(CatchAction action)
|
public bool OnReleased(CatchAction action)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case CatchAction.MoveLeft:
|
case CatchAction.MoveLeft:
|
||||||
currentDirection++;
|
currentDirection++;
|
||||||
return true;
|
return true;
|
||||||
case CatchAction.MoveRight:
|
case CatchAction.MoveRight:
|
||||||
currentDirection--;
|
currentDirection--;
|
||||||
return true;
|
return true;
|
||||||
case CatchAction.Dash:
|
case CatchAction.Dash:
|
||||||
Dashing = false;
|
Dashing = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
|
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const double BASE_SPEED = 1.0 / 512;
|
public const double BASE_SPEED = 1.0 / 512;
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
if (currentDirection == 0) return;
|
if (currentDirection == 0) return;
|
||||||
|
|
||||||
var direction = Math.Sign(currentDirection);
|
var direction = Math.Sign(currentDirection);
|
||||||
|
|
||||||
double dashModifier = Dashing ? 1 : 0.5;
|
double dashModifier = Dashing ? 1 : 0.5;
|
||||||
|
|
||||||
if (hyperDashModifier != 1 && (HyperDashDirection == 0 || direction == Math.Sign(HyperDashDirection)))
|
if (hyperDashModifier != 1 && (HyperDashDirection == 0 || direction == Math.Sign(HyperDashDirection)))
|
||||||
dashModifier = hyperDashModifier;
|
dashModifier = hyperDashModifier;
|
||||||
|
|
||||||
Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y);
|
Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y);
|
||||||
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
|
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drop any fruit off the plate.
|
/// Drop any fruit off the plate.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Drop()
|
public void Drop()
|
||||||
{
|
{
|
||||||
var fruit = caughtFruit.ToArray();
|
var fruit = caughtFruit.ToArray();
|
||||||
|
|
||||||
foreach (var f in fruit)
|
foreach (var f in fruit)
|
||||||
{
|
{
|
||||||
if (ExplodingFruitTarget != null)
|
if (ExplodingFruitTarget != null)
|
||||||
{
|
{
|
||||||
f.Anchor = Anchor.TopLeft;
|
f.Anchor = Anchor.TopLeft;
|
||||||
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
||||||
|
|
||||||
caughtFruit.Remove(f);
|
caughtFruit.Remove(f);
|
||||||
|
|
||||||
ExplodingFruitTarget.Add(f);
|
ExplodingFruitTarget.Add(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
f.MoveToY(f.Y + 75, 750, Easing.InSine);
|
f.MoveToY(f.Y + 75, 750, Easing.InSine);
|
||||||
f.FadeOut(750);
|
f.FadeOut(750);
|
||||||
f.Expire();
|
f.Expire();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Explode any fruit off the plate.
|
/// Explode any fruit off the plate.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Explode()
|
public void Explode()
|
||||||
{
|
{
|
||||||
var fruit = caughtFruit.ToArray();
|
var fruit = caughtFruit.ToArray();
|
||||||
|
|
||||||
foreach (var f in fruit)
|
foreach (var f in fruit)
|
||||||
{
|
{
|
||||||
var originalX = f.X * Scale.X;
|
var originalX = f.X * Scale.X;
|
||||||
|
|
||||||
if (ExplodingFruitTarget != null)
|
if (ExplodingFruitTarget != null)
|
||||||
{
|
{
|
||||||
f.Anchor = Anchor.TopLeft;
|
f.Anchor = Anchor.TopLeft;
|
||||||
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
||||||
|
|
||||||
caughtFruit.Remove(f);
|
caughtFruit.Remove(f);
|
||||||
|
|
||||||
ExplodingFruitTarget.Add(f);
|
ExplodingFruitTarget.Add(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
|
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
|
||||||
.Then()
|
.Then()
|
||||||
.MoveToY(f.Y + 50, 500, Easing.InSine);
|
.MoveToY(f.Y + 50, 500, Easing.InSine);
|
||||||
|
|
||||||
f.MoveToX(f.X + originalX * 6, 1000);
|
f.MoveToX(f.X + originalX * 6, 1000);
|
||||||
f.FadeOut(750);
|
f.FadeOut(750);
|
||||||
|
|
||||||
f.Expire();
|
f.Expire();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,60 +1,60 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||||
|
|
||||||
private bool isForCurrentRuleset;
|
private bool isForCurrentRuleset;
|
||||||
|
|
||||||
[NonParallelizable]
|
[NonParallelizable]
|
||||||
[TestCase("basic", false)]
|
[TestCase("basic", false)]
|
||||||
public void Test(string name, bool isForCurrentRuleset)
|
public void Test(string name, bool isForCurrentRuleset)
|
||||||
{
|
{
|
||||||
this.isForCurrentRuleset = isForCurrentRuleset;
|
this.isForCurrentRuleset = isForCurrentRuleset;
|
||||||
base.Test(name);
|
base.Test(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||||
{
|
{
|
||||||
yield return new ConvertValue
|
yield return new ConvertValue
|
||||||
{
|
{
|
||||||
StartTime = hitObject.StartTime,
|
StartTime = hitObject.StartTime,
|
||||||
EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime,
|
EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime,
|
||||||
Column = ((ManiaHitObject)hitObject).Column
|
Column = ((ManiaHitObject)hitObject).Column
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new ManiaBeatmapConverter(isForCurrentRuleset, beatmap);
|
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new ManiaBeatmapConverter(isForCurrentRuleset, beatmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ConvertValue : IEquatable<ConvertValue>
|
public struct ConvertValue : IEquatable<ConvertValue>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A sane value to account for osu!stable using ints everwhere.
|
/// A sane value to account for osu!stable using ints everwhere.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float conversion_lenience = 2;
|
private const float conversion_lenience = 2;
|
||||||
|
|
||||||
public double StartTime;
|
public double StartTime;
|
||||||
public double EndTime;
|
public double EndTime;
|
||||||
public int Column;
|
public int Column;
|
||||||
|
|
||||||
public bool Equals(ConvertValue other)
|
public bool Equals(ConvertValue other)
|
||||||
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
|
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
|
||||||
&& Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience)
|
&& Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience)
|
||||||
&& Column == other.Column;
|
&& Column == other.Column;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,179 +1,179 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Replays;
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseAutoGeneration : OsuTestCase
|
public class TestCaseAutoGeneration : OsuTestCase
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSingleNote()
|
public void TestSingleNote()
|
||||||
{
|
{
|
||||||
// | |
|
// | |
|
||||||
// | - |
|
// | - |
|
||||||
// | |
|
// | |
|
||||||
|
|
||||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
|
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
|
||||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||||
|
|
||||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
|
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSingleHoldNote()
|
public void TestSingleHoldNote()
|
||||||
{
|
{
|
||||||
// | |
|
// | |
|
||||||
// | * |
|
// | * |
|
||||||
// | * |
|
// | * |
|
||||||
// | * |
|
// | * |
|
||||||
// | |
|
// | |
|
||||||
|
|
||||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
|
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 });
|
||||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||||
|
|
||||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Special1), "Special1 has not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
|
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Special1), "Special1 has not been released");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSingleNoteChord()
|
public void TestSingleNoteChord()
|
||||||
{
|
{
|
||||||
// | | |
|
// | | |
|
||||||
// | - | - |
|
// | - | - |
|
||||||
// | | |
|
// | | |
|
||||||
|
|
||||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||||
beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 });
|
beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 });
|
||||||
|
|
||||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestHoldNoteChord()
|
public void TestHoldNoteChord()
|
||||||
{
|
{
|
||||||
// | | |
|
// | | |
|
||||||
// | * | * |
|
// | * | * |
|
||||||
// | * | * |
|
// | * | * |
|
||||||
// | * | * |
|
// | * | * |
|
||||||
// | | |
|
// | | |
|
||||||
|
|
||||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 });
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 });
|
||||||
|
|
||||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been released");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSingleNoteStair()
|
public void TestSingleNoteStair()
|
||||||
{
|
{
|
||||||
// | | |
|
// | | |
|
||||||
// | | - |
|
// | | - |
|
||||||
// | - | |
|
// | - | |
|
||||||
// | | |
|
// | | |
|
||||||
|
|
||||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||||
beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 });
|
beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 });
|
||||||
|
|
||||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
|
Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
|
||||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
||||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time");
|
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time");
|
||||||
Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time");
|
Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time");
|
||||||
Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
|
Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
|
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
|
Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestHoldNoteStair()
|
public void TestHoldNoteStair()
|
||||||
{
|
{
|
||||||
// | | |
|
// | | |
|
||||||
// | | * |
|
// | | * |
|
||||||
// | * | * |
|
// | * | * |
|
||||||
// | * | * |
|
// | * | * |
|
||||||
// | * | |
|
// | * | |
|
||||||
// | | |
|
// | | |
|
||||||
|
|
||||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 });
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 });
|
||||||
|
|
||||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
|
Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
|
||||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
||||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time");
|
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time");
|
||||||
Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time");
|
Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time");
|
||||||
Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
|
Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key1, ManiaAction.Key2), "Key1 & Key2 have not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key1), "Key1 has not been released");
|
Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key1), "Key1 has not been released");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has been released");
|
Assert.IsTrue(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has been released");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
|
Assert.IsFalse(checkContains(generated.Frames[4], ManiaAction.Key2), "Key2 has not been released");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestHoldNoteWithReleasePress()
|
public void TestHoldNoteWithReleasePress()
|
||||||
{
|
{
|
||||||
// | | |
|
// | | |
|
||||||
// | * | - |
|
// | * | - |
|
||||||
// | * | |
|
// | * | |
|
||||||
// | * | |
|
// | * | |
|
||||||
// | | |
|
// | | |
|
||||||
|
|
||||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 2 });
|
||||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY });
|
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY });
|
||||||
beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 });
|
beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 });
|
||||||
|
|
||||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||||
|
|
||||||
Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames");
|
Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames");
|
||||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
||||||
Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time");
|
Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time");
|
||||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time");
|
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[1], ManiaAction.Key1), "Key1 has not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
|
Assert.IsFalse(checkContains(generated.Frames[2], ManiaAction.Key1), "Key1 has not been released");
|
||||||
Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key2), "Key2 has not been pressed");
|
Assert.IsTrue(checkContains(generated.Frames[2], ManiaAction.Key2), "Key2 has not been pressed");
|
||||||
Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been released");
|
Assert.IsFalse(checkContains(generated.Frames[3], ManiaAction.Key2), "Key2 has not been released");
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool checkContains(ReplayFrame frame, params ManiaAction[] actions) => actions.All(action => ((ManiaReplayFrame)frame).Actions.Contains(action));
|
private bool checkContains(ReplayFrame frame, params ManiaAction[] actions) => actions.All(action => ((ManiaReplayFrame)frame).Actions.Contains(action));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,96 +1,96 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseManiaHitObjects : OsuTestCase
|
public class TestCaseManiaHitObjects : OsuTestCase
|
||||||
{
|
{
|
||||||
public TestCaseManiaHitObjects()
|
public TestCaseManiaHitObjects()
|
||||||
{
|
{
|
||||||
Add(new FillFlowContainer
|
Add(new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(10, 0),
|
Spacing = new Vector2(10, 0),
|
||||||
// Imagine that the containers containing the drawable notes are the "columns"
|
// Imagine that the containers containing the drawable notes are the "columns"
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Normal note column",
|
Name = "Normal note column",
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Width = 50,
|
Width = 50,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Timing section",
|
Name = "Timing section",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
RelativeChildSize = new Vector2(1, 10000),
|
RelativeChildSize = new Vector2(1, 10000),
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new DrawableNote(new Note(), ManiaAction.Key1)
|
new DrawableNote(new Note(), ManiaAction.Key1)
|
||||||
{
|
{
|
||||||
Y = 5000,
|
Y = 5000,
|
||||||
LifetimeStart = double.MinValue,
|
LifetimeStart = double.MinValue,
|
||||||
LifetimeEnd = double.MaxValue,
|
LifetimeEnd = double.MaxValue,
|
||||||
AccentColour = Color4.Red
|
AccentColour = Color4.Red
|
||||||
},
|
},
|
||||||
new DrawableNote(new Note(), ManiaAction.Key1)
|
new DrawableNote(new Note(), ManiaAction.Key1)
|
||||||
{
|
{
|
||||||
Y = 6000,
|
Y = 6000,
|
||||||
LifetimeStart = double.MinValue,
|
LifetimeStart = double.MinValue,
|
||||||
LifetimeEnd = double.MaxValue,
|
LifetimeEnd = double.MaxValue,
|
||||||
AccentColour = Color4.Red
|
AccentColour = Color4.Red
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Hold note column",
|
Name = "Hold note column",
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Width = 50,
|
Width = 50,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Timing section",
|
Name = "Timing section",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
RelativeChildSize = new Vector2(1, 10000),
|
RelativeChildSize = new Vector2(1, 10000),
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
new DrawableHoldNote(new HoldNote { Duration = 1000 } , ManiaAction.Key1)
|
new DrawableHoldNote(new HoldNote { Duration = 1000 } , ManiaAction.Key1)
|
||||||
{
|
{
|
||||||
Y = 5000,
|
Y = 5000,
|
||||||
Height = 1000,
|
Height = 1000,
|
||||||
LifetimeStart = double.MinValue,
|
LifetimeStart = double.MinValue,
|
||||||
LifetimeEnd = double.MaxValue,
|
LifetimeEnd = double.MaxValue,
|
||||||
AccentColour = Color4.Red
|
AccentColour = Color4.Red
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,193 +1,193 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Configuration;
|
using osu.Game.Rulesets.Mania.Configuration;
|
||||||
using osu.Game.Rulesets.Mania.Judgements;
|
using osu.Game.Rulesets.Mania.Judgements;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCaseManiaPlayfield : OsuTestCase
|
public class TestCaseManiaPlayfield : OsuTestCase
|
||||||
{
|
{
|
||||||
private const double start_time = 500;
|
private const double start_time = 500;
|
||||||
private const double duration = 500;
|
private const double duration = 500;
|
||||||
|
|
||||||
protected override double TimePerAction => 200;
|
protected override double TimePerAction => 200;
|
||||||
|
|
||||||
private RulesetInfo maniaRuleset;
|
private RulesetInfo maniaRuleset;
|
||||||
|
|
||||||
public TestCaseManiaPlayfield()
|
public TestCaseManiaPlayfield()
|
||||||
{
|
{
|
||||||
var rng = new Random(1337);
|
var rng = new Random(1337);
|
||||||
|
|
||||||
AddStep("1 column", () => createPlayfield(1));
|
AddStep("1 column", () => createPlayfield(1));
|
||||||
AddStep("4 columns", () => createPlayfield(4));
|
AddStep("4 columns", () => createPlayfield(4));
|
||||||
AddStep("5 columns", () => createPlayfield(5));
|
AddStep("5 columns", () => createPlayfield(5));
|
||||||
AddStep("8 columns", () => createPlayfield(8));
|
AddStep("8 columns", () => createPlayfield(8));
|
||||||
AddStep("4 + 4 columns", () =>
|
AddStep("4 + 4 columns", () =>
|
||||||
{
|
{
|
||||||
var stages = new List<StageDefinition>
|
var stages = new List<StageDefinition>
|
||||||
{
|
{
|
||||||
new StageDefinition { Columns = 4 },
|
new StageDefinition { Columns = 4 },
|
||||||
new StageDefinition { Columns = 4 },
|
new StageDefinition { Columns = 4 },
|
||||||
};
|
};
|
||||||
createPlayfield(stages);
|
createPlayfield(stages);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("2 + 4 + 2 columns", () =>
|
AddStep("2 + 4 + 2 columns", () =>
|
||||||
{
|
{
|
||||||
var stages = new List<StageDefinition>
|
var stages = new List<StageDefinition>
|
||||||
{
|
{
|
||||||
new StageDefinition { Columns = 2 },
|
new StageDefinition { Columns = 2 },
|
||||||
new StageDefinition { Columns = 4 },
|
new StageDefinition { Columns = 4 },
|
||||||
new StageDefinition { Columns = 2 },
|
new StageDefinition { Columns = 2 },
|
||||||
};
|
};
|
||||||
createPlayfield(stages);
|
createPlayfield(stages);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("1 + 8 + 1 columns", () =>
|
AddStep("1 + 8 + 1 columns", () =>
|
||||||
{
|
{
|
||||||
var stages = new List<StageDefinition>
|
var stages = new List<StageDefinition>
|
||||||
{
|
{
|
||||||
new StageDefinition { Columns = 1 },
|
new StageDefinition { Columns = 1 },
|
||||||
new StageDefinition { Columns = 8 },
|
new StageDefinition { Columns = 8 },
|
||||||
new StageDefinition { Columns = 1 },
|
new StageDefinition { Columns = 1 },
|
||||||
};
|
};
|
||||||
createPlayfield(stages);
|
createPlayfield(stages);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("Reversed", () => createPlayfield(4, true));
|
AddStep("Reversed", () => createPlayfield(4, true));
|
||||||
|
|
||||||
AddStep("Notes with input", () => createPlayfieldWithNotes());
|
AddStep("Notes with input", () => createPlayfieldWithNotes());
|
||||||
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true));
|
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true));
|
||||||
AddStep("Notes with gravity", () => createPlayfieldWithNotes());
|
AddStep("Notes with gravity", () => createPlayfieldWithNotes());
|
||||||
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true));
|
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true));
|
||||||
|
|
||||||
AddStep("Hit explosion", () =>
|
AddStep("Hit explosion", () =>
|
||||||
{
|
{
|
||||||
var playfield = createPlayfield(4);
|
var playfield = createPlayfield(4);
|
||||||
|
|
||||||
int col = rng.Next(0, 4);
|
int col = rng.Next(0, 4);
|
||||||
|
|
||||||
var note = new DrawableNote(new Note { Column = col }, ManiaAction.Key1)
|
var note = new DrawableNote(new Note { Column = col }, ManiaAction.Key1)
|
||||||
{
|
{
|
||||||
AccentColour = playfield.Columns.ElementAt(col).AccentColour
|
AccentColour = playfield.Columns.ElementAt(col).AccentColour
|
||||||
};
|
};
|
||||||
|
|
||||||
playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
|
playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
|
||||||
playfield.Columns[col].OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
|
playfield.Columns[col].OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private DependencyContainer dependencies;
|
private DependencyContainer dependencies;
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||||
=> dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
=> dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(RulesetStore rulesets, SettingsStore settings)
|
private void load(RulesetStore rulesets, SettingsStore settings)
|
||||||
{
|
{
|
||||||
maniaRuleset = rulesets.GetRuleset(3);
|
maniaRuleset = rulesets.GetRuleset(3);
|
||||||
|
|
||||||
dependencies.Cache(new ManiaConfigManager(settings, maniaRuleset, 4));
|
dependencies.Cache(new ManiaConfigManager(settings, maniaRuleset, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ManiaPlayfield createPlayfield(int cols, bool inverted = false)
|
private ManiaPlayfield createPlayfield(int cols, bool inverted = false)
|
||||||
{
|
{
|
||||||
var stages = new List<StageDefinition>
|
var stages = new List<StageDefinition>
|
||||||
{
|
{
|
||||||
new StageDefinition { Columns = cols },
|
new StageDefinition { Columns = cols },
|
||||||
};
|
};
|
||||||
|
|
||||||
return createPlayfield(stages, inverted);
|
return createPlayfield(stages, inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ManiaPlayfield createPlayfield(List<StageDefinition> stages, bool inverted = false)
|
private ManiaPlayfield createPlayfield(List<StageDefinition> stages, bool inverted = false)
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
|
|
||||||
var inputManager = new ManiaInputManager(maniaRuleset, stages.Sum(g => g.Columns)) { RelativeSizeAxes = Axes.Both };
|
var inputManager = new ManiaInputManager(maniaRuleset, stages.Sum(g => g.Columns)) { RelativeSizeAxes = Axes.Both };
|
||||||
Add(inputManager);
|
Add(inputManager);
|
||||||
|
|
||||||
ManiaPlayfield playfield;
|
ManiaPlayfield playfield;
|
||||||
|
|
||||||
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
});
|
});
|
||||||
|
|
||||||
playfield.Inverted.Value = inverted;
|
playfield.Inverted.Value = inverted;
|
||||||
|
|
||||||
return playfield;
|
return playfield;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createPlayfieldWithNotes(bool inverted = false)
|
private void createPlayfieldWithNotes(bool inverted = false)
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
|
|
||||||
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
|
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
|
||||||
|
|
||||||
var inputManager = new ManiaInputManager(maniaRuleset, 4) { RelativeSizeAxes = Axes.Both };
|
var inputManager = new ManiaInputManager(maniaRuleset, 4) { RelativeSizeAxes = Axes.Both };
|
||||||
Add(inputManager);
|
Add(inputManager);
|
||||||
|
|
||||||
ManiaPlayfield playfield;
|
ManiaPlayfield playfield;
|
||||||
var stages = new List<StageDefinition>
|
var stages = new List<StageDefinition>
|
||||||
{
|
{
|
||||||
new StageDefinition { Columns = 4 },
|
new StageDefinition { Columns = 4 },
|
||||||
};
|
};
|
||||||
|
|
||||||
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Clock = new FramedClock(rateAdjustClock)
|
Clock = new FramedClock(rateAdjustClock)
|
||||||
});
|
});
|
||||||
|
|
||||||
playfield.Inverted.Value = inverted;
|
playfield.Inverted.Value = inverted;
|
||||||
|
|
||||||
for (double t = start_time; t <= start_time + duration; t += 100)
|
for (double t = start_time; t <= start_time + duration; t += 100)
|
||||||
{
|
{
|
||||||
playfield.Add(new DrawableNote(new Note
|
playfield.Add(new DrawableNote(new Note
|
||||||
{
|
{
|
||||||
StartTime = t,
|
StartTime = t,
|
||||||
Column = 0
|
Column = 0
|
||||||
}, ManiaAction.Key1));
|
}, ManiaAction.Key1));
|
||||||
|
|
||||||
playfield.Add(new DrawableNote(new Note
|
playfield.Add(new DrawableNote(new Note
|
||||||
{
|
{
|
||||||
StartTime = t,
|
StartTime = t,
|
||||||
Column = 3
|
Column = 3
|
||||||
}, ManiaAction.Key4));
|
}, ManiaAction.Key4));
|
||||||
}
|
}
|
||||||
|
|
||||||
playfield.Add(new DrawableHoldNote(new HoldNote
|
playfield.Add(new DrawableHoldNote(new HoldNote
|
||||||
{
|
{
|
||||||
StartTime = start_time,
|
StartTime = start_time,
|
||||||
Duration = duration,
|
Duration = duration,
|
||||||
Column = 1
|
Column = 1
|
||||||
}, ManiaAction.Key2));
|
}, ManiaAction.Key2));
|
||||||
|
|
||||||
playfield.Add(new DrawableHoldNote(new HoldNote
|
playfield.Add(new DrawableHoldNote(new HoldNote
|
||||||
{
|
{
|
||||||
StartTime = start_time,
|
StartTime = start_time,
|
||||||
Duration = duration,
|
Duration = duration,
|
||||||
Column = 2
|
Column = 2
|
||||||
}, ManiaAction.Key3));
|
}, ManiaAction.Key3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||||
{
|
{
|
||||||
public TestCasePerformancePoints()
|
public TestCasePerformancePoints()
|
||||||
: base(new ManiaRuleset())
|
: base(new ManiaRuleset())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Project References">
|
<ItemGroup Label="Project References">
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -1,33 +1,33 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps
|
namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||||
{
|
{
|
||||||
public class ManiaBeatmap : Beatmap<ManiaHitObject>
|
public class ManiaBeatmap : Beatmap<ManiaHitObject>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The definitions for each stage in a <see cref="ManiaPlayfield"/>.
|
/// The definitions for each stage in a <see cref="ManiaPlayfield"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<StageDefinition> Stages = new List<StageDefinition>();
|
public List<StageDefinition> Stages = new List<StageDefinition>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Total number of columns represented by all stages in this <see cref="ManiaBeatmap"/>.
|
/// Total number of columns represented by all stages in this <see cref="ManiaBeatmap"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int TotalColumns => Stages.Sum(g => g.Columns);
|
public int TotalColumns => Stages.Sum(g => g.Columns);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="ManiaBeatmap"/>.
|
/// Creates a new <see cref="ManiaBeatmap"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="defaultStage">The initial stages.</param>
|
/// <param name="defaultStage">The initial stages.</param>
|
||||||
public ManiaBeatmap(StageDefinition defaultStage)
|
public ManiaBeatmap(StageDefinition defaultStage)
|
||||||
{
|
{
|
||||||
Stages.Add(defaultStage);
|
Stages.Add(defaultStage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,225 +1,225 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
|
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
|
||||||
using osu.Game.Rulesets.Mania.MathUtils;
|
using osu.Game.Rulesets.Mania.MathUtils;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps
|
namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||||
{
|
{
|
||||||
public class ManiaBeatmapConverter : BeatmapConverter<ManiaHitObject>
|
public class ManiaBeatmapConverter : BeatmapConverter<ManiaHitObject>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum number of previous notes to consider for density calculation.
|
/// Maximum number of previous notes to consider for density calculation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int max_notes_for_density = 7;
|
private const int max_notes_for_density = 7;
|
||||||
|
|
||||||
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
|
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
|
||||||
|
|
||||||
public int TargetColumns;
|
public int TargetColumns;
|
||||||
public readonly bool IsForCurrentRuleset;
|
public readonly bool IsForCurrentRuleset;
|
||||||
|
|
||||||
private Pattern lastPattern = new Pattern();
|
private Pattern lastPattern = new Pattern();
|
||||||
private FastRandom random;
|
private FastRandom random;
|
||||||
|
|
||||||
private ManiaBeatmap beatmap;
|
private ManiaBeatmap beatmap;
|
||||||
|
|
||||||
public ManiaBeatmapConverter(bool isForCurrentRuleset, Beatmap original)
|
public ManiaBeatmapConverter(bool isForCurrentRuleset, Beatmap original)
|
||||||
{
|
{
|
||||||
IsForCurrentRuleset = isForCurrentRuleset;
|
IsForCurrentRuleset = isForCurrentRuleset;
|
||||||
|
|
||||||
var roundedCircleSize = Math.Round(original.BeatmapInfo.BaseDifficulty.CircleSize);
|
var roundedCircleSize = Math.Round(original.BeatmapInfo.BaseDifficulty.CircleSize);
|
||||||
var roundedOverallDifficulty = Math.Round(original.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
var roundedOverallDifficulty = Math.Round(original.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
||||||
|
|
||||||
if (isForCurrentRuleset)
|
if (isForCurrentRuleset)
|
||||||
TargetColumns = (int)Math.Max(1, roundedCircleSize);
|
TargetColumns = (int)Math.Max(1, roundedCircleSize);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
float percentSliderOrSpinner = (float)original.HitObjects.Count(h => h is IHasEndTime) / original.HitObjects.Count;
|
float percentSliderOrSpinner = (float)original.HitObjects.Count(h => h is IHasEndTime) / original.HitObjects.Count;
|
||||||
if (percentSliderOrSpinner < 0.2)
|
if (percentSliderOrSpinner < 0.2)
|
||||||
TargetColumns = 7;
|
TargetColumns = 7;
|
||||||
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
|
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
|
||||||
TargetColumns = roundedOverallDifficulty > 5 ? 7 : 6;
|
TargetColumns = roundedOverallDifficulty > 5 ? 7 : 6;
|
||||||
else if (percentSliderOrSpinner > 0.6)
|
else if (percentSliderOrSpinner > 0.6)
|
||||||
TargetColumns = roundedOverallDifficulty > 4 ? 5 : 4;
|
TargetColumns = roundedOverallDifficulty > 4 ? 5 : 4;
|
||||||
else
|
else
|
||||||
TargetColumns = Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7));
|
TargetColumns = Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original)
|
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original)
|
||||||
{
|
{
|
||||||
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
|
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
|
||||||
|
|
||||||
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
|
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
|
||||||
random = new FastRandom(seed);
|
random = new FastRandom(seed);
|
||||||
|
|
||||||
return base.ConvertBeatmap(original);
|
return base.ConvertBeatmap(original);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Beatmap<ManiaHitObject> CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns });
|
protected override Beatmap<ManiaHitObject> CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns });
|
||||||
|
|
||||||
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
|
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
|
||||||
{
|
{
|
||||||
var maniaOriginal = original as ManiaHitObject;
|
var maniaOriginal = original as ManiaHitObject;
|
||||||
if (maniaOriginal != null)
|
if (maniaOriginal != null)
|
||||||
{
|
{
|
||||||
yield return maniaOriginal;
|
yield return maniaOriginal;
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap);
|
var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap);
|
||||||
|
|
||||||
if (objects == null)
|
if (objects == null)
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
foreach (ManiaHitObject obj in objects)
|
foreach (ManiaHitObject obj in objects)
|
||||||
yield return obj;
|
yield return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<double> prevNoteTimes = new List<double>(max_notes_for_density);
|
private readonly List<double> prevNoteTimes = new List<double>(max_notes_for_density);
|
||||||
private double density = int.MaxValue;
|
private double density = int.MaxValue;
|
||||||
private void computeDensity(double newNoteTime)
|
private void computeDensity(double newNoteTime)
|
||||||
{
|
{
|
||||||
if (prevNoteTimes.Count == max_notes_for_density)
|
if (prevNoteTimes.Count == max_notes_for_density)
|
||||||
prevNoteTimes.RemoveAt(0);
|
prevNoteTimes.RemoveAt(0);
|
||||||
prevNoteTimes.Add(newNoteTime);
|
prevNoteTimes.Add(newNoteTime);
|
||||||
|
|
||||||
density = (prevNoteTimes[prevNoteTimes.Count - 1] - prevNoteTimes[0]) / prevNoteTimes.Count;
|
density = (prevNoteTimes[prevNoteTimes.Count - 1] - prevNoteTimes[0]) / prevNoteTimes.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double lastTime;
|
private double lastTime;
|
||||||
private Vector2 lastPosition;
|
private Vector2 lastPosition;
|
||||||
private PatternType lastStair;
|
private PatternType lastStair;
|
||||||
private void recordNote(double time, Vector2 position)
|
private void recordNote(double time, Vector2 position)
|
||||||
{
|
{
|
||||||
lastTime = time;
|
lastTime = time;
|
||||||
lastPosition = position;
|
lastPosition = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method that generates hit objects for osu!mania specific beatmaps.
|
/// Method that generates hit objects for osu!mania specific beatmaps.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="original">The original hit object.</param>
|
/// <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>
|
/// <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>
|
/// <returns>The hit objects generated.</returns>
|
||||||
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, Beatmap originalBeatmap)
|
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, Beatmap originalBeatmap)
|
||||||
{
|
{
|
||||||
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
|
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
|
||||||
|
|
||||||
Pattern newPattern = generator.Generate();
|
Pattern newPattern = generator.Generate();
|
||||||
lastPattern = newPattern;
|
lastPattern = newPattern;
|
||||||
|
|
||||||
return newPattern.HitObjects;
|
return newPattern.HitObjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method that generates hit objects for non-osu!mania beatmaps.
|
/// Method that generates hit objects for non-osu!mania beatmaps.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="original">The original hit object.</param>
|
/// <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>
|
/// <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>
|
/// <returns>The hit objects generated.</returns>
|
||||||
private IEnumerable<ManiaHitObject> generateConverted(HitObject original, Beatmap originalBeatmap)
|
private IEnumerable<ManiaHitObject> generateConverted(HitObject original, Beatmap originalBeatmap)
|
||||||
{
|
{
|
||||||
var endTimeData = original as IHasEndTime;
|
var endTimeData = original as IHasEndTime;
|
||||||
var distanceData = original as IHasDistance;
|
var distanceData = original as IHasDistance;
|
||||||
var positionData = original as IHasPosition;
|
var positionData = original as IHasPosition;
|
||||||
|
|
||||||
Patterns.PatternGenerator conversion = null;
|
Patterns.PatternGenerator conversion = null;
|
||||||
|
|
||||||
if (distanceData != null)
|
if (distanceData != null)
|
||||||
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
|
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
|
||||||
else if (endTimeData != null)
|
else if (endTimeData != null)
|
||||||
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap);
|
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap);
|
||||||
else if (positionData != null)
|
else if (positionData != null)
|
||||||
{
|
{
|
||||||
computeDensity(original.StartTime);
|
computeDensity(original.StartTime);
|
||||||
|
|
||||||
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
|
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
|
||||||
|
|
||||||
recordNote(original.StartTime, positionData.Position);
|
recordNote(original.StartTime, positionData.Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversion == null)
|
if (conversion == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
Pattern newPattern = conversion.Generate();
|
Pattern newPattern = conversion.Generate();
|
||||||
|
|
||||||
lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
|
lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
|
||||||
lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
|
lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
|
||||||
|
|
||||||
return newPattern.HitObjects;
|
return newPattern.HitObjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A pattern generator for osu!mania-specific beatmaps.
|
/// A pattern generator for osu!mania-specific beatmaps.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
|
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
|
||||||
{
|
{
|
||||||
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
|
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
|
||||||
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
|
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Pattern Generate()
|
public override Pattern Generate()
|
||||||
{
|
{
|
||||||
var endTimeData = HitObject as IHasEndTime;
|
var endTimeData = HitObject as IHasEndTime;
|
||||||
var positionData = HitObject as IHasXPosition;
|
var positionData = HitObject as IHasXPosition;
|
||||||
|
|
||||||
int column = GetColumn(positionData?.X ?? 0);
|
int column = GetColumn(positionData?.X ?? 0);
|
||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
if (endTimeData != null)
|
if (endTimeData != null)
|
||||||
{
|
{
|
||||||
pattern.Add(new HoldNote
|
pattern.Add(new HoldNote
|
||||||
{
|
{
|
||||||
StartTime = HitObject.StartTime,
|
StartTime = HitObject.StartTime,
|
||||||
Duration = endTimeData.Duration,
|
Duration = endTimeData.Duration,
|
||||||
Column = column,
|
Column = column,
|
||||||
Head = { Samples = sampleInfoListAt(HitObject.StartTime) },
|
Head = { Samples = sampleInfoListAt(HitObject.StartTime) },
|
||||||
Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) },
|
Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (positionData != null)
|
else if (positionData != null)
|
||||||
{
|
{
|
||||||
pattern.Add(new Note
|
pattern.Add(new Note
|
||||||
{
|
{
|
||||||
StartTime = HitObject.StartTime,
|
StartTime = HitObject.StartTime,
|
||||||
Samples = HitObject.Samples,
|
Samples = HitObject.Samples,
|
||||||
Column = column
|
Column = column
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the sample info list at a point in time.
|
/// Retrieves the sample info list at a point in time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The time to retrieve the sample info list from.</param>
|
/// <param name="time">The time to retrieve the sample info list from.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private List<SampleInfo> sampleInfoListAt(double time)
|
private List<SampleInfo> sampleInfoListAt(double time)
|
||||||
{
|
{
|
||||||
var curveData = HitObject as IHasCurve;
|
var curveData = HitObject as IHasCurve;
|
||||||
|
|
||||||
if (curveData == null)
|
if (curveData == null)
|
||||||
return HitObject.Samples;
|
return HitObject.Samples;
|
||||||
|
|
||||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
|
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
|
||||||
|
|
||||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||||
return curveData.RepeatSamples[index];
|
return curveData.RepeatSamples[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,491 +1,491 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.MathUtils;
|
using osu.Game.Rulesets.Mania.MathUtils;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A pattern generator for IHasDistance hit objects.
|
/// A pattern generator for IHasDistance hit objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class DistanceObjectPatternGenerator : PatternGenerator
|
internal class DistanceObjectPatternGenerator : PatternGenerator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base osu! slider scoring distance.
|
/// Base osu! slider scoring distance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float osu_base_scoring_distance = 100;
|
private const float osu_base_scoring_distance = 100;
|
||||||
|
|
||||||
private readonly double endTime;
|
private readonly double endTime;
|
||||||
private readonly double segmentDuration;
|
private readonly double segmentDuration;
|
||||||
private readonly int spanCount;
|
private readonly int spanCount;
|
||||||
|
|
||||||
private PatternType convertType;
|
private PatternType convertType;
|
||||||
|
|
||||||
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
|
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
|
||||||
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
|
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
|
||||||
{
|
{
|
||||||
convertType = PatternType.None;
|
convertType = PatternType.None;
|
||||||
if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
|
if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
|
||||||
convertType = PatternType.LowProbability;
|
convertType = PatternType.LowProbability;
|
||||||
|
|
||||||
var distanceData = hitObject as IHasDistance;
|
var distanceData = hitObject as IHasDistance;
|
||||||
var repeatsData = hitObject as IHasRepeats;
|
var repeatsData = hitObject as IHasRepeats;
|
||||||
|
|
||||||
spanCount = repeatsData?.SpanCount() ?? 1;
|
spanCount = repeatsData?.SpanCount() ?? 1;
|
||||||
|
|
||||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
||||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime);
|
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime);
|
||||||
|
|
||||||
// The true distance, accounting for any repeats
|
// The true distance, accounting for any repeats
|
||||||
double distance = (distanceData?.Distance ?? 0) * spanCount;
|
double distance = (distanceData?.Distance ?? 0) * spanCount;
|
||||||
// The velocity of the osu! hit object - calculated as the velocity of a slider
|
// 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;
|
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
|
||||||
// The duration of the osu! hit object
|
// The duration of the osu! hit object
|
||||||
double osuDuration = distance / osuVelocity;
|
double osuDuration = distance / osuVelocity;
|
||||||
|
|
||||||
endTime = hitObject.StartTime + osuDuration;
|
endTime = hitObject.StartTime + osuDuration;
|
||||||
segmentDuration = (endTime - HitObject.StartTime) / spanCount;
|
segmentDuration = (endTime - HitObject.StartTime) / spanCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Pattern Generate()
|
public override Pattern Generate()
|
||||||
{
|
{
|
||||||
if (spanCount > 1)
|
if (spanCount > 1)
|
||||||
{
|
{
|
||||||
if (segmentDuration <= 90)
|
if (segmentDuration <= 90)
|
||||||
return generateRandomHoldNotes(HitObject.StartTime, 1);
|
return generateRandomHoldNotes(HitObject.StartTime, 1);
|
||||||
|
|
||||||
if (segmentDuration <= 120)
|
if (segmentDuration <= 120)
|
||||||
{
|
{
|
||||||
convertType |= PatternType.ForceNotStack;
|
convertType |= PatternType.ForceNotStack;
|
||||||
return generateRandomNotes(HitObject.StartTime, spanCount + 1);
|
return generateRandomNotes(HitObject.StartTime, spanCount + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segmentDuration <= 160)
|
if (segmentDuration <= 160)
|
||||||
return generateStair(HitObject.StartTime);
|
return generateStair(HitObject.StartTime);
|
||||||
|
|
||||||
if (segmentDuration <= 200 && ConversionDifficulty > 3)
|
if (segmentDuration <= 200 && ConversionDifficulty > 3)
|
||||||
return generateRandomMultipleNotes(HitObject.StartTime);
|
return generateRandomMultipleNotes(HitObject.StartTime);
|
||||||
|
|
||||||
double duration = endTime - HitObject.StartTime;
|
double duration = endTime - HitObject.StartTime;
|
||||||
if (duration >= 4000)
|
if (duration >= 4000)
|
||||||
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
|
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
|
||||||
|
|
||||||
if (segmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
|
if (segmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
|
||||||
return generateTiledHoldNotes(HitObject.StartTime);
|
return generateTiledHoldNotes(HitObject.StartTime);
|
||||||
|
|
||||||
return generateHoldAndNormalNotes(HitObject.StartTime);
|
return generateHoldAndNormalNotes(HitObject.StartTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segmentDuration <= 110)
|
if (segmentDuration <= 110)
|
||||||
{
|
{
|
||||||
if (PreviousPattern.ColumnWithObjects < TotalColumns)
|
if (PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||||
convertType |= PatternType.ForceNotStack;
|
convertType |= PatternType.ForceNotStack;
|
||||||
else
|
else
|
||||||
convertType &= ~PatternType.ForceNotStack;
|
convertType &= ~PatternType.ForceNotStack;
|
||||||
return generateRandomNotes(HitObject.StartTime, segmentDuration < 80 ? 1 : 2);
|
return generateRandomNotes(HitObject.StartTime, segmentDuration < 80 ? 1 : 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 6.5)
|
||||||
{
|
{
|
||||||
if ((convertType & PatternType.LowProbability) > 0)
|
if ((convertType & PatternType.LowProbability) > 0)
|
||||||
return generateNRandomNotes(HitObject.StartTime, 0.78, 0.3, 0);
|
return generateNRandomNotes(HitObject.StartTime, 0.78, 0.3, 0);
|
||||||
return generateNRandomNotes(HitObject.StartTime, 0.85, 0.36, 0.03);
|
return generateNRandomNotes(HitObject.StartTime, 0.85, 0.36, 0.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 4)
|
if (ConversionDifficulty > 4)
|
||||||
{
|
{
|
||||||
if ((convertType & PatternType.LowProbability) > 0)
|
if ((convertType & PatternType.LowProbability) > 0)
|
||||||
return generateNRandomNotes(HitObject.StartTime, 0.43, 0.08, 0);
|
return generateNRandomNotes(HitObject.StartTime, 0.43, 0.08, 0);
|
||||||
return generateNRandomNotes(HitObject.StartTime, 0.56, 0.18, 0);
|
return generateNRandomNotes(HitObject.StartTime, 0.56, 0.18, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 2.5)
|
if (ConversionDifficulty > 2.5)
|
||||||
{
|
{
|
||||||
if ((convertType & PatternType.LowProbability) > 0)
|
if ((convertType & PatternType.LowProbability) > 0)
|
||||||
return generateNRandomNotes(HitObject.StartTime, 0.3, 0, 0);
|
return generateNRandomNotes(HitObject.StartTime, 0.3, 0, 0);
|
||||||
return generateNRandomNotes(HitObject.StartTime, 0.37, 0.08, 0);
|
return generateNRandomNotes(HitObject.StartTime, 0.37, 0.08, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((convertType & PatternType.LowProbability) > 0)
|
if ((convertType & PatternType.LowProbability) > 0)
|
||||||
return generateNRandomNotes(HitObject.StartTime, 0.17, 0, 0);
|
return generateNRandomNotes(HitObject.StartTime, 0.17, 0, 0);
|
||||||
return generateNRandomNotes(HitObject.StartTime, 0.27, 0, 0);
|
return generateNRandomNotes(HitObject.StartTime, 0.27, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates random hold notes that start at an span the same amount of rows.
|
/// Generates random hold notes that start at an span the same amount of rows.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="startTime">Start time of each hold note.</param>
|
/// <param name="startTime">Start time of each hold note.</param>
|
||||||
/// <param name="noteCount">Number of hold notes.</param>
|
/// <param name="noteCount">Number of hold notes.</param>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
private Pattern generateRandomHoldNotes(double startTime, int noteCount)
|
private Pattern generateRandomHoldNotes(double startTime, int noteCount)
|
||||||
{
|
{
|
||||||
// - - - -
|
// - - - -
|
||||||
// ■ - ■ ■
|
// ■ - ■ ■
|
||||||
// □ - □ □
|
// □ - □ □
|
||||||
// ■ - ■ ■
|
// ■ - ■ ■
|
||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects;
|
int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects;
|
||||||
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
|
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
|
||||||
{
|
{
|
||||||
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
|
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
addToPattern(pattern, nextColumn, startTime, endTime);
|
addToPattern(pattern, nextColumn, startTime, endTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is can't be combined with the above loop due to RNG
|
// This is can't be combined with the above loop due to RNG
|
||||||
for (int i = 0; i < noteCount - usableColumns; i++)
|
for (int i = 0; i < noteCount - usableColumns; i++)
|
||||||
{
|
{
|
||||||
while (pattern.ColumnHasObject(nextColumn))
|
while (pattern.ColumnHasObject(nextColumn))
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
addToPattern(pattern, nextColumn, startTime, endTime);
|
addToPattern(pattern, nextColumn, startTime, endTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates random notes, with one note per row and no stacking.
|
/// Generates random notes, with one note per row and no stacking.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="startTime">The start time.</param>
|
/// <param name="startTime">The start time.</param>
|
||||||
/// <param name="noteCount">The number of notes.</param>
|
/// <param name="noteCount">The number of notes.</param>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
private Pattern generateRandomNotes(double startTime, int noteCount)
|
private Pattern generateRandomNotes(double startTime, int noteCount)
|
||||||
{
|
{
|
||||||
// - - - -
|
// - - - -
|
||||||
// x - - -
|
// x - - -
|
||||||
// - - x -
|
// - - x -
|
||||||
// - - - x
|
// - - - x
|
||||||
// x - - -
|
// x - - -
|
||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||||
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||||
{
|
{
|
||||||
while (PreviousPattern.ColumnHasObject(nextColumn))
|
while (PreviousPattern.ColumnHasObject(nextColumn))
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
int lastColumn = nextColumn;
|
int lastColumn = nextColumn;
|
||||||
for (int i = 0; i < noteCount; i++)
|
for (int i = 0; i < noteCount; i++)
|
||||||
{
|
{
|
||||||
addToPattern(pattern, nextColumn, startTime, startTime);
|
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||||
while (nextColumn == lastColumn)
|
while (nextColumn == lastColumn)
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
|
|
||||||
lastColumn = nextColumn;
|
lastColumn = nextColumn;
|
||||||
startTime += segmentDuration;
|
startTime += segmentDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a stair of notes, with one note per row.
|
/// Generates a stair of notes, with one note per row.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="startTime">The start time.</param>
|
/// <param name="startTime">The start time.</param>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
private Pattern generateStair(double startTime)
|
private Pattern generateStair(double startTime)
|
||||||
{
|
{
|
||||||
// - - - -
|
// - - - -
|
||||||
// x - - -
|
// x - - -
|
||||||
// - x - -
|
// - x - -
|
||||||
// - - x -
|
// - - x -
|
||||||
// - - - x
|
// - - - x
|
||||||
// - - x -
|
// - - x -
|
||||||
// - x - -
|
// - x - -
|
||||||
// x - - -
|
// x - - -
|
||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||||
bool increasing = Random.NextDouble() > 0.5;
|
bool increasing = Random.NextDouble() > 0.5;
|
||||||
|
|
||||||
for (int i = 0; i <= spanCount; i++)
|
for (int i = 0; i <= spanCount; i++)
|
||||||
{
|
{
|
||||||
addToPattern(pattern, column, startTime, startTime);
|
addToPattern(pattern, column, startTime, startTime);
|
||||||
startTime += segmentDuration;
|
startTime += segmentDuration;
|
||||||
|
|
||||||
// Check if we're at the borders of the stage, and invert the pattern if so
|
// Check if we're at the borders of the stage, and invert the pattern if so
|
||||||
if (increasing)
|
if (increasing)
|
||||||
{
|
{
|
||||||
if (column >= TotalColumns - 1)
|
if (column >= TotalColumns - 1)
|
||||||
{
|
{
|
||||||
increasing = false;
|
increasing = false;
|
||||||
column--;
|
column--;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
column++;
|
column++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (column <= RandomStart)
|
if (column <= RandomStart)
|
||||||
{
|
{
|
||||||
increasing = true;
|
increasing = true;
|
||||||
column++;
|
column++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
column--;
|
column--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates random notes with 1-2 notes per row and no stacking.
|
/// Generates random notes with 1-2 notes per row and no stacking.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="startTime">The start time.</param>
|
/// <param name="startTime">The start time.</param>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
private Pattern generateRandomMultipleNotes(double startTime)
|
private Pattern generateRandomMultipleNotes(double startTime)
|
||||||
{
|
{
|
||||||
// - - - -
|
// - - - -
|
||||||
// x - - -
|
// x - - -
|
||||||
// - x x -
|
// - x x -
|
||||||
// - - - x
|
// - - - x
|
||||||
// x - x -
|
// x - x -
|
||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
bool legacy = TotalColumns >= 4 && TotalColumns <= 8;
|
bool legacy = TotalColumns >= 4 && TotalColumns <= 8;
|
||||||
int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0));
|
int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0));
|
||||||
|
|
||||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||||
for (int i = 0; i <= spanCount; i++)
|
for (int i = 0; i <= spanCount; i++)
|
||||||
{
|
{
|
||||||
addToPattern(pattern, nextColumn, startTime, startTime);
|
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||||
|
|
||||||
nextColumn += interval;
|
nextColumn += interval;
|
||||||
if (nextColumn >= TotalColumns - RandomStart)
|
if (nextColumn >= TotalColumns - RandomStart)
|
||||||
nextColumn = nextColumn - TotalColumns - RandomStart + (legacy ? 1 : 0);
|
nextColumn = nextColumn - TotalColumns - RandomStart + (legacy ? 1 : 0);
|
||||||
nextColumn += RandomStart;
|
nextColumn += RandomStart;
|
||||||
|
|
||||||
// If we're in 2K, let's not add many consecutive doubles
|
// If we're in 2K, let's not add many consecutive doubles
|
||||||
if (TotalColumns > 2)
|
if (TotalColumns > 2)
|
||||||
addToPattern(pattern, nextColumn, startTime, startTime);
|
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||||
|
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
startTime += segmentDuration;
|
startTime += segmentDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates random hold notes. The amount of hold notes generated is determined by probabilities.
|
/// Generates random hold notes. The amount of hold notes generated is determined by probabilities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="startTime">The hold note start time.</param>
|
/// <param name="startTime">The hold note start time.</param>
|
||||||
/// <param name="p2">The probability required for 2 hold notes to be generated.</param>
|
/// <param name="p2">The probability required for 2 hold notes to be generated.</param>
|
||||||
/// <param name="p3">The probability required for 3 hold notes to be generated.</param>
|
/// <param name="p3">The probability required for 3 hold notes to be generated.</param>
|
||||||
/// <param name="p4">The probability required for 4 hold notes to be generated.</param>
|
/// <param name="p4">The probability required for 4 hold notes to be generated.</param>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
private Pattern generateNRandomNotes(double startTime, double p2, double p3, double p4)
|
private Pattern generateNRandomNotes(double startTime, double p2, double p3, double p4)
|
||||||
{
|
{
|
||||||
// - - - -
|
// - - - -
|
||||||
// ■ - ■ ■
|
// ■ - ■ ■
|
||||||
// □ - □ □
|
// □ - □ □
|
||||||
// ■ - ■ ■
|
// ■ - ■ ■
|
||||||
|
|
||||||
switch (TotalColumns)
|
switch (TotalColumns)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
p2 = 0;
|
p2 = 0;
|
||||||
p3 = 0;
|
p3 = 0;
|
||||||
p4 = 0;
|
p4 = 0;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
p2 = Math.Min(p2, 0.1);
|
p2 = Math.Min(p2, 0.1);
|
||||||
p3 = 0;
|
p3 = 0;
|
||||||
p4 = 0;
|
p4 = 0;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
p2 = Math.Min(p2, 0.3);
|
p2 = Math.Min(p2, 0.3);
|
||||||
p3 = Math.Min(p3, 0.04);
|
p3 = Math.Min(p3, 0.04);
|
||||||
p4 = 0;
|
p4 = 0;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
p2 = Math.Min(p2, 0.34);
|
p2 = Math.Min(p2, 0.34);
|
||||||
p3 = Math.Min(p3, 0.1);
|
p3 = Math.Min(p3, 0.1);
|
||||||
p4 = Math.Min(p4, 0.03);
|
p4 = Math.Min(p4, 0.03);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDoubleSample(SampleInfo 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;
|
bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0;
|
||||||
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
|
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
|
||||||
|
|
||||||
if (canGenerateTwoNotes)
|
if (canGenerateTwoNotes)
|
||||||
p2 = 1;
|
p2 = 1;
|
||||||
|
|
||||||
return generateRandomHoldNotes(startTime, GetRandomNoteCount(p2, p3, p4));
|
return generateRandomHoldNotes(startTime, GetRandomNoteCount(p2, p3, p4));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates tiled hold notes. You can think of this as a stair of hold notes.
|
/// Generates tiled hold notes. You can think of this as a stair of hold notes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="startTime">The first hold note start time.</param>
|
/// <param name="startTime">The first hold note start time.</param>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
private Pattern generateTiledHoldNotes(double startTime)
|
private Pattern generateTiledHoldNotes(double startTime)
|
||||||
{
|
{
|
||||||
// - - - -
|
// - - - -
|
||||||
// ■ ■ ■ ■
|
// ■ ■ ■ ■
|
||||||
// □ □ □ □
|
// □ □ □ □
|
||||||
// □ □ □ □
|
// □ □ □ □
|
||||||
// □ □ □ ■
|
// □ □ □ ■
|
||||||
// □ □ ■ -
|
// □ □ ■ -
|
||||||
// □ ■ - -
|
// □ ■ - -
|
||||||
// ■ - - -
|
// ■ - - -
|
||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
int columnRepeat = Math.Min(spanCount, TotalColumns);
|
int columnRepeat = Math.Min(spanCount, TotalColumns);
|
||||||
|
|
||||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||||
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||||
{
|
{
|
||||||
while (PreviousPattern.ColumnHasObject(nextColumn))
|
while (PreviousPattern.ColumnHasObject(nextColumn))
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < columnRepeat; i++)
|
for (int i = 0; i < columnRepeat; i++)
|
||||||
{
|
{
|
||||||
while (pattern.ColumnHasObject(nextColumn))
|
while (pattern.ColumnHasObject(nextColumn))
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
|
|
||||||
addToPattern(pattern, nextColumn, startTime, endTime);
|
addToPattern(pattern, nextColumn, startTime, endTime);
|
||||||
startTime += segmentDuration;
|
startTime += segmentDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a hold note alongside normal notes.
|
/// Generates a hold note alongside normal notes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="startTime">The start time of notes.</param>
|
/// <param name="startTime">The start time of notes.</param>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
private Pattern generateHoldAndNormalNotes(double startTime)
|
private Pattern generateHoldAndNormalNotes(double startTime)
|
||||||
{
|
{
|
||||||
// - - - -
|
// - - - -
|
||||||
// ■ x x -
|
// ■ x x -
|
||||||
// ■ - x x
|
// ■ - x x
|
||||||
// ■ x - x
|
// ■ x - x
|
||||||
// ■ - x x
|
// ■ - x x
|
||||||
|
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||||
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||||
{
|
{
|
||||||
while (PreviousPattern.ColumnHasObject(holdColumn))
|
while (PreviousPattern.ColumnHasObject(holdColumn))
|
||||||
holdColumn = Random.Next(RandomStart, TotalColumns);
|
holdColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the hold note
|
// Create the hold note
|
||||||
addToPattern(pattern, holdColumn, startTime, endTime);
|
addToPattern(pattern, holdColumn, startTime, endTime);
|
||||||
|
|
||||||
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
int noteCount;
|
int noteCount;
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 6.5)
|
||||||
noteCount = GetRandomNoteCount(0.63, 0);
|
noteCount = GetRandomNoteCount(0.63, 0);
|
||||||
else if (ConversionDifficulty > 4)
|
else if (ConversionDifficulty > 4)
|
||||||
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0.12 : 0.45, 0);
|
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0.12 : 0.45, 0);
|
||||||
else if (ConversionDifficulty > 2.5)
|
else if (ConversionDifficulty > 2.5)
|
||||||
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0 : 0.24, 0);
|
noteCount = GetRandomNoteCount(TotalColumns < 6 ? 0 : 0.24, 0);
|
||||||
else
|
else
|
||||||
noteCount = 0;
|
noteCount = 0;
|
||||||
noteCount = Math.Min(TotalColumns - 1, noteCount);
|
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);
|
bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP);
|
||||||
|
|
||||||
var rowPattern = new Pattern();
|
var rowPattern = new Pattern();
|
||||||
for (int i = 0; i <= spanCount; i++)
|
for (int i = 0; i <= spanCount; i++)
|
||||||
{
|
{
|
||||||
if (!(ignoreHead && startTime == HitObject.StartTime))
|
if (!(ignoreHead && startTime == HitObject.StartTime))
|
||||||
{
|
{
|
||||||
for (int j = 0; j < noteCount; j++)
|
for (int j = 0; j < noteCount; j++)
|
||||||
{
|
{
|
||||||
while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn)
|
while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn)
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
addToPattern(rowPattern, nextColumn, startTime, startTime);
|
addToPattern(rowPattern, nextColumn, startTime, startTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern.Add(rowPattern);
|
pattern.Add(rowPattern);
|
||||||
rowPattern.Clear();
|
rowPattern.Clear();
|
||||||
|
|
||||||
startTime += segmentDuration;
|
startTime += segmentDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the sample info list at a point in time.
|
/// Retrieves the sample info list at a point in time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The time to retrieve the sample info list from.</param>
|
/// <param name="time">The time to retrieve the sample info list from.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private List<SampleInfo> sampleInfoListAt(double time)
|
private List<SampleInfo> sampleInfoListAt(double time)
|
||||||
{
|
{
|
||||||
var curveData = HitObject as IHasCurve;
|
var curveData = HitObject as IHasCurve;
|
||||||
|
|
||||||
if (curveData == null)
|
if (curveData == null)
|
||||||
return HitObject.Samples;
|
return HitObject.Samples;
|
||||||
|
|
||||||
double segmentTime = (endTime - HitObject.StartTime) / spanCount;
|
double segmentTime = (endTime - HitObject.StartTime) / spanCount;
|
||||||
|
|
||||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||||
return curveData.RepeatSamples[index];
|
return curveData.RepeatSamples[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs and adds a note to a pattern.
|
/// Constructs and adds a note to a pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pattern">The pattern to add to.</param>
|
/// <param name="pattern">The pattern to add to.</param>
|
||||||
/// <param name="column">The column to add the note to.</param>
|
/// <param name="column">The column to add the note to.</param>
|
||||||
/// <param name="startTime">The start time of the note.</param>
|
/// <param name="startTime">The start time of the note.</param>
|
||||||
/// <param name="endTime">The end time of the note (set to <paramref name="startTime"/> for a non-hold note).</param>
|
/// <param name="endTime">The end time of the note (set to <paramref name="startTime"/> for a non-hold note).</param>
|
||||||
private void addToPattern(Pattern pattern, int column, double startTime, double endTime)
|
private void addToPattern(Pattern pattern, int column, double startTime, double endTime)
|
||||||
{
|
{
|
||||||
ManiaHitObject newObject;
|
ManiaHitObject newObject;
|
||||||
|
|
||||||
if (startTime == endTime)
|
if (startTime == endTime)
|
||||||
{
|
{
|
||||||
newObject = new Note
|
newObject = new Note
|
||||||
{
|
{
|
||||||
StartTime = startTime,
|
StartTime = startTime,
|
||||||
Samples = sampleInfoListAt(startTime),
|
Samples = sampleInfoListAt(startTime),
|
||||||
Column = column
|
Column = column
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var holdNote = new HoldNote
|
var holdNote = new HoldNote
|
||||||
{
|
{
|
||||||
StartTime = startTime,
|
StartTime = startTime,
|
||||||
Column = column,
|
Column = column,
|
||||||
Duration = endTime - startTime,
|
Duration = endTime - startTime,
|
||||||
Head = { Samples = sampleInfoListAt(startTime) },
|
Head = { Samples = sampleInfoListAt(startTime) },
|
||||||
Tail = { Samples = sampleInfoListAt(endTime) }
|
Tail = { Samples = sampleInfoListAt(endTime) }
|
||||||
};
|
};
|
||||||
|
|
||||||
newObject = holdNote;
|
newObject = holdNote;
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern.Add(newObject);
|
pattern.Add(newObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,102 +1,102 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Rulesets.Mania.MathUtils;
|
using osu.Game.Rulesets.Mania.MathUtils;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
{
|
{
|
||||||
internal class EndTimeObjectPatternGenerator : PatternGenerator
|
internal class EndTimeObjectPatternGenerator : PatternGenerator
|
||||||
{
|
{
|
||||||
private readonly double endTime;
|
private readonly double endTime;
|
||||||
|
|
||||||
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Beatmap originalBeatmap)
|
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Beatmap originalBeatmap)
|
||||||
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
|
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
|
||||||
{
|
{
|
||||||
var endtimeData = HitObject as IHasEndTime;
|
var endtimeData = HitObject as IHasEndTime;
|
||||||
|
|
||||||
endTime = endtimeData?.EndTime ?? 0;
|
endTime = endtimeData?.EndTime ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Pattern Generate()
|
public override Pattern Generate()
|
||||||
{
|
{
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
bool generateHold = endTime - HitObject.StartTime >= 100;
|
bool generateHold = endTime - HitObject.StartTime >= 100;
|
||||||
|
|
||||||
if (TotalColumns == 8)
|
if (TotalColumns == 8)
|
||||||
{
|
{
|
||||||
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000)
|
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000)
|
||||||
addToPattern(pattern, 0, generateHold);
|
addToPattern(pattern, 0, generateHold);
|
||||||
else
|
else
|
||||||
addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold);
|
addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold);
|
||||||
}
|
}
|
||||||
else if (TotalColumns > 0)
|
else if (TotalColumns > 0)
|
||||||
addToPattern(pattern, getNextRandomColumn(0), generateHold);
|
addToPattern(pattern, getNextRandomColumn(0), generateHold);
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Picks a random column after a column.
|
/// Picks a random column after a column.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="start">The starting column.</param>
|
/// <param name="start">The starting column.</param>
|
||||||
/// <returns>A random column after <paramref name="start"/>.</returns>
|
/// <returns>A random column after <paramref name="start"/>.</returns>
|
||||||
private int getNextRandomColumn(int start)
|
private int getNextRandomColumn(int start)
|
||||||
{
|
{
|
||||||
int nextColumn = Random.Next(start, TotalColumns);
|
int nextColumn = Random.Next(start, TotalColumns);
|
||||||
|
|
||||||
while (PreviousPattern.ColumnHasObject(nextColumn))
|
while (PreviousPattern.ColumnHasObject(nextColumn))
|
||||||
nextColumn = Random.Next(start, TotalColumns);
|
nextColumn = Random.Next(start, TotalColumns);
|
||||||
|
|
||||||
return nextColumn;
|
return nextColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs and adds a note to a pattern.
|
/// Constructs and adds a note to a pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pattern">The pattern to add to.</param>
|
/// <param name="pattern">The pattern to add to.</param>
|
||||||
/// <param name="column">The column to add the note to.</param>
|
/// <param name="column">The column to add the note to.</param>
|
||||||
/// <param name="holdNote">Whether to add a hold note.</param>
|
/// <param name="holdNote">Whether to add a hold note.</param>
|
||||||
private void addToPattern(Pattern pattern, int column, bool holdNote)
|
private void addToPattern(Pattern pattern, int column, bool holdNote)
|
||||||
{
|
{
|
||||||
ManiaHitObject newObject;
|
ManiaHitObject newObject;
|
||||||
|
|
||||||
if (holdNote)
|
if (holdNote)
|
||||||
{
|
{
|
||||||
var hold = new HoldNote
|
var hold = new HoldNote
|
||||||
{
|
{
|
||||||
StartTime = HitObject.StartTime,
|
StartTime = HitObject.StartTime,
|
||||||
Column = column,
|
Column = column,
|
||||||
Duration = endTime - HitObject.StartTime
|
Duration = endTime - HitObject.StartTime
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hold.Head.Samples == null)
|
if (hold.Head.Samples == null)
|
||||||
hold.Head.Samples = new List<SampleInfo>();
|
hold.Head.Samples = new List<SampleInfo>();
|
||||||
|
|
||||||
hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL });
|
hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL });
|
||||||
|
|
||||||
hold.Tail.Samples = HitObject.Samples;
|
hold.Tail.Samples = HitObject.Samples;
|
||||||
|
|
||||||
newObject = hold;
|
newObject = hold;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
newObject = new Note
|
newObject = new Note
|
||||||
{
|
{
|
||||||
StartTime = HitObject.StartTime,
|
StartTime = HitObject.StartTime,
|
||||||
Samples = HitObject.Samples,
|
Samples = HitObject.Samples,
|
||||||
Column = column
|
Column = column
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern.Add(newObject);
|
pattern.Add(newObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,401 +1,401 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Mania.MathUtils;
|
using osu.Game.Rulesets.Mania.MathUtils;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
{
|
{
|
||||||
internal class HitObjectPatternGenerator : PatternGenerator
|
internal class HitObjectPatternGenerator : PatternGenerator
|
||||||
{
|
{
|
||||||
public PatternType StairType { get; private set; }
|
public PatternType StairType { get; private set; }
|
||||||
|
|
||||||
private readonly PatternType convertType;
|
private readonly PatternType convertType;
|
||||||
|
|
||||||
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, Beatmap originalBeatmap)
|
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)
|
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
|
||||||
{
|
{
|
||||||
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
|
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
|
||||||
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
|
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
|
||||||
|
|
||||||
StairType = lastStair;
|
StairType = lastStair;
|
||||||
|
|
||||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
||||||
EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime);
|
EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime);
|
||||||
|
|
||||||
var positionData = hitObject as IHasPosition;
|
var positionData = hitObject as IHasPosition;
|
||||||
|
|
||||||
float positionSeparation = ((positionData?.Position ?? Vector2.Zero) - previousPosition).Length;
|
float positionSeparation = ((positionData?.Position ?? Vector2.Zero) - previousPosition).Length;
|
||||||
double timeSeparation = hitObject.StartTime - previousTime;
|
double timeSeparation = hitObject.StartTime - previousTime;
|
||||||
|
|
||||||
if (timeSeparation <= 80)
|
if (timeSeparation <= 80)
|
||||||
{
|
{
|
||||||
// More than 187 BPM
|
// More than 187 BPM
|
||||||
convertType |= PatternType.ForceNotStack | PatternType.KeepSingle;
|
convertType |= PatternType.ForceNotStack | PatternType.KeepSingle;
|
||||||
}
|
}
|
||||||
else if (timeSeparation <= 95)
|
else if (timeSeparation <= 95)
|
||||||
{
|
{
|
||||||
// More than 157 BPM
|
// More than 157 BPM
|
||||||
convertType |= PatternType.ForceNotStack | PatternType.KeepSingle | lastStair;
|
convertType |= PatternType.ForceNotStack | PatternType.KeepSingle | lastStair;
|
||||||
}
|
}
|
||||||
else if (timeSeparation <= 105)
|
else if (timeSeparation <= 105)
|
||||||
{
|
{
|
||||||
// More than 140 BPM
|
// More than 140 BPM
|
||||||
convertType |= PatternType.ForceNotStack | PatternType.LowProbability;
|
convertType |= PatternType.ForceNotStack | PatternType.LowProbability;
|
||||||
}
|
}
|
||||||
else if (timeSeparation <= 125)
|
else if (timeSeparation <= 125)
|
||||||
{
|
{
|
||||||
// More than 120 BPM
|
// More than 120 BPM
|
||||||
convertType |= PatternType.ForceNotStack;
|
convertType |= PatternType.ForceNotStack;
|
||||||
}
|
}
|
||||||
else if (timeSeparation <= 135 && positionSeparation < 20)
|
else if (timeSeparation <= 135 && positionSeparation < 20)
|
||||||
{
|
{
|
||||||
// More than 111 BPM stream
|
// More than 111 BPM stream
|
||||||
convertType |= PatternType.Cycle | PatternType.KeepSingle;
|
convertType |= PatternType.Cycle | PatternType.KeepSingle;
|
||||||
}
|
}
|
||||||
else if (timeSeparation <= 150 && positionSeparation < 20)
|
else if (timeSeparation <= 150 && positionSeparation < 20)
|
||||||
{
|
{
|
||||||
// More than 100 BPM stream
|
// More than 100 BPM stream
|
||||||
convertType |= PatternType.ForceStack | PatternType.LowProbability;
|
convertType |= PatternType.ForceStack | PatternType.LowProbability;
|
||||||
}
|
}
|
||||||
else if (positionSeparation < 20 && density >= timingPoint.BeatLength / 2.5)
|
else if (positionSeparation < 20 && density >= timingPoint.BeatLength / 2.5)
|
||||||
{
|
{
|
||||||
// Low density stream
|
// Low density stream
|
||||||
convertType |= PatternType.Reverse | PatternType.LowProbability;
|
convertType |= PatternType.Reverse | PatternType.LowProbability;
|
||||||
}
|
}
|
||||||
else if (density < timingPoint.BeatLength / 2.5 || effectPoint.KiaiMode)
|
else if (density < timingPoint.BeatLength / 2.5 || effectPoint.KiaiMode)
|
||||||
{
|
{
|
||||||
// High density
|
// High density
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
convertType |= PatternType.LowProbability;
|
convertType |= PatternType.LowProbability;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Pattern Generate()
|
public override Pattern Generate()
|
||||||
{
|
{
|
||||||
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
|
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
|
||||||
|
|
||||||
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
|
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
|
||||||
{
|
{
|
||||||
// Generate a new pattern by copying the last hit objects in reverse-column order
|
// Generate a new pattern by copying the last hit objects in reverse-column order
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
for (int i = RandomStart; i < TotalColumns; i++)
|
for (int i = RandomStart; i < TotalColumns; i++)
|
||||||
if (PreviousPattern.ColumnHasObject(i))
|
if (PreviousPattern.ColumnHasObject(i))
|
||||||
addToPattern(pattern, RandomStart + TotalColumns - i - 1);
|
addToPattern(pattern, RandomStart + TotalColumns - i - 1);
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1
|
if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1
|
||||||
// If we convert to 7K + 1, let's not overload the special key
|
// If we convert to 7K + 1, let's not overload the special key
|
||||||
&& (TotalColumns != 8 || lastColumn != 0)
|
&& (TotalColumns != 8 || lastColumn != 0)
|
||||||
// Make sure the last column was not the centre column
|
// Make sure the last column was not the centre column
|
||||||
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
||||||
{
|
{
|
||||||
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
int column = RandomStart + TotalColumns - lastColumn - 1;
|
int column = RandomStart + TotalColumns - lastColumn - 1;
|
||||||
addToPattern(pattern, column);
|
addToPattern(pattern, column);
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any())
|
if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any())
|
||||||
{
|
{
|
||||||
// Generate a new pattern by placing on the already filled columns
|
// Generate a new pattern by placing on the already filled columns
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
for (int i = RandomStart; i < TotalColumns; i++)
|
for (int i = RandomStart; i < TotalColumns; i++)
|
||||||
if (PreviousPattern.ColumnHasObject(i))
|
if (PreviousPattern.ColumnHasObject(i))
|
||||||
addToPattern(pattern, i);
|
addToPattern(pattern, i);
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((convertType & PatternType.Stair) > 0 && PreviousPattern.HitObjects.Count() == 1)
|
if ((convertType & PatternType.Stair) > 0 && PreviousPattern.HitObjects.Count() == 1)
|
||||||
{
|
{
|
||||||
// Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
|
// Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
int targetColumn = lastColumn + 1;
|
int targetColumn = lastColumn + 1;
|
||||||
if (targetColumn == TotalColumns)
|
if (targetColumn == TotalColumns)
|
||||||
{
|
{
|
||||||
targetColumn = RandomStart;
|
targetColumn = RandomStart;
|
||||||
StairType = PatternType.ReverseStair;
|
StairType = PatternType.ReverseStair;
|
||||||
}
|
}
|
||||||
|
|
||||||
addToPattern(pattern, targetColumn);
|
addToPattern(pattern, targetColumn);
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((convertType & PatternType.ReverseStair) > 0 && PreviousPattern.HitObjects.Count() == 1)
|
if ((convertType & PatternType.ReverseStair) > 0 && PreviousPattern.HitObjects.Count() == 1)
|
||||||
{
|
{
|
||||||
// Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
|
// Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
int targetColumn = lastColumn - 1;
|
int targetColumn = lastColumn - 1;
|
||||||
if (targetColumn == RandomStart - 1)
|
if (targetColumn == RandomStart - 1)
|
||||||
{
|
{
|
||||||
targetColumn = TotalColumns - 1;
|
targetColumn = TotalColumns - 1;
|
||||||
StairType = PatternType.Stair;
|
StairType = PatternType.Stair;
|
||||||
}
|
}
|
||||||
|
|
||||||
addToPattern(pattern, targetColumn);
|
addToPattern(pattern, targetColumn);
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((convertType & PatternType.KeepSingle) > 0)
|
if ((convertType & PatternType.KeepSingle) > 0)
|
||||||
return generateRandomNotes(1);
|
return generateRandomNotes(1);
|
||||||
|
|
||||||
if ((convertType & PatternType.Mirror) > 0)
|
if ((convertType & PatternType.Mirror) > 0)
|
||||||
{
|
{
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 6.5)
|
||||||
return generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
return generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
||||||
if (ConversionDifficulty > 4)
|
if (ConversionDifficulty > 4)
|
||||||
return generateRandomPatternWithMirrored(0.12, 0.17, 0);
|
return generateRandomPatternWithMirrored(0.12, 0.17, 0);
|
||||||
return generateRandomPatternWithMirrored(0.12, 0, 0);
|
return generateRandomPatternWithMirrored(0.12, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 6.5)
|
if (ConversionDifficulty > 6.5)
|
||||||
{
|
{
|
||||||
if ((convertType & PatternType.LowProbability) > 0)
|
if ((convertType & PatternType.LowProbability) > 0)
|
||||||
return generateRandomPattern(0.78, 0.42, 0, 0);
|
return generateRandomPattern(0.78, 0.42, 0, 0);
|
||||||
return generateRandomPattern(1, 0.62, 0, 0);
|
return generateRandomPattern(1, 0.62, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 4)
|
if (ConversionDifficulty > 4)
|
||||||
{
|
{
|
||||||
if ((convertType & PatternType.LowProbability) > 0)
|
if ((convertType & PatternType.LowProbability) > 0)
|
||||||
return generateRandomPattern(0.35, 0.08, 0, 0);
|
return generateRandomPattern(0.35, 0.08, 0, 0);
|
||||||
return generateRandomPattern(0.52, 0.15, 0, 0);
|
return generateRandomPattern(0.52, 0.15, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConversionDifficulty > 2)
|
if (ConversionDifficulty > 2)
|
||||||
{
|
{
|
||||||
if ((convertType & PatternType.LowProbability) > 0)
|
if ((convertType & PatternType.LowProbability) > 0)
|
||||||
return generateRandomPattern(0.18, 0, 0, 0);
|
return generateRandomPattern(0.18, 0, 0, 0);
|
||||||
return generateRandomPattern(0.45, 0, 0, 0);
|
return generateRandomPattern(0.45, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return generateRandomPattern(0, 0, 0, 0);
|
return generateRandomPattern(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates random notes.
|
/// Generates random notes.
|
||||||
/// <para>
|
/// <para>
|
||||||
/// This will generate as many as it can up to <paramref name="noteCount"/>, accounting for
|
/// This will generate as many as it can up to <paramref name="noteCount"/>, accounting for
|
||||||
/// any stacks if <see cref="convertType"/> is forcing no stacks.
|
/// any stacks if <see cref="convertType"/> is forcing no stacks.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="noteCount">The amount of notes to generate.</param>
|
/// <param name="noteCount">The amount of notes to generate.</param>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
private Pattern generateRandomNotes(int noteCount)
|
private Pattern generateRandomNotes(int noteCount)
|
||||||
{
|
{
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
bool allowStacking = (convertType & PatternType.ForceNotStack) == 0;
|
bool allowStacking = (convertType & PatternType.ForceNotStack) == 0;
|
||||||
|
|
||||||
if (!allowStacking)
|
if (!allowStacking)
|
||||||
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
|
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
|
||||||
|
|
||||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||||
for (int i = 0; i < noteCount; i++)
|
for (int i = 0; i < noteCount; i++)
|
||||||
{
|
{
|
||||||
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking)
|
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking)
|
||||||
{
|
{
|
||||||
if ((convertType & PatternType.Gathered) > 0)
|
if ((convertType & PatternType.Gathered) > 0)
|
||||||
{
|
{
|
||||||
nextColumn++;
|
nextColumn++;
|
||||||
if (nextColumn == TotalColumns)
|
if (nextColumn == TotalColumns)
|
||||||
nextColumn = RandomStart;
|
nextColumn = RandomStart;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
}
|
}
|
||||||
|
|
||||||
addToPattern(pattern, nextColumn);
|
addToPattern(pattern, nextColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this hit object can generate a note in the special column.
|
/// Whether this hit object can generate a note in the special column.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
|
private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a random pattern.
|
/// Generates a random pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="p2">Probability for 2 notes to be generated.</param>
|
/// <param name="p2">Probability for 2 notes to be generated.</param>
|
||||||
/// <param name="p3">Probability for 3 notes to be generated.</param>
|
/// <param name="p3">Probability for 3 notes to be generated.</param>
|
||||||
/// <param name="p4">Probability for 4 notes to be generated.</param>
|
/// <param name="p4">Probability for 4 notes to be generated.</param>
|
||||||
/// <param name="p5">Probability for 5 notes to be generated.</param>
|
/// <param name="p5">Probability for 5 notes to be generated.</param>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
private Pattern generateRandomPattern(double p2, double p3, double p4, double p5)
|
private Pattern generateRandomPattern(double p2, double p3, double p4, double p5)
|
||||||
{
|
{
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
pattern.Add(generateRandomNotes(getRandomNoteCount(p2, p3, p4, p5)));
|
pattern.Add(generateRandomNotes(getRandomNoteCount(p2, p3, p4, p5)));
|
||||||
|
|
||||||
if (RandomStart > 0 && hasSpecialColumn)
|
if (RandomStart > 0 && hasSpecialColumn)
|
||||||
addToPattern(pattern, 0);
|
addToPattern(pattern, 0);
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a random pattern which has both normal and mirrored notes.
|
/// Generates a random pattern which has both normal and mirrored notes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="centreProbability">The probability for a note to be added to the centre column.</param>
|
/// <param name="centreProbability">The probability for a note to be added to the centre column.</param>
|
||||||
/// <param name="p2">Probability for 2 notes to be generated.</param>
|
/// <param name="p2">Probability for 2 notes to be generated.</param>
|
||||||
/// <param name="p3">Probability for 3 notes to be generated.</param>
|
/// <param name="p3">Probability for 3 notes to be generated.</param>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3)
|
private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3)
|
||||||
{
|
{
|
||||||
var pattern = new Pattern();
|
var pattern = new Pattern();
|
||||||
|
|
||||||
bool addToCentre;
|
bool addToCentre;
|
||||||
int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre);
|
int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre);
|
||||||
|
|
||||||
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
|
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
|
||||||
int nextColumn = Random.Next(RandomStart, columnLimit);
|
int nextColumn = Random.Next(RandomStart, columnLimit);
|
||||||
for (int i = 0; i < noteCount; i++)
|
for (int i = 0; i < noteCount; i++)
|
||||||
{
|
{
|
||||||
while (pattern.ColumnHasObject(nextColumn))
|
while (pattern.ColumnHasObject(nextColumn))
|
||||||
nextColumn = Random.Next(RandomStart, columnLimit);
|
nextColumn = Random.Next(RandomStart, columnLimit);
|
||||||
|
|
||||||
// Add normal note
|
// Add normal note
|
||||||
addToPattern(pattern, nextColumn);
|
addToPattern(pattern, nextColumn);
|
||||||
// Add mirrored note
|
// Add mirrored note
|
||||||
addToPattern(pattern, RandomStart + TotalColumns - nextColumn - 1);
|
addToPattern(pattern, RandomStart + TotalColumns - nextColumn - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addToCentre)
|
if (addToCentre)
|
||||||
addToPattern(pattern, TotalColumns / 2);
|
addToPattern(pattern, TotalColumns / 2);
|
||||||
|
|
||||||
if (RandomStart > 0 && hasSpecialColumn)
|
if (RandomStart > 0 && hasSpecialColumn)
|
||||||
addToPattern(pattern, 0);
|
addToPattern(pattern, 0);
|
||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a count of notes to be generated from a list of probabilities.
|
/// Generates a count of notes to be generated from a list of probabilities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="p2">Probability for 2 notes to be generated.</param>
|
/// <param name="p2">Probability for 2 notes to be generated.</param>
|
||||||
/// <param name="p3">Probability for 3 notes to be generated.</param>
|
/// <param name="p3">Probability for 3 notes to be generated.</param>
|
||||||
/// <param name="p4">Probability for 4 notes to be generated.</param>
|
/// <param name="p4">Probability for 4 notes to be generated.</param>
|
||||||
/// <param name="p5">Probability for 5 notes to be generated.</param>
|
/// <param name="p5">Probability for 5 notes to be generated.</param>
|
||||||
/// <returns>The amount of notes to be generated.</returns>
|
/// <returns>The amount of notes to be generated.</returns>
|
||||||
private int getRandomNoteCount(double p2, double p3, double p4, double p5)
|
private int getRandomNoteCount(double p2, double p3, double p4, double p5)
|
||||||
{
|
{
|
||||||
switch (TotalColumns)
|
switch (TotalColumns)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
p2 = 0;
|
p2 = 0;
|
||||||
p3 = 0;
|
p3 = 0;
|
||||||
p4 = 0;
|
p4 = 0;
|
||||||
p5 = 0;
|
p5 = 0;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
p2 = Math.Min(p2, 0.1);
|
p2 = Math.Min(p2, 0.1);
|
||||||
p3 = 0;
|
p3 = 0;
|
||||||
p4 = 0;
|
p4 = 0;
|
||||||
p5 = 0;
|
p5 = 0;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
p2 = Math.Min(p2, 0.23);
|
p2 = Math.Min(p2, 0.23);
|
||||||
p3 = Math.Min(p3, 0.04);
|
p3 = Math.Min(p3, 0.04);
|
||||||
p4 = 0;
|
p4 = 0;
|
||||||
p5 = 0;
|
p5 = 0;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
p3 = Math.Min(p3, 0.15);
|
p3 = Math.Min(p3, 0.15);
|
||||||
p4 = Math.Min(p4, 0.03);
|
p4 = Math.Min(p4, 0.03);
|
||||||
p5 = 0;
|
p5 = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP))
|
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP))
|
||||||
p2 = 1;
|
p2 = 1;
|
||||||
|
|
||||||
return GetRandomNoteCount(p2, p3, p4, p5);
|
return GetRandomNoteCount(p2, p3, p4, p5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a count of notes to be generated from a list of probabilities.
|
/// Generates a count of notes to be generated from a list of probabilities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="centreProbability">The probability for a note to be added to the centre column.</param>
|
/// <param name="centreProbability">The probability for a note to be added to the centre column.</param>
|
||||||
/// <param name="p2">Probability for 2 notes to be generated.</param>
|
/// <param name="p2">Probability for 2 notes to be generated.</param>
|
||||||
/// <param name="p3">Probability for 3 notes to be generated.</param>
|
/// <param name="p3">Probability for 3 notes to be generated.</param>
|
||||||
/// <param name="addToCentre">Whether to add a note to the centre column.</param>
|
/// <param name="addToCentre">Whether to add a note to the centre column.</param>
|
||||||
/// <returns>The amount of notes to be generated. The note to be added to the centre column will NOT be part of this count.</returns>
|
/// <returns>The amount of notes to be generated. The note to be added to the centre column will NOT be part of this count.</returns>
|
||||||
private int getRandomNoteCountMirrored(double centreProbability, double p2, double p3, out bool addToCentre)
|
private int getRandomNoteCountMirrored(double centreProbability, double p2, double p3, out bool addToCentre)
|
||||||
{
|
{
|
||||||
addToCentre = false;
|
addToCentre = false;
|
||||||
|
|
||||||
if ((convertType & PatternType.ForceNotStack) > 0)
|
if ((convertType & PatternType.ForceNotStack) > 0)
|
||||||
return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3);
|
return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3);
|
||||||
|
|
||||||
switch (TotalColumns)
|
switch (TotalColumns)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
centreProbability = 0;
|
centreProbability = 0;
|
||||||
p2 = 0;
|
p2 = 0;
|
||||||
p3 = 0;
|
p3 = 0;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
centreProbability = Math.Min(centreProbability, 0.03);
|
centreProbability = Math.Min(centreProbability, 0.03);
|
||||||
p2 = 0;
|
p2 = 0;
|
||||||
p3 = 0;
|
p3 = 0;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
centreProbability = 0;
|
centreProbability = 0;
|
||||||
p2 = Math.Min(p2 * 2, 0.2);
|
p2 = Math.Min(p2 * 2, 0.2);
|
||||||
p3 = 0;
|
p3 = 0;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
centreProbability = Math.Min(centreProbability, 0.03);
|
centreProbability = Math.Min(centreProbability, 0.03);
|
||||||
p3 = 0;
|
p3 = 0;
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
centreProbability = 0;
|
centreProbability = 0;
|
||||||
p2 = Math.Min(p2 * 2, 0.5);
|
p2 = Math.Min(p2 * 2, 0.5);
|
||||||
p3 = Math.Min(p3 * 2, 0.15);
|
p3 = Math.Min(p3 * 2, 0.15);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
double centreVal = Random.NextDouble();
|
double centreVal = Random.NextDouble();
|
||||||
int noteCount = GetRandomNoteCount(p2, p3);
|
int noteCount = GetRandomNoteCount(p2, p3);
|
||||||
|
|
||||||
addToCentre = TotalColumns % 2 != 0 && noteCount != 3 && centreVal > 1 - centreProbability;
|
addToCentre = TotalColumns % 2 != 0 && noteCount != 3 && centreVal > 1 - centreProbability;
|
||||||
return noteCount;
|
return noteCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs and adds a note to a pattern.
|
/// Constructs and adds a note to a pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pattern">The pattern to add to.</param>
|
/// <param name="pattern">The pattern to add to.</param>
|
||||||
/// <param name="column">The column to add the note to.</param>
|
/// <param name="column">The column to add the note to.</param>
|
||||||
private void addToPattern(Pattern pattern, int column)
|
private void addToPattern(Pattern pattern, int column)
|
||||||
{
|
{
|
||||||
pattern.Add(new Note
|
pattern.Add(new Note
|
||||||
{
|
{
|
||||||
StartTime = HitObject.StartTime,
|
StartTime = HitObject.StartTime,
|
||||||
Samples = HitObject.Samples,
|
Samples = HitObject.Samples,
|
||||||
Column = column
|
Column = column
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,123 +1,123 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.MathUtils;
|
using osu.Game.Rulesets.Mania.MathUtils;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A pattern generator for legacy hit objects.
|
/// A pattern generator for legacy hit objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal abstract class PatternGenerator : Patterns.PatternGenerator
|
internal abstract class PatternGenerator : Patterns.PatternGenerator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The column index at which to start generating random notes.
|
/// The column index at which to start generating random notes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly int RandomStart;
|
protected readonly int RandomStart;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The random number generator to use.
|
/// The random number generator to use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly FastRandom Random;
|
protected readonly FastRandom Random;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The beatmap which <see cref="HitObject"/> is being converted from.
|
/// The beatmap which <see cref="HitObject"/> is being converted from.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Beatmap OriginalBeatmap;
|
protected readonly Beatmap OriginalBeatmap;
|
||||||
|
|
||||||
protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
|
protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap)
|
||||||
: base(hitObject, beatmap, previousPattern)
|
: base(hitObject, beatmap, previousPattern)
|
||||||
{
|
{
|
||||||
if (random == null) throw new ArgumentNullException(nameof(random));
|
if (random == null) throw new ArgumentNullException(nameof(random));
|
||||||
if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap));
|
if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap));
|
||||||
|
|
||||||
Random = random;
|
Random = random;
|
||||||
OriginalBeatmap = originalBeatmap;
|
OriginalBeatmap = originalBeatmap;
|
||||||
|
|
||||||
RandomStart = TotalColumns == 8 ? 1 : 0;
|
RandomStart = TotalColumns == 8 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts an x-position into a column.
|
/// Converts an x-position into a column.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="position">The x-position.</param>
|
/// <param name="position">The x-position.</param>
|
||||||
/// <param name="allowSpecial">Whether to treat as 7K + 1.</param>
|
/// <param name="allowSpecial">Whether to treat as 7K + 1.</param>
|
||||||
/// <returns>The column.</returns>
|
/// <returns>The column.</returns>
|
||||||
protected int GetColumn(float position, bool allowSpecial = false)
|
protected int GetColumn(float position, bool allowSpecial = false)
|
||||||
{
|
{
|
||||||
if (allowSpecial && TotalColumns == 8)
|
if (allowSpecial && TotalColumns == 8)
|
||||||
{
|
{
|
||||||
const float local_x_divisor = 512f / 7;
|
const float local_x_divisor = 512f / 7;
|
||||||
return MathHelper.Clamp((int)Math.Floor(position / local_x_divisor), 0, 6) + 1;
|
return MathHelper.Clamp((int)Math.Floor(position / local_x_divisor), 0, 6) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
float localXDivisor = 512f / TotalColumns;
|
float localXDivisor = 512f / TotalColumns;
|
||||||
return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, TotalColumns - 1);
|
return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, TotalColumns - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a count of notes to be generated from probabilities.
|
/// Generates a count of notes to be generated from probabilities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="p2">Probability for 2 notes to be generated.</param>
|
/// <param name="p2">Probability for 2 notes to be generated.</param>
|
||||||
/// <param name="p3">Probability for 3 notes to be generated.</param>
|
/// <param name="p3">Probability for 3 notes to be generated.</param>
|
||||||
/// <param name="p4">Probability for 4 notes to be generated.</param>
|
/// <param name="p4">Probability for 4 notes to be generated.</param>
|
||||||
/// <param name="p5">Probability for 5 notes to be generated.</param>
|
/// <param name="p5">Probability for 5 notes to be generated.</param>
|
||||||
/// <param name="p6">Probability for 6 notes to be generated.</param>
|
/// <param name="p6">Probability for 6 notes to be generated.</param>
|
||||||
/// <returns>The amount of notes to be generated.</returns>
|
/// <returns>The amount of notes to be generated.</returns>
|
||||||
protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0)
|
protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0)
|
||||||
{
|
{
|
||||||
if (p2 < 0 || p2 > 1) throw new ArgumentOutOfRangeException(nameof(p2));
|
if (p2 < 0 || p2 > 1) throw new ArgumentOutOfRangeException(nameof(p2));
|
||||||
if (p3 < 0 || p3 > 1) throw new ArgumentOutOfRangeException(nameof(p3));
|
if (p3 < 0 || p3 > 1) throw new ArgumentOutOfRangeException(nameof(p3));
|
||||||
if (p4 < 0 || p4 > 1) throw new ArgumentOutOfRangeException(nameof(p4));
|
if (p4 < 0 || p4 > 1) throw new ArgumentOutOfRangeException(nameof(p4));
|
||||||
if (p5 < 0 || p5 > 1) throw new ArgumentOutOfRangeException(nameof(p5));
|
if (p5 < 0 || p5 > 1) throw new ArgumentOutOfRangeException(nameof(p5));
|
||||||
if (p6 < 0 || p6 > 1) throw new ArgumentOutOfRangeException(nameof(p6));
|
if (p6 < 0 || p6 > 1) throw new ArgumentOutOfRangeException(nameof(p6));
|
||||||
|
|
||||||
double val = Random.NextDouble();
|
double val = Random.NextDouble();
|
||||||
if (val >= 1 - p6)
|
if (val >= 1 - p6)
|
||||||
return 6;
|
return 6;
|
||||||
if (val >= 1 - p5)
|
if (val >= 1 - p5)
|
||||||
return 5;
|
return 5;
|
||||||
if (val >= 1 - p4)
|
if (val >= 1 - p4)
|
||||||
return 4;
|
return 4;
|
||||||
if (val >= 1 - p3)
|
if (val >= 1 - p3)
|
||||||
return 3;
|
return 3;
|
||||||
return val >= 1 - p2 ? 2 : 1;
|
return val >= 1 - p2 ? 2 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double? conversionDifficulty;
|
private double? conversionDifficulty;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A difficulty factor used for various conversion methods from osu!stable.
|
/// A difficulty factor used for various conversion methods from osu!stable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected double ConversionDifficulty
|
protected double ConversionDifficulty
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (conversionDifficulty != null)
|
if (conversionDifficulty != null)
|
||||||
return conversionDifficulty.Value;
|
return conversionDifficulty.Value;
|
||||||
|
|
||||||
HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault();
|
HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault();
|
||||||
HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault();
|
HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault();
|
||||||
|
|
||||||
double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0);
|
double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0);
|
||||||
drainTime -= OriginalBeatmap.TotalBreakTime;
|
drainTime -= OriginalBeatmap.TotalBreakTime;
|
||||||
|
|
||||||
if (drainTime == 0)
|
if (drainTime == 0)
|
||||||
drainTime = 10000000;
|
drainTime = 10000000;
|
||||||
|
|
||||||
// We need this in seconds
|
// We need this in seconds
|
||||||
drainTime /= 1000;
|
drainTime /= 1000;
|
||||||
|
|
||||||
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
|
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
|
||||||
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
|
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
|
||||||
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
|
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
|
||||||
|
|
||||||
return conversionDifficulty.Value;
|
return conversionDifficulty.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,65 +1,65 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of pattern to generate. Used for legacy patterns.
|
/// The type of pattern to generate. Used for legacy patterns.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Flags]
|
[Flags]
|
||||||
internal enum PatternType
|
internal enum PatternType
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Keep the same as last row.
|
/// Keep the same as last row.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ForceStack = 1 << 0,
|
ForceStack = 1 << 0,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Keep different from last row.
|
/// Keep different from last row.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ForceNotStack = 1 << 1,
|
ForceNotStack = 1 << 1,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Keep as single note at its original position.
|
/// Keep as single note at its original position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
KeepSingle = 1 << 2,
|
KeepSingle = 1 << 2,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use a lower random value.
|
/// Use a lower random value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
LowProbability = 1 << 3,
|
LowProbability = 1 << 3,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reserved.
|
/// Reserved.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Alternate = 1 << 4,
|
Alternate = 1 << 4,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ignore the repeat count.
|
/// Ignore the repeat count.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ForceSigSlider = 1 << 5,
|
ForceSigSlider = 1 << 5,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert slider to circle.
|
/// Convert slider to circle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ForceNotSlider = 1 << 6,
|
ForceNotSlider = 1 << 6,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Notes gathered together.
|
/// Notes gathered together.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Gathered = 1 << 7,
|
Gathered = 1 << 7,
|
||||||
Mirror = 1 << 8,
|
Mirror = 1 << 8,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Change 0 -> 6.
|
/// Change 0 -> 6.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Reverse = 1 << 9,
|
Reverse = 1 << 9,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 1 -> 5 -> 1 -> 5 like reverse.
|
/// 1 -> 5 -> 1 -> 5 like reverse.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Cycle = 1 << 10,
|
Cycle = 1 << 10,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Next note will be at column + 1.
|
/// Next note will be at column + 1.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Stair = 1 << 11,
|
Stair = 1 << 11,
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Next note will be at column - 1.
|
/// Next note will be at column - 1.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ReverseStair = 1 << 12
|
ReverseStair = 1 << 12
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,57 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a pattern containing hit objects.
|
/// Creates a pattern containing hit objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class Pattern
|
internal class Pattern
|
||||||
{
|
{
|
||||||
private readonly List<ManiaHitObject> hitObjects = new List<ManiaHitObject>();
|
private readonly List<ManiaHitObject> hitObjects = new List<ManiaHitObject>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All the hit objects contained in this pattern.
|
/// All the hit objects contained in this pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<ManiaHitObject> HitObjects => hitObjects;
|
public IEnumerable<ManiaHitObject> HitObjects => hitObjects;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check whether a column of this patterns contains a hit object.
|
/// Check whether a column of this patterns contains a hit object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="column">The column index.</param>
|
/// <param name="column">The column index.</param>
|
||||||
/// <returns>Whether the column with index <paramref name="column"/> contains a hit object.</returns>
|
/// <returns>Whether the column with index <paramref name="column"/> contains a hit object.</returns>
|
||||||
public bool ColumnHasObject(int column) => hitObjects.Exists(h => h.Column == column);
|
public bool ColumnHasObject(int column) => hitObjects.Exists(h => h.Column == column);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Amount of columns taken up by hit objects in this pattern.
|
/// Amount of columns taken up by hit objects in this pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int ColumnWithObjects => HitObjects.GroupBy(h => h.Column).Count();
|
public int ColumnWithObjects => HitObjects.GroupBy(h => h.Column).Count();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a hit object to this pattern.
|
/// Adds a hit object to this pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hitObject">The hit object to add.</param>
|
/// <param name="hitObject">The hit object to add.</param>
|
||||||
public void Add(ManiaHitObject hitObject) => hitObjects.Add(hitObject);
|
public void Add(ManiaHitObject hitObject) => hitObjects.Add(hitObject);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copies hit object from another pattern to this one.
|
/// Copies hit object from another pattern to this one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="other">The other pattern.</param>
|
/// <param name="other">The other pattern.</param>
|
||||||
public void Add(Pattern other) => hitObjects.AddRange(other.HitObjects);
|
public void Add(Pattern other) => hitObjects.AddRange(other.HitObjects);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clears this pattern, removing all hit objects.
|
/// Clears this pattern, removing all hit objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Clear() => hitObjects.Clear();
|
public void Clear() => hitObjects.Clear();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a hit object from this pattern.
|
/// Removes a hit object from this pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hitObject">The hit object to remove.</param>
|
/// <param name="hitObject">The hit object to remove.</param>
|
||||||
public bool Remove(ManiaHitObject hitObject) => hitObjects.Remove(hitObject);
|
public bool Remove(ManiaHitObject hitObject) => hitObjects.Remove(hitObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,50 +1,50 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generator to create a pattern <see cref="Pattern"/> from a hit object.
|
/// Generator to create a pattern <see cref="Pattern"/> from a hit object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal abstract class PatternGenerator
|
internal abstract class PatternGenerator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The last pattern.
|
/// The last pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Pattern PreviousPattern;
|
protected readonly Pattern PreviousPattern;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hit object to create the pattern for.
|
/// The hit object to create the pattern for.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly HitObject HitObject;
|
protected readonly HitObject HitObject;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The beatmap which <see cref="HitObject"/> is a part of.
|
/// The beatmap which <see cref="HitObject"/> is a part of.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly ManiaBeatmap Beatmap;
|
protected readonly ManiaBeatmap Beatmap;
|
||||||
|
|
||||||
protected readonly int TotalColumns;
|
protected readonly int TotalColumns;
|
||||||
|
|
||||||
protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
|
protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
|
||||||
{
|
{
|
||||||
if (hitObject == null) throw new ArgumentNullException(nameof(hitObject));
|
if (hitObject == null) throw new ArgumentNullException(nameof(hitObject));
|
||||||
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
|
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
|
||||||
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
|
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
|
||||||
|
|
||||||
HitObject = hitObject;
|
HitObject = hitObject;
|
||||||
Beatmap = beatmap;
|
Beatmap = beatmap;
|
||||||
PreviousPattern = previousPattern;
|
PreviousPattern = previousPattern;
|
||||||
|
|
||||||
TotalColumns = Beatmap.TotalColumns;
|
TotalColumns = Beatmap.TotalColumns;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates the pattern for <see cref="HitObject"/>, filled with hit objects.
|
/// Generates the pattern for <see cref="HitObject"/>, filled with hit objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||||
public abstract Pattern Generate();
|
public abstract Pattern Generate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps
|
namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines properties for each stage in a <see cref="ManiaPlayfield"/>.
|
/// Defines properties for each stage in a <see cref="ManiaPlayfield"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct StageDefinition
|
public struct StageDefinition
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of <see cref="Column"/>s which this stage contains.
|
/// The number of <see cref="Column"/>s which this stage contains.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Columns;
|
public int Columns;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the column index is a special column for this stage.
|
/// Whether the column index is a special column for this stage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="column">The 0-based column index.</param>
|
/// <param name="column">The 0-based column index.</param>
|
||||||
/// <returns>Whether the column is a special column.</returns>
|
/// <returns>Whether the column is a special column.</returns>
|
||||||
public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2;
|
public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.Configuration.Tracking;
|
using osu.Framework.Configuration.Tracking;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Configuration;
|
using osu.Game.Rulesets.Configuration;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Configuration
|
namespace osu.Game.Rulesets.Mania.Configuration
|
||||||
{
|
{
|
||||||
public class ManiaConfigManager : RulesetConfigManager<ManiaSetting>
|
public class ManiaConfigManager : RulesetConfigManager<ManiaSetting>
|
||||||
{
|
{
|
||||||
public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant)
|
public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant)
|
||||||
: base(settings, ruleset, variant)
|
: base(settings, ruleset, variant)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void InitialiseDefaults()
|
protected override void InitialiseDefaults()
|
||||||
{
|
{
|
||||||
base.InitialiseDefaults();
|
base.InitialiseDefaults();
|
||||||
|
|
||||||
Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0);
|
Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||||
{
|
{
|
||||||
new TrackedSetting<double>(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
|
new TrackedSetting<double>(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ManiaSetting
|
public enum ManiaSetting
|
||||||
{
|
{
|
||||||
ScrollTime
|
ScrollTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Judgements
|
namespace osu.Game.Rulesets.Mania.Judgements
|
||||||
{
|
{
|
||||||
public class HoldNoteTailJudgement : ManiaJudgement
|
public class HoldNoteTailJudgement : ManiaJudgement
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the hold note has been released too early and shouldn't give full score for the release.
|
/// Whether the hold note has been released too early and shouldn't give full score for the release.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasBroken;
|
public bool HasBroken;
|
||||||
|
|
||||||
protected override int NumericResultFor(HitResult result)
|
protected override int NumericResultFor(HitResult result)
|
||||||
{
|
{
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
return base.NumericResultFor(result);
|
return base.NumericResultFor(result);
|
||||||
case HitResult.Great:
|
case HitResult.Great:
|
||||||
case HitResult.Perfect:
|
case HitResult.Perfect:
|
||||||
return base.NumericResultFor(HasBroken ? HitResult.Good : result);
|
return base.NumericResultFor(HasBroken ? HitResult.Good : result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Judgements
|
namespace osu.Game.Rulesets.Mania.Judgements
|
||||||
{
|
{
|
||||||
public class HoldNoteTickJudgement : ManiaJudgement
|
public class HoldNoteTickJudgement : ManiaJudgement
|
||||||
{
|
{
|
||||||
public override bool AffectsCombo => false;
|
public override bool AffectsCombo => false;
|
||||||
|
|
||||||
protected override int NumericResultFor(HitResult result) => 20;
|
protected override int NumericResultFor(HitResult result) => 20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Judgements
|
namespace osu.Game.Rulesets.Mania.Judgements
|
||||||
{
|
{
|
||||||
public class ManiaJudgement : Judgement
|
public class ManiaJudgement : Judgement
|
||||||
{
|
{
|
||||||
protected override int NumericResultFor(HitResult result)
|
protected override int NumericResultFor(HitResult result)
|
||||||
{
|
{
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
case HitResult.Meh:
|
case HitResult.Meh:
|
||||||
return 50;
|
return 50;
|
||||||
case HitResult.Ok:
|
case HitResult.Ok:
|
||||||
return 100;
|
return 100;
|
||||||
case HitResult.Good:
|
case HitResult.Good:
|
||||||
return 200;
|
return 200;
|
||||||
case HitResult.Great:
|
case HitResult.Great:
|
||||||
case HitResult.Perfect:
|
case HitResult.Perfect:
|
||||||
return 300;
|
return 300;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,146 +1,146 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania
|
namespace osu.Game.Rulesets.Mania
|
||||||
{
|
{
|
||||||
internal class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject>
|
internal class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject>
|
||||||
{
|
{
|
||||||
private const double star_scaling_factor = 0.018;
|
private const double star_scaling_factor = 0.018;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size strain_step.
|
/// 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.
|
/// 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.
|
/// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const double strain_step = 400;
|
private const double strain_step = 400;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The weighting of each strain value decays to this number * it's previous value
|
/// The weighting of each strain value decays to this number * it's previous value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const double decay_weight = 0.9;
|
private const double decay_weight = 0.9;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// HitObjects are stored as a member variable.
|
/// HitObjects are stored as a member variable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly List<ManiaHitObjectDifficulty> difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
|
private readonly List<ManiaHitObjectDifficulty> difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
|
||||||
|
|
||||||
public ManiaDifficultyCalculator(Beatmap beatmap)
|
public ManiaDifficultyCalculator(Beatmap beatmap)
|
||||||
: base(beatmap)
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public ManiaDifficultyCalculator(Beatmap beatmap, Mod[] mods)
|
public ManiaDifficultyCalculator(Beatmap beatmap, Mod[] mods)
|
||||||
: base(beatmap, mods)
|
: base(beatmap, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||||
{
|
{
|
||||||
// Fill our custom DifficultyHitObject class, that carries additional information
|
// Fill our custom DifficultyHitObject class, that carries additional information
|
||||||
difficultyHitObjects.Clear();
|
difficultyHitObjects.Clear();
|
||||||
|
|
||||||
int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7;
|
int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7;
|
||||||
|
|
||||||
foreach (var hitObject in Beatmap.HitObjects)
|
foreach (var hitObject in Beatmap.HitObjects)
|
||||||
difficultyHitObjects.Add(new ManiaHitObjectDifficulty(hitObject, columnCount));
|
difficultyHitObjects.Add(new ManiaHitObjectDifficulty(hitObject, columnCount));
|
||||||
|
|
||||||
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
||||||
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
|
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
|
||||||
|
|
||||||
if (!calculateStrainValues())
|
if (!calculateStrainValues())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
double starRating = calculateDifficulty() * star_scaling_factor;
|
double starRating = calculateDifficulty() * star_scaling_factor;
|
||||||
|
|
||||||
categoryDifficulty?.Add("Strain", starRating);
|
categoryDifficulty?.Add("Strain", starRating);
|
||||||
|
|
||||||
return starRating;
|
return starRating;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool calculateStrainValues()
|
private bool calculateStrainValues()
|
||||||
{
|
{
|
||||||
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
|
// 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())
|
using (List<ManiaHitObjectDifficulty>.Enumerator hitObjectsEnumerator = difficultyHitObjects.GetEnumerator())
|
||||||
{
|
{
|
||||||
if (!hitObjectsEnumerator.MoveNext())
|
if (!hitObjectsEnumerator.MoveNext())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
ManiaHitObjectDifficulty current = hitObjectsEnumerator.Current;
|
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.
|
// 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())
|
while (hitObjectsEnumerator.MoveNext())
|
||||||
{
|
{
|
||||||
var next = hitObjectsEnumerator.Current;
|
var next = hitObjectsEnumerator.Current;
|
||||||
next?.CalculateStrains(current, TimeRate);
|
next?.CalculateStrains(current, TimeRate);
|
||||||
current = next;
|
current = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private double calculateDifficulty()
|
private double calculateDifficulty()
|
||||||
{
|
{
|
||||||
double actualStrainStep = strain_step * TimeRate;
|
double actualStrainStep = strain_step * TimeRate;
|
||||||
|
|
||||||
// Find the highest strain value within each strain step
|
// Find the highest strain value within each strain step
|
||||||
List<double> highestStrains = new List<double>();
|
List<double> highestStrains = new List<double>();
|
||||||
double intervalEndTime = actualStrainStep;
|
double intervalEndTime = actualStrainStep;
|
||||||
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
||||||
|
|
||||||
ManiaHitObjectDifficulty previousHitObject = null;
|
ManiaHitObjectDifficulty previousHitObject = null;
|
||||||
foreach (var hitObject in difficultyHitObjects)
|
foreach (var hitObject in difficultyHitObjects)
|
||||||
{
|
{
|
||||||
// While we are beyond the current interval push the currently available maximum to our strain list
|
// While we are beyond the current interval push the currently available maximum to our strain list
|
||||||
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
||||||
{
|
{
|
||||||
highestStrains.Add(maximumStrain);
|
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
|
// 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.
|
// until the beginning of the next interval.
|
||||||
if (previousHitObject == null)
|
if (previousHitObject == null)
|
||||||
{
|
{
|
||||||
maximumStrain = 0;
|
maximumStrain = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
|
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);
|
double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
|
||||||
maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay;
|
maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go to the next time interval
|
// Go to the next time interval
|
||||||
intervalEndTime += actualStrainStep;
|
intervalEndTime += actualStrainStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain maximum strain
|
// Obtain maximum strain
|
||||||
double strain = hitObject.IndividualStrain + hitObject.OverallStrain;
|
double strain = hitObject.IndividualStrain + hitObject.OverallStrain;
|
||||||
maximumStrain = Math.Max(strain, maximumStrain);
|
maximumStrain = Math.Max(strain, maximumStrain);
|
||||||
|
|
||||||
previousHitObject = hitObject;
|
previousHitObject = hitObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the weighted sum over the highest strains for each interval
|
// Build the weighted sum over the highest strains for each interval
|
||||||
double difficulty = 0;
|
double difficulty = 0;
|
||||||
double weight = 1;
|
double weight = 1;
|
||||||
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
|
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
|
||||||
|
|
||||||
foreach (double strain in highestStrains)
|
foreach (double strain in highestStrains)
|
||||||
{
|
{
|
||||||
difficulty += weight * strain;
|
difficulty += weight * strain;
|
||||||
weight *= decay_weight;
|
weight *= decay_weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
return difficulty;
|
return difficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap);
|
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,64 +1,64 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania
|
namespace osu.Game.Rulesets.Mania
|
||||||
{
|
{
|
||||||
public class ManiaInputManager : RulesetInputManager<ManiaAction>
|
public class ManiaInputManager : RulesetInputManager<ManiaAction>
|
||||||
{
|
{
|
||||||
public ManiaInputManager(RulesetInfo ruleset, int variant)
|
public ManiaInputManager(RulesetInfo ruleset, int variant)
|
||||||
: base(ruleset, variant, SimultaneousBindingMode.Unique)
|
: base(ruleset, variant, SimultaneousBindingMode.Unique)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ManiaAction
|
public enum ManiaAction
|
||||||
{
|
{
|
||||||
[Description("Special 1")]
|
[Description("Special 1")]
|
||||||
Special1 = 1,
|
Special1 = 1,
|
||||||
[Description("Special 2")]
|
[Description("Special 2")]
|
||||||
Special2,
|
Special2,
|
||||||
|
|
||||||
// This offsets the start value of normal keys in-case we add more special keys
|
// This offsets the start value of normal keys in-case we add more special keys
|
||||||
// above at a later time, without breaking replays/configs.
|
// above at a later time, without breaking replays/configs.
|
||||||
[Description("Key 1")]
|
[Description("Key 1")]
|
||||||
Key1 = 10,
|
Key1 = 10,
|
||||||
[Description("Key 2")]
|
[Description("Key 2")]
|
||||||
Key2,
|
Key2,
|
||||||
[Description("Key 3")]
|
[Description("Key 3")]
|
||||||
Key3,
|
Key3,
|
||||||
[Description("Key 4")]
|
[Description("Key 4")]
|
||||||
Key4,
|
Key4,
|
||||||
[Description("Key 5")]
|
[Description("Key 5")]
|
||||||
Key5,
|
Key5,
|
||||||
[Description("Key 6")]
|
[Description("Key 6")]
|
||||||
Key6,
|
Key6,
|
||||||
[Description("Key 7")]
|
[Description("Key 7")]
|
||||||
Key7,
|
Key7,
|
||||||
[Description("Key 8")]
|
[Description("Key 8")]
|
||||||
Key8,
|
Key8,
|
||||||
[Description("Key 9")]
|
[Description("Key 9")]
|
||||||
Key9,
|
Key9,
|
||||||
[Description("Key 10")]
|
[Description("Key 10")]
|
||||||
Key10,
|
Key10,
|
||||||
[Description("Key 11")]
|
[Description("Key 11")]
|
||||||
Key11,
|
Key11,
|
||||||
[Description("Key 12")]
|
[Description("Key 12")]
|
||||||
Key12,
|
Key12,
|
||||||
[Description("Key 13")]
|
[Description("Key 13")]
|
||||||
Key13,
|
Key13,
|
||||||
[Description("Key 14")]
|
[Description("Key 14")]
|
||||||
Key14,
|
Key14,
|
||||||
[Description("Key 15")]
|
[Description("Key 15")]
|
||||||
Key15,
|
Key15,
|
||||||
[Description("Key 16")]
|
[Description("Key 16")]
|
||||||
Key16,
|
Key16,
|
||||||
[Description("Key 17")]
|
[Description("Key 17")]
|
||||||
Key17,
|
Key17,
|
||||||
[Description("Key 18")]
|
[Description("Key 18")]
|
||||||
Key18,
|
Key18,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,312 +1,381 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Mods;
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Mania.Replays;
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
using osu.Game.Rulesets.Replays.Types;
|
using osu.Game.Rulesets.Replays.Types;
|
||||||
|
using osu.Game.Beatmaps.Legacy;
|
||||||
namespace osu.Game.Rulesets.Mania
|
|
||||||
{
|
namespace osu.Game.Rulesets.Mania
|
||||||
public class ManiaRuleset : Ruleset
|
{
|
||||||
{
|
public class ManiaRuleset : Ruleset
|
||||||
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset);
|
{
|
||||||
|
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset);
|
||||||
public override IEnumerable<Mod> GetModsFor(ModType type)
|
|
||||||
{
|
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
|
||||||
switch (type)
|
{
|
||||||
{
|
if (mods.HasFlag(LegacyMods.Nightcore))
|
||||||
case ModType.DifficultyReduction:
|
yield return new ManiaModNightcore();
|
||||||
return new Mod[]
|
else if (mods.HasFlag(LegacyMods.DoubleTime))
|
||||||
{
|
yield return new ManiaModDoubleTime();
|
||||||
new ManiaModEasy(),
|
|
||||||
new ManiaModNoFail(),
|
if (mods.HasFlag(LegacyMods.Autoplay))
|
||||||
new MultiMod
|
yield return new ManiaModAutoplay();
|
||||||
{
|
|
||||||
Mods = new Mod[]
|
if (mods.HasFlag(LegacyMods.Easy))
|
||||||
{
|
yield return new ManiaModEasy();
|
||||||
new ManiaModHalfTime(),
|
|
||||||
new ManiaModDaycore(),
|
if (mods.HasFlag(LegacyMods.FadeIn))
|
||||||
},
|
yield return new ManiaModFadeIn();
|
||||||
},
|
|
||||||
};
|
if (mods.HasFlag(LegacyMods.Flashlight))
|
||||||
|
yield return new ManiaModFlashlight();
|
||||||
case ModType.DifficultyIncrease:
|
|
||||||
return new Mod[]
|
if (mods.HasFlag(LegacyMods.HalfTime))
|
||||||
{
|
yield return new ManiaModHalfTime();
|
||||||
new ManiaModHardRock(),
|
|
||||||
new MultiMod
|
if (mods.HasFlag(LegacyMods.HardRock))
|
||||||
{
|
yield return new ManiaModHardRock();
|
||||||
Mods = new Mod[]
|
|
||||||
{
|
if (mods.HasFlag(LegacyMods.Hidden))
|
||||||
new ManiaModSuddenDeath(),
|
yield return new ManiaModHidden();
|
||||||
new ManiaModPerfect(),
|
|
||||||
},
|
if (mods.HasFlag(LegacyMods.Key1))
|
||||||
},
|
yield return new ManiaModKey1();
|
||||||
new MultiMod
|
|
||||||
{
|
if (mods.HasFlag(LegacyMods.Key2))
|
||||||
Mods = new Mod[]
|
yield return new ManiaModKey2();
|
||||||
{
|
|
||||||
new ManiaModDoubleTime(),
|
if (mods.HasFlag(LegacyMods.Key3))
|
||||||
new ManiaModNightcore(),
|
yield return new ManiaModKey3();
|
||||||
},
|
|
||||||
},
|
if (mods.HasFlag(LegacyMods.Key4))
|
||||||
new MultiMod
|
yield return new ManiaModKey4();
|
||||||
{
|
|
||||||
Mods = new Mod[]
|
if (mods.HasFlag(LegacyMods.Key5))
|
||||||
{
|
yield return new ManiaModKey5();
|
||||||
new ManiaModFadeIn(),
|
|
||||||
new ManiaModHidden(),
|
if (mods.HasFlag(LegacyMods.Key6))
|
||||||
}
|
yield return new ManiaModKey6();
|
||||||
},
|
|
||||||
new ManiaModFlashlight(),
|
if (mods.HasFlag(LegacyMods.Key7))
|
||||||
};
|
yield return new ManiaModKey7();
|
||||||
|
|
||||||
case ModType.Special:
|
if (mods.HasFlag(LegacyMods.Key8))
|
||||||
return new Mod[]
|
yield return new ManiaModKey8();
|
||||||
{
|
|
||||||
new MultiMod
|
if (mods.HasFlag(LegacyMods.Key9))
|
||||||
{
|
yield return new ManiaModKey9();
|
||||||
Mods = new Mod[]
|
|
||||||
{
|
if (mods.HasFlag(LegacyMods.NoFail))
|
||||||
new ManiaModKey4(),
|
yield return new ManiaModNoFail();
|
||||||
new ManiaModKey5(),
|
|
||||||
new ManiaModKey6(),
|
if (mods.HasFlag(LegacyMods.Perfect))
|
||||||
new ManiaModKey7(),
|
yield return new ManiaModPerfect();
|
||||||
new ManiaModKey8(),
|
|
||||||
new ManiaModKey9(),
|
if (mods.HasFlag(LegacyMods.Random))
|
||||||
new ManiaModKey1(),
|
yield return new ManiaModRandom();
|
||||||
new ManiaModKey2(),
|
|
||||||
new ManiaModKey3(),
|
if (mods.HasFlag(LegacyMods.SuddenDeath))
|
||||||
},
|
yield return new ManiaModSuddenDeath();
|
||||||
},
|
}
|
||||||
new ManiaModRandom(),
|
|
||||||
new ManiaModDualStages(),
|
public override IEnumerable<Mod> GetModsFor(ModType type)
|
||||||
new ManiaModMirror(),
|
{
|
||||||
new MultiMod
|
switch (type)
|
||||||
{
|
{
|
||||||
Mods = new Mod[]
|
case ModType.DifficultyReduction:
|
||||||
{
|
return new Mod[]
|
||||||
new ManiaModAutoplay(),
|
{
|
||||||
new ModCinema(),
|
new ManiaModEasy(),
|
||||||
},
|
new ManiaModNoFail(),
|
||||||
},
|
new MultiMod
|
||||||
};
|
{
|
||||||
|
Mods = new Mod[]
|
||||||
default:
|
{
|
||||||
return new Mod[] { };
|
new ManiaModHalfTime(),
|
||||||
}
|
new ManiaModDaycore(),
|
||||||
}
|
},
|
||||||
|
},
|
||||||
public override string Description => "osu!mania";
|
};
|
||||||
|
|
||||||
public override string ShortName => "mania";
|
case ModType.DifficultyIncrease:
|
||||||
|
return new Mod[]
|
||||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
|
{
|
||||||
|
new ManiaModHardRock(),
|
||||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods);
|
new MultiMod
|
||||||
|
{
|
||||||
public override int? LegacyID => 3;
|
Mods = new Mod[]
|
||||||
|
{
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
|
new ManiaModSuddenDeath(),
|
||||||
|
new ManiaModPerfect(),
|
||||||
public ManiaRuleset(RulesetInfo rulesetInfo = null)
|
},
|
||||||
: base(rulesetInfo)
|
},
|
||||||
{
|
new MultiMod
|
||||||
}
|
{
|
||||||
|
Mods = new Mod[]
|
||||||
public override IEnumerable<int> AvailableVariants
|
{
|
||||||
{
|
new ManiaModDoubleTime(),
|
||||||
get
|
new ManiaModNightcore(),
|
||||||
{
|
},
|
||||||
for (int i = 1; i <= 9; i++)
|
},
|
||||||
yield return (int)PlayfieldType.Single + i;
|
new MultiMod
|
||||||
for (int i = 2; i <= 18; i += 2)
|
{
|
||||||
yield return (int)PlayfieldType.Dual + i;
|
Mods = new Mod[]
|
||||||
}
|
{
|
||||||
}
|
new ManiaModFadeIn(),
|
||||||
|
new ManiaModHidden(),
|
||||||
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
|
}
|
||||||
{
|
},
|
||||||
switch (getPlayfieldType(variant))
|
new ManiaModFlashlight(),
|
||||||
{
|
};
|
||||||
case PlayfieldType.Single:
|
|
||||||
return new VariantMappingGenerator
|
case ModType.Special:
|
||||||
{
|
return new Mod[]
|
||||||
LeftKeys = new[]
|
{
|
||||||
{
|
new MultiMod
|
||||||
InputKey.A,
|
{
|
||||||
InputKey.S,
|
Mods = new Mod[]
|
||||||
InputKey.D,
|
{
|
||||||
InputKey.F
|
new ManiaModKey4(),
|
||||||
},
|
new ManiaModKey5(),
|
||||||
RightKeys = new[]
|
new ManiaModKey6(),
|
||||||
{
|
new ManiaModKey7(),
|
||||||
InputKey.J,
|
new ManiaModKey8(),
|
||||||
InputKey.K,
|
new ManiaModKey9(),
|
||||||
InputKey.L,
|
new ManiaModKey1(),
|
||||||
InputKey.Semicolon
|
new ManiaModKey2(),
|
||||||
},
|
new ManiaModKey3(),
|
||||||
SpecialKey = InputKey.Space,
|
},
|
||||||
SpecialAction = ManiaAction.Special1,
|
},
|
||||||
NormalActionStart = ManiaAction.Key1,
|
new ManiaModRandom(),
|
||||||
}.GenerateKeyBindingsFor(variant, out _);
|
new ManiaModDualStages(),
|
||||||
case PlayfieldType.Dual:
|
new ManiaModMirror(),
|
||||||
int keys = getDualStageKeyCount(variant);
|
new MultiMod
|
||||||
|
{
|
||||||
var stage1Bindings = new VariantMappingGenerator
|
Mods = new Mod[]
|
||||||
{
|
{
|
||||||
LeftKeys = new[]
|
new ManiaModAutoplay(),
|
||||||
{
|
new ModCinema(),
|
||||||
InputKey.Number1,
|
},
|
||||||
InputKey.Number2,
|
},
|
||||||
InputKey.Number3,
|
};
|
||||||
InputKey.Number4,
|
|
||||||
},
|
default:
|
||||||
RightKeys = new[]
|
return new Mod[] { };
|
||||||
{
|
}
|
||||||
InputKey.Z,
|
}
|
||||||
InputKey.X,
|
|
||||||
InputKey.C,
|
public override string Description => "osu!mania";
|
||||||
InputKey.V
|
|
||||||
},
|
public override string ShortName => "mania";
|
||||||
SpecialKey = InputKey.Tilde,
|
|
||||||
SpecialAction = ManiaAction.Special1,
|
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
|
||||||
NormalActionStart = ManiaAction.Key1
|
|
||||||
}.GenerateKeyBindingsFor(keys, out var nextNormal);
|
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods);
|
||||||
|
|
||||||
var stage2Bindings = new VariantMappingGenerator
|
public override int? LegacyID => 3;
|
||||||
{
|
|
||||||
LeftKeys = new[]
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();
|
||||||
{
|
|
||||||
InputKey.Number7,
|
public ManiaRuleset(RulesetInfo rulesetInfo = null)
|
||||||
InputKey.Number8,
|
: base(rulesetInfo)
|
||||||
InputKey.Number9,
|
{
|
||||||
InputKey.Number0
|
}
|
||||||
},
|
|
||||||
RightKeys = new[]
|
public override IEnumerable<int> AvailableVariants
|
||||||
{
|
{
|
||||||
InputKey.O,
|
get
|
||||||
InputKey.P,
|
{
|
||||||
InputKey.BracketLeft,
|
for (int i = 1; i <= 9; i++)
|
||||||
InputKey.BracketRight
|
yield return (int)PlayfieldType.Single + i;
|
||||||
},
|
for (int i = 2; i <= 18; i += 2)
|
||||||
SpecialKey = InputKey.BackSlash,
|
yield return (int)PlayfieldType.Dual + i;
|
||||||
SpecialAction = ManiaAction.Special2,
|
}
|
||||||
NormalActionStart = nextNormal
|
}
|
||||||
}.GenerateKeyBindingsFor(keys, out _);
|
|
||||||
|
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
|
||||||
return stage1Bindings.Concat(stage2Bindings);
|
{
|
||||||
}
|
switch (getPlayfieldType(variant))
|
||||||
|
{
|
||||||
return new KeyBinding[0];
|
case PlayfieldType.Single:
|
||||||
}
|
return new VariantMappingGenerator
|
||||||
|
{
|
||||||
public override string GetVariantName(int variant)
|
LeftKeys = new[]
|
||||||
{
|
{
|
||||||
switch (getPlayfieldType(variant))
|
InputKey.A,
|
||||||
{
|
InputKey.S,
|
||||||
default:
|
InputKey.D,
|
||||||
return $"{variant}K";
|
InputKey.F
|
||||||
case PlayfieldType.Dual:
|
},
|
||||||
{
|
RightKeys = new[]
|
||||||
var keys = getDualStageKeyCount(variant);
|
{
|
||||||
return $"{keys}K + {keys}K";
|
InputKey.J,
|
||||||
}
|
InputKey.K,
|
||||||
}
|
InputKey.L,
|
||||||
}
|
InputKey.Semicolon
|
||||||
|
},
|
||||||
/// <summary>
|
SpecialKey = InputKey.Space,
|
||||||
/// Finds the number of keys for each stage in a <see cref="PlayfieldType.Dual"/> variant.
|
SpecialAction = ManiaAction.Special1,
|
||||||
/// </summary>
|
NormalActionStart = ManiaAction.Key1,
|
||||||
/// <param name="variant">The variant.</param>
|
}.GenerateKeyBindingsFor(variant, out _);
|
||||||
private int getDualStageKeyCount(int variant) => (variant - (int)PlayfieldType.Dual) / 2;
|
case PlayfieldType.Dual:
|
||||||
|
int keys = getDualStageKeyCount(variant);
|
||||||
/// <summary>
|
|
||||||
/// Finds the <see cref="PlayfieldType"/> that corresponds to a variant value.
|
var stage1Bindings = new VariantMappingGenerator
|
||||||
/// </summary>
|
{
|
||||||
/// <param name="variant">The variant value.</param>
|
LeftKeys = new[]
|
||||||
/// <returns>The <see cref="PlayfieldType"/> that corresponds to <paramref name="variant"/>.</returns>
|
{
|
||||||
private PlayfieldType getPlayfieldType(int variant)
|
InputKey.Number1,
|
||||||
{
|
InputKey.Number2,
|
||||||
return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast<int>().OrderByDescending(i => i).First(v => variant >= v);
|
InputKey.Number3,
|
||||||
}
|
InputKey.Number4,
|
||||||
|
},
|
||||||
private class VariantMappingGenerator
|
RightKeys = new[]
|
||||||
{
|
{
|
||||||
/// <summary>
|
InputKey.Z,
|
||||||
/// All the <see cref="InputKey"/>s available to the left hand.
|
InputKey.X,
|
||||||
/// </summary>
|
InputKey.C,
|
||||||
public InputKey[] LeftKeys;
|
InputKey.V
|
||||||
|
},
|
||||||
/// <summary>
|
SpecialKey = InputKey.Tilde,
|
||||||
/// All the <see cref="InputKey"/>s available to the right hand.
|
SpecialAction = ManiaAction.Special1,
|
||||||
/// </summary>
|
NormalActionStart = ManiaAction.Key1
|
||||||
public InputKey[] RightKeys;
|
}.GenerateKeyBindingsFor(keys, out var nextNormal);
|
||||||
|
|
||||||
/// <summary>
|
var stage2Bindings = new VariantMappingGenerator
|
||||||
/// The <see cref="InputKey"/> for the special key.
|
{
|
||||||
/// </summary>
|
LeftKeys = new[]
|
||||||
public InputKey SpecialKey;
|
{
|
||||||
|
InputKey.Number7,
|
||||||
/// <summary>
|
InputKey.Number8,
|
||||||
/// The <see cref="ManiaAction"/> at which the normal columns should begin.
|
InputKey.Number9,
|
||||||
/// </summary>
|
InputKey.Number0
|
||||||
public ManiaAction NormalActionStart;
|
},
|
||||||
|
RightKeys = new[]
|
||||||
/// <summary>
|
{
|
||||||
/// The <see cref="ManiaAction"/> for the special column.
|
InputKey.O,
|
||||||
/// </summary>
|
InputKey.P,
|
||||||
public ManiaAction SpecialAction;
|
InputKey.BracketLeft,
|
||||||
|
InputKey.BracketRight
|
||||||
/// <summary>
|
},
|
||||||
/// Generates a list of <see cref="KeyBinding"/>s for a specific number of columns.
|
SpecialKey = InputKey.BackSlash,
|
||||||
/// </summary>
|
SpecialAction = ManiaAction.Special2,
|
||||||
/// <param name="columns">The number of columns that need to be bound.</param>
|
NormalActionStart = nextNormal
|
||||||
/// <param name="nextNormalAction">The next <see cref="ManiaAction"/> to use for normal columns.</param>
|
}.GenerateKeyBindingsFor(keys, out _);
|
||||||
/// <returns>The keybindings.</returns>
|
|
||||||
public IEnumerable<KeyBinding> GenerateKeyBindingsFor(int columns, out ManiaAction nextNormalAction)
|
return stage1Bindings.Concat(stage2Bindings);
|
||||||
{
|
}
|
||||||
ManiaAction currentNormalAction = NormalActionStart;
|
|
||||||
|
return new KeyBinding[0];
|
||||||
var bindings = new List<KeyBinding>();
|
}
|
||||||
|
|
||||||
for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++)
|
public override string GetVariantName(int variant)
|
||||||
bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++));
|
{
|
||||||
|
switch (getPlayfieldType(variant))
|
||||||
for (int i = 0; i < columns / 2; i++)
|
{
|
||||||
bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++));
|
default:
|
||||||
|
return $"{variant}K";
|
||||||
if (columns % 2 == 1)
|
case PlayfieldType.Dual:
|
||||||
bindings.Add(new KeyBinding(SpecialKey, SpecialAction));
|
{
|
||||||
|
var keys = getDualStageKeyCount(variant);
|
||||||
nextNormalAction = currentNormalAction;
|
return $"{keys}K + {keys}K";
|
||||||
return bindings;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
public enum PlayfieldType
|
/// Finds the number of keys for each stage in a <see cref="PlayfieldType.Dual"/> variant.
|
||||||
{
|
/// </summary>
|
||||||
/// <summary>
|
/// <param name="variant">The variant.</param>
|
||||||
/// Columns are grouped into a single stage.
|
private int getDualStageKeyCount(int variant) => (variant - (int)PlayfieldType.Dual) / 2;
|
||||||
/// Number of columns in this stage lies at (item - Single).
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
Single = 0,
|
/// Finds the <see cref="PlayfieldType"/> that corresponds to a variant value.
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Columns are grouped into two stages.
|
/// <param name="variant">The variant value.</param>
|
||||||
/// Overall number of columns lies at (item - Dual), further computation is required for
|
/// <returns>The <see cref="PlayfieldType"/> that corresponds to <paramref name="variant"/>.</returns>
|
||||||
/// number of columns in each individual stage.
|
private PlayfieldType getPlayfieldType(int variant)
|
||||||
/// </summary>
|
{
|
||||||
Dual = 1000,
|
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,91 +1,91 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.MathUtils
|
namespace osu.Game.Rulesets.Mania.MathUtils
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A PRNG specified in http://heliosphan.org/fastrandom.html.
|
/// A PRNG specified in http://heliosphan.org/fastrandom.html.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class FastRandom
|
internal class FastRandom
|
||||||
{
|
{
|
||||||
private const double int_to_real = 1.0 / (int.MaxValue + 1.0);
|
private const double int_to_real = 1.0 / (int.MaxValue + 1.0);
|
||||||
private const uint int_mask = 0x7FFFFFFF;
|
private const uint int_mask = 0x7FFFFFFF;
|
||||||
private const uint y = 842502087;
|
private const uint y = 842502087;
|
||||||
private const uint z = 3579807591;
|
private const uint z = 3579807591;
|
||||||
private const uint w = 273326509;
|
private const uint w = 273326509;
|
||||||
private uint _x, _y = y, _z = z, _w = w;
|
private uint _x, _y = y, _z = z, _w = w;
|
||||||
|
|
||||||
public FastRandom(int seed)
|
public FastRandom(int seed)
|
||||||
{
|
{
|
||||||
_x = (uint)seed;
|
_x = (uint)seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FastRandom()
|
public FastRandom()
|
||||||
: this(Environment.TickCount)
|
: this(Environment.TickCount)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a random unsigned integer within the range [<see cref="uint.MinValue"/>, <see cref="uint.MaxValue"/>).
|
/// Generates a random unsigned integer within the range [<see cref="uint.MinValue"/>, <see cref="uint.MaxValue"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The random value.</returns>
|
/// <returns>The random value.</returns>
|
||||||
public uint NextUInt()
|
public uint NextUInt()
|
||||||
{
|
{
|
||||||
uint t = _x ^ _x << 11;
|
uint t = _x ^ _x << 11;
|
||||||
_x = _y;
|
_x = _y;
|
||||||
_y = _z;
|
_y = _z;
|
||||||
_z = _w;
|
_z = _w;
|
||||||
return _w = _w ^ _w >> 19 ^ t ^ t >> 8;
|
return _w = _w ^ _w >> 19 ^ t ^ t >> 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a random integer value within the range [0, <see cref="int.MaxValue"/>).
|
/// Generates a random integer value within the range [0, <see cref="int.MaxValue"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The random value.</returns>
|
/// <returns>The random value.</returns>
|
||||||
public int Next() => (int)(int_mask & NextUInt());
|
public int Next() => (int)(int_mask & NextUInt());
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a random integer value within the range [0, <paramref name="upperBound"/>).
|
/// Generates a random integer value within the range [0, <paramref name="upperBound"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="upperBound">The upper bound.</param>
|
/// <param name="upperBound">The upper bound.</param>
|
||||||
/// <returns>The random value.</returns>
|
/// <returns>The random value.</returns>
|
||||||
public int Next(int upperBound) => (int)(NextDouble() * upperBound);
|
public int Next(int upperBound) => (int)(NextDouble() * upperBound);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a random integer value within the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
|
/// Generates a random integer value within the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lowerBound">The lower bound of the range.</param>
|
/// <param name="lowerBound">The lower bound of the range.</param>
|
||||||
/// <param name="upperBound">The upper bound of the range.</param>
|
/// <param name="upperBound">The upper bound of the range.</param>
|
||||||
/// <returns>The random value.</returns>
|
/// <returns>The random value.</returns>
|
||||||
public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound));
|
public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a random double value within the range [0, 1).
|
/// Generates a random double value within the range [0, 1).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The random value.</returns>
|
/// <returns>The random value.</returns>
|
||||||
public double NextDouble() => int_to_real * Next();
|
public double NextDouble() => int_to_real * Next();
|
||||||
|
|
||||||
private uint bitBuffer;
|
private uint bitBuffer;
|
||||||
private int bitIndex = 32;
|
private int bitIndex = 32;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls.
|
/// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The random value.</returns>
|
/// <returns>The random value.</returns>
|
||||||
public bool NextBool()
|
public bool NextBool()
|
||||||
{
|
{
|
||||||
if (bitIndex == 32)
|
if (bitIndex == 32)
|
||||||
{
|
{
|
||||||
bitBuffer = NextUInt();
|
bitBuffer = NextUInt();
|
||||||
bitIndex = 1;
|
bitIndex = 1;
|
||||||
|
|
||||||
return (bitBuffer & 1) == 1;
|
return (bitBuffer & 1) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bitIndex++;
|
bitIndex++;
|
||||||
return ((bitBuffer >>= 1) & 1) == 1;
|
return ((bitBuffer >>= 1) & 1) == 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public interface IPlayfieldTypeMod : IApplicableMod
|
public interface IPlayfieldTypeMod : IApplicableMod
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="PlayfieldType"/> which this <see cref="IPlayfieldTypeMod"/> requires.
|
/// The <see cref="PlayfieldType"/> which this <see cref="IPlayfieldTypeMod"/> requires.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
PlayfieldType PlayfieldType { get; }
|
PlayfieldType PlayfieldType { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public abstract class ManiaKeyMod : Mod, IApplicableToBeatmapConverter<ManiaHitObject>
|
public abstract class ManiaKeyMod : Mod, IApplicableToBeatmapConverter<ManiaHitObject>
|
||||||
{
|
{
|
||||||
public override string ShortenedName => Name;
|
public override string ShortenedName => Name;
|
||||||
public abstract int KeyCount { get; }
|
public abstract int KeyCount { get; }
|
||||||
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
|
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
|
||||||
public override bool Ranked => true;
|
public override bool Ranked => true;
|
||||||
|
|
||||||
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
|
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
|
||||||
{
|
{
|
||||||
var mbc = (ManiaBeatmapConverter)beatmapConverter;
|
var mbc = (ManiaBeatmapConverter)beatmapConverter;
|
||||||
|
|
||||||
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
||||||
if (mbc.IsForCurrentRuleset)
|
if (mbc.IsForCurrentRuleset)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mbc.TargetColumns = KeyCount;
|
mbc.TargetColumns = KeyCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Replays;
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
|
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
|
||||||
{
|
{
|
||||||
protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap)
|
protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap)
|
||||||
{
|
{
|
||||||
return new Score
|
return new Score
|
||||||
{
|
{
|
||||||
User = new User { Username = "osu!topus!" },
|
User = new User { Username = "osu!topus!" },
|
||||||
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
|
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModDaycore : ModDaycore
|
public class ManiaModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.5;
|
public override double ScoreMultiplier => 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModDoubleTime : ModDoubleTime
|
public class ManiaModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,52 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter<ManiaHitObject>, IApplicableToRulesetContainer<ManiaHitObject>
|
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter<ManiaHitObject>, IApplicableToRulesetContainer<ManiaHitObject>
|
||||||
{
|
{
|
||||||
public override string Name => "Dual Stages";
|
public override string Name => "Dual Stages";
|
||||||
public override string ShortenedName => "DS";
|
public override string ShortenedName => "DS";
|
||||||
public override string Description => @"Double the stages, double the fun!";
|
public override string Description => @"Double the stages, double the fun!";
|
||||||
public override double ScoreMultiplier => 0;
|
public override double ScoreMultiplier => 0;
|
||||||
|
|
||||||
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
|
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
|
||||||
{
|
{
|
||||||
var mbc = (ManiaBeatmapConverter)beatmapConverter;
|
var mbc = (ManiaBeatmapConverter)beatmapConverter;
|
||||||
|
|
||||||
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
||||||
if (mbc.IsForCurrentRuleset)
|
if (mbc.IsForCurrentRuleset)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mbc.TargetColumns *= 2;
|
mbc.TargetColumns *= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
|
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
|
||||||
{
|
{
|
||||||
var mrc = (ManiaRulesetContainer)rulesetContainer;
|
var mrc = (ManiaRulesetContainer)rulesetContainer;
|
||||||
|
|
||||||
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
||||||
if (mrc.IsForCurrentRuleset)
|
if (mrc.IsForCurrentRuleset)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var newDefinitions = new List<StageDefinition>();
|
var newDefinitions = new List<StageDefinition>();
|
||||||
foreach (var existing in mrc.Beatmap.Stages)
|
foreach (var existing in mrc.Beatmap.Stages)
|
||||||
{
|
{
|
||||||
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||||
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||||
}
|
}
|
||||||
|
|
||||||
mrc.Beatmap.Stages = newDefinitions;
|
mrc.Beatmap.Stages = newDefinitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlayfieldType PlayfieldType => PlayfieldType.Dual;
|
public PlayfieldType PlayfieldType => PlayfieldType.Dual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModEasy : ModEasy
|
public class ManiaModEasy : ModEasy
|
||||||
{
|
{
|
||||||
public override string Description => @"More forgiving HP drain, less accuracy required, and three lives!";
|
public override string Description => @"More forgiving HP drain, less accuracy required, and three lives!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModFadeIn : Mod
|
public class ManiaModFadeIn : Mod
|
||||||
{
|
{
|
||||||
public override string Name => "Fade In";
|
public override string Name => "Fade In";
|
||||||
public override string ShortenedName => "FI";
|
public override string ShortenedName => "FI";
|
||||||
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
|
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
|
||||||
public override ModType Type => ModType.DifficultyIncrease;
|
public override ModType Type => ModType.DifficultyIncrease;
|
||||||
public override string Description => @"Keys appear out of nowhere!";
|
public override string Description => @"Keys appear out of nowhere!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override bool Ranked => true;
|
public override bool Ranked => true;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModFlashlight : ModFlashlight
|
public class ManiaModFlashlight : ModFlashlight
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModHidden) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModHidden) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModHalfTime : ModHalfTime
|
public class ManiaModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.5;
|
public override double ScoreMultiplier => 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModHardRock : ModHardRock
|
public class ManiaModHardRock : ModHardRock
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModHidden : ModHidden
|
public class ManiaModHidden : ModHidden
|
||||||
{
|
{
|
||||||
public override string Description => @"Keys fade out before you hit them!";
|
public override string Description => @"Keys fade out before you hit them!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModKey1 : ManiaKeyMod
|
public class ManiaModKey1 : ManiaKeyMod
|
||||||
{
|
{
|
||||||
public override int KeyCount => 1;
|
public override int KeyCount => 1;
|
||||||
public override string Name => "One Key";
|
public override string Name => "One Key";
|
||||||
public override string ShortenedName => "1K";
|
public override string ShortenedName => "1K";
|
||||||
public override string Description => @"Play with one key.";
|
public override string Description => @"Play with one key.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user