1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-15 23:47:25 +08:00

Merge remote-tracking branch 'refs/remotes/ppy/master'

This commit is contained in:
Shawdooow 2017-09-18 23:09:41 -04:00
commit 70a295764c
361 changed files with 8844 additions and 4155 deletions

6
NuGet.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="opentk-develop" value="https://www.myget.org/F/opentk-develop" />
</packageSources>
</configuration>

View File

@ -1,3 +1,4 @@
# 2017-09-14
clone_depth: 1
version: '{branch}-{build}'
configuration: Debug

@ -1 +1 @@
Subproject commit 5c22092e590d589927962b8d0173dae5f9b1405c
Subproject commit 3f4545aae82650dc87cac7dd5df64e6e47918da1

@ -1 +1 @@
Subproject commit f6042e1cb37cfad6c879d0e1245f7880c7fcd5f5
Subproject commit a4418111f8ed2350a6fd46fe69258884f0757745

View File

@ -33,7 +33,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.17.1.0" newVersion="0.17.1.0" />
<bindingRedirect oldVersion="0.0.0.0-0.18.1.0" newVersion="0.18.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@ -17,8 +17,8 @@ namespace osu.Desktop.Deploy
{
internal static class Program
{
private const string nuget_path = @"packages\NuGet.CommandLine.3.5.0\tools\NuGet.exe";
private const string squirrel_path = @"packages\squirrel.windows.1.5.2\tools\Squirrel.exe";
private const string nuget_path = @"packages\NuGet.CommandLine.4.3.0\tools\NuGet.exe";
private const string squirrel_path = @"packages\squirrel.windows.1.7.8\tools\Squirrel.exe";
private const string msbuild_path = @"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe";
public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"];
@ -100,7 +100,7 @@ namespace osu.Desktop.Deploy
updateAssemblyInfo(version);
write("Running build process...");
runCommand(msbuild_path, $"/v:quiet /m /t:{TargetName.Replace('.', '_')} /p:OutputPath={stagingPath};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...");
runCommand(nuget_path, $"pack {NuSpecName} -Version {version} -Properties Configuration=Deploy -OutputDirectory {stagingPath} -BasePath {stagingPath}");

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Desktop.Deploy</RootNamespace>
<AssemblyName>osu.Desktop.Deploy</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
@ -66,22 +66,23 @@
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll</HintPath>
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SharpCompress, Version=0.17.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll</HintPath>
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SharpCompress, Version=0.18.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Squirrel, Version=1.7.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll</HintPath>
<Reference Include="Squirrel, Version=1.7.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />

View File

@ -6,9 +6,9 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<packages>
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net452" />
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net452" />
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net452" />
<package id="NuGet.CommandLine" version="4.1.0" targetFramework="net452" developmentDependency="true" />
<package id="SharpCompress" version="0.17.1" targetFramework="net452" />
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
<package id="NuGet.CommandLine" version="4.3.0" targetFramework="net461" developmentDependency="true" />
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
<package id="Splat" version="2.0.0" targetFramework="net452" />
<package id="squirrel.windows" version="1.7.5" targetFramework="net452" />
<package id="squirrel.windows" version="1.7.8" targetFramework="net461" />
</packages>

View File

@ -0,0 +1,25 @@
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
<!-- XQuartz compatibility (X11 on Mac) -->
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
</configuration>

View File

@ -1,20 +1,17 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Desktop.Platform;
using osu.Framework.Testing;
using osu.Game;
namespace osu.Desktop.Tests.Visual
{
[TestFixture]
public abstract class OsuTestCase : TestCase
{
[Test]
public override void RunTest()
{
using (var host = new HeadlessGameHost())
using (var host = new HeadlessGameHost(realtime: false))
host.Run(new OsuTestCaseTestRunner(this));
}

View File

@ -12,50 +12,104 @@ namespace osu.Desktop.Tests.Visual
{
public override string Description => "BeatmapDetails tab of BeatmapDetailArea";
private readonly BeatmapDetails details;
public TestCaseBeatmapDetails()
{
BeatmapDetails details;
Add(details = new BeatmapDetails
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(150),
Beatmap = new BeatmapInfo
});
AddStep("beatmap all metrics", () => details.Beatmap = new BeatmapInfo
{
Version = "All Metrics",
Metadata = new BeatmapMetadata
{
Version = "VisualTest",
Metadata = new BeatmapMetadata
{
Source = "Some guy",
Tags = "beatmap metadata example with a very very long list of tags and not much creativity",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 7,
ApproachRate = 3.5f,
OverallDifficulty = 5.7f,
DrainRate = 1,
},
StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 10),
Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6),
},
Source = "osu!lazer",
Tags = "this beatmap has all the metrics",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 7,
DrainRate = 1,
OverallDifficulty = 5.7f,
ApproachRate = 3.5f,
},
StarDifficulty = 5.3f,
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 10),
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
},
});
AddRepeatStep("fail values", newRetryAndFailValues, 10);
}
AddStep("beatmap ratings", () => details.Beatmap = new BeatmapInfo
{
Version = "Only Ratings",
Metadata = new BeatmapMetadata
{
Source = "osu!lazer",
Tags = "this beatmap has ratings metrics but not retries or fails",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 6,
DrainRate = 9,
OverallDifficulty = 6,
ApproachRate = 6,
},
StarDifficulty = 4.8f,
Metrics = new BeatmapMetrics
{
Ratings = Enumerable.Range(0, 10),
},
});
private int lastRange = 1;
AddStep("beatmap fails retries", () => details.Beatmap = new BeatmapInfo
{
Version = "Only Retries and Fails",
Metadata = new BeatmapMetadata
{
Source = "osu!lazer",
Tags = "this beatmap has retries and fails but no ratings",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 3.7f,
DrainRate = 6,
OverallDifficulty = 6,
ApproachRate = 7,
},
StarDifficulty = 2.91f,
Metrics = new BeatmapMetrics
{
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
},
});
private void newRetryAndFailValues()
{
details.Beatmap.Metrics.Fails = Enumerable.Range(lastRange, 100).Select(i => i % 12 - 6);
details.Beatmap.Metrics.Retries = Enumerable.Range(lastRange - 3, 100).Select(i => i % 12 - 6);
details.Beatmap = details.Beatmap;
lastRange += 100;
AddStep("beatmap no metrics", () => details.Beatmap = new BeatmapInfo
{
Version = "No Metrics",
Metadata = new BeatmapMetadata
{
Source = "osu!lazer",
Tags = "this beatmap has no metrics",
},
Difficulty = new BeatmapDifficulty
{
CircleSize = 5,
DrainRate = 5,
OverallDifficulty = 5.5f,
ApproachRate = 6.5f,
},
StarDifficulty = 1.97f,
Metrics = new BeatmapMetrics(),
});
AddStep("null beatmap", () => details.Beatmap = null);
}
}
}
}

View File

@ -69,28 +69,28 @@ namespace osu.Desktop.Tests.Visual
private class MyContextMenuContainer : Container, IHasContextMenu
{
public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuContextMenuItem(@"Some option"),
new OsuContextMenuItem(@"Highlighted option", MenuItemType.Highlighted),
new OsuContextMenuItem(@"Another option"),
new OsuContextMenuItem(@"Choose me please"),
new OsuContextMenuItem(@"And me too"),
new OsuContextMenuItem(@"Trying to fill"),
new OsuContextMenuItem(@"Destructive option", MenuItemType.Destructive),
new OsuMenuItem(@"Some option"),
new OsuMenuItem(@"Highlighted option", MenuItemType.Highlighted),
new OsuMenuItem(@"Another option"),
new OsuMenuItem(@"Choose me please"),
new OsuMenuItem(@"And me too"),
new OsuMenuItem(@"Trying to fill"),
new OsuMenuItem(@"Destructive option", MenuItemType.Destructive),
};
}
private class AnotherContextMenuContainer : Container, IHasContextMenu
{
public ContextMenuItem[] ContextMenuItems => new ContextMenuItem[]
public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuContextMenuItem(@"Simple option"),
new OsuContextMenuItem(@"Simple very very long option"),
new OsuContextMenuItem(@"Change width", MenuItemType.Highlighted) { Action = () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint) },
new OsuContextMenuItem(@"Change height", MenuItemType.Highlighted) { Action = () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint) },
new OsuContextMenuItem(@"Change width back", MenuItemType.Destructive) { Action = () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint) },
new OsuContextMenuItem(@"Change height back", MenuItemType.Destructive) { Action = () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint) },
new OsuMenuItem(@"Simple option"),
new OsuMenuItem(@"Simple very very long option"),
new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)),
new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)),
};
}
}

View File

@ -41,12 +41,14 @@ namespace osu.Desktop.Tests.Visual
{
new BeatmapSetInfo
{
OnlineBeatmapSetID = 578332,
Metadata = new BeatmapMetadata
{
Title = @"OrVid",
Artist = @"An",
Author = @"RLC",
Source = @"",
Tags = @"acuticnotes an-fillnote revid tear tearvid encrpted encryption axi axivid quad her hervid recoll",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@ -71,12 +73,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 599627,
Metadata = new BeatmapMetadata
{
Title = @"tiny lamp",
Artist = @"fhana",
Author = @"Sotarks",
Source = @"ぎんぎつね",
Tags = @"lantis junichi sato yuxuki waga kevin mitsunaga towana gingitsune opening op full ver version kalibe collab collaboration",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@ -101,12 +105,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 513268,
Metadata = new BeatmapMetadata
{
Title = @"At Gwanghwamun",
Artist = @"KYUHYUN",
Author = @"Cerulean Veyron",
Source = @"",
Tags = @"soul ballad kh super junior sj suju 슈퍼주니어 kt뮤직 sm엔터테인먼트 s.m.entertainment kt music 1st mini album ep",
},
OnlineInfo = new BeatmapSetOnlineInfo
{
@ -146,12 +152,14 @@ namespace osu.Desktop.Tests.Visual
},
new BeatmapSetInfo
{
OnlineBeatmapSetID = 586841,
Metadata = new BeatmapMetadata
{
Title = @"RHAPSODY OF BLUE SKY",
Artist = @"fhana",
Author = @"[Kamiya]",
Source = @"小林さんちのメイドラゴン",
Tags = @"kobayashi san chi no maidragon aozora no opening anime maid dragon oblivion karen dynamix imoutosan pata-mon gxytcgxytc",
},
OnlineInfo = new BeatmapSetOnlineInfo
{

View File

@ -0,0 +1,84 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Menus;
namespace osu.Desktop.Tests.Visual
{
public class TestCaseEditorMenuBar : OsuTestCase
{
public TestCaseEditorMenuBar()
{
Add(new EditorMenuBar
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Y = 50,
Items = new[]
{
new EditorMenuBarItem("File")
{
Items = new[]
{
new EditorMenuItem("Clear All Notes"),
new EditorMenuItem("Open Difficulty..."),
new EditorMenuItem("Save"),
new EditorMenuItem("Create a new Difficulty..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Revert to Saved"),
new EditorMenuItem("Revert to Saved (Full)"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Test Beatmap"),
new EditorMenuItem("Open AiMod"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Upload Beatmap..."),
new EditorMenuItem("Export Package"),
new EditorMenuItem("Export Map Package"),
new EditorMenuItem("Import from..."),
new EditorMenuItemSpacer(),
new EditorMenuItem("Open Song Folder"),
new EditorMenuItem("Open .osu in Notepad"),
new EditorMenuItem("Open .osb in Notepad"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Exit"),
}
},
new EditorMenuBarItem("Timing")
{
Items = new[]
{
new EditorMenuItem("Time Signature"),
new EditorMenuItem("Metronome Clicks"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Add Timing Section"),
new EditorMenuItem("Add Inheriting Section"),
new EditorMenuItem("Reset Current Section"),
new EditorMenuItem("Delete Timing Section"),
new EditorMenuItem("Resnap Current Section"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Timing Setup"),
new EditorMenuItemSpacer(),
new EditorMenuItem("Resnap All Notes", MenuItemType.Destructive),
new EditorMenuItem("Move all notes in time...", MenuItemType.Destructive),
new EditorMenuItem("Recalculate Slider Lengths", MenuItemType.Destructive),
new EditorMenuItem("Delete All Timing Sections", MenuItemType.Destructive),
new EditorMenuItemSpacer(),
new EditorMenuItem("Set Current Position as Preview Point"),
}
},
new EditorMenuBarItem("Testing")
{
Items = new[]
{
new EditorMenuItem("Item 1"),
new EditorMenuItem("Item 2"),
new EditorMenuItem("Item 3"),
}
},
}
});
}
}
}

View File

@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using OpenTK;
@ -110,10 +109,7 @@ namespace osu.Desktop.Tests.Visual
h.Depth = depth++;
if (auto)
{
h.State = ArmedState.Hit;
h.Judgement = new OsuJudgement { Result = HitResult.Hit };
}
playfieldContainer.Add(h);
var proxyable = h as IDrawableHitObjectWithProxiedApproach;

View File

@ -2,12 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.MathUtils;
using osu.Game.Screens.Play;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
namespace osu.Desktop.Tests.Visual
@ -41,38 +37,5 @@ namespace osu.Desktop.Tests.Visual
Add(kc);
}
private class TestSliderBar<T> : SliderBar<T> where T : struct
{
public Color4 Color
{
get { return Box.Colour; }
set { Box.Colour = value; }
}
public Color4 SelectionColor
{
get { return SelectionBox.Colour; }
set { SelectionBox.Colour = value; }
}
protected readonly Box SelectionBox;
protected readonly Box Box;
public TestSliderBar()
{
Children = new Drawable[]
{
Box = new Box { RelativeSizeAxes = Axes.Both },
SelectionBox = new Box { RelativeSizeAxes = Axes.Both }
};
}
protected override void UpdateValue(float value)
{
SelectionBox.ScaleTo(
new Vector2(value, 1),
300, Easing.OutQuint);
}
}
}
}

View File

@ -1,13 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
using OpenTK;
namespace osu.Desktop.Tests.Visual
{
@ -24,7 +24,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.XH,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -42,7 +42,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.X,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -60,7 +60,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.SH,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -78,7 +78,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.S,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -96,7 +96,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.A,
Accuracy = 100,
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -114,7 +114,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.B,
Accuracy = 98.26,
Accuracy = 0.9826,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -132,7 +132,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.C,
Accuracy = 96.54,
Accuracy = 0.9654,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -150,7 +150,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
Accuracy = 60.25,
Accuracy = 0.6025,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -168,7 +168,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
Accuracy = 51.40,
Accuracy = 0.5140,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
@ -186,7 +186,7 @@ namespace osu.Desktop.Tests.Visual
new Score
{
Rank = ScoreRank.F,
Accuracy = 42.22,
Accuracy = 0.4222,
MaxCombo = 244,
TotalScore = 1707827,
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },

View File

@ -3,6 +3,7 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using OpenTK;
@ -40,8 +41,20 @@ namespace osu.Desktop.Tests.Visual
RelativeChildSize = new Vector2(1, 10000),
Children = new[]
{
new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red },
new DrawableNote(new Note { StartTime = 6000 }) { AccentColour = Color4.Red }
new DrawableNote(new Note(), ManiaAction.Key1)
{
Y = 5000,
LifetimeStart = double.MinValue,
LifetimeEnd = double.MaxValue,
AccentColour = Color4.Red
},
new DrawableNote(new Note(), ManiaAction.Key1)
{
Y = 6000,
LifetimeStart = double.MinValue,
LifetimeEnd = double.MaxValue,
AccentColour = Color4.Red
}
}
}
}
@ -62,11 +75,14 @@ namespace osu.Desktop.Tests.Visual
RelativeChildSize = new Vector2(1, 10000),
Children = new[]
{
new DrawableHoldNote(new HoldNote
new DrawableHoldNote(new HoldNote(), ManiaAction.Key1)
{
StartTime = 5000,
Duration = 1000
}) { AccentColour = Color4.Red }
Y = 5000,
Height = 1000,
LifetimeStart = double.MinValue,
LifetimeEnd = double.MaxValue,
AccentColour = Color4.Red
}
}
}
}

View File

@ -3,108 +3,36 @@
using System;
using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Timing;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Timing;
using OpenTK;
using OpenTK.Input;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Desktop.Tests.Visual
{
internal class TestCaseManiaPlayfield : OsuTestCase
{
private const double start_time = 500;
private const double duration = 500;
public override string Description => @"Mania playfield";
protected override double TimePerAction => 200;
private RulesetInfo maniaRuleset;
public TestCaseManiaPlayfield()
{
Action<int, SpecialColumnPosition> createPlayfield = (cols, pos) =>
{
Clear();
Add(new ManiaPlayfield(cols)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
SpecialColumnPosition = pos,
Scale = new Vector2(1, -1)
});
};
const double start_time = 500;
const double duration = 500;
Func<double, bool, SpeedAdjustmentContainer> createTimingChange = (time, gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
{
TimingPoint = { BeatLength = 1000 }
}, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
Action<bool> createPlayfieldWithNotes = gravity =>
{
Clear();
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
ManiaPlayfield playField;
Add(playField = new ManiaPlayfield(4)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1, -1),
Clock = new FramedClock(rateAdjustClock)
});
if (!gravity)
playField.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
for (double t = start_time; t <= start_time + duration; t += 100)
{
if (gravity)
playField.Columns.ElementAt(0).Add(createTimingChange(t, true));
playField.Add(new DrawableNote(new Note
{
StartTime = t,
Column = 0
}, new Bindable<Key>(Key.D)));
if (gravity)
playField.Columns.ElementAt(3).Add(createTimingChange(t, true));
playField.Add(new DrawableNote(new Note
{
StartTime = t,
Column = 3
}, new Bindable<Key>(Key.K)));
}
if (gravity)
playField.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
playField.Add(new DrawableHoldNote(new HoldNote
{
StartTime = start_time,
Duration = duration,
Column = 1
}, new Bindable<Key>(Key.F)));
if (gravity)
playField.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
playField.Add(new DrawableHoldNote(new HoldNote
{
StartTime = start_time,
Duration = duration,
Column = 2
}, new Bindable<Key>(Key.J)));
};
var rng = new Random(1337);
AddStep("1 column", () => createPlayfield(1, SpecialColumnPosition.Normal));
AddStep("4 columns", () => createPlayfield(4, SpecialColumnPosition.Normal));
@ -114,29 +42,121 @@ namespace osu.Desktop.Tests.Visual
AddStep("8 columns", () => createPlayfield(8, SpecialColumnPosition.Normal));
AddStep("Left special style", () => createPlayfield(8, SpecialColumnPosition.Left));
AddStep("Right special style", () => createPlayfield(8, SpecialColumnPosition.Right));
AddStep("Reversed", () => createPlayfield(4, SpecialColumnPosition.Normal, true));
AddStep("Notes with input", () => createPlayfieldWithNotes(false));
AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(false, true));
AddStep("Notes with gravity", () => createPlayfieldWithNotes(true));
AddWaitStep((int)Math.Ceiling((start_time + duration) / TimePerAction));
}
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true, true));
private void triggerKeyDown(Column column)
{
column.TriggerOnKeyDown(new InputState(), new KeyDownEventArgs
AddStep("Hit explosion", () =>
{
Key = column.Key,
Repeat = false
var playfield = createPlayfield(4, SpecialColumnPosition.Normal);
int col = rng.Next(0, 4);
var note = new DrawableNote(new Note { Column = col }, ManiaAction.Key1)
{
AccentColour = playfield.Columns.ElementAt(col).AccentColour
};
playfield.OnJudgement(note, new ManiaJudgement { Result = HitResult.Perfect });
});
}
private void triggerKeyUp(Column column)
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
column.TriggerOnKeyUp(new InputState(), new KeyUpEventArgs
maniaRuleset = rulesets.GetRuleset(3);
}
private SpeedAdjustmentContainer createTimingChange(double time, bool gravity) => new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(time)
{
TimingPoint = { BeatLength = 1000 }
}, gravity ? ScrollingAlgorithm.Gravity : ScrollingAlgorithm.Basic);
private ManiaPlayfield createPlayfield(int cols, SpecialColumnPosition specialPos, bool inverted = false)
{
Clear();
var inputManager = new ManiaInputManager(maniaRuleset, cols) { RelativeSizeAxes = Axes.Both };
Add(inputManager);
ManiaPlayfield playfield;
inputManager.Add(playfield = new ManiaPlayfield(cols)
{
Key = column.Key
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
SpecialColumnPosition = specialPos
});
playfield.Inverted.Value = inverted;
return playfield;
}
private void createPlayfieldWithNotes(bool gravity, bool inverted = false)
{
Clear();
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
var inputManager = new ManiaInputManager(maniaRuleset, 4) { RelativeSizeAxes = Axes.Both };
Add(inputManager);
ManiaPlayfield playfield;
inputManager.Add(playfield = new ManiaPlayfield(4)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Clock = new FramedClock(rateAdjustClock)
});
playfield.Inverted.Value = inverted;
if (!gravity)
playfield.Columns.ForEach(c => c.Add(createTimingChange(0, false)));
for (double t = start_time; t <= start_time + duration; t += 100)
{
if (gravity)
playfield.Columns.ElementAt(0).Add(createTimingChange(t, true));
playfield.Add(new DrawableNote(new Note
{
StartTime = t,
Column = 0
}, ManiaAction.Key1));
if (gravity)
playfield.Columns.ElementAt(3).Add(createTimingChange(t, true));
playfield.Add(new DrawableNote(new Note
{
StartTime = t,
Column = 3
}, ManiaAction.Key4));
}
if (gravity)
playfield.Columns.ElementAt(1).Add(createTimingChange(start_time, true));
playfield.Add(new DrawableHoldNote(new HoldNote
{
StartTime = start_time,
Duration = duration,
Column = 1
}, ManiaAction.Key2));
if (gravity)
playfield.Columns.ElementAt(2).Add(createTimingChange(start_time, true));
playfield.Add(new DrawableHoldNote(new HoldNote
{
StartTime = start_time,
Duration = duration,
Column = 2
}, ManiaAction.Key3));
}
}
}

View File

@ -32,7 +32,7 @@ namespace osu.Desktop.Tests.Visual
backingDatabase.CreateTable<StoreVersion>();
rulesets = new RulesetStore(backingDatabase);
manager = new BeatmapManager(storage, null, backingDatabase, rulesets);
manager = new BeatmapManager(storage, null, backingDatabase, rulesets, null);
for (int i = 0; i < 100; i += 10)
manager.Import(createTestBeatmapSet(i));

View File

@ -1,18 +1,18 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Desktop.Tests.Beatmaps;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Play;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Mods;
using System.Linq;
using osu.Game.Beatmaps.Formats;
using System.Text;
using System.IO;
namespace osu.Desktop.Tests.Visual
{
@ -33,55 +33,739 @@ namespace osu.Desktop.Tests.Visual
{
base.LoadComplete();
var objects = new List<HitObject>();
int time = 1500;
for (int i = 0; i < 50; i++)
{
objects.Add(new HitCircle
{
StartTime = time,
Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : OsuPlayfield.BASE_SIZE.X,
i % 4 < 2 ? 0 : OsuPlayfield.BASE_SIZE.Y),
NewCombo = i % 4 == 0
});
time += 500;
}
Beatmap b = new Beatmap
{
HitObjects = objects,
BeatmapInfo = new BeatmapInfo
{
Difficulty = new BeatmapDifficulty(),
Ruleset = rulesets.Query<RulesetInfo>().First(),
Metadata = new BeatmapMetadata
{
Artist = @"Unknown",
Title = @"Sample Beatmap",
Author = @"peppy",
}
}
};
WorkingBeatmap beatmap = new TestWorkingBeatmap(b);
Add(new Box
{
RelativeSizeAxes = Framework.Graphics.Axes.Both,
Colour = Color4.Black,
});
Add(Player = CreatePlayer(beatmap));
foreach (var r in rulesets.Query<RulesetInfo>())
AddStep(r.Name, () => loadPlayerFor(r));
loadPlayerFor(rulesets.Query<RulesetInfo>().First());
}
protected virtual Player CreatePlayer(WorkingBeatmap beatmap)
private void loadPlayerFor(RulesetInfo r)
{
Beatmap beatmap;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data)))
using (var reader = new StreamReader(stream))
beatmap = BeatmapDecoder.GetDecoder(reader).Decode(reader);
beatmap.BeatmapInfo.Ruleset = r;
var instance = r.CreateInstance();
WorkingBeatmap working = new TestWorkingBeatmap(beatmap);
working.Mods.Value = new[] { instance.GetAllMods().First(m => m is ModNoFail) };
if (Player != null)
Remove(Player);
Add(Player = CreatePlayer(working, instance));
}
protected virtual Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
{
return new Player
{
InitialBeatmap = beatmap
};
}
private const string test_beatmap_data =
@"osu file format v14
[General]
AudioLeadIn: 500
PreviewTime: 53498
Countdown: 0
SampleSet: Soft
StackLeniency: 0.7
Mode: 0
LetterboxInBreaks: 0
WidescreenStoryboard: 1
[Editor]
DistanceSpacing: 1.2
BeatDivisor: 4
GridSize: 4
TimelineZoom: 1
[Metadata]
Title:My Love
TitleUnicode:My Love
Artist:Kuba Oms
ArtistUnicode:Kuba Oms
Creator:W h i t e
Version:Hard
Source:ADHD
Tags:Monthly Beatmapping Contest Electronic folk pop w_h_i_t_e
BeatmapID:397534
BeatmapSetID:163112
[Difficulty]
HPDrainRate:5
CircleSize:4
OverallDifficulty:6
ApproachRate:7
SliderMultiplier:1.44
SliderTickRate:2
[Events]
//Break Periods
2,69870,83770
2,152170,158770
//Storyboard Layer 0 (Background)
//Storyboard Layer 1 (Fail)
//Storyboard Layer 2 (Pass)
//Storyboard Layer 3 (Foreground)
//Storyboard Sound Samples
[TimingPoints]
2170,468.75,4,2,0,40,1,0
4045,-100,4,2,0,30,0,0
4162,-100,4,2,0,40,0,0
5920,-100,4,2,0,30,0,0
6037,-100,4,2,0,40,0,0
7795,-100,4,2,0,30,0,0
7912,-100,4,2,0,40,0,0
9670,-100,4,2,0,40,0,0
9787,-100,4,2,0,50,0,0
11545,-100,4,2,0,40,0,0
11662,-100,4,2,0,50,0,0
13420,-100,4,2,0,40,0,0
13537,-100,4,2,0,50,0,0
15295,-100,4,2,0,40,0,0
15412,-100,4,2,0,50,0,0
17170,-100,4,2,0,40,0,0
17287,-100,4,2,0,50,0,0
19045,-100,4,2,0,40,0,0
19162,-100,4,2,0,50,0,0
20920,-100,4,2,0,40,0,0
21037,-100,4,2,0,50,0,0
22795,-100,4,2,0,40,0,0
22912,-100,4,2,0,50,0,0
24670,-100,4,2,0,70,0,0
37560,-200,4,2,0,30,0,0
38263,-200,4,2,0,5,0,0
38966,-100,4,2,0,30,0,0
39670,-100,4,2,0,70,0,0
53732,-100,4,2,0,40,0,0
54670,-100,4,2,0,80,0,1
55138,-100,4,2,0,60,0,1
55255,-100,4,2,0,80,0,1
56076,-100,4,2,0,60,0,1
56193,-100,4,2,0,80,0,1
57013,-100,4,2,0,60,0,1
57130,-100,4,2,0,80,0,1
57951,-100,4,2,0,60,0,1
58068,-100,4,2,0,80,0,1
58888,-100,4,2,0,60,0,1
59005,-100,4,2,0,80,0,1
59826,-100,4,2,0,60,0,1
59943,-100,4,2,0,80,0,1
60763,-100,4,2,0,60,0,1
60880,-100,4,2,0,80,0,1
61701,-100,4,2,0,60,0,1
61818,-100,4,2,0,80,0,1
62638,-100,4,2,0,60,0,1
62755,-100,4,2,0,80,0,1
63576,-100,4,2,0,60,0,1
63693,-100,4,2,0,80,0,1
64513,-100,4,2,0,60,0,1
64630,-100,4,2,0,80,0,1
65451,-100,4,2,0,60,0,1
65568,-100,4,2,0,80,0,1
66388,-100,4,2,0,60,0,1
66505,-100,4,2,0,80,0,1
67326,-100,4,2,0,60,0,1
67443,-100,4,2,0,80,0,1
68263,-100,4,2,0,60,0,1
68380,-100,4,2,0,80,0,1
69201,-100,4,2,0,60,0,1
69318,-100,4,2,0,80,0,1
69670,-100,4,2,0,70,0,0
84670,-100,4,2,0,70,0,0
97560,-200,4,2,0,70,0,0
97795,-200,4,2,0,30,0,0
98966,-100,4,2,0,30,0,0
99670,-100,4,2,0,70,0,0
113732,-100,4,2,0,40,0,0
114670,-100,4,2,0,80,0,1
115138,-100,4,2,0,60,0,1
115255,-100,4,2,0,80,0,1
116076,-100,4,2,0,60,0,1
116193,-100,4,2,0,80,0,1
117013,-100,4,2,0,60,0,1
117130,-100,4,2,0,80,0,1
117951,-100,4,2,0,60,0,1
118068,-100,4,2,0,80,0,1
118888,-100,4,2,0,60,0,1
119005,-100,4,2,0,80,0,1
119826,-100,4,2,0,60,0,1
119943,-100,4,2,0,80,0,1
120763,-100,4,2,0,60,0,1
120880,-100,4,2,0,80,0,1
121701,-100,4,2,0,60,0,1
121818,-100,4,2,0,80,0,1
122638,-100,4,2,0,60,0,1
122755,-100,4,2,0,80,0,1
123576,-100,4,2,0,60,0,1
123693,-100,4,2,0,80,0,1
124513,-100,4,2,0,60,0,1
124630,-100,4,2,0,80,0,1
125451,-100,4,2,0,60,0,1
125568,-100,4,2,0,80,0,1
126388,-100,4,2,0,60,0,1
126505,-100,4,2,0,80,0,1
127326,-100,4,2,0,60,0,1
127443,-100,4,2,0,80,0,1
128263,-100,4,2,0,60,0,1
128380,-100,4,2,0,80,0,1
129201,-100,4,2,0,60,0,1
129318,-100,4,2,0,80,0,1
129670,-200,4,2,0,40,0,0
144670,-133.333333333333,4,2,0,40,0,0
159670,-133.333333333333,4,2,0,40,0,0
163420,-133.333333333333,4,2,0,45,0,0
163888,-125,4,2,0,50,0,0
164357,-117.647058823529,4,2,0,55,0,0
164826,-111.111111111111,4,2,0,60,0,0
165295,-105.263157894737,4,2,0,65,0,0
165763,-100,4,2,0,70,0,0
166232,-100,4,2,0,40,0,0
167170,-100,4,2,0,80,0,1
167638,-100,4,2,0,60,0,1
167755,-100,4,2,0,80,0,1
168576,-100,4,2,0,60,0,1
168693,-100,4,2,0,80,0,1
169513,-100,4,2,0,60,0,1
169630,-100,4,2,0,80,0,1
170451,-100,4,2,0,60,0,1
170568,-100,4,2,0,80,0,1
171388,-100,4,2,0,60,0,1
171505,-100,4,2,0,80,0,1
172326,-100,4,2,0,60,0,1
172443,-100,4,2,0,80,0,1
173263,-100,4,2,0,60,0,1
173380,-100,4,2,0,80,0,1
174201,-100,4,2,0,60,0,1
174318,-100,4,2,0,80,0,1
175138,-100,4,2,0,60,0,1
175255,-100,4,2,0,80,0,1
176076,-100,4,2,0,60,0,1
176193,-100,4,2,0,80,0,1
177013,-100,4,2,0,60,0,1
177130,-100,4,2,0,80,0,1
177951,-100,4,2,0,60,0,1
178068,-100,4,2,0,80,0,1
178888,-100,4,2,0,60,0,1
179005,-100,4,2,0,80,0,1
179826,-100,4,2,0,60,0,1
179943,-100,4,2,0,80,0,1
180763,-100,4,2,0,60,0,1
180880,-100,4,2,0,80,0,1
180998,-100,4,2,0,80,0,0
181466,-100,4,2,0,60,0,0
181584,-100,4,2,0,80,0,0
181935,-100,4,2,0,80,0,0
182170,-100,4,2,0,80,0,1
182638,-100,4,2,0,60,0,1
182755,-100,4,2,0,80,0,1
183576,-100,4,2,0,60,0,1
183693,-100,4,2,0,80,0,1
184513,-100,4,2,0,60,0,1
184630,-100,4,2,0,80,0,1
185451,-100,4,2,0,60,0,1
185568,-100,4,2,0,80,0,1
186388,-100,4,2,0,60,0,1
186505,-100,4,2,0,80,0,1
187326,-100,4,2,0,60,0,1
187443,-100,4,2,0,80,0,1
188263,-100,4,2,0,60,0,1
188380,-100,4,2,0,80,0,1
189201,-100,4,2,0,60,0,1
189318,-100,4,2,0,80,0,1
190138,-100,4,2,0,60,0,1
190255,-100,4,2,0,80,0,1
191076,-100,4,2,0,60,0,1
191193,-100,4,2,0,80,0,1
192013,-100,4,2,0,60,0,1
192130,-100,4,2,0,80,0,1
192951,-100,4,2,0,60,0,1
193068,-100,4,2,0,80,0,1
193888,-100,4,2,0,60,0,1
194005,-100,4,2,0,80,0,1
194826,-100,4,2,0,60,0,1
194943,-100,4,2,0,80,0,1
195295,-100,4,2,0,50,0,1
195529,-100,4,2,0,52,0,1
195646,-100,4,2,0,54,0,1
195763,-100,4,2,0,56,0,1
195880,-100,4,2,0,58,0,1
195998,-100,4,2,0,60,0,1
196115,-100,4,2,0,62,0,1
196232,-100,4,2,0,64,0,1
196349,-100,4,2,0,68,0,1
196466,-100,4,2,0,70,0,1
196584,-100,4,2,0,72,0,1
196701,-100,4,2,0,74,0,1
196818,-100,4,2,0,76,0,1
196935,-100,4,2,0,78,0,1
197052,-100,4,2,0,80,0,1
197170,-100,4,2,0,80,0,0
197873,-100,4,2,0,60,0,0
197990,-100,4,2,0,80,0,0
198341,-100,4,2,0,60,0,0
199045,-100,4,2,0,80,0,0
199279,-100,4,2,0,60,0,0
199630,-100,4,2,0,80,0,0
200216,-100,4,2,0,60,0,0
200334,-100,4,2,0,80,0,0
201623,-100,4,2,0,60,0,0
201740,-100,4,2,0,80,0,0
202326,-100,4,2,0,60,0,0
202443,-100,4,2,0,80,0,0
203029,-100,4,2,0,60,0,0
203498,-100,4,2,0,80,0,0
203966,-100,4,2,0,60,0,0
204201,-100,4,2,0,80,0,0
205373,-100,4,2,0,60,0,0
205490,-100,4,2,0,80,0,0
205841,-100,4,2,0,60,0,0
206076,-100,4,2,0,60,0,0
206545,-100,4,2,0,80,0,0
206779,-100,4,2,0,60,0,0
207130,-100,4,2,0,80,0,0
207716,-100,4,2,0,60,0,0
207951,-100,4,2,0,80,0,0
209123,-100,4,2,0,60,0,0
209240,-100,4,2,0,80,0,0
209826,-100,4,2,0,60,0,0
209943,-100,4,2,0,80,0,0
210529,-100,4,2,0,60,0,0
210880,-100,4,2,0,80,0,0
211232,-100,4,2,0,60,0,0
211701,-100,4,2,0,70,0,0
212170,-100,4,2,0,80,0,0
212873,-100,4,2,0,60,0,0
212990,-100,4,2,0,80,0,0
213341,-100,4,2,0,60,0,0
213576,-100,4,2,0,60,0,0
214045,-100,4,2,0,80,0,0
214279,-100,4,2,0,60,0,0
214630,-100,4,2,0,80,0,0
215216,-100,4,2,0,60,0,0
215451,-100,4,2,0,80,0,0
216623,-100,4,2,0,60,0,0
216740,-100,4,2,0,80,0,0
217326,-100,4,2,0,60,0,0
217443,-100,4,2,0,80,0,0
218029,-100,4,2,0,60,0,0
218498,-100,4,2,0,80,0,0
218732,-100,4,2,0,50,0,0
219670,-100,4,2,0,70,0,0
220138,-100,4,2,0,65,0,0
220373,-100,4,2,0,45,0,0
220490,-100,4,2,0,65,0,0
220607,-100,4,2,0,60,0,0
220841,-100,4,2,0,35,0,0
221076,-100,4,2,0,35,0,0
221545,-100,4,2,0,50,0,0
221779,-100,4,2,0,30,0,0
222013,-111.111111111111,4,2,0,25,0,0
222130,-111.111111111111,4,2,0,40,0,0
222482,-125,4,2,0,40,0,0
222716,-125,4,2,0,20,0,0
222951,-100,4,2,0,15,0,0
223420,-100,4,2,0,30,0,0
224357,-100,4,2,0,25,0,0
225295,-100,4,2,0,20,0,0
226232,-100,4,2,0,15,0,0
226701,-100,4,2,0,10,0,0
227170,-100,4,2,0,5,0,0
[Colours]
Combo1 : 17,254,176
Combo2 : 173,255,95
Combo3 : 255,88,100
Combo4 : 255,94,55
[HitObjects]
320,256,2170,6,0,P|256:284|192:256,1,144,4|0,0:0|0:0,0:0:0:0:
144,184,2873,1,0,0:0:0:0:
108,260,3107,2,0,P|112:296|100:336,1,72
28,288,3576,2,0,P|24:252|36:212,1,72,0|0,0:0|0:0,0:0:0:0:
76,140,4045,6,0,L|220:136,1,144,4|0,0:0|0:0,0:0:0:0:
292,88,4748,1,0,0:0:0:0:
292,88,4982,2,0,P|304:120|300:168,1,72
388,168,5451,2,0,P|396:133|416:103,1,72,0|0,0:0|0:0,0:0:0:0:
472,172,5920,6,0,B|470:200|457:222|457:222|488:256|476:308,1,144,4|0,0:0|0:0,0:0:0:0:
396,280,6623,1,0,0:0:0:0:
324,328,6857,2,0,P|288:332|252:324,1,72
180,280,7326,2,0,L|108:284,1,72,0|0,0:0|0:0,0:0:0:0:
256,192,7795,12,0,9670,0:0:0:0:
428,212,10138,1,0,0:0:0:0:
292,320,10607,1,0,0:0:0:0:
184,184,11076,2,0,L|112:180,1,72,0|0,0:0|0:0,0:0:0:0:
24,172,11545,5,6,0:0:0:0:
160,280,12013,1,0,0:0:0:0:
268,144,12482,1,0,0:0:0:0:
132,36,12951,2,0,L|204:32,1,72,0|0,0:0|0:0,0:0:0:0:
284,60,13420,6,0,P|340:100|344:180,2,144,6|0|0,0:0|0:0|0:0,0:0:0:0:
268,144,14591,1,0,0:0:0:0:
284,228,14826,2,0,P|316:248|364:252,1,72,0|0,0:0|0:0,0:0:0:0:
436,248,15295,6,0,P|372:272|344:340,1,144,6|2,0:0|0:0,0:0:0:0:
168,338,16232,2,0,P|141:273|76:248,1,144,2|2,0:0|0:0,0:0:0:0:
4,296,16935,1,0,0:0:0:0:
80,336,17170,5,6,0:0:0:0:
44,168,17638,1,0,0:0:0:0:
212,128,18107,1,0,0:0:0:0:
248,296,18576,2,0,P|284:288|320:292,1,72,0|0,0:0|0:0,0:0:0:0:
400,324,19045,5,6,0:0:0:0:
280,200,19513,1,0,0:0:0:0:
368,52,19982,1,0,0:0:0:0:
488,176,20451,2,0,P|452:168|416:172,1,72,0|0,0:0|0:0,0:0:0:0:
336,200,20920,6,0,P|284:216|200:192,1,144,6|0,0:0|0:0,0:0:0:0:
200,192,21857,2,0,L|204:264,1,72,0|0,0:3|0:0,0:0:0:0:
117,244,22326,2,0,L|120:172,1,72,0|0,0:0|0:0,0:0:0:0:
40,152,22795,6,0,L|28:296,2,144,6|0|0,0:0|0:0|0:0,0:0:0:0:
152,24,24201,1,0,0:0:0:0:
220,76,24435,1,0,3:0:0:0:
304,56,24670,6,0,P|288:120|296:196,1,144,4|2,0:3|0:3,0:0:0:0:
344,268,25373,1,0,0:0:0:0:
416,316,25607,2,0,P|452:312|508:316,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
244,344,26545,6,0,P|176:356|108:328,1,144,4|2,0:3|0:3,0:0:0:0:
60,256,27248,1,0,0:0:0:0:
36,172,27482,2,0,L|40:100,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
188,252,28420,6,0,P|192:184|196:100,1,144,4|2,0:3|0:3,0:0:0:0:
140,40,29123,1,0,0:0:0:0:
140,40,29357,2,0,B|172:16|220:24|220:24|288:36,1,144,0|2,0:0|0:3,0:0:0:0:
364,52,30060,1,0,0:0:0:0:
308,116,30295,6,0,B|300:168|300:168|328:256,1,144,4|2,0:3|0:3,0:0:0:0:
340,340,30998,1,0,0:0:0:0:
260,308,31232,2,0,L|188:304,1,72,0|2,0:0|0:3,0:0:0:0:
100,296,31701,1,2,0:3:0:0:
136,374,31935,1,0,0:0:0:0:
152,224,32170,6,0,P|160:152|132:88,1,144,4|2,0:3|0:3,0:0:0:0:
56,48,32873,1,0,0:0:0:0:
60,136,33107,2,0,L|56:208,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
224,76,34045,6,0,P|289:104|360:96,1,144,4|2,0:3|0:3,0:0:0:0:
432,48,34748,1,0,0:0:0:0:
440,132,34982,2,0,B|432:156|432:156|436:204,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
448,304,35920,6,0,B|412:315|380:292|380:292|348:269|312:280,1,144,4|2,0:3|0:3,0:0:0:0:
332,364,36623,1,0,0:0:0:0:
247,339,36857,2,0,P|230:308|225:273,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
312,280,37560,6,0,L|316:172,1,108
134,35,38966,5,0,0:0:0:0:
72,96,39201,2,0,P|119:119|171:111,1,108,0|0,0:0|0:0,0:0:0:0:
192,100,39670,6,0,L|200:172,1,72,4|2,0:0|0:0,0:0:0:0:
147,240,40138,2,0,P|133:272|132:308,1,72,0|2,1:0|0:0,0:0:0:0:
216,292,40607,2,0,B|260:308|260:308|356:292,1,144,4|0,2:3|1:0,1:0:0:0:
356,292,41310,1,2,0:0:0:0:
436,327,41545,6,0,P|441:292|435:257,1,72,4|2,0:3|0:0,0:0:0:0:
364,204,42013,2,0,P|336:144|352:68,1,144,0|4,1:0|2:3,1:0:0:0:
404,0,42716,1,2,0:0:0:0:
440,80,42951,2,0,B|464:84|464:84|512:80,1,72,0|2,1:0|0:0,0:0:0:0:
351,71,43420,6,0,B|296:68|296:68|268:76|268:76|196:72,1,144,4|0,2:3|1:0,1:0:0:0:
120,68,44123,1,2,0:0:0:0:
160,144,44357,2,0,P|172:180|168:232,1,72,4|2,0:3|0:0,0:0:0:0:
76,264,44826,2,0,P|76:228|88:194,1,72,0|2,1:0|0:0,0:0:0:0:
160,144,45295,5,4,0:3:0:0:
244,164,45529,1,2,0:0:0:0:
268,248,45763,2,0,L|344:252,1,72,0|2,1:0|0:0,0:0:0:0:
408,156,46232,2,0,L|336:159,1,72,4|2,0:3|0:0,0:0:0:0:
212,72,46701,2,0,L|288:76,1,72,0|2,1:0|0:0,0:0:0:0:
400,72,47170,6,0,P|464:96|488:172,1,144,4|0,2:0|1:0,1:0:0:0:
476,248,47873,1,2,0:0:0:0:
436,324,48107,2,0,L|284:320,1,144,4|0,2:3|1:0,1:0:0:0:
204,316,48810,1,2,0:0:0:0:
127,355,49045,6,0,P|120:321|124:285,1,72,4|2,0:3|0:0,0:0:0:0:
192,232,49513,2,0,L|335:228,1,144,0|4,1:0|2:3,1:0:0:0:
412,188,50216,1,2,0:0:0:0:
444,108,50451,2,0,P|452:72|448:36,1,72,0|2,1:0|0:0,0:0:0:0:
368,68,50920,6,0,B|332:79|300:56|300:56|268:33|232:44,1,144,4|0,2:3|1:0,1:0:0:0:
152,76,51623,1,2,0:0:0:0:
76,116,51857,2,0,L|80:268,1,144,4|0,2:3|1:0,1:0:0:0:
80,260,52560,1,2,0:0:0:0:
8,308,52795,6,0,P|34:334|69:346,1,72,4|2,0:3|0:0,0:0:0:0:
148,312,53263,2,0,P|163:278|162:241,1,72,0|2,1:0|0:0,0:0:0:0:
156,156,53732,5,0,3:0:0:0:
156,156,53966,1,2,0:0:0:0:
236,196,54201,2,0,L|312:192,1,72,8|0,0:3|0:0,0:0:0:0:
368,256,54670,6,0,P|392:216|352:116,1,144,4|2,0:0|1:2,0:0:0:0:
288,92,55373,1,0,0:0:0:0:
360,40,55607,2,0,L|432:36,1,72,4|0,0:3|3:0,0:0:0:0:
288,92,56076,2,0,L|216:88,1,72,2|0,1:2|0:0,0:0:0:0:
132,72,56545,6,0,P|172:88|200:184,1,144,4|2,0:3|1:2,0:0:0:0:
143,241,57248,1,0,0:0:0:0:
65,202,57482,2,0,P|87:174|119:157,1,72,4|0,0:3|3:0,0:0:0:0:
132,324,57951,2,0,P|98:312|72:288,1,72,2|0,1:2|0:0,0:0:0:0:
143,241,58420,6,0,L|288:240,1,144,4|2,0:3|1:2,0:0:0:0:
372,240,59123,1,0,0:0:0:0:
330,314,59357,2,0,P|318:350|322:390,1,72,4|0,0:3|3:0,0:0:0:0:
452,264,59826,2,0,P|453:228|442:194,1,72,2|0,1:2|0:0,0:0:0:0:
384,128,60295,6,0,B|336:144|336:144|244:128,1,144,4|2,0:3|1:2,0:0:0:0:
164,160,60998,2,0,P|160:116|168:88,1,72,0|4,0:0|0:3,0:0:0:0:
244,128,61466,2,0,P|248:172|240:200,1,72,0|2,3:0|1:2,0:0:0:0:
168,248,61935,1,0,0:0:0:0:
120,320,62170,6,0,P|196:328|252:272,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
80,244,63341,1,0,3:0:0:0:
100,160,63576,2,0,L|24:156,1,72,2|0,1:2|0:0,0:0:0:0:
180,128,64045,6,0,P|249:138|304:94,1,144,4|2,0:3|1:2,0:0:0:0:
226,57,64748,1,0,0:0:0:0:
304,94,64982,2,0,L|300:166,1,72,4|0,0:3|3:0,0:0:0:0:
377,203,65451,2,0,L|388:132,1,72,2|0,1:2|0:0,0:0:0:0:
468,180,65920,6,0,L|432:328,1,144,4|2,0:3|1:2,0:0:0:0:
276,252,66857,2,0,P|208:248|140:280,1,144,4|2,0:3|1:2,0:0:0:0:
84,344,67560,1,0,0:0:0:0:
56,260,67795,6,0,L|52:188,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
168,128,68732,2,0,L|172:56,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
244,168,69435,1,0,0:0:0:0:
332,164,69670,1,4,0:3:0:0:
208,328,84670,6,0,P|224:264|216:188,1,144,4|2,0:3|0:3,0:0:0:0:
168,116,85373,1,0,0:0:0:0:
96,68,85607,2,0,P|60:72|4:68,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
268,40,86545,6,0,P|336:28|404:56,1,144,4|2,0:3|0:3,0:0:0:0:
452,128,87248,1,0,0:0:0:0:
476,212,87482,2,0,L|472:284,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
324,132,88420,6,0,P|320:200|316:284,1,144,4|2,0:3|0:3,0:0:0:0:
372,344,89123,1,0,0:0:0:0:
372,344,89357,2,0,B|340:368|292:360|292:360|224:348,1,144,0|2,0:0|0:3,0:0:0:0:
148,332,90060,1,0,0:0:0:0:
204,268,90295,6,0,B|212:216|212:216|184:128,1,144,4|2,0:3|0:3,0:0:0:0:
172,44,90998,1,0,0:0:0:0:
252,76,91232,2,0,L|324:80,1,72,0|2,0:0|0:3,0:0:0:0:
412,88,91701,1,2,0:3:0:0:
377,9,91935,1,0,0:0:0:0:
360,160,92170,6,0,P|352:232|380:296,1,144,4|2,0:3|0:3,0:0:0:0:
456,336,92873,1,0,0:0:0:0:
452,248,93107,2,0,L|456:176,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
288,308,94045,6,0,P|223:280|152:288,1,144,4|2,0:3|0:3,0:0:0:0:
80,336,94748,1,0,0:0:0:0:
72,252,94982,2,0,B|80:228|80:228|76:180,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
64,80,95920,6,0,B|100:69|132:92|132:92|164:115|200:104,1,144,4|2,0:3|0:3,0:0:0:0:
180,20,96623,1,0,0:0:0:0:
265,45,96857,2,0,P|282:76|287:111,2,72,0|0|2,0:0|0:0|0:3,0:0:0:0:
200,104,97560,1,0,0:0:0:0:
200,104,97677,1,0,0:0:0:0:
200,104,97795,6,0,B|196:142|217:166|217:166|176:180|160:220,1,144,4|0,0:3|0:0,0:0:0:0:
240,248,98966,5,0,0:0:0:0:
202,325,99201,2,0,P|254:333|301:309,1,108,0|0,0:0|0:0,0:0:0:0:
315,292,99670,6,0,L|323:220,1,72,4|2,0:0|0:0,0:0:0:0:
365,144,100138,2,0,P|379:112|380:76,1,72,0|2,1:0|0:0,0:0:0:0:
296,92,100607,2,0,B|252:76|252:76|156:92,1,144,4|0,2:3|1:0,1:0:0:0:
156,92,101310,1,2,0:0:0:0:
76,57,101545,6,0,P|71:92|77:127,1,72,4|2,0:3|0:0,0:0:0:0:
148,180,102013,2,0,P|176:240|160:316,1,144,0|4,1:0|2:3,1:0:0:0:
108,384,102716,1,2,0:0:0:0:
72,304,102951,2,0,B|48:300|48:300|0:304,1,72,0|2,1:0|0:0,0:0:0:0:
161,313,103420,6,0,B|216:316|216:316|244:308|244:308|316:312,1,144,4|0,2:3|1:0,1:0:0:0:
392,316,104123,1,2,0:0:0:0:
352,240,104357,2,0,P|340:204|344:152,1,72,4|2,0:3|0:0,0:0:0:0:
436,120,104826,2,0,P|436:156|424:190,1,72,0|2,1:0|0:0,0:0:0:0:
352,240,105295,5,4,0:3:0:0:
268,220,105529,1,2,0:0:0:0:
244,136,105763,2,0,L|168:132,1,72,0|2,1:0|0:0,0:0:0:0:
104,228,106232,2,0,L|176:225,1,72,4|2,0:3|0:0,0:0:0:0:
300,312,106701,2,0,L|224:308,1,72,0|2,1:0|0:0,0:0:0:0:
112,312,107170,6,0,P|48:288|24:212,1,144,4|0,2:0|1:0,1:0:0:0:
36,136,107873,1,2,0:0:0:0:
76,60,108107,2,0,L|228:64,1,144,4|0,2:3|1:0,1:0:0:0:
308,68,108810,1,2,0:0:0:0:
385,29,109045,6,0,P|392:63|388:99,1,72,4|2,0:3|0:0,0:0:0:0:
320,152,109513,2,0,L|177:156,1,144,0|4,1:0|2:3,1:0:0:0:
100,196,110216,1,2,0:0:0:0:
68,276,110451,2,0,P|60:312|64:348,1,72,0|2,1:0|0:0,0:0:0:0:
144,316,110920,6,0,B|180:305|212:328|212:328|244:351|280:340,1,144,4|0,2:3|1:0,1:0:0:0:
360,308,111623,1,2,0:0:0:0:
436,268,111857,2,0,L|432:116,1,144,4|0,2:3|1:0,1:0:0:0:
432,124,112560,1,2,0:0:0:0:
504,76,112795,6,0,P|478:50|443:38,1,72,4|2,0:3|0:0,0:0:0:0:
364,72,113263,2,0,P|349:106|350:143,1,72,0|2,1:0|0:0,0:0:0:0:
356,228,113732,5,0,3:0:0:0:
356,228,113966,1,2,0:0:0:0:
276,188,114201,2,0,L|200:192,1,72,8|0,0:3|0:0,0:0:0:0:
144,128,114670,6,0,P|120:168|160:268,1,144,4|2,0:0|1:2,0:0:0:0:
224,292,115373,1,0,0:0:0:0:
152,344,115607,2,0,L|80:348,1,72,4|0,0:3|3:0,0:0:0:0:
224,292,116076,2,0,L|296:296,1,72,2|0,1:2|0:0,0:0:0:0:
380,312,116545,6,0,P|340:296|312:200,1,144,4|2,0:3|1:2,0:0:0:0:
369,143,117248,1,0,0:0:0:0:
447,182,117482,2,0,P|425:210|393:227,1,72,4|0,0:3|3:0,0:0:0:0:
380,60,117951,2,0,P|414:72|440:96,1,72,2|0,1:2|0:0,0:0:0:0:
369,143,118420,6,0,L|224:144,1,144,4|2,0:3|1:2,0:0:0:0:
140,144,119123,1,0,0:0:0:0:
182,70,119357,2,0,P|194:34|190:-6,1,72,4|0,0:3|3:0,0:0:0:0:
60,120,119826,2,0,P|59:156|70:190,1,72,2|0,1:2|0:0,0:0:0:0:
128,256,120295,6,0,B|176:240|176:240|268:256,1,144,4|2,0:3|1:2,0:0:0:0:
348,224,120998,2,0,P|352:268|344:296,1,72,0|4,0:0|0:3,0:0:0:0:
268,256,121466,2,0,P|264:212|272:184,1,72,0|2,3:0|1:2,0:0:0:0:
344,136,121935,1,0,0:0:0:0:
392,64,122170,6,0,P|316:56|260:112,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
432,140,123341,1,0,3:0:0:0:
412,224,123576,2,0,L|488:228,1,72,2|0,1:2|0:0,0:0:0:0:
332,256,124045,6,0,P|263:246|208:290,1,144,4|2,0:3|1:2,0:0:0:0:
286,327,124748,1,0,0:0:0:0:
208,290,124982,2,0,L|212:218,1,72,4|0,0:3|3:0,0:0:0:0:
135,181,125451,2,0,L|124:252,1,72,2|0,1:2|0:0,0:0:0:0:
44,204,125920,6,0,L|80:56,1,144,4|2,0:3|1:2,0:0:0:0:
236,132,126857,2,0,P|304:136|372:104,1,144,4|2,0:3|1:2,0:0:0:0:
428,40,127560,1,0,0:0:0:0:
456,124,127795,6,0,L|460:196,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
344,256,128732,2,0,L|340:328,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
268,216,129435,1,0,0:0:0:0:
180,220,129670,5,4,2:0:0:0:
256,40,130373,1,2,0:0:0:0:
64,68,131076,1,2,0:0:0:0:
92,136,131310,1,0,0:0:0:0:
64,204,131545,6,0,L|60:288,1,72
31,343,132248,2,0,P|86:345|127:309,1,108
332,220,133420,5,2,0:0:0:0:
256,40,134123,1,2,0:0:0:0:
448,68,134826,1,2,0:0:0:0:
420,136,135060,1,0,0:0:0:0:
448,204,135295,6,0,L|452:288,1,72,2|0,0:0|0:0,0:0:0:0:
480,343,135998,2,0,P|426:345|385:309,1,108
256,192,137170,5,2,0:0:0:0:
156,360,137873,1,2,0:0:0:0:
356,360,138576,2,0,L|352:308,1,36,2|0,0:0|0:0,0:0:0:0:
304,268,139045,6,0,P|336:253|372:252,1,72
448,260,139748,2,0,L|444:152,1,108
256,192,140920,5,2,0:0:0:0:
356,24,141623,1,2,0:0:0:0:
156,24,142326,2,0,L|160:72,1,36,2|0,0:0|0:0,0:0:0:0:
208,116,142795,6,0,P|176:131|140:132,1,72,2|0,0:0|0:0,0:0:0:0:
64,124,143498,2,0,L|68:232,1,108
68,232,144670,5,4,0:3:0:0:
216,320,145138,1,4,0:3:0:0:
304,172,145607,1,4,0:3:0:0:
156,84,146075,1,4,0:3:0:0:
296,320,146545,5,4,0:3:0:0:
208,172,147013,1,4,0:3:0:0:
356,84,147482,1,4,0:3:0:0:
444,232,147950,1,4,0:3:0:0:
296,320,148420,6,0,P|252:328|192:296,2,108.000004119873,4|4|4,0:3|0:3|0:3,0:0:0:0:
260,248,149591,1,0,0:0:0:0:
320,196,149826,2,0,L|316:140,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0:
120,236,159670,6,0,L|176:232,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0:
160,152,160138,2,0,L|104:156,1,54.0000020599366,2|0,0:0|0:0,0:0:0:0:
240,180,160607,2,0,P|292:188|344:172,1,108.000004119873,4|2,0:3|0:0,3:0:0:0:
408,120,161310,1,0,3:0:0:0:
424,200,161545,6,0,L|420:256,1,54.0000020599366,4|0,0:3|0:0,0:0:0:0:
376,320,162013,2,0,P|396:328|480:304,2,108.000004119873,2|6|2,2:0|0:3|2:0,3:0:0:0:
312,268,163185,1,0,0:0:0:0:
296,348,163420,6,0,L|240:344,1,54.0000020599366,4|0,3:0|3:0,0:0:0:0:
160,320,163888,2,0,L|100:316,1,57.6,4|0,3:0|3:0,0:0:0:0:
64,232,164357,6,0,L|128:228,1,61.2000011672974,4|0,3:0|3:0,0:0:0:0:
204,200,164825,2,0,L|268:196,1,61.2000011672974,4|0,3:0|3:0,0:0:0:0:
232,108,165295,6,0,L|164:104,1,68.399998173523,4|0,3:0|3:0,0:0:0:0:
80,84,165763,2,0,L|4:80,1,72,4|0,3:0|3:0,0:0:0:0:
324,120,167170,6,0,P|388:128|456:92,1,144,4|2,0:0|1:2,0:0:0:0:
496,168,167873,1,0,0:0:0:0:
496,168,168107,2,0,P|484:204|488:256,1,72,4|0,0:3|3:0,0:0:0:0:
408,296,168576,2,0,P|398:261|378:231,1,72,2|0,1:2|0:0,0:0:0:0:
296,200,169045,6,0,B|228:228|156:204,1,144,4|2,0:3|1:2,0:0:0:0:
84,156,169748,1,0,0:0:0:0:
80,244,169982,2,0,L|76:316,1,72,4|0,0:3|3:0,0:0:0:0:
170,274,170451,2,0,L|156:204,1,72,2|0,1:2|0:0,0:0:0:0:
216,140,170920,6,0,L|284:276,1,144,4|2,0:3|1:2,0:0:0:0:
320,344,171623,1,0,0:0:0:0:
372,276,171857,2,0,P|366:240|349:207,1,72,4|0,0:3|3:0,0:0:0:0:
312,132,172326,2,0,L|276:60,1,72,2|0,1:2|0:0,0:0:0:0:
208,20,172795,6,0,P|272:36|348:12,1,144,4|2,0:3|1:2,0:0:0:0:
424,48,173498,2,0,L|412:132,1,72,0|4,0:0|0:3,0:0:0:0:
484,168,173966,2,0,L|472:252,1,72,0|2,3:0|1:2,0:0:0:0:
400,280,174435,1,0,0:0:0:0:
346,348,174670,6,0,P|414:363|472:324,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
312,268,175841,1,0,3:0:0:0:
256,336,176076,2,0,L|184:332,1,72,2|0,1:2|0:0,0:0:0:0:
80,244,176545,6,0,B|140:248|140:248|164:244|164:244|223:247,1,144,4|2,0:3|1:2,0:0:0:0:
312,268,177248,1,0,0:0:0:0:
224,247,177482,2,0,P|240:215|272:187,1,72,4|0,0:3|3:0,0:0:0:0:
204,131,177951,2,0,P|233:111|275:103,1,72,2|0,1:2|0:0,0:0:0:0:
240,23,178420,6,0,B|280:15|316:35|316:35|376:71,1,144,4|2,0:3|1:2,0:0:0:0:
399,236,179357,2,0,B|359:244|323:224|323:224|263:188,1,144,4|2,0:3|1:2,0:0:0:0:
204,132,180060,1,0,0:0:0:0:
184,216,180295,6,0,L|188:288,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
120,156,180998,1,0,0:0:0:0:
56,96,181232,2,0,L|60:24,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
36,180,181935,1,0,0:0:0:0:
100,240,182170,6,0,P|144:300|116:380,2,144,4|2|4,0:0|1:2|0:3,0:0:0:0:
60,316,183341,1,0,0:0:0:0:
220,352,183576,2,0,L|308:348,1,72,2|0,1:2|0:0,0:0:0:0:
396,264,184045,6,0,B|336:268|336:268|312:264|312:264|253:267,1,144,4|2,0:3|1:2,0:0:0:0:
253,267,184748,1,0,0:0:0:0:
268,180,184982,2,0,L|339:177,1,72,4|0,0:3|0:0,0:0:0:0:
164,280,185451,2,0,L|92:282,1,72,2|0,1:2|0:0,0:0:0:0:
52,208,185920,6,0,P|8:268|32:344,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
140,212,187091,1,0,0:0:0:0:
92,284,187326,2,0,P|104:316|100:368,1,72,2|0,1:2|0:0,0:0:0:0:
52,208,187795,6,0,P|48:136|76:72,1,144,4|2,0:3|1:2,0:0:0:0:
160,52,188498,2,0,P|188:28|220:16,1,72,0|4,0:0|0:3,0:0:0:0:
232,100,188966,2,0,P|268:93|301:98,1,72,0|2,0:0|1:2,0:0:0:0:
372,152,189435,1,0,0:0:0:0:
420,224,189670,6,0,P|428:296|400:360,2,144,4|2|4,0:3|1:2|0:3,0:0:0:0:
372,152,190841,1,0,0:0:0:0:
392,68,191076,2,0,L|465:64,1,72,2|0,1:2|0:0,0:0:0:0:
304,92,191545,6,0,P|236:104|168:76,1,144,4|2,0:3|1:2,0:0:0:0:
108,12,192248,1,0,0:0:0:0:
168,76,192482,2,0,L|172:152,1,72,4|0,0:3|0:0,0:0:0:0:
80,136,192951,2,0,L|101:204,1,72,2|0,1:2|0:0,0:0:0:0:
12,220,193420,6,0,B|50:279|50:279|80:300|120:292,1,144,4|2,0:3|1:2,0:0:0:0:
284,232,194357,2,0,B|320:221|352:244|352:244|384:267|420:256,1,144,4|2,0:3|1:2,0:0:0:0:
488,200,195060,1,0,0:0:0:0:
507,284,195295,6,0,P|492:315|464:338,1,72,4|0,0:0|0:0,0:0:0:0:
380,356,195763,2,0,L|236:352,1,144,0|4,1:0|0:3,0:0:0:0:
152,328,196466,1,0,3:0:0:0:
64,336,196701,2,0,P|29:325|4:300,1,72,0|0,1:0|0:0,0:0:0:0:
76,252,197170,6,0,P|108:188|96:116,1,144,4|0,0:0|1:0,0:0:0:0:
36,56,197873,1,2,0:0:0:0:
120,32,198107,2,0,L|192:28,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
248,152,199045,6,0,P|280:168|304:196,1,72,4|2,0:3|0:0,0:0:0:0:
336,277,199513,2,0,P|306:296|269:303,1,72,2|0,1:2|0:0,0:0:0:0:
183,290,199982,2,0,P|180:254|193:219,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
436,252,200920,6,0,P|404:188|416:116,1,144,4|0,0:3|1:0,0:0:0:0:
476,56,201623,1,2,0:0:0:0:
392,32,201857,2,0,L|320:28,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0:
264,152,202795,6,0,P|232:168|208:196,1,72,4|2,0:3|0:0,0:0:0:0:
176,277,203263,2,0,P|205:296|242:303,1,72,2|0,1:2|0:0,0:0:0:0:
329,290,203732,2,0,P|331:254|318:219,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
72,324,204670,6,0,B|60:272|60:272|76:180,1,144,4|0,0:0|1:0,0:0:0:0:
92,96,205373,1,2,0:0:0:0:
8,124,205607,2,0,P|5:88|14:53,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
168,192,206545,6,0,P|200:174|237:173,1,72,4|2,0:3|0:0,0:0:0:0:
320,160,207013,2,0,P|318:196|301:229,1,72,2|0,1:2|0:0,0:0:0:0:
272,307,207482,2,0,P|240:287|221:256,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
440,324,208420,6,0,B|452:272|452:272|436:180,1,144,4|0,0:3|1:0,0:0:0:0:
420,96,209123,1,2,0:0:0:0:
504,124,209357,2,0,P|507:88|498:53,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0:
344,192,210295,6,0,P|311:174|274:173,1,72,4|2,0:3|0:0,0:0:0:0:
190,156,210763,2,0,P|191:192|208:225,1,72,2|0,1:2|0:0,0:0:0:0:
288,256,211232,1,4,0:3:0:0:
132,332,211701,1,0,1:0:0:0:
28,192,212170,6,0,P|16:120|44:56,1,144,4|0,0:0|1:0,0:0:0:0:
120,16,212873,1,2,0:0:0:0:
204,32,213107,2,0,L|304:28,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
192,204,214045,6,0,P|196:240|216:272,1,72,4|2,0:3|0:0,0:0:0:0:
298,241,214513,2,0,P|327:219|345:186,1,72,6|0,1:2|0:0,0:0:0:0:
280,132,214982,2,0,P|246:117|209:118,2,72,4|2|0,0:3|0:0|1:0,0:0:0:0:
484,192,215920,6,0,P|496:120|468:56,1,144,4|0,0:3|1:0,0:0:0:0:
392,16,216623,1,2,0:0:0:0:
308,32,216857,2,0,L|208:28,2,72,4|0|2,0:3|0:0|1:2,0:0:0:0:
320,204,217795,6,0,P|316:240|296:272,1,72,4|2,0:3|0:0,0:0:0:0:
213,241,218263,2,0,P|184:219|166:186,1,72,2|0,1:2|0:0,0:0:0:0:
232,132,218732,2,0,B|260:112|300:116|300:116|384:128,1,144,4|0,0:3|1:0,0:0:0:0:
348,336,219670,6,0,B|320:356|280:352|280:352|196:340,1,144,4|0,0:0|1:0,0:0:0:0:
124,328,220373,1,2,0:0:0:0:
54,276,220607,2,0,P|41:308|39:345,2,72,4|2|2,0:3|0:0|1:2,0:0:0:0:
156,80,221545,6,0,L|251:94,1,72,4|2,0:3|0:0,0:0:0:0:
212,169,222013,2,0,L|148:160,1,64.799998022461,2|0,1:2|0:0,0:0:0:0:
140,240,222482,2,0,L|216:252,2,57.6,4|2|0,0:3|0:0|1:0,0:0:0:0:
256,192,223420,12,0,227170,0:0:0:0:
";
}
}

View File

@ -2,9 +2,9 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets;
using osu.Game.Screens.Play;
using System.Linq;
namespace osu.Desktop.Tests.Visual
{
@ -12,11 +12,10 @@ namespace osu.Desktop.Tests.Visual
{
public override string Description => @"Testing replay playback.";
protected override Player CreatePlayer(WorkingBeatmap beatmap)
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
{
beatmap.Mods.Value = new Mod[] { new OsuModAutoplay() };
return base.CreatePlayer(beatmap);
beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
return base.CreatePlayer(beatmap, ruleset);
}
}
}

View File

@ -3,18 +3,23 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using OpenTK;
using osu.Desktop.Tests.Beatmaps;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets.UI;
namespace osu.Desktop.Tests.Visual
@ -22,6 +27,7 @@ namespace osu.Desktop.Tests.Visual
/// <summary>
/// The most minimal implementation of a playfield with scrolling hit objects.
/// </summary>
[TestFixture]
public class TestCaseScrollingPlayfield : OsuTestCase
{
public TestCaseScrollingPlayfield()
@ -58,12 +64,72 @@ namespace osu.Desktop.Tests.Visual
AddStep("Reverse direction", () =>
{
horizontalRulesetContainer.Playfield.Reversed.Toggle();
verticalRulesetContainer.Playfield.Reversed.Toggle();
horizontalRulesetContainer.Playfield.Reverse();
verticalRulesetContainer.Playfield.Reverse();
});
}
private class TestRulesetContainer : ScrollingRulesetContainer<TestPlayfield, TestHitObject, TestJudgement>
[Test]
public void TestSpeedAdjustmentOrdering()
{
var hitObjectContainer = new ScrollingPlayfield.ScrollingHitObjectContainer(Axes.X);
var speedAdjustments = new[]
{
new SpeedAdjustmentContainer(new MultiplierControlPoint()),
new SpeedAdjustmentContainer(new MultiplierControlPoint(1000)
{
TimingPoint = new TimingControlPoint { BeatLength = 500 }
}),
new SpeedAdjustmentContainer(new MultiplierControlPoint(2000)
{
TimingPoint = new TimingControlPoint { BeatLength = 1000 },
DifficultyPoint = new DifficultyControlPoint { SpeedMultiplier = 2}
}),
new SpeedAdjustmentContainer(new MultiplierControlPoint(3000)
{
TimingPoint = new TimingControlPoint { BeatLength = 1000 },
DifficultyPoint = new DifficultyControlPoint { SpeedMultiplier = 1}
}),
};
var hitObjects = new[]
{
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = -1000 }),
new DrawableTestHitObject(Axes.X, new TestHitObject()),
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 1000 }),
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 2000 }),
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 3000 }),
new DrawableTestHitObject(Axes.X, new TestHitObject { StartTime = 4000 }),
};
hitObjects.ForEach(h => hitObjectContainer.Add(h));
speedAdjustments.ForEach(hitObjectContainer.AddSpeedAdjustment);
// The 0th index in hitObjectContainer.SpeedAdjustments is the "default" control point
// Check multiplier of the default speed adjustment
Assert.AreEqual(1, hitObjectContainer.SpeedAdjustments[0].ControlPoint.Multiplier);
Assert.AreEqual(1, speedAdjustments[0].ControlPoint.Multiplier);
Assert.AreEqual(2, speedAdjustments[1].ControlPoint.Multiplier);
Assert.AreEqual(2, speedAdjustments[2].ControlPoint.Multiplier);
Assert.AreEqual(1, speedAdjustments[3].ControlPoint.Multiplier);
// Check insertion of hit objects
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[4].Contains(hitObjects[0]));
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[2].Contains(hitObjects[2]));
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[1].Contains(hitObjects[3]));
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[4]));
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[5]));
hitObjectContainer.RemoveSpeedAdjustment(hitObjectContainer.SpeedAdjustments[3]);
// The hit object contained in this speed adjustment should be resorted into the one occuring before it
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
}
private class TestRulesetContainer : ScrollingRulesetContainer<TestPlayfield, TestHitObject>
{
private readonly Axes scrollingAxes;
@ -77,16 +143,18 @@ namespace osu.Desktop.Tests.Visual
public override ScoreProcessor CreateScoreProcessor() => new TestScoreProcessor();
public override PassThroughInputManager CreateInputManager() => new PassThroughInputManager();
protected override BeatmapConverter<TestHitObject> CreateBeatmapConverter() => new TestBeatmapConverter();
protected override Playfield<TestHitObject, TestJudgement> CreatePlayfield() => new TestPlayfield(scrollingAxes);
protected override Playfield CreatePlayfield() => new TestPlayfield(scrollingAxes);
protected override DrawableHitObject<TestHitObject, TestJudgement> GetVisualRepresentation(TestHitObject h) => new DrawableTestHitObject(scrollingAxes, h);
protected override DrawableHitObject<TestHitObject> GetVisualRepresentation(TestHitObject h) => new DrawableTestHitObject(scrollingAxes, h);
}
private class TestScoreProcessor : ScoreProcessor<TestHitObject, TestJudgement>
private class TestScoreProcessor : ScoreProcessor<TestHitObject>
{
protected override void OnNewJudgement(TestJudgement judgement)
protected override void OnNewJudgement(Judgement judgement)
{
}
}
@ -101,7 +169,7 @@ namespace osu.Desktop.Tests.Visual
}
}
private class DrawableTestHitObject : DrawableScrollingHitObject<TestHitObject, TestJudgement>
private class DrawableTestHitObject : DrawableScrollingHitObject<TestHitObject>
{
public DrawableTestHitObject(Axes scrollingAxes, TestHitObject hitObject)
: base(hitObject)
@ -117,14 +185,12 @@ namespace osu.Desktop.Tests.Visual
});
}
protected override TestJudgement CreateJudgement() => new TestJudgement();
protected override void UpdateState(ArmedState state)
{
}
}
private class TestPlayfield : ScrollingPlayfield<TestHitObject, TestJudgement>
private class TestPlayfield : ScrollingPlayfield
{
protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content;
@ -142,17 +208,13 @@ namespace osu.Desktop.Tests.Visual
content = new Container { RelativeSizeAxes = Axes.Both }
};
}
public void Reverse() => Reversed.Toggle();
}
private class TestHitObject : HitObject
{
}
private class TestJudgement : Judgement
{
public override string ResultString { get { throw new NotImplementedException(); } }
public override string MaxResultString { get { throw new NotImplementedException(); } }
}
}
}
}

View File

@ -0,0 +1,90 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing;
using osu.Game;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
using osu.Game.Storyboards.Drawables;
namespace osu.Desktop.Tests.Visual
{
internal class TestCaseStoryboard : OsuTestCase
{
public override string Description => @"Tests storyboards.";
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
private readonly Container<DrawableStoryboard> storyboardContainer;
private DrawableStoryboard storyboard;
public TestCaseStoryboard()
{
Clock = new FramedClock();
Add(new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
storyboardContainer = new Container<DrawableStoryboard>
{
RelativeSizeAxes = Axes.Both,
},
},
});
Add(new MusicController
{
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
State = Visibility.Visible,
});
AddStep("Restart", restart);
AddToggleStep("Passing", passing => { if (storyboard != null) storyboard.Passing = passing; });
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
beatmapBacking.BindTo(game.Beatmap);
beatmapBacking.ValueChanged += beatmapChanged;
}
private void beatmapChanged(WorkingBeatmap working)
=> loadStoryboard(working);
private void restart()
{
var track = beatmapBacking.Value.Track;
track.Reset();
loadStoryboard(beatmapBacking.Value);
track.Start();
}
private void loadStoryboard(WorkingBeatmap working)
{
if (storyboard != null)
storyboardContainer.Remove(storyboard);
var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true };
decoupledClock.ChangeSource(working.Track);
storyboardContainer.Clock = decoupledClock;
storyboardContainer.Add(storyboard = working.Beatmap.Storyboard.CreateDrawable());
storyboard.Passing = false;
}
}
}

View File

@ -14,6 +14,11 @@ using osu.Game.Rulesets.Taiko.UI;
using OpenTK;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps;
using osu.Desktop.Tests.Beatmaps;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
namespace osu.Desktop.Tests.Visual
{
@ -27,10 +32,11 @@ namespace osu.Desktop.Tests.Visual
protected override double TimePerAction => default_duration * 2;
private readonly Random rng = new Random(1337);
private readonly TaikoPlayfield playfield;
private readonly Container playfieldContainer;
private TaikoRulesetContainer rulesetContainer;
private Container playfieldContainer;
public TestCaseTaikoPlayfield()
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
AddStep("Hit!", () => addHitJudgement(false));
AddStep("Kiai hit", () => addHitJudgement(true));
@ -51,6 +57,25 @@ namespace osu.Desktop.Tests.Visual
AddStep("Height test 5", () => changePlayfieldSize(5));
AddStep("Reset height", () => changePlayfieldSize(6));
var controlPointInfo = new ControlPointInfo();
controlPointInfo.TimingPoints.Add(new TimingControlPoint());
WorkingBeatmap beatmap = new TestWorkingBeatmap(new Beatmap
{
HitObjects = new List<HitObject> { new CentreHit() },
BeatmapInfo = new BeatmapInfo
{
Difficulty = new BeatmapDifficulty(),
Metadata = new BeatmapMetadata
{
Artist = @"Unknown",
Title = @"Sample Beatmap",
Author = @"peppy",
},
},
ControlPointInfo = controlPointInfo
});
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
Add(playfieldContainer = new Container
@ -58,12 +83,9 @@ namespace osu.Desktop.Tests.Visual
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = TaikoPlayfield.DEFAULT_HEIGHT,
Height = 768,
Clock = new FramedClock(rateAdjustClock),
Children = new[]
{
playfield = new TaikoPlayfield()
}
Children = new[] { rulesetContainer = new TaikoRulesetContainer(rulesets.GetRuleset(1).CreateInstance(), beatmap, true) }
});
}
@ -106,7 +128,7 @@ namespace osu.Desktop.Tests.Visual
private void addHitJudgement(bool kiai)
{
TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great;
HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great;
var cpi = new ControlPointInfo();
cpi.EffectPoints.Add(new EffectControlPoint
@ -117,56 +139,35 @@ namespace osu.Desktop.Tests.Visual
Hit hit = new Hit();
hit.ApplyDefaults(cpi, new BeatmapDifficulty());
var h = new DrawableTestHit(hit)
{
X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f),
Judgement = new TaikoJudgement
{
Result = HitResult.Hit,
TaikoResult = hitResult,
TimeOffset = 0
}
};
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
playfield.OnJudgement(h);
rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult });
if (RNG.Next(10) == 0)
{
h.Judgement.SecondHit = true;
playfield.OnJudgement(h);
rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult });
rulesetContainer.Playfield.OnJudgement(h, new TaikoStrongHitJudgement());
}
}
private void addMissJudgement()
{
playfield.OnJudgement(new DrawableTestHit(new Hit())
{
Judgement = new TaikoJudgement
{
Result = HitResult.Miss,
TimeOffset = 0
}
});
rulesetContainer.Playfield.OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss });
}
private void addBarLine(bool major, double delay = scroll_time)
{
BarLine bl = new BarLine
{
StartTime = playfield.Time.Current + delay,
ScrollTime = scroll_time
};
BarLine bl = new BarLine { StartTime = rulesetContainer.Playfield.Time.Current + delay };
playfield.AddBarLine(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
rulesetContainer.Playfield.Add(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
}
private void addSwell(double duration = default_duration)
{
playfield.Add(new DrawableSwell(new Swell
rulesetContainer.Playfield.Add(new DrawableSwell(new Swell
{
StartTime = playfield.Time.Current + scroll_time,
StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
Duration = duration,
ScrollTime = scroll_time
}));
}
@ -177,54 +178,49 @@ namespace osu.Desktop.Tests.Visual
var d = new DrumRoll
{
StartTime = playfield.Time.Current + scroll_time,
StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
IsStrong = strong,
Duration = duration,
ScrollTime = scroll_time,
};
playfield.Add(new DrawableDrumRoll(d));
rulesetContainer.Playfield.Add(new DrawableDrumRoll(d));
}
private void addCentreHit(bool strong)
{
Hit h = new Hit
{
StartTime = playfield.Time.Current + scroll_time,
ScrollTime = scroll_time,
StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
IsStrong = strong
};
if (strong)
playfield.Add(new DrawableCentreHitStrong(h));
rulesetContainer.Playfield.Add(new DrawableCentreHitStrong(h));
else
playfield.Add(new DrawableCentreHit(h));
rulesetContainer.Playfield.Add(new DrawableCentreHit(h));
}
private void addRimHit(bool strong)
{
Hit h = new Hit
{
StartTime = playfield.Time.Current + scroll_time,
ScrollTime = scroll_time,
StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
IsStrong = strong
};
if (strong)
playfield.Add(new DrawableRimHitStrong(h));
rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h));
else
playfield.Add(new DrawableRimHit(h));
rulesetContainer.Playfield.Add(new DrawableRimHit(h));
}
private class DrawableTestHit : DrawableHitObject<TaikoHitObject, TaikoJudgement>
private class DrawableTestHit : DrawableHitObject<TaikoHitObject>
{
public DrawableTestHit(TaikoHitObject hitObject)
: base(hitObject)
{
}
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected override void UpdateState(ArmedState state)
{
}

View File

@ -33,13 +33,10 @@ namespace osu.Desktop.Tests.Visual
Rank = 2148,
PP = 4567.89m
},
AllRankHistories = new User.RankHistories
RankHistory = new User.RankHistoryData
{
Osu = new User.RankHistory
{
Mode = @"osu",
Data = Enumerable.Range(2345,45).Concat(Enumerable.Range(2109,40)).ToArray()
}
Mode = @"osu",
Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
}
}, false));
AddStep("Show ppy", () => profile.ShowUser(new User

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Desktop.Tests</RootNamespace>
<AssemblyName>osu.Desktop.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@ -35,14 +35,15 @@
<ItemGroup>
<Reference Include="mscorlib" />
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.framework, Version=3.7.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll</HintPath>
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
@ -87,9 +88,11 @@
<Compile Include="Visual\TestCaseManiaHitObjects.cs" />
<Compile Include="Visual\TestCaseManiaPlayfield.cs" />
<Compile Include="Visual\TestCaseMedalOverlay.cs" />
<Compile Include="Visual\TestCaseEditorMenuBar.cs" />
<Compile Include="Visual\TestCaseMenuButtonSystem.cs" />
<Compile Include="Visual\TestCaseMenuOverlays.cs" />
<Compile Include="Visual\TestCaseMods.cs" />
<Compile Include="Visual\TestCaseStoryboard.cs" />
<Compile Include="Visual\TestCaseMusicController.cs" />
<Compile Include="Visual\TestCaseNotificationOverlay.cs" />
<Compile Include="Visual\TestCaseOnScreenDisplay.cs" />
@ -155,6 +158,7 @@
<Link>osu.licenseheader</Link>
</None>
<None Include="app.config" />
<None Include="OpenTK.dll.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>

View File

@ -4,9 +4,9 @@ Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<packages>
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
<package id="NUnit" version="3.7.1" targetFramework="net45" />
<package id="ppy.OpenTK" version="3.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
<package id="NUnit" version="3.8.1" targetFramework="net461" />
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" />
<package id="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
<package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" />

View File

@ -22,7 +22,7 @@
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
<SignAssembly>false</SignAssembly>
<TargetZone>LocalIntranet</TargetZone>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>

View File

@ -1,7 +1,3 @@
<!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>

View File

@ -20,16 +20,11 @@ namespace osu.Desktop
{
internal class OsuGameDesktop : OsuGame
{
private readonly VersionManager versionManager;
private VersionManager versionManager;
public OsuGameDesktop(string[] args = null)
: base(args)
{
versionManager = new VersionManager
{
Depth = int.MinValue,
State = Visibility.Hidden
};
}
public override Storage GetStorageForStableInstall()
@ -88,11 +83,15 @@ namespace osu.Desktop
{
base.LoadComplete();
LoadComponentAsync(versionManager, Add);
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue });
ScreenChanged += s =>
{
if (!versionManager.IsPresent && s is Intro)
if (s is Intro && s.ChildScreen == null)
{
Add(versionManager);
versionManager.State = Visibility.Visible;
}
};
}

View File

@ -24,11 +24,6 @@ namespace osu.Desktop
public override void SetHost(GameHost host)
{
base.SetHost(host);
host.UpdateThread.InactiveHz = host.UpdateThread.ActiveHz;
host.DrawThread.InactiveHz = host.DrawThread.ActiveHz;
host.InputThread.InactiveHz = host.InputThread.ActiveHz;
host.Window.CursorState |= CursorState.Hidden;
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
@ -19,6 +20,7 @@ using OpenTK.Graphics;
using System.Net.Http;
using osu.Framework.Logging;
using osu.Game;
using osu.Game.Configuration;
namespace osu.Desktop.Overlays
{
@ -26,17 +28,22 @@ namespace osu.Desktop.Overlays
{
private UpdateManager updateManager;
private NotificationOverlay notificationOverlay;
private OsuConfigManager config;
private OsuGameBase game;
public override bool HandleInput => false;
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game)
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config)
{
notificationOverlay = notification;
this.config = config;
this.game = game;
AutoSizeAxes = Axes.Both;
Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre;
Alpha = 0;
Children = new Drawable[]
@ -91,6 +98,42 @@ namespace osu.Desktop.Overlays
checkForUpdateAsync();
}
protected override void LoadComplete()
{
base.LoadComplete();
var version = game.Version;
var lastVersion = config.Get<string>(OsuSetting.Version);
if (game.IsDeployedBuild && version != lastVersion)
{
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).
if (!string.IsNullOrEmpty(lastVersion))
Scheduler.AddDelayed(() => notificationOverlay.Post(new UpdateCompleteNotification(version)), 5000);
}
}
private class UpdateCompleteNotification : SimpleNotification
{
public UpdateCompleteNotification(string version)
{
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
Icon = FontAwesome.fa_check_square;
Activated = delegate
{
Process.Start($"https://github.com/ppy/osu/releases/tag/v{version}");
return true;
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
IconBackgound.Colour = colours.BlueDark;
}
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);

15
osu.Desktop/app.config Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="SharpCompress" publicKeyToken="afb0a02973931d96" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.18.1.0" newVersion="0.18.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -22,7 +22,7 @@
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
<SignAssembly>false</SignAssembly>
<TargetZone>LocalIntranet</TargetZone>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
@ -69,7 +69,8 @@
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>CuttingEdge NoUpdate</DefineConstants>
<DefineConstants>
</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib>
@ -104,6 +105,7 @@
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
<StartArguments>--tests</StartArguments>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="DeltaCompressionDotNet, Version=1.1.0.0, Culture=neutral, PublicKeyToken=1d14d6e5194e7f4a, processorArchitecture=MSIL">
@ -136,36 +138,28 @@
</Reference>
<Reference Include="mscorlib" />
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll</HintPath>
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SharpCompress, Version=0.17.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll</HintPath>
<Reference Include="SharpCompress, Version=0.18.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Squirrel, Version=1.7.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll</HintPath>
<Reference Include="Squirrel, Version=1.7.8.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.Primitives, Version=4.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
@ -173,6 +167,7 @@
<None Include="..\osu.licenseheader">
<Link>osu.licenseheader</Link>
</None>
<None Include="app.config" />
<None Include="OpenTK.dll.config" />
<None Include="osu!.res" />
<None Include="packages.config" />

View File

@ -5,10 +5,9 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-->
<packages>
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
<package id="ppy.OpenTK" version="3.0" targetFramework="net45" />
<package id="SharpCompress" version="0.17.1" targetFramework="net45" />
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
<package id="SharpCompress" version="0.18.1" targetFramework="net461" />
<package id="Splat" version="2.0.0" targetFramework="net45" />
<package id="squirrel.windows" version="1.7.5" targetFramework="net45" />
<package id="squirrel.windows" version="1.7.8" targetFramework="net461" />
</packages>

View File

@ -3,11 +3,11 @@
using System.ComponentModel;
using osu.Framework.Input.Bindings;
using osu.Game.Input.Bindings;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch
{
public class CatchInputManager : DatabasedKeyBindingInputManager<CatchAction>
public class CatchInputManager : RulesetInputManager<CatchAction>
{
public CatchInputManager(RulesetInfo ruleset)
: base(ruleset, 0, SimultaneousBindingMode.Unique)

View File

@ -1,18 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Scoring;
using osu.Framework.Input.Bindings;
namespace osu.Game.Rulesets.Catch
@ -23,12 +19,12 @@ namespace osu.Game.Rulesets.Catch
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{
new KeyBinding(Key.Z, CatchAction.MoveLeft),
new KeyBinding(Key.Left, CatchAction.MoveLeft),
new KeyBinding(Key.X, CatchAction.MoveRight),
new KeyBinding(Key.Right, CatchAction.MoveRight),
new KeyBinding(Key.LShift, CatchAction.Dash),
new KeyBinding(Key.RShift, CatchAction.Dash),
new KeyBinding(InputKey.Z, CatchAction.MoveLeft),
new KeyBinding(InputKey.Left, CatchAction.MoveLeft),
new KeyBinding(InputKey.X, CatchAction.MoveRight),
new KeyBinding(InputKey.Right, CatchAction.MoveRight),
new KeyBinding(InputKey.Shift, CatchAction.Dash),
new KeyBinding(InputKey.Shift, CatchAction.Dash),
};
public override IEnumerable<Mod> GetModsFor(ModType type)
@ -95,23 +91,12 @@ namespace osu.Game.Rulesets.Catch
}
}
public override Mod GetAutoplayMod() => new ModAutoplay();
public override string Description => "osu!catch";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
public override IEnumerable<KeyCounter> CreateGameplayKeys() => new KeyCounter[]
{
new KeyCounterKeyboard(Key.ShiftLeft),
new KeyCounterMouse(MouseButton.Left),
new KeyCounterMouse(MouseButton.Right)
};
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new CatchDifficultyCalculator(beatmap);
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor();
public override int LegacyID => 2;
public CatchRuleset(RulesetInfo rulesetInfo)

View File

@ -7,8 +7,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
public class CatchJudgement : Judgement
{
public override string ResultString => string.Empty;
public override string MaxResultString => string.Empty;
// todo: wangs
}
}

View File

@ -9,14 +9,14 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableFruit : DrawableScrollingHitObject<CatchBaseHit, CatchJudgement>
public class DrawableFruit : DrawableScrollingHitObject<CatchBaseHit>
{
private const float pulp_size = 30;
@ -98,14 +98,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
};
}
protected override CatchJudgement CreateJudgement() => new CatchJudgement();
private const float preempt = 1000;
protected override void CheckJudgement(bool userTriggered)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (Judgement.TimeOffset > 0)
Judgement.Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Hit : HitResult.Miss;
if (timeOffset > 0)
AddJudgement(new Judgement { Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Perfect : HitResult.Miss });
}
protected override void UpdateState(ArmedState state)

View File

@ -1,7 +1,3 @@
<!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>

View File

@ -1,34 +1,21 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.Scoring
{
internal class CatchScoreProcessor : ScoreProcessor<CatchBaseHit, CatchJudgement>
internal class CatchScoreProcessor : ScoreProcessor<CatchBaseHit>
{
public CatchScoreProcessor()
{
}
public CatchScoreProcessor(RulesetContainer<CatchBaseHit, CatchJudgement> rulesetContainer)
public CatchScoreProcessor(RulesetContainer<CatchBaseHit> rulesetContainer)
: base(rulesetContainer)
{
}
protected override void Reset()
{
base.Reset();
Health.Value = 1;
Accuracy.Value = 1;
}
protected override void OnNewJudgement(CatchJudgement judgement)
{
}
}
}

View File

@ -2,17 +2,16 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.UI;
using OpenTK;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Catch.UI
{
public class CatchPlayfield : ScrollingPlayfield<CatchBaseHit, CatchJudgement>
public class CatchPlayfield : ScrollingPlayfield
{
protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content;
@ -44,22 +43,23 @@ namespace osu.Game.Rulesets.Catch.UI
};
}
public override void Add(DrawableHitObject<CatchBaseHit, CatchJudgement> h)
public override void Add(DrawableHitObject h)
{
h.Depth = (float)h.HitObject.StartTime;
base.Add(h);
var fruit = (DrawableFruit)h;
fruit.CheckPosition = catcherArea.CheckIfWeCanCatch;
fruit.OnJudgement += Fruit_OnJudgement;
}
private void Fruit_OnJudgement(DrawableHitObject<CatchBaseHit, CatchJudgement> obj)
public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
if (obj.Judgement.Result == HitResult.Hit)
if (judgement.IsHit)
{
Vector2 screenPosition = obj.ScreenSpaceDrawQuad.Centre;
Remove(obj);
catcherArea.Add(obj, screenPosition);
Vector2 screenPosition = judgedObject.ScreenSpaceDrawQuad.Centre;
Remove(judgedObject);
catcherArea.Add(judgedObject, screenPosition);
}
}
}

View File

@ -5,7 +5,6 @@ using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Scoring;
@ -15,7 +14,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.UI
{
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchBaseHit, CatchJudgement>
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchBaseHit>
{
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
@ -26,11 +25,11 @@ namespace osu.Game.Rulesets.Catch.UI
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
protected override Playfield<CatchBaseHit, CatchJudgement> CreatePlayfield() => new CatchPlayfield();
protected override Playfield CreatePlayfield() => new CatchPlayfield();
public override PassThroughInputManager CreateKeyBindingInputManager() => new CatchInputManager(Ruleset?.RulesetInfo);
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
protected override DrawableHitObject<CatchBaseHit, CatchJudgement> GetVisualRepresentation(CatchBaseHit h)
protected override DrawableHitObject<CatchBaseHit> GetVisualRepresentation(CatchBaseHit h)
{
if (h is Fruit)
return new DrawableFruit(h);

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Objects.Drawables;
@ -22,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
private Catcher catcher;
public void Add(DrawableHitObject<CatchBaseHit, CatchJudgement> fruit, Vector2 screenPosition) => catcher.AddToStack(fruit, screenPosition);
public void Add(DrawableHitObject fruit, Vector2 screenPosition) => catcher.AddToStack(fruit, screenPosition);
public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.Position) < catcher.DrawSize.X / DrawSize.X / 2;
@ -85,7 +84,7 @@ namespace osu.Game.Rulesets.Catch.UI
var additive = createCatcherSprite();
additive.RelativePositionAxes = Axes.Both;
additive.BlendingMode = BlendingMode.Additive;
additive.Blending = BlendingMode.Additive;
additive.Position = Position;
additive.Scale = Scale;
@ -152,7 +151,7 @@ namespace osu.Game.Rulesets.Catch.UI
X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime / 1800 * speed, 0, 1);
}
public void AddToStack(DrawableHitObject<CatchBaseHit, CatchJudgement> fruit, Vector2 absolutePosition)
public void AddToStack(DrawableHitObject fruit, Vector2 absolutePosition)
{
fruit.RelativePositionAxes = Axes.None;
fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0);
@ -161,11 +160,10 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.Origin = Anchor.BottomCentre;
fruit.Scale *= 0.7f;
fruit.LifetimeEnd = double.MaxValue;
fruit.Depth = (float)Time.Current;
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
while (Children.OfType<DrawableFruit>().Any(f => Vector2.DistanceSquared(f.Position, fruit.Position) < distance * distance))
while (Children.OfType<DrawableFruit>().Any(f => Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
{
fruit.X += RNG.Next(-5, 5);
fruit.Y -= RNG.Next(0, 5);

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Catch</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Catch</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@ -34,7 +34,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
@ -69,6 +69,7 @@
<None Include="..\osu.licenseheader">
<Link>osu.licenseheader</Link>
</None>
<None Include="app.config" />
<None Include="OpenTK.dll.config" />
<None Include="packages.config" />
</ItemGroup>

View File

@ -1,9 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<packages>
<package id="ppy.OpenTK" version="3.0" targetFramework="net45" />
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
</packages>

View File

@ -28,12 +28,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
private Pattern lastPattern = new Pattern();
private FastRandom random;
private Beatmap beatmap;
private bool isForCurrentRuleset;
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
private readonly int availableColumns;
private readonly bool isForCurrentRuleset;
public ManiaBeatmapConverter(bool isForCurrentRuleset, int availableColumns)
{
this.isForCurrentRuleset = isForCurrentRuleset;
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
this.isForCurrentRuleset = isForCurrentRuleset;
this.availableColumns = availableColumns;
}
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original)
{
beatmap = original;
BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty;
@ -41,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
random = new FastRandom(seed);
return base.ConvertBeatmap(original, isForCurrentRuleset);
return base.ConvertBeatmap(original);
}
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
@ -89,7 +97,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original)
{
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern);
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
Pattern newPattern = generator.Generate();
lastPattern = newPattern;
@ -113,14 +121,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
Patterns.PatternGenerator conversion = null;
if (distanceData != null)
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern);
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
else if (endTimeData != null)
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap);
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, availableColumns);
else if (positionData != null)
{
computeDensity(original.StartTime);
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair);
conversion = new HitObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern, lastTime, lastPosition, density, lastStair);
recordNote(original.StartTime, positionData.Position);
}
@ -142,8 +150,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// </summary>
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern)
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
: base(random, hitObject, beatmap, availableColumns, previousPattern)
{
}

View File

@ -29,8 +29,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private PatternType convertType;
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern)
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
: base(random, hitObject, beatmap, availableColumns, previousPattern)
{
convertType = PatternType.None;
if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// The true distance, accounting for any repeats
double distance = (distanceData?.Distance ?? 0) * repeatCount;
// The velocity of the osu! hit object - calculated as the velocity of a slider
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier / (timingPoint.BeatLength * difficultyPoint.SpeedMultiplier);
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
// The duration of the osu! hit object
double osuDuration = distance / osuVelocity;

View File

@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
private readonly double endTime;
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap)
: base(random, hitObject, beatmap, new Pattern())
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns)
: base(random, hitObject, beatmap, availableColumns, new Pattern())
{
var endtimeData = HitObject as IHasEndTime;

View File

@ -20,9 +20,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType;
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
: base(random, hitObject, beatmap, previousPattern)
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
: base(random, hitObject, beatmap, availableColumns, previousPattern)
{
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
StairType = lastStair;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);

View File

@ -25,11 +25,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// </summary>
protected readonly FastRandom Random;
protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
: base(hitObject, beatmap, previousPattern)
protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
: base(hitObject, beatmap, availableColumns, previousPattern)
{
Random = random;
if (random == null) throw new ArgumentNullException(nameof(random));
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
Random = random;
RandomStart = AvailableColumns == 8 ? 1 : 0;
}
@ -62,6 +66,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <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)
{
if (p2 < 0 || p2 > 1) throw new ArgumentOutOfRangeException(nameof(p2));
if (p3 < 0 || p3 > 1) throw new ArgumentOutOfRangeException(nameof(p3));
if (p4 < 0 || p4 > 1) throw new ArgumentOutOfRangeException(nameof(p4));
if (p5 < 0 || p5 > 1) throw new ArgumentOutOfRangeException(nameof(p5));
if (p6 < 0 || p6 > 1) throw new ArgumentOutOfRangeException(nameof(p6));
double val = Random.NextDouble();
if (val >= 1 - p6)
return 6;

View File

@ -32,13 +32,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
/// </summary>
protected readonly Beatmap Beatmap;
protected PatternGenerator(HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
protected PatternGenerator(HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
{
PreviousPattern = previousPattern;
if (hitObject == null) throw new ArgumentNullException(nameof(hitObject));
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
HitObject = hitObject;
Beatmap = beatmap;
AvailableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize);
AvailableColumns = availableColumns;
PreviousPattern = previousPattern;
}
/// <summary>

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Judgements
{
@ -145,18 +146,18 @@ namespace osu.Game.Rulesets.Mania.Judgements
/// </summary>
/// <param name="hitOffset">The time offset.</param>
/// <returns>The hit result, or null if the time offset results in a miss.</returns>
public ManiaHitResult? ResultFor(double hitOffset)
public HitResult? ResultFor(double hitOffset)
{
if (hitOffset <= Perfect / 2)
return ManiaHitResult.Perfect;
return HitResult.Perfect;
if (hitOffset <= Great / 2)
return ManiaHitResult.Great;
return HitResult.Great;
if (hitOffset <= Good / 2)
return ManiaHitResult.Good;
return HitResult.Good;
if (hitOffset <= Ok / 2)
return ManiaHitResult.Ok;
return HitResult.Ok;
if (hitOffset <= Bad / 2)
return ManiaHitResult.Bad;
return HitResult.Meh;
return null;
}

View File

@ -1,6 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Judgements
{
public class HoldNoteTailJudgement : ManiaJudgement
@ -10,27 +12,15 @@ namespace osu.Game.Rulesets.Mania.Judgements
/// </summary>
public bool HasBroken;
public override int NumericResultForScore(ManiaHitResult result)
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return base.NumericResultForScore(result);
case ManiaHitResult.Great:
case ManiaHitResult.Perfect:
return base.NumericResultForScore(HasBroken ? ManiaHitResult.Good : result);
}
}
public override int NumericResultForAccuracy(ManiaHitResult result)
{
switch (result)
{
default:
return base.NumericResultForAccuracy(result);
case ManiaHitResult.Great:
case ManiaHitResult.Perfect:
return base.NumericResultForAccuracy(HasBroken ? ManiaHitResult.Good : result);
return base.NumericResultFor(result);
case HitResult.Great:
case HitResult.Perfect:
return base.NumericResultFor(HasBroken ? HitResult.Good : result);
}
}
}

View File

@ -1,13 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Judgements
{
public class HoldNoteTickJudgement : ManiaJudgement
{
public override bool AffectsCombo => false;
public override int NumericResultForScore(ManiaHitResult result) => 20;
public override int NumericResultForAccuracy(ManiaHitResult result) => 0; // Don't count ticks into accuracy
protected override int NumericResultFor(HitResult result) => 20;
}
}

View File

@ -1,21 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Rulesets.Mania.Judgements
{
public enum ManiaHitResult
{
[Description("PERFECT")]
Perfect,
[Description("GREAT")]
Great,
[Description("GOOD")]
Good,
[Description("OK")]
Ok,
[Description("BAD")]
Bad
}
}

View File

@ -8,75 +8,22 @@ namespace osu.Game.Rulesets.Mania.Judgements
{
public class ManiaJudgement : Judgement
{
/// <summary>
/// The maximum possible hit result.
/// </summary>
public const ManiaHitResult MAX_HIT_RESULT = ManiaHitResult.Perfect;
/// <summary>
/// The result value for the combo portion of the score.
/// </summary>
public int ResultValueForScore => Result == HitResult.Miss ? 0 : NumericResultForScore(ManiaResult);
/// <summary>
/// The result value for the accuracy portion of the score.
/// </summary>
public int ResultValueForAccuracy => Result == HitResult.Miss ? 0 : NumericResultForAccuracy(ManiaResult);
/// <summary>
/// The maximum result value for the combo portion of the score.
/// </summary>
public int MaxResultValueForScore => NumericResultForScore(MAX_HIT_RESULT);
/// <summary>
/// The maximum result value for the accuracy portion of the score.
/// </summary>
public int MaxResultValueForAccuracy => NumericResultForAccuracy(MAX_HIT_RESULT);
public override string ResultString => string.Empty;
public override string MaxResultString => string.Empty;
/// <summary>
/// The hit result.
/// </summary>
public ManiaHitResult ManiaResult;
public virtual int NumericResultForScore(ManiaHitResult result)
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return 0;
case ManiaHitResult.Bad:
case HitResult.Meh:
return 50;
case ManiaHitResult.Ok:
case HitResult.Ok:
return 100;
case ManiaHitResult.Good:
case HitResult.Good:
return 200;
case ManiaHitResult.Great:
case ManiaHitResult.Perfect:
case HitResult.Great:
case HitResult.Perfect:
return 300;
}
}
public virtual int NumericResultForAccuracy(ManiaHitResult result)
{
switch (result)
{
default:
return 0;
case ManiaHitResult.Bad:
return 50;
case ManiaHitResult.Ok:
return 100;
case ManiaHitResult.Good:
return 200;
case ManiaHitResult.Great:
return 300;
case ManiaHitResult.Perfect:
return 305;
}
}
}
}

View File

@ -6,6 +6,7 @@ using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using System.Collections.Generic;
using System;
namespace osu.Game.Rulesets.Mania
{
@ -21,6 +22,6 @@ namespace osu.Game.Rulesets.Mania
return 0;
}
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize)));
}
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania
{
public class ManiaInputManager : RulesetInputManager<ManiaAction>
{
public ManiaInputManager(RulesetInfo ruleset, int variant)
: base(ruleset, variant, SimultaneousBindingMode.Unique)
{
}
}
public enum ManiaAction
{
[Description("Special")]
Special,
[Description("Key 1")]
Key1 = 10,
[Description("Key 2")]
Key2,
[Description("Key 3")]
Key3,
[Description("Key 4")]
Key4,
[Description("Key 5")]
Key5,
[Description("Key 6")]
Key6,
[Description("Key 7")]
Key7,
[Description("Key 8")]
Key8,
[Description("Key 9")]
Key9
}
}

View File

@ -6,12 +6,10 @@ using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania
{
@ -93,7 +91,7 @@ namespace osu.Game.Rulesets.Mania
{
Mods = new Mod[]
{
new ModAutoplay(),
new ManiaModAutoplay(),
new ModCinema(),
},
},
@ -105,23 +103,55 @@ namespace osu.Game.Rulesets.Mania
}
}
public override Mod GetAutoplayMod() => new ModAutoplay();
public override string Description => "osu!mania";
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
public override IEnumerable<KeyCounter> CreateGameplayKeys() => new KeyCounter[] { /* Todo: Should be keymod specific */ };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap);
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor();
public override int LegacyID => 3;
public ManiaRuleset(RulesetInfo rulesetInfo)
: base(rulesetInfo)
{
}
public override IEnumerable<int> AvailableVariants => new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0)
{
var leftKeys = new[]
{
InputKey.A,
InputKey.S,
InputKey.D,
InputKey.F
};
var rightKeys = new[]
{
InputKey.J,
InputKey.K,
InputKey.L,
InputKey.Semicolon
};
ManiaAction currentKey = ManiaAction.Key1;
var bindings = new List<KeyBinding>();
for (int i = leftKeys.Length - variant / 2; i < leftKeys.Length; i++)
bindings.Add(new KeyBinding(leftKeys[i], currentKey++));
for (int i = 0; i < variant / 2; i++)
bindings.Add(new KeyBinding(rightKeys[i], currentKey++));
if (variant % 2 == 1)
bindings.Add(new KeyBinding(InputKey.Space, ManiaAction.Special));
return bindings;
}
public override string GetVariantName(int variant) => $"{variant}K";
}
}

View File

@ -4,6 +4,16 @@
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using System;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Scoring;
using osu.Game.Users;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods
{
@ -68,6 +78,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModFadeIn : Mod
{
public override string Name => "FadeIn";
public override string ShortenedName => "FI";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_hidden;
public override ModType Type => ModType.DifficultyIncrease;
public override double ScoreMultiplier => 1;
@ -75,15 +86,27 @@ namespace osu.Game.Rulesets.Mania.Mods
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
}
public class ManiaModRandom : Mod
public class ManiaModRandom : Mod, IApplicableMod<ManiaHitObject>
{
public override string Name => "Random";
public override string ShortenedName => "RD";
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
public override string Description => @"Shuffle around the notes!";
public override double ScoreMultiplier => 1;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{
int availableColumns = ((ManiaRulesetContainer)rulesetContainer).AvailableColumns;
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
}
}
public abstract class ManiaKeyMod : Mod
{
public override string ShortenedName => Name;
public abstract int KeyCount { get; }
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true;
@ -146,8 +169,29 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModKeyCoop : Mod
{
public override string Name => "KeyCoop";
public override string ShortenedName => "2P";
public override string Description => @"Double the key amount, double the fun!";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
}
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
{
private int availableColumns;
public override void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{
// Todo: This shouldn't be done, we should be getting a ManiaBeatmap which should store AvailableColumns
// But this is dependent on a _lot_ of refactoring
var maniaRulesetContainer = (ManiaRulesetContainer)rulesetContainer;
availableColumns = maniaRulesetContainer.AvailableColumns;
base.ApplyToRulesetContainer(rulesetContainer);
}
protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap) => new Score
{
User = new User { Username = "osu!topus!" },
Replay = new ManiaAutoGenerator(beatmap, availableColumns).Generate(),
};
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
@ -15,6 +16,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModGravity : Mod, IGenerateSpeedAdjustments
{
public override string Name => "Gravity";
public override string ShortenedName => "GR";
public override double ScoreMultiplier => 0;
@ -23,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public void ApplyToRulesetContainer(ManiaRulesetContainer rulesetContainer, ref List<SpeedAdjustmentContainer>[] hitObjectTimingChanges, ref List<SpeedAdjustmentContainer> barlineTimingChanges)
{
// We have to generate one speed adjustment per hit object for gravity
foreach (ManiaHitObject obj in rulesetContainer.Objects)
foreach (ManiaHitObject obj in rulesetContainer.Objects.OfType<ManiaHitObject>())
{
MultiplierControlPoint controlPoint = rulesetContainer.CreateControlPointAt(obj.StartTime);
// Beat length has too large of an effect for gravity, so we'll force it to a constant value for now

View File

@ -1,30 +1,32 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using OpenTK.Input;
using osu.Framework.Input;
using OpenTK;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Input.Bindings;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
/// <summary>
/// Visualises a <see cref="HoldNote"/> hit object.
/// </summary>
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>, IKeyBindingHandler<ManiaAction>
{
private readonly DrawableNote head;
private readonly DrawableNote tail;
private readonly GlowPiece glowPiece;
private readonly BodyPiece bodyPiece;
private readonly Container<DrawableHoldNoteTick> tickContainer;
private readonly Container fullHeightContainer;
/// <summary>
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
@ -36,21 +38,26 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary>
private bool hasBroken;
public DrawableHoldNote(HoldNote hitObject, Bindable<Key> key = null)
: base(hitObject, key)
public DrawableHoldNote(HoldNote hitObject, ManiaAction action)
: base(hitObject, action)
{
RelativeSizeAxes = Axes.Both;
Height = (float)HitObject.Duration;
AddRange(new Drawable[]
{
// For now the body piece covers the entire height of the container
// whereas possibly in the future we don't want to extend under the head/tail.
// This will be fixed when new designs are given or the current design is finalized.
// The hit object itself cannot be used for various elements because the tail overshoots it
// So a specialized container that is updated to contain the tail height is used
fullHeightContainer = new Container
{
RelativeSizeAxes = Axes.X,
Child = glowPiece = new GlowPiece()
},
bodyPiece = new BodyPiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
},
tickContainer = new Container<DrawableHoldNoteTick>
{
@ -58,12 +65,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativeChildOffset = new Vector2(0, (float)HitObject.StartTime),
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
},
head = new DrawableHeadNote(this, key)
head = new DrawableHeadNote(this, action)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
tail = new DrawableTailNote(this, key)
tail = new DrawableTailNote(this, action)
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre
@ -96,6 +103,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
tickContainer.Children.ForEach(t => t.AccentColour = value);
glowPiece.AccentColour = value;
bodyPiece.AccentColour = value;
head.AccentColour = value;
tail.AccentColour = value;
@ -106,16 +114,26 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
protected override void Update()
{
// Make sure the keypress happened within the body of the hold note
base.Update();
// Make the body piece not lie under the head note
bodyPiece.Y = head.Height;
bodyPiece.Height = DrawHeight - head.Height;
// Make the fullHeightContainer "contain" the height of the tail note, keeping in mind
// that the tail note overshoots the height of this hit object
fullHeightContainer.Height = DrawHeight + tail.Height;
}
public bool OnPressed(ManiaAction action)
{
// Make sure the action happened within the body of the hold note
if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime)
return false;
if (args.Key != Key)
return false;
if (args.Repeat)
if (action != Action)
return false;
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
@ -126,19 +144,19 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return true;
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
public bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!holdStartTime.HasValue)
return false;
if (args.Key != Key)
if (action != Action)
return false;
holdStartTime = null;
// If the key has been released too early, the user should not receive full score for the release
if (!tail.Judged)
if (!tail.AllJudged)
hasBroken = true;
return true;
@ -151,26 +169,28 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
private readonly DrawableHoldNote holdNote;
public DrawableHeadNote(DrawableHoldNote holdNote, Bindable<Key> key = null)
: base(holdNote.HitObject.Head, key)
public DrawableHeadNote(DrawableHoldNote holdNote, ManiaAction action)
: base(holdNote.HitObject.Head, action)
{
this.holdNote = holdNote;
RelativePositionAxes = Axes.None;
Y = 0;
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
GlowPiece.Alpha = 0;
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
public override bool OnPressed(ManiaAction action)
{
if (!base.OnKeyDown(state, args))
return false;
// We only want to trigger a holding state from the head if the head has received a judgement
if (!Judged)
if (!base.OnPressed(action))
return false;
// If the key has been released too early, the user should not receive full score for the release
if (Judgement.Result == HitResult.Miss)
if (Judgements.Any(j => j.Result == HitResult.Miss))
holdNote.hasBroken = true;
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
@ -188,38 +208,58 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
private readonly DrawableHoldNote holdNote;
public DrawableTailNote(DrawableHoldNote holdNote, Bindable<Key> key = null)
: base(holdNote.HitObject.Tail, key)
public DrawableTailNote(DrawableHoldNote holdNote, ManiaAction action)
: base(holdNote.HitObject.Tail, action)
{
this.holdNote = holdNote;
RelativePositionAxes = Axes.None;
Y = 0;
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
GlowPiece.Alpha = 0;
}
protected override ManiaJudgement CreateJudgement() => new HoldNoteTailJudgement();
protected override void CheckJudgement(bool userTriggered)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
base.CheckJudgement(userTriggered);
if (!userTriggered)
{
if (timeOffset > HitObject.HitWindows.Bad / 2)
{
AddJudgement(new HoldNoteTailJudgement
{
Result = HitResult.Miss,
HasBroken = holdNote.hasBroken
});
}
var tailJudgement = Judgement as HoldNoteTailJudgement;
if (tailJudgement == null)
return;
}
double offset = Math.Abs(timeOffset);
if (offset > HitObject.HitWindows.Miss / 2)
return;
tailJudgement.HasBroken = holdNote.hasBroken;
AddJudgement(new HoldNoteTailJudgement
{
Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss,
HasBroken = holdNote.hasBroken
});
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down
public override bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!holdNote.holdStartTime.HasValue)
return false;
if (Judgement.Result != HitResult.None)
return false;
if (args.Key != Key)
if (action != Action)
return false;
UpdateJudgement(true);
@ -227,12 +267,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// Handled by the hold note, which will set holding = false
return false;
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
// Tail doesn't handle key down
return false;
}
}
}
}

View File

@ -23,11 +23,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary>
public Func<double?> HoldStartTime;
/// <summary>
/// References whether the user is currently holding the hold note.
/// </summary>
public Func<bool> IsHolding;
private readonly Container glowContainer;
public DrawableHoldNoteTick(HoldNoteTick hitObject)
@ -36,9 +31,15 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
Y = (float)HitObject.StartTime;
RelativeSizeAxes = Axes.X;
Size = new Vector2(1);
// Life time managed by the parent DrawableHoldNote
LifetimeStart = double.MinValue;
LifetimeEnd = double.MaxValue;
Children = new[]
{
glowContainer = new CircularContainer
@ -58,9 +59,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
};
// Set the default glow
AccentColour = Color4.White;
}
public override Color4 AccentColour
@ -80,9 +78,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
protected override ManiaJudgement CreateJudgement() => new HoldNoteTickJudgement();
protected override void CheckJudgement(bool userTriggered)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
return;
@ -93,8 +89,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (HoldStartTime?.Invoke() > HitObject.StartTime)
return;
Judgement.ManiaResult = ManiaHitResult.Perfect;
Judgement.Result = HitResult.Hit;
AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
}
protected override void UpdateState(ArmedState state)
@ -109,10 +104,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override void Update()
{
if (Judgement.Result != HitResult.None)
if (AllJudged)
return;
if (IsHolding?.Invoke() != true)
if (HoldStartTime?.Invoke() == null)
return;
UpdateJudgement(true);

View File

@ -1,31 +1,30 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Configuration;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
public abstract class DrawableManiaHitObject<TObject> : DrawableScrollingHitObject<ManiaHitObject, ManiaJudgement>
public abstract class DrawableManiaHitObject<TObject> : DrawableScrollingHitObject<ManiaHitObject>
where TObject : ManiaHitObject
{
/// <summary>
/// The key that will trigger input for this hit object.
/// </summary>
protected Bindable<Key> Key { get; private set; } = new Bindable<Key>();
protected ManiaAction Action { get; }
public new TObject HitObject;
protected DrawableManiaHitObject(TObject hitObject, Bindable<Key> key = null)
protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null)
: base(hitObject)
{
RelativePositionAxes = Axes.Y;
HitObject = hitObject;
if (key != null)
Key.BindTo(key);
if (action != null)
Action = action.Value;
}
public override Color4 AccentColour
@ -38,7 +37,5 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.AccentColour = value;
}
}
protected override ManiaJudgement CreateJudgement() => new ManiaJudgement();
}
}

View File

@ -3,10 +3,8 @@
using System;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Objects.Drawables;
@ -16,21 +14,33 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// <summary>
/// Visualises a <see cref="Note"/> hit object.
/// </summary>
public class DrawableNote : DrawableManiaHitObject<Note>
public class DrawableNote : DrawableManiaHitObject<Note>, IKeyBindingHandler<ManiaAction>
{
protected readonly GlowPiece GlowPiece;
private readonly LaneGlowPiece laneGlowPiece;
private readonly NotePiece headPiece;
public DrawableNote(Note hitObject, Bindable<Key> key = null)
: base(hitObject, key)
public DrawableNote(Note hitObject, ManiaAction action)
: base(hitObject, action)
{
RelativeSizeAxes = Axes.X;
Height = 100;
AutoSizeAxes = Axes.Y;
Add(headPiece = new NotePiece
Children = new Drawable[]
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
});
laneGlowPiece = new LaneGlowPiece
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
GlowPiece = new GlowPiece(),
headPiece = new NotePiece
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
}
};
}
public override Color4 AccentColour
@ -42,57 +52,41 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return;
base.AccentColour = value;
laneGlowPiece.AccentColour = value;
GlowPiece.AccentColour = value;
headPiece.AccentColour = value;
}
}
protected override void CheckJudgement(bool userTriggered)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (Judgement.TimeOffset > HitObject.HitWindows.Bad / 2)
Judgement.Result = HitResult.Miss;
if (timeOffset > HitObject.HitWindows.Bad / 2)
AddJudgement(new ManiaJudgement { Result = HitResult.Miss });
return;
}
double offset = Math.Abs(Judgement.TimeOffset);
double offset = Math.Abs(timeOffset);
if (offset > HitObject.HitWindows.Miss / 2)
return;
ManiaHitResult? tmpResult = HitObject.HitWindows.ResultFor(offset);
if (tmpResult.HasValue)
{
Judgement.Result = HitResult.Hit;
Judgement.ManiaResult = tmpResult.Value;
}
else
Judgement.Result = HitResult.Miss;
AddJudgement(new ManiaJudgement { Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss });
}
protected override void UpdateState(ArmedState state)
{
switch (State)
{
case ArmedState.Hit:
Colour = Color4.Green;
break;
}
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
public virtual bool OnPressed(ManiaAction action)
{
if (Judgement.Result != HitResult.None)
return false;
if (args.Key != Key)
return false;
if (args.Repeat)
if (action != Action)
return false;
return UpdateJudgement(true);
}
public virtual bool OnReleased(ManiaAction action) => false;
}
}

View File

@ -1,7 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Caching;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@ -14,22 +17,61 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
/// </summary>
internal class BodyPiece : Container, IHasAccentColour
{
private readonly Box box;
private readonly Container subtractionLayer;
private readonly Drawable background;
private readonly BufferedContainer foreground;
private readonly BufferedContainer subtractionContainer;
public BodyPiece()
{
RelativeSizeAxes = Axes.Both;
Blending = BlendingMode.Additive;
Children = new[]
{
box = new Box
background = new Box { RelativeSizeAxes = Axes.Both },
foreground = new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.3f
CacheDrawnFrameBuffer = true,
Children = new Drawable[]
{
new Box { RelativeSizeAxes = Axes.Both },
subtractionContainer = new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
// This is needed because we're blending with another object
BackgroundColour = Color4.White.Opacity(0),
CacheDrawnFrameBuffer = true,
// The 'hole' is achieved by subtracting the result of this container with the parent
Blending = new BlendingParameters { AlphaEquation = BlendingEquation.ReverseSubtract },
Child = subtractionLayer = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
// Height computed in Update
Width = 1,
Masking = true,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
}
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
updateAccentColour();
}
private Color4 accentColour;
public Color4 AccentColour
{
@ -40,8 +82,51 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
return;
accentColour = value;
box.Colour = accentColour;
updateAccentColour();
}
}
private Cached subtractionCache = new Cached();
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) > 0)
subtractionCache.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void Update()
{
base.Update();
if (!subtractionCache.IsValid)
{
subtractionLayer.Width = 5;
subtractionLayer.Height = Math.Max(0, DrawHeight - DrawWidth);
subtractionLayer.EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.White,
Type = EdgeEffectType.Glow,
Radius = DrawWidth
};
foreground.ForceRedraw();
subtractionContainer.ForceRedraw();
subtractionCache.Validate();
}
}
private void updateAccentColour()
{
if (!IsLoaded)
return;
foreground.Colour = AccentColour.Opacity(0.4f);
background.Colour = AccentColour.Opacity(0.2f);
subtractionCache.Invalidate();
}
}
}

View File

@ -0,0 +1,65 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
public class GlowPiece : CompositeDrawable, IHasAccentColour
{
private const float glow_alpha = 0.7f;
private const float glow_radius = 5;
public GlowPiece()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
InternalChild = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
};
}
protected override void LoadComplete()
{
base.LoadComplete();
updateGlow();
}
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour; }
set
{
if (accentColour == value)
return;
accentColour = value;
updateGlow();
}
}
private void updateGlow()
{
if (!IsLoaded)
return;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = AccentColour.Opacity(glow_alpha),
Radius = glow_radius,
Hollow = true
};
}
}
}

View File

@ -0,0 +1,85 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
public class LaneGlowPiece : CompositeDrawable, IHasAccentColour
{
private const float total_height = 100;
private const float glow_height = 50;
private const float glow_alpha = 0.4f;
private const float edge_alpha = 0.3f;
public LaneGlowPiece()
{
BypassAutoSizeAxes = Axes.Both;
RelativeSizeAxes = Axes.X;
Height = total_height;
InternalChildren = new[]
{
new Container
{
Name = "Left edge",
RelativeSizeAxes = Axes.Y,
Width = 1,
Children = createGradient(edge_alpha)
},
new Container
{
Name = "Right edge",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = 1,
Children = createGradient(edge_alpha)
},
new Container
{
Name = "Glow",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = glow_height,
Children = createGradient(glow_alpha)
}
};
}
private Drawable[] createGradient(float alpha) => new Drawable[]
{
new Box
{
Name = "Top",
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Blending = BlendingMode.Additive,
Colour = ColourInfo.GradientVertical(Color4.Transparent, Color4.White.Opacity(alpha))
},
new Box
{
Name = "Bottom",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Blending = BlendingMode.Additive,
Colour = ColourInfo.GradientVertical(Color4.White.Opacity(alpha), Color4.Transparent)
}
};
public Color4 AccentColour
{
get { return Colour; }
set { Colour = value; }
}
}
}

View File

@ -37,6 +37,17 @@ namespace osu.Game.Rulesets.Mania.Objects
}
}
public override int Column
{
get { return base.Column; }
set
{
base.Column = value;
Head.Column = value;
Tail.Column = value;
}
}
/// <summary>
/// The head note of the hold.
/// </summary>
@ -80,7 +91,8 @@ namespace osu.Game.Rulesets.Mania.Objects
{
ret.Add(new HoldNoteTick
{
StartTime = t
StartTime = t,
Column = Column
});
}

View File

@ -8,6 +8,6 @@ namespace osu.Game.Rulesets.Mania.Objects
{
public abstract class ManiaHitObject : HitObject, IHasColumn
{
public int Column { get; set; }
public virtual int Column { get; set; }
}
}

View File

@ -1,7 +1,3 @@
<!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<configuration>
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>

View File

@ -0,0 +1,133 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
using osu.Game.Users;
namespace osu.Game.Rulesets.Mania.Replays
{
internal class ManiaAutoGenerator : AutoGenerator<ManiaHitObject>
{
private const double release_delay = 20;
private readonly int availableColumns;
public ManiaAutoGenerator(Beatmap<ManiaHitObject> beatmap, int availableColumns)
: base(beatmap)
{
this.availableColumns = availableColumns;
Replay = new Replay { User = new User { Username = @"Autoplay" } };
}
protected Replay Replay;
public override Replay Generate()
{
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
Replay.Frames.Add(new ReplayFrame(-100000, null, null, ReplayButtonState.None));
double[] holdEndTimes = new double[availableColumns];
for (int i = 0; i < availableColumns; i++)
holdEndTimes[i] = double.NegativeInfinity;
// Notes are handled row-by-row
foreach (var objGroup in Beatmap.HitObjects.GroupBy(h => h.StartTime))
{
double groupTime = objGroup.Key;
int activeColumns = 0;
// Get the previously held-down active columns
for (int i = 0; i < availableColumns; i++)
{
if (holdEndTimes[i] > groupTime)
activeColumns |= 1 << i;
}
// Add on the group columns, keeping track of the held notes for the next rows
foreach (var obj in objGroup)
{
var holdNote = obj as HoldNote;
if (holdNote != null)
holdEndTimes[obj.Column] = Math.Max(holdEndTimes[obj.Column], holdNote.EndTime);
activeColumns |= 1 << obj.Column;
}
Replay.Frames.Add(new ReplayFrame(groupTime, activeColumns, null, ReplayButtonState.None));
// Add the release frames. We can't do this with the loop above because we need activeColumns to be fully populated
foreach (var obj in objGroup.GroupBy(h => (h as IHasEndTime)?.EndTime ?? h.StartTime + release_delay).OrderBy(h => h.Key))
{
var groupEndTime = obj.Key;
int activeColumnsAtEnd = 0;
for (int i = 0; i < availableColumns; i++)
{
if (holdEndTimes[i] > groupEndTime)
activeColumnsAtEnd |= 1 << i;
}
Replay.Frames.Add(new ReplayFrame(groupEndTime, activeColumnsAtEnd, 0, ReplayButtonState.None));
}
}
Replay.Frames = Replay.Frames
// Pick the maximum activeColumns for all frames at the same time
.GroupBy(f => f.Time)
.Select(g => new ReplayFrame(g.First().Time, maxMouseX(g), 0, ReplayButtonState.None))
// The addition of release frames above maybe result in unordered frames, but we need them ordered
.OrderBy(f => f.Time)
.ToList();
return Replay;
}
/// <summary>
/// Finds the maximum <see cref="ReplayFrame.MouseX"/> by count of bits from a grouping of <see cref="ReplayFrame"/>s.
/// </summary>
/// <param name="group">The <see cref="ReplayFrame"/> grouping to search.</param>
/// <returns>The maximum <see cref="ReplayFrame.MouseX"/> by count of bits.</returns>
private float maxMouseX(IGrouping<double, ReplayFrame> group)
{
int currentCount = -1;
int currentMax = 0;
foreach (var val in group)
{
int newCount = countBits((int)(val.MouseX ?? 0));
if (newCount > currentCount)
{
currentCount = newCount;
currentMax = (int)(val.MouseX ?? 0);
}
}
return currentMax;
}
/// <summary>
/// Counts the number of bits set in a value.
/// </summary>
/// <param name="value">The value to count.</param>
/// <returns>The number of set bits.</returns>
private int countBits(int value)
{
int count = 0;
while (value > 0)
{
if ((value & 1) > 0)
count++;
value >>= 1;
}
return count;
}
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Input;
using osu.Game.Rulesets.Replays;
namespace osu.Game.Rulesets.Mania.Replays
{
internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler
{
public ManiaFramedReplayInputHandler(Replay replay)
: base(replay)
{
}
public override List<InputState> GetPendingStates()
{
var actions = new List<ManiaAction>();
int activeColumns = (int)(CurrentFrame.MouseX ?? 0);
int counter = 0;
while (activeColumns > 0)
{
if ((activeColumns & 1) > 0)
actions.Add(ManiaAction.Key1 + counter);
counter++;
activeColumns >>= 1;
}
return new List<InputState> { new ReplayState<ManiaAction> { PressedActions = actions } };
}
}
}

View File

@ -1,9 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Drawables;
@ -12,34 +12,8 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Scoring
{
internal class ManiaScoreProcessor : ScoreProcessor<ManiaHitObject, ManiaJudgement>
internal class ManiaScoreProcessor : ScoreProcessor<ManiaHitObject>
{
/// <summary>
/// The maximum score achievable.
/// Does _not_ include bonus score - for bonus score see <see cref="bonusScore"/>.
/// </summary>
private const int max_score = 1000000;
/// <summary>
/// The amount of the score attributed to combo.
/// </summary>
private const double combo_portion_max = max_score * 0.2;
/// <summary>
/// The amount of the score attributed to accuracy.
/// </summary>
private const double accuracy_portion_max = max_score * 0.8;
/// <summary>
/// The factor used to determine relevance of combos.
/// </summary>
private const double combo_base = 4;
/// <summary>
/// The combo value at which hit objects result in the max score possible.
/// </summary>
private const int combo_relevance_cap = 400;
/// <summary>
/// The hit HP multiplier at OD = 0.
/// </summary>
@ -115,52 +89,16 @@ namespace osu.Game.Rulesets.Mania.Scoring
/// </summary>
private double hpMultiplier = 1;
/// <summary>
/// The cumulative combo portion of the score.
/// </summary>
private double comboScore => combo_portion_max * comboPortion / maxComboPortion;
/// <summary>
/// The cumulative accuracy portion of the score.
/// </summary>
private double accuracyScore => accuracy_portion_max * Math.Pow(Accuracy, 4) * totalHits / maxTotalHits;
/// <summary>
/// The cumulative bonus score.
/// This is added on top of <see cref="max_score"/>, thus the total score can exceed <see cref="max_score"/>.
/// </summary>
private double bonusScore;
/// <summary>
/// The <see cref="comboPortion"/> achieved by a perfect playthrough.
/// </summary>
private double maxComboPortion;
/// <summary>
/// The portion of the score dedicated to combo.
/// </summary>
private double comboPortion;
/// <summary>
/// The <see cref="totalHits"/> achieved by a perfect playthrough.
/// </summary>
private int maxTotalHits;
/// <summary>
/// The total hits.
/// </summary>
private int totalHits;
public ManiaScoreProcessor()
{
}
public ManiaScoreProcessor(RulesetContainer<ManiaHitObject, ManiaJudgement> rulesetContainer)
public ManiaScoreProcessor(RulesetContainer<ManiaHitObject> rulesetContainer)
: base(rulesetContainer)
{
}
protected override void ComputeTargets(Beatmap<ManiaHitObject> beatmap)
protected override void SimulateAutoplay(Beatmap<ManiaHitObject> beatmap)
{
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.Difficulty;
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
@ -172,40 +110,18 @@ namespace osu.Game.Rulesets.Mania.Scoring
{
var holdNote = obj as HoldNote;
if (obj is Note)
{
AddJudgement(new ManiaJudgement
{
Result = HitResult.Hit,
ManiaResult = ManiaHitResult.Perfect
});
}
else if (holdNote != null)
if (holdNote != null)
{
// Head
AddJudgement(new ManiaJudgement
{
Result = HitResult.Hit,
ManiaResult = ManiaJudgement.MAX_HIT_RESULT
});
AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
// Ticks
int tickCount = holdNote.Ticks.Count();
for (int i = 0; i < tickCount; i++)
{
AddJudgement(new HoldNoteTickJudgement
{
Result = HitResult.Hit,
ManiaResult = ManiaJudgement.MAX_HIT_RESULT,
});
}
AddJudgement(new HoldNoteTailJudgement
{
Result = HitResult.Hit,
ManiaResult = ManiaJudgement.MAX_HIT_RESULT
});
AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
}
AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
}
if (!HasFailed)
@ -214,81 +130,45 @@ namespace osu.Game.Rulesets.Mania.Scoring
hpMultiplier *= 1.01;
hpMissMultiplier *= 0.98;
Reset();
Reset(false);
}
maxTotalHits = totalHits;
maxComboPortion = comboPortion;
}
protected override void OnNewJudgement(ManiaJudgement judgement)
protected override void OnNewJudgement(Judgement judgement)
{
base.OnNewJudgement(judgement);
bool isTick = judgement is HoldNoteTickJudgement;
if (!isTick)
totalHits++;
switch (judgement.Result)
if (isTick)
{
case HitResult.Miss:
Health.Value += hpMissMultiplier * hp_increase_miss;
break;
case HitResult.Hit:
if (isTick)
{
Health.Value += hpMultiplier * hp_increase_tick;
bonusScore += judgement.ResultValueForScore;
}
else
{
switch (judgement.ManiaResult)
{
case ManiaHitResult.Bad:
Health.Value += hpMultiplier * hp_increase_bad;
break;
case ManiaHitResult.Ok:
Health.Value += hpMultiplier * hp_increase_ok;
break;
case ManiaHitResult.Good:
Health.Value += hpMultiplier * hp_increase_good;
break;
case ManiaHitResult.Great:
Health.Value += hpMultiplier * hp_increase_great;
break;
case ManiaHitResult.Perfect:
Health.Value += hpMultiplier * hp_increase_perfect;
break;
}
// A factor that is applied to make higher combos more relevant
double comboRelevance = Math.Min(Math.Max(0.5, Math.Log(Combo.Value, combo_base)), Math.Log(combo_relevance_cap, combo_base));
comboPortion += judgement.ResultValueForScore * comboRelevance;
}
break;
if (judgement.IsHit)
Health.Value += hpMultiplier * hp_increase_tick;
}
int scoreForAccuracy = 0;
int maxScoreForAccuracy = 0;
foreach (var j in Judgements)
else
{
scoreForAccuracy += j.ResultValueForAccuracy;
maxScoreForAccuracy += j.MaxResultValueForAccuracy;
switch (judgement.Result)
{
case HitResult.Miss:
Health.Value += hpMissMultiplier * hp_increase_miss;
break;
case HitResult.Meh:
Health.Value += hpMultiplier * hp_increase_bad;
break;
case HitResult.Ok:
Health.Value += hpMultiplier * hp_increase_ok;
break;
case HitResult.Good:
Health.Value += hpMultiplier * hp_increase_good;
break;
case HitResult.Great:
Health.Value += hpMultiplier * hp_increase_great;
break;
case HitResult.Perfect:
Health.Value += hpMultiplier * hp_increase_perfect;
break;
}
}
Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy;
TotalScore.Value = comboScore + accuracyScore + bonusScore;
}
protected override void Reset()
{
base.Reset();
Health.Value = 1;
bonusScore = 0;
comboPortion = 0;
totalHits = 0;
}
}
}

View File

@ -3,24 +3,21 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Colour;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using System;
using osu.Framework.Configuration;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Mania.UI
{
public class Column : ScrollingPlayfield<ManiaHitObject, ManiaJudgement>, IHasAccentColour
public class Column : ScrollingPlayfield, IHasAccentColour
{
private const float key_icon_size = 10;
private const float key_icon_corner_radius = 3;
@ -32,18 +29,21 @@ namespace osu.Game.Rulesets.Mania.UI
private const float column_width = 45;
private const float special_column_width = 70;
/// <summary>
/// The key that will trigger input actions for this column and hit objects contained inside it.
/// </summary>
public Bindable<Key> Key = new Bindable<Key>();
public ManiaAction Action;
private readonly Box background;
private readonly Container hitTargetBar;
private readonly Container keyIcon;
internal readonly Container TopLevelContainer;
private readonly Container explosionContainer;
protected override Container<Drawable> Content => content;
private readonly Container<Drawable> content;
private const float opacity_released = 0.1f;
private const float opacity_pressed = 0.25f;
public Column()
: base(Axes.Y)
{
@ -53,9 +53,9 @@ namespace osu.Game.Rulesets.Mania.UI
{
background = new Box
{
Name = "Foreground",
Name = "Background",
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f
Alpha = opacity_released
},
new Container
{
@ -101,8 +101,13 @@ namespace osu.Game.Rulesets.Mania.UI
// For column lighting, we need to capture input events before the notes
new InputTarget
{
KeyDown = onKeyDown,
KeyUp = onKeyUp
Pressed = onPressed,
Released = onReleased
},
explosionContainer = new Container
{
Name = "Hit explosions",
RelativeSizeAxes = Axes.Both
}
}
},
@ -141,8 +146,11 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
}
}
},
TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
};
TopLevelContainer.Add(explosionContainer.CreateProxy());
}
public override Axes RelativeSizeAxes => Axes.Y;
@ -193,31 +201,38 @@ namespace osu.Game.Rulesets.Mania.UI
/// Adds a DrawableHitObject to this Playfield.
/// </summary>
/// <param name="hitObject">The DrawableHitObject to add.</param>
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> hitObject)
public override void Add(DrawableHitObject hitObject)
{
hitObject.Depth = (float)hitObject.HitObject.StartTime;
hitObject.AccentColour = AccentColour;
HitObjects.Add(hitObject);
}
private bool onKeyDown(InputState state, KeyDownEventArgs args)
public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
if (args.Repeat)
return false;
if (!judgement.IsHit)
return;
if (args.Key == Key)
explosionContainer.Add(new HitExplosion(judgedObject));
}
private bool onPressed(ManiaAction action)
{
if (action == Action)
{
background.FadeTo(background.Alpha + 0.2f, 50, Easing.OutQuint);
background.FadeTo(opacity_pressed, 50, Easing.OutQuint);
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint);
}
return false;
}
private bool onKeyUp(InputState state, KeyUpEventArgs args)
private bool onReleased(ManiaAction action)
{
if (args.Key == Key)
if (action == Action)
{
background.FadeTo(0.2f, 800, Easing.OutQuart);
background.FadeTo(opacity_released, 800, Easing.OutQuart);
keyIcon.ScaleTo(1f, 400, Easing.OutQuart);
}
@ -227,10 +242,10 @@ namespace osu.Game.Rulesets.Mania.UI
/// <summary>
/// This is a simple container which delegates various input events that have to be captured before the notes.
/// </summary>
private class InputTarget : Container
private class InputTarget : Container, IKeyBindingHandler<ManiaAction>
{
public Func<InputState, KeyDownEventArgs, bool> KeyDown;
public Func<InputState, KeyUpEventArgs, bool> KeyUp;
public Func<ManiaAction, bool> Pressed;
public Func<ManiaAction, bool> Released;
public InputTarget()
{
@ -239,8 +254,8 @@ namespace osu.Game.Rulesets.Mania.UI
Alpha = 0;
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => KeyDown?.Invoke(state, args) ?? false;
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => KeyUp?.Invoke(state, args) ?? false;
public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false;
public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false;
}
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Mania.UI
{
internal class DrawableManiaJudgement : DrawableJudgement
{
public DrawableManiaJudgement(Judgement judgement)
: base(judgement)
{
JudgementText.TextSize = 25;
}
protected override void LoadComplete()
{
base.LoadComplete();
this.FadeInFromZero(50, Easing.OutQuint);
if (Judgement.IsHit)
{
this.ScaleTo(0.8f);
this.ScaleTo(1, 250, Easing.OutElastic);
this.Delay(50).FadeOut(200).ScaleTo(0.75f, 250);
}
Expire();
}
}
}

View File

@ -0,0 +1,64 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
internal class HitExplosion : CompositeDrawable
{
private readonly Box inner;
public HitExplosion(DrawableHitObject judgedObject)
{
bool isTick = judgedObject is DrawableHoldNoteTick;
Anchor = Anchor.TopCentre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
Size = new Vector2(isTick ? 0.5f : 1);
FillMode = FillMode.Fit;
Blending = BlendingMode.Additive;
Color4 accent = isTick ? Color4.White : judgedObject.AccentColour;
InternalChild = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = 1,
BorderColour = accent,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = accent,
Radius = 10,
Hollow = true
},
Child = inner = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = accent,
Alpha = 1,
AlwaysPresent = true,
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500).Expire();
inner.FadeOut(250);
}
}
}

View File

@ -6,30 +6,24 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Graphics.Containers;
using System;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using OpenTK.Input;
using System.Linq;
using System.Collections.Generic;
using osu.Framework.Configuration;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaPlayfield : ScrollingPlayfield<ManiaHitObject, ManiaJudgement>
public class ManiaPlayfield : ScrollingPlayfield
{
public const float HIT_TARGET_POSITION = 50;
/// <summary>
/// Default column keys, expanding outwards from the middle as more column are added.
/// E.g. 2 columns use FJ, 4 columns use DFJK, 6 use SDFJKL, etc...
/// </summary>
private static readonly Key[] default_keys = { Key.A, Key.S, Key.D, Key.F, Key.J, Key.K, Key.L, Key.Semicolon };
private SpecialColumnPosition specialColumnPosition;
/// <summary>
/// The style to use for the special column.
@ -45,6 +39,11 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
/// <summary>
/// Whether this playfield should be inverted. This flips everything inside the playfield.
/// </summary>
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
private readonly FlowContainer<Column> columns;
public IEnumerable<Column> Columns => columns.Children;
@ -54,6 +53,8 @@ namespace osu.Game.Rulesets.Mania.UI
private List<Color4> normalColumnColours = new List<Color4>();
private Color4 specialColumnColour;
private readonly Container<DrawableManiaJudgement> judgements;
private readonly int columnCount;
public ManiaPlayfield(int columnCount)
@ -64,21 +65,23 @@ namespace osu.Game.Rulesets.Mania.UI
if (columnCount <= 0)
throw new ArgumentException("Can't have zero or fewer columns.");
Inverted.Value = true;
Container topLevelContainer;
InternalChildren = new Drawable[]
{
new Container
{
Name = "Playfield elements",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Masking = true,
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Children = new Drawable[]
{
new Container
{
Name = "Masked elements",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Name = "Columns mask",
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Masking = true,
@ -86,6 +89,7 @@ namespace osu.Game.Rulesets.Mania.UI
{
new Box
{
Name = "Background",
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black
},
@ -97,39 +101,63 @@ namespace osu.Game.Rulesets.Mania.UI
Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Left = 1, Right = 1 },
Spacing = new Vector2(1, 0)
}
},
}
},
new Container
{
Name = "Barlines mask",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = HIT_TARGET_POSITION },
Children = new[]
RelativeSizeAxes = Axes.Y,
Width = 1366, // Bar lines should only be masked on the vertical axis
BypassAutoSizeAxes = Axes.Both,
Masking = true,
Child = content = new Container
{
content = new Container
{
Name = "Bar lines",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
// Width is set in the Update method
}
Name = "Bar lines",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
}
}
},
judgements = new Container<DrawableManiaJudgement>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Y = HIT_TARGET_POSITION + 150,
BypassAutoSizeAxes = Axes.Both
},
topLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
}
}
};
var currentAction = ManiaAction.Key1;
for (int i = 0; i < columnCount; i++)
{
var c = new Column();
c.VisibleTimeRange.BindTo(VisibleTimeRange);
c.IsSpecial = isSpecialColumn(i);
c.Action = c.IsSpecial ? ManiaAction.Special : currentAction++;
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
columns.Add(c);
AddNested(c);
}
Inverted.ValueChanged += invertedChanged;
Inverted.TriggerChange();
}
private void invertedChanged(bool newValue)
{
Scale = new Vector2(1, newValue ? -1 : 1);
judgements.Scale = Scale;
}
[BackgroundDependencyLoader]
@ -144,15 +172,11 @@ namespace osu.Game.Rulesets.Mania.UI
specialColumnColour = colours.BlueDark;
// Set the special column + colour + key
for (int i = 0; i < columnCount; i++)
foreach (var column in Columns)
{
Column column = Columns.ElementAt(i);
column.IsSpecial = isSpecialColumn(i);
if (!column.IsSpecial)
continue;
column.Key.Value = Key.Space;
column.AccentColour = specialColumnColour;
}
@ -166,21 +190,19 @@ namespace osu.Game.Rulesets.Mania.UI
nonSpecialColumns[i].AccentColour = colour;
nonSpecialColumns[nonSpecialColumns.Count - 1 - i].AccentColour = colour;
}
}
// We'll set the keys for non-special columns in another separate loop because it's not mirrored like the above colours
// Todo: This needs to go when we get to bindings and use Button1, ..., ButtonN instead
for (int i = 0; i < nonSpecialColumns.Count; i++)
public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
var maniaObject = (ManiaHitObject)judgedObject.HitObject;
columns[maniaObject.Column].OnJudgement(judgedObject, judgement);
judgements.Clear();
judgements.Add(new DrawableManiaJudgement(judgement)
{
Column column = nonSpecialColumns[i];
int keyOffset = default_keys.Length / 2 - nonSpecialColumns.Count / 2 + i;
if (keyOffset >= 0 && keyOffset < default_keys.Length)
column.Key.Value = default_keys[keyOffset];
else
// There is no default key defined for this column. Let's set this to Unknown for now
// however note that this will be gone after bindings are in place
column.Key.Value = Key.Unknown;
}
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
}
/// <summary>
@ -202,7 +224,8 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
public override void Add(DrawableHitObject<ManiaHitObject, ManiaJudgement> h) => Columns.ElementAt(h.HitObject.Column).Add(h);
public override void Add(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column).Add(h);
public void Add(DrawableBarLine barline) => HitObjects.Add(barline);
protected override void Update()

View File

@ -5,36 +5,37 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Lists;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mania.Timing;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Timing;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaRulesetContainer : ScrollingRulesetContainer<ManiaPlayfield, ManiaHitObject, ManiaJudgement>
public class ManiaRulesetContainer : ScrollingRulesetContainer<ManiaPlayfield, ManiaHitObject>
{
/// <summary>
/// Preferred column count. This will only have an effect during the initialization of the play field.
/// The number of columns which the <see cref="ManiaPlayfield"/> should display, and which
/// the beatmap converter will attempt to convert beatmaps to use.
/// </summary>
public int PreferredColumns;
public int AvailableColumns { get; private set; }
public IEnumerable<DrawableBarLine> BarLines;
@ -75,36 +76,47 @@ namespace osu.Game.Rulesets.Mania.UI
BarLines.ForEach(Playfield.Add);
}
protected override void ApplyBeatmap()
{
base.ApplyBeatmap();
PreferredColumns = (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize));
}
protected sealed override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(PreferredColumns)
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(AvailableColumns)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
// Invert by default for now (should be moved to config/skin later)
Scale = new Vector2(1, -1)
};
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, AvailableColumns);
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter()
{
Bindable<Key> key = Playfield.Columns.ElementAt(h.Column).Key;
if (IsForCurrentRuleset)
AvailableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize));
else
{
float percentSliderOrSpinner = (float)WorkingBeatmap.Beatmap.HitObjects.Count(h => h is IHasEndTime) / WorkingBeatmap.Beatmap.HitObjects.Count;
if (percentSliderOrSpinner < 0.2)
AvailableColumns = 7;
else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize) >= 5)
AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 5 ? 7 : 6;
else if (percentSliderOrSpinner > 0.6)
AvailableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 4 ? 5 : 4;
else
AvailableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) + 1, 7));
}
return new ManiaBeatmapConverter(IsForCurrentRuleset, AvailableColumns);
}
protected override DrawableHitObject<ManiaHitObject> GetVisualRepresentation(ManiaHitObject h)
{
ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action;
var holdNote = h as HoldNote;
if (holdNote != null)
return new DrawableHoldNote(holdNote, key);
return new DrawableHoldNote(holdNote, action);
var note = h as Note;
if (note != null)
return new DrawableNote(note, key);
return new DrawableNote(note, action);
return null;
}
@ -112,5 +124,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic);
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
}
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>osu.Game.Rulesets.Mania</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Mania</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@ -34,7 +34,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll</HintPath>
<HintPath>$(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
@ -60,7 +60,6 @@
<Compile Include="Judgements\HitWindows.cs" />
<Compile Include="Judgements\HoldNoteTailJudgement.cs" />
<Compile Include="Judgements\HoldNoteTickJudgement.cs" />
<Compile Include="Judgements\ManiaHitResult.cs" />
<Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Mods\IGenerateSpeedAdjustments.cs" />
@ -70,8 +69,12 @@
<Compile Include="Objects\Drawables\DrawableManiaHitObject.cs" />
<Compile Include="Objects\Drawables\DrawableNote.cs" />
<Compile Include="Objects\Drawables\Pieces\BodyPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\GlowPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\LaneGlowPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\NotePiece.cs" />
<Compile Include="Objects\Types\IHasColumn.cs" />
<Compile Include="Replays\ManiaAutoGenerator.cs" />
<Compile Include="Replays\ManiaFramedReplayInputHandler.cs" />
<Compile Include="Scoring\ManiaScoreProcessor.cs" />
<Compile Include="Objects\BarLine.cs" />
<Compile Include="Objects\HoldNote.cs" />
@ -79,9 +82,12 @@
<Compile Include="Objects\ManiaHitObject.cs" />
<Compile Include="Objects\Note.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ManiaInputManager.cs" />
<Compile Include="Timing\GravityScrollingContainer.cs" />
<Compile Include="Timing\ScrollingAlgorithm.cs" />
<Compile Include="UI\Column.cs" />
<Compile Include="UI\DrawableManiaJudgement.cs" />
<Compile Include="UI\HitExplosion.cs" />
<Compile Include="UI\ManiaRulesetContainer.cs" />
<Compile Include="UI\ManiaPlayfield.cs" />
<Compile Include="ManiaRuleset.cs" />
@ -95,14 +101,6 @@
<Project>{C76BF5B3-985E-4D39-95FE-97C9C879B83A}</Project>
<Name>osu.Framework</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
<Project>{C92A607B-1FDD-4954-9F92-03FF547D9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{F167E17A-7DE6-4AF5-B920-A5112296C695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}</Project>
<Name>osu.Game</Name>
@ -112,6 +110,7 @@
<None Include="..\osu.licenseheader">
<Link>osu.licenseheader</Link>
</None>
<None Include="app.config" />
<None Include="OpenTK.dll.config" />
<None Include="packages.config" />
</ItemGroup>

View File

@ -1,8 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
<packages>
<package id="ppy.OpenTK" version="3.0" targetFramework="net45" />
<package id="OpenTK" version="3.0.0-git00009" targetFramework="net461" />
</packages>

View File

@ -1,7 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
@ -64,8 +64,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
//We are no longer within stacking range of the next object.
break;
if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
{
stackBaseIndex = n;
@ -130,14 +130,14 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
* o <- hitCircle has stack of -1
* o <- hitCircle has stack of -2
*/
if (objectN is Slider && Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
if (objectN is Slider && Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{
int offset = objectI.StackHeight - objectN.StackHeight + 1;
for (int j = n + 1; j <= i; j++)
{
//For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above).
OsuHitObject objectJ = beatmap.HitObjects[j];
if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stack_distance)
if (Vector2Extensions.Distance(objectN.EndPosition, objectJ.Position) < stack_distance)
objectJ.StackHeight -= offset;
}
@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
break;
}
if (Vector2.Distance(objectN.Position, objectI.Position) < stack_distance)
if (Vector2Extensions.Distance(objectN.Position, objectI.Position) < stack_distance)
{
//Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out.
//NOTE: Sliders with start positions stacking are a special case that is also handled here.
@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
//We are no longer within stacking range of the previous object.
break;
if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
if (Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{
objectN.StackHeight = objectI.StackHeight + 1;
objectI = objectN;

View File

@ -4,49 +4,31 @@
using OpenTK;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Framework.Extensions;
using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Judgements
{
public class OsuJudgement : Judgement
{
public override HitResult MaxResult => HitResult.Great;
/// <summary>
/// The positional hit offset.
/// </summary>
public Vector2 PositionOffset;
/// <summary>
/// The score the user achieved.
/// </summary>
public OsuScoreResult Score;
/// <summary>
/// The score which would be achievable on a perfect hit.
/// </summary>
public OsuScoreResult MaxScore = OsuScoreResult.Hit300;
public override string ResultString => Score.GetDescription();
public override string MaxResultString => MaxScore.GetDescription();
public int ScoreValue => scoreToInt(Score);
public int MaxScoreValue => scoreToInt(MaxScore);
private int scoreToInt(OsuScoreResult result)
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return 0;
case OsuScoreResult.Hit50:
case HitResult.Meh:
return 50;
case OsuScoreResult.Hit100:
case HitResult.Good:
return 100;
case OsuScoreResult.Hit300:
case HitResult.Great:
return 300;
case OsuScoreResult.SliderTick:
return 10;
}
}

View File

@ -7,8 +7,13 @@ using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Mods
{
@ -28,10 +33,23 @@ namespace osu.Game.Rulesets.Osu.Mods
public override double ScoreMultiplier => 1.06;
}
public class OsuModHardRock : ModHardRock
public class OsuModHardRock : ModHardRock, IApplicableMod<OsuHitObject>
{
public override double ScoreMultiplier => 1.06;
public override bool Ranked => true;
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)
{
rulesetContainer.Objects.OfType<OsuHitObject>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Y));
rulesetContainer.Objects.OfType<Slider>().ForEach(s =>
{
var newControlPoints = new List<Vector2>();
s.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
s.ControlPoints = newControlPoints;
s.Curve?.Calculate(); // Recalculate the slider curve
});
}
}
public class OsuModSuddenDeath : ModSuddenDeath
@ -78,6 +96,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModSpunOut : Mod
{
public override string Name => "Spun Out";
public override string ShortenedName => "SO";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
public override string Description => @"Spinners will be automatically completed";
public override double ScoreMultiplier => 0.9;
@ -88,6 +107,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModAutopilot : Mod
{
public override string Name => "Autopilot";
public override string ShortenedName => "AP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot;
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 0;
@ -108,6 +128,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModTarget : Mod
{
public override string Name => "Target";
public override string ShortenedName => "TP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
public override string Description => @"";
public override double ScoreMultiplier => 1;

View File

@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
new Box
{
Size = new Vector2(width),
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Alpha = 0.5f,

View File

@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@ -38,9 +39,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Colour = AccentColour,
Hit = () =>
{
if (Judgement.Result != HitResult.None) return false;
if (AllJudged)
return false;
Judgement.PositionOffset = Vector2.Zero; //todo: set to correct value
UpdateJudgement(true);
return true;
},
@ -65,24 +66,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Size = circle.DrawSize;
}
protected override void CheckJudgement(bool userTriggered)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (Judgement.TimeOffset > HitObject.HitWindowFor(OsuScoreResult.Hit50))
Judgement.Result = HitResult.Miss;
if (timeOffset > HitObject.HitWindowFor(HitResult.Meh))
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
return;
}
double hitOffset = Math.Abs(Judgement.TimeOffset);
if (hitOffset < HitObject.HitWindowFor(OsuScoreResult.Hit50))
AddJudgement(new OsuJudgement
{
Judgement.Result = HitResult.Hit;
Judgement.Score = HitObject.ScoreResultForOffset(hitOffset);
}
else
Judgement.Result = HitResult.Miss;
Result = HitObject.ScoreResultForOffset(Math.Abs(timeOffset)),
PositionOffset = Vector2.Zero //todo: set to correct value
});
}
protected override void UpdateInitialState()

View File

@ -3,12 +3,12 @@
using System.ComponentModel;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Framework.Graphics;
using System.Linq;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject, OsuJudgement>
public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject>
{
public const float TIME_PREEMPT = 600;
public const float TIME_FADEIN = 400;
@ -21,8 +21,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Alpha = 0;
}
protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.Hit300 };
protected sealed override void UpdateState(ArmedState state)
{
FinishTransforms();
@ -33,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
UpdatePreemptState();
using (BeginDelayedSequence(TIME_PREEMPT + Judgement.TimeOffset, true))
using (BeginDelayedSequence(TIME_PREEMPT + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true))
UpdateCurrentState(state);
}
}
@ -51,6 +49,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected virtual void UpdateCurrentState(ArmedState state)
{
}
private OsuInputManager osuActionInputManager;
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
}
public enum ComboResult
@ -62,18 +63,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
[Description(@"Amazing")]
Perfect
}
public enum OsuScoreResult
{
[Description(@"Miss")]
Miss,
[Description(@"50")]
Hit50,
[Description(@"100")]
Hit100,
[Description(@"300")]
Hit300,
[Description(@"10")]
SliderTick
}
}

View File

@ -9,9 +9,10 @@ using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuJudgement : DrawableJudgement<OsuJudgement>
public class DrawableOsuJudgement : DrawableJudgement
{
public DrawableOsuJudgement(OsuJudgement judgement) : base(judgement)
public DrawableOsuJudgement(OsuJudgement judgement)
: base(judgement)
{
}

View File

@ -8,6 +8,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@ -64,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Scale = s.Scale,
ComboColour = s.ComboColour,
Samples = s.Samples,
}),
})
};
components.Add(body);
@ -114,33 +115,31 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
bouncer2.Position = slider.Curve.PositionAt(body.SnakedEnd ?? 0);
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
if (initialCircle.Judgement?.Result != HitResult.Hit)
if (!initialCircle.Judgements.Any(j => j.IsHit))
initialCircle.Position = slider.Curve.PositionAt(progress);
foreach (var c in components) c.UpdateProgress(progress, repeat);
foreach (var t in ticks.Children) t.Tracking = ball.Tracking;
}
protected override void CheckJudgement(bool userTriggered)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered && Time.Current >= slider.EndTime)
{
var ticksCount = ticks.Children.Count + 1;
var ticksHit = ticks.Children.Count(t => t.Judgement.Result == HitResult.Hit);
if (initialCircle.Judgement.Result == HitResult.Hit)
var ticksHit = ticks.Children.Count(t => t.Judgements.Any(j => j.IsHit));
if (initialCircle.Judgements.Any(j => j.IsHit))
ticksHit++;
var hitFraction = (double)ticksHit / ticksCount;
if (hitFraction == 1 && initialCircle.Judgement.Score == OsuScoreResult.Hit300)
Judgement.Score = OsuScoreResult.Hit300;
else if (hitFraction >= 0.5 && initialCircle.Judgement.Score >= OsuScoreResult.Hit100)
Judgement.Score = OsuScoreResult.Hit100;
if (hitFraction == 1 && initialCircle.Judgements.Any(j => j.Result == HitResult.Great))
AddJudgement(new OsuJudgement { Result = HitResult.Great });
else if (hitFraction >= 0.5 && initialCircle.Judgements.Any(j => j.Result >= HitResult.Good))
AddJudgement(new OsuJudgement { Result = HitResult.Good });
else if (hitFraction > 0)
Judgement.Score = OsuScoreResult.Hit50;
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
else
Judgement.Score = OsuScoreResult.Miss;
Judgement.Result = Judgement.Score != OsuScoreResult.Miss ? HitResult.Hit : HitResult.Miss;
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
}
}

View File

@ -4,10 +4,10 @@
using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@ -22,8 +22,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public override bool RemoveWhenNotAlive => false;
protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.SliderTick };
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{
this.sliderTick = sliderTick;
@ -49,13 +47,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
};
}
protected override void CheckJudgement(bool userTriggered)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (Judgement.TimeOffset >= 0)
{
Judgement.Result = Tracking ? HitResult.Hit : HitResult.Miss;
Judgement.Score = Tracking ? OsuScoreResult.SliderTick : OsuScoreResult.Miss;
}
if (timeOffset >= 0)
AddJudgement(new OsuJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
}
protected override void UpdatePreemptState()

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
@ -10,6 +11,7 @@ using OpenTK.Graphics;
using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Screens.Ranking;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
@ -106,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1);
protected override void CheckJudgement(bool userTriggered)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (Time.Current < HitObject.StartTime) return;
@ -128,26 +130,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!userTriggered && Time.Current >= spinner.EndTime)
{
if (Progress >= 1)
{
Judgement.Score = OsuScoreResult.Hit300;
Judgement.Result = HitResult.Hit;
}
AddJudgement(new OsuJudgement { Result = HitResult.Great });
else if (Progress > .9)
{
Judgement.Score = OsuScoreResult.Hit100;
Judgement.Result = HitResult.Hit;
}
AddJudgement(new OsuJudgement { Result = HitResult.Good });
else if (Progress > .75)
{
Judgement.Score = OsuScoreResult.Hit50;
Judgement.Result = HitResult.Hit;
}
else
{
Judgement.Score = OsuScoreResult.Miss;
if (Time.Current >= spinner.EndTime)
Judgement.Result = HitResult.Miss;
}
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
else if (Time.Current >= spinner.EndTime)
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
}
}
@ -165,6 +154,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
glow.Colour = colours.BlueDark;
}
protected override void Update()
{
disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
base.Update();
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
new TrianglesPiece
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Alpha = 0.5f,
}
};

View File

@ -16,14 +16,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
BlendingMode = BlendingMode.Additive;
Blending = BlendingMode.Additive;
Alpha = 0;
Children = new Drawable[]
{
new TrianglesPiece
{
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f,
}

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
BlendingMode = BlendingMode.Additive;
Blending = BlendingMode.Additive;
Alpha = 0;
Children = new Drawable[]

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BlendingMode = BlendingMode.Additive,
Blending = BlendingMode.Additive,
Alpha = 0.5f
}
};

View File

@ -1,10 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
@ -37,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
this.slider = slider;
Masking = true;
AutoSizeAxes = Axes.Both;
BlendingMode = BlendingMode.Additive;
Blending = BlendingMode.Additive;
Origin = Anchor.Centre;
BorderThickness = 10;
BorderColour = Color4.Orange;
@ -96,11 +98,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return base.OnMouseMove(state);
}
// If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos);
private bool tracking;
public bool Tracking
{
get { return tracking; }
set
private set
{
if (value == tracking) return;
@ -117,8 +122,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
base.Update();
// Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
if (Time.Current < slider.EndTime)
Tracking = canCurrentlyTrack && lastState != null && ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) && lastState.Mouse.HasMainButtonPressed;
Tracking = canCurrentlyTrack && lastState != null && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
}
public void UpdateProgress(double progress, int repeat)

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