Merge branch 'master' of git://github.com/ppy/osu into tooltips

# Conflicts:
#	osu.Game/Graphics/Cursor/MenuCursor.cs
This commit is contained in:
Jorolf
2017-04-13 23:12:07 +02:00
221 changed files with 3921 additions and 2284 deletions

4
.vscode/launch.json vendored
View File

@ -11,7 +11,7 @@
"preLaunchTask": "build", "preLaunchTask": "build",
"runtimeExecutable": null, "runtimeExecutable": null,
"env": {}, "env": {},
"externalConsole": false "console": "internalConsole"
}, },
{ {
"name": "Launch Desktop", "name": "Launch Desktop",
@ -23,7 +23,7 @@
"preLaunchTask": "build", "preLaunchTask": "build",
"runtimeExecutable": null, "runtimeExecutable": null,
"env": {}, "env": {},
"externalConsole": false "console": "internalConsole"
}, },
{ {
"name": "Attach", "name": "Attach",

20
.vscode/tasks.json vendored
View File

@ -2,25 +2,23 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558 // See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format // for the documentation about the tasks.json format
"version": "0.1.0", "version": "0.1.0",
"taskSelector": "/t:",
"tasks": [
{
"taskName": "build",
"isShellCommand": true,
"showOutput": "silent",
"command": "xbuild",
"windows": { "windows": {
"command": "msbuild" "command": "msbuild"
}, },
"linux": {
"command": "xbuild"
},
"args": [ "args": [
// Ask msbuild to generate full paths for file names. // Ask msbuild to generate full paths for file names.
"/property:GenerateFullPaths=true" "/property:GenerateFullPaths=true"
], ],
"taskSelector": "/t:",
"showOutput": "silent",
"tasks": [
{
"taskName": "build",
// Show the output window only if unrecognized errors occur.
"showOutput": "silent",
// Use the standard MS compiler pattern to detect errors, warnings and infos // Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile",
"isBuildCommand": true
} }
] ]
} }

View File

@ -10,7 +10,7 @@ install:
- cmd: git submodule update --init --recursive - cmd: git submodule update --init --recursive
- cmd: choco install resharper-clt -y - cmd: choco install resharper-clt -y
- cmd: choco install nvika -y - cmd: choco install nvika -y
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.1/CodeFileSanity.exe - cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.2/CodeFileSanity.exe
before_build: before_build:
- cmd: CodeFileSanity.exe - cmd: CodeFileSanity.exe
- cmd: nuget restore - cmd: nuget restore

View File

@ -21,4 +21,16 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup> </startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="DeltaCompressionDotNet.MsDelta" publicKeyToken="46b2138a390abf55" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.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> </configuration>

View File

@ -68,9 +68,8 @@
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll</HintPath> <HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll</HintPath> <HintPath>$(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll</HintPath>

View File

@ -6,7 +6,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<packages> <packages>
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net452" /> <package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net452" />
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net452" /> <package id="Mono.Cecil" version="0.9.6.4" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" /> <package id="Newtonsoft.Json" version="10.0.2" targetFramework="net452" />
<package id="NuGet.CommandLine" version="3.5.0" targetFramework="net452" developmentDependency="true" /> <package id="NuGet.CommandLine" version="3.5.0" targetFramework="net452" developmentDependency="true" />
<package id="Splat" version="2.0.0" targetFramework="net452" /> <package id="Splat" version="2.0.0" targetFramework="net452" />
<package id="squirrel.windows" version="1.5.2" targetFramework="net452" /> <package id="squirrel.windows" version="1.5.2" targetFramework="net452" />

View File

@ -33,18 +33,17 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="mscorlib" /> <Reference Include="mscorlib" />
<Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll</HintPath> <HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private> </Reference>
<Reference Include="nunit.framework, Version=3.6.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\NUnit.3.6.1\lib\net45\nunit.framework.dll</HintPath>
</Reference> </Reference>
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath> <HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="Newtonsoft.Json">
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="SQLiteNetExtensions"> <Reference Include="SQLiteNetExtensions">
<HintPath>$(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll</HintPath> <HintPath>$(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll</HintPath>
</Reference> </Reference>

View File

@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
--> -->
<packages> <packages>
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" /> <package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
<package id="NUnit" version="3.5.0" targetFramework="net45" /> <package id="NUnit" version="3.6.1" targetFramework="net45" />
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" /> <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="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
<package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" /> <package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" />

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.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Screens.Select;
using System.Linq;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseBeatmapDetails : TestCase
{
public override string Description => "BeatmapDetails tab of BeatmapDetailArea";
private BeatmapDetails details;
public override void Reset()
{
base.Reset();
Add(details = new BeatmapDetails
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(150),
Beatmap = new BeatmapInfo
{
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),
},
},
});
AddRepeatStep("fail values", newRetryAndFailValues, 10);
}
private int lastRange = 1;
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;
}
}
}

View File

@ -3,15 +3,12 @@
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using osu.Game.Overlays; using osu.Game.Overlays;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
internal class TestCaseChatDisplay : TestCase internal class TestCaseChatDisplay : TestCase
{ {
private ScheduledDelegate messageRequest;
public override string Description => @"Testing chat api and overlay"; public override string Description => @"Testing chat api and overlay";
public override void Reset() public override void Reset()

View File

@ -0,0 +1,42 @@
// 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.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using System.Linq;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseGraph : TestCase
{
public override string Description => "graph";
private BarGraph graph;
public override void Reset()
{
base.Reset();
Children = new[]
{
graph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(0.5f),
},
};
AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1,10).Select(i => (float)i));
AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i));
AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i));
AddStep("Bottom to top", () => graph.Direction = BarDirection.BottomToTop);
AddStep("Top to bottom", () => graph.Direction = BarDirection.TopToBottom);
AddStep("Left to right", () => graph.Direction = BarDirection.LeftToRight);
AddStep("Right to left", () => graph.Direction = BarDirection.RightToLeft);
}
}
}

View File

@ -99,6 +99,7 @@ namespace osu.Desktop.VisualTests.Tests
AddToggleStep(@"auto", state => { auto = state; load(mode); }); AddToggleStep(@"auto", state => { auto = state; load(mode); });
BasicSliderBar<double> sliderBar;
Add(new Container Add(new Container
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
@ -107,16 +108,17 @@ namespace osu.Desktop.VisualTests.Tests
Children = new Drawable[] Children = new Drawable[]
{ {
new SpriteText { Text = "Playback Speed" }, new SpriteText { Text = "Playback Speed" },
new BasicSliderBar<double> sliderBar = new BasicSliderBar<double>
{ {
Width = 150, Width = 150,
Height = 10, Height = 10,
SelectionColor = Color4.Orange, SelectionColor = Color4.Orange,
Bindable = playbackSpeed
} }
} }
}); });
sliderBar.Current.BindTo(playbackSpeed);
framedClock.ProcessFrame(); framedClock.ProcessFrame();
var clockAdjustContainer = new Container var clockAdjustContainer = new Container

View File

@ -44,6 +44,8 @@ namespace osu.Desktop.VisualTests.Tests
kc.Add(new KeyCounterKeyboard(key)); kc.Add(new KeyCounterKeyboard(key));
}); });
TestSliderBar<int> sliderBar;
Add(new Container Add(new Container
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
@ -52,16 +54,17 @@ namespace osu.Desktop.VisualTests.Tests
Children = new Drawable[] Children = new Drawable[]
{ {
new SpriteText { Text = "FadeTime" }, new SpriteText { Text = "FadeTime" },
new TestSliderBar<int> sliderBar =new TestSliderBar<int>
{ {
Width = 150, Width = 150,
Height = 10, Height = 10,
SelectionColor = Color4.Orange, SelectionColor = Color4.Orange,
Bindable = bindable
} }
} }
}); });
sliderBar.Current.BindTo(bindable);
Add(kc); Add(kc);
} }
private class TestSliderBar<T> : SliderBar<T> where T : struct private class TestSliderBar<T> : SliderBar<T> where T : struct

View File

@ -0,0 +1,59 @@
// 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.Containers;
using osu.Framework.Logging;
using osu.Framework.Testing;
using osu.Game.Screens.Play;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseMenuOverlays : TestCase
{
public override string Description => @"Tests pause and fail overlays";
private PauseOverlay pauseOverlay;
private FailOverlay failOverlay;
private int retryCount;
public override void Reset()
{
base.Reset();
retryCount = 0;
Add(pauseOverlay = new PauseOverlay
{
OnResume = () => Logger.Log(@"Resume"),
OnRetry = () => Logger.Log(@"Retry"),
OnQuit = () => Logger.Log(@"Quit"),
});
Add(failOverlay = new FailOverlay
{
OnRetry = () => Logger.Log(@"Retry"),
OnQuit = () => Logger.Log(@"Quit"),
});
AddStep(@"Pause", delegate {
if(failOverlay.State == Visibility.Visible)
{
failOverlay.Hide();
}
pauseOverlay.Show();
});
AddStep("Fail", delegate {
if (pauseOverlay.State == Visibility.Visible)
{
pauseOverlay.Hide();
}
failOverlay.Show();
});
AddStep("Add Retry", delegate
{
retryCount++;
pauseOverlay.Retries = retryCount;
failOverlay.Retries = retryCount;
});
}
}
}

View File

@ -30,7 +30,9 @@ namespace osu.Desktop.VisualTests.Tests
Anchor = Anchor.Centre Anchor = Anchor.Centre
}; };
Add(mc); Add(mc);
AddToggleStep(@"Show", state => mc.State = state ? Visibility.Visible : Visibility.Hidden);
AddToggleStep(@"toggle visibility", state => mc.State = state ? Visibility.Visible : Visibility.Hidden);
AddStep(@"show", () => mc.State = Visibility.Visible);
} }
} }
} }

View File

@ -1,38 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Logging;
using osu.Framework.Testing;
using osu.Game.Screens.Play;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCasePauseOverlay : TestCase
{
public override string Description => @"Tests the pause overlay";
private PauseOverlay pauseOverlay;
private int retryCount;
public override void Reset()
{
base.Reset();
Add(pauseOverlay = new PauseOverlay
{
Depth = -1,
OnResume = () => Logger.Log(@"Resume"),
OnRetry = () => Logger.Log(@"Retry"),
OnQuit = () => Logger.Log(@"Quit")
});
AddStep("Pause", pauseOverlay.Show);
AddStep("Add Retry", delegate
{
retryCount++;
pauseOverlay.Retries = retryCount;
});
retryCount = 0;
}
}
}

View File

@ -14,7 +14,7 @@ namespace osu.Desktop.VisualTests.Tests
{ {
internal class TestCasePlaySongSelect : TestCase internal class TestCasePlaySongSelect : TestCase
{ {
private BeatmapDatabase db, oldDb; private BeatmapDatabase db;
private TestStorage storage; private TestStorage storage;
private PlaySongSelect songSelect; private PlaySongSelect songSelect;
@ -44,13 +44,13 @@ namespace osu.Desktop.VisualTests.Tests
AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; }); AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
} }
protected override void Dispose(bool isDisposing) //protected override void Dispose(bool isDisposing)
{ //{
if (oldDb != null) // if (oldDb != null)
db = null; // db = null;
base.Dispose(isDisposing); // base.Dispose(isDisposing);
} //}
private BeatmapSetInfo createTestBeatmapSet(int i) private BeatmapSetInfo createTestBeatmapSet(int i)
{ {

View File

@ -83,10 +83,7 @@ namespace osu.Desktop.VisualTests.Tests
Colour = Color4.Black, Colour = Color4.Black,
}); });
Add(new PlayerLoader(Player = CreatePlayer(beatmap)) Add(Player = CreatePlayer(beatmap));
{
Beatmap = beatmap
});
} }
protected virtual Player CreatePlayer(WorkingBeatmap beatmap) protected virtual Player CreatePlayer(WorkingBeatmap beatmap)

View File

@ -1,24 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Input.Handlers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Modes.Mods; using osu.Game.Modes.Mods;
using osu.Game.Modes.Osu.Mods; using osu.Game.Modes.Osu.Mods;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using System;
using System.IO;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
internal class TestCaseReplay : TestCasePlayer internal class TestCaseReplay : TestCasePlayer
{ {
private WorkingBeatmap beatmap;
private InputHandler replay;
private Func<Stream> getReplayStream;
public override string Description => @"Testing replay playback."; public override string Description => @"Testing replay playback.";
protected override Player CreatePlayer(WorkingBeatmap beatmap) protected override Player CreatePlayer(WorkingBeatmap beatmap)

View File

@ -22,8 +22,6 @@ namespace osu.Desktop.VisualTests.Tests
int numerator = 0, denominator = 0; int numerator = 0, denominator = 0;
bool maniaHold = false;
ScoreCounter score = new ScoreCounter(7) ScoreCounter score = new ScoreCounter(7)
{ {
Origin = Anchor.TopRight, Origin = Anchor.TopRight,

View File

@ -36,9 +36,9 @@ namespace osu.Desktop.VisualTests.Tests
filter.PinItem(GroupMode.All); filter.PinItem(GroupMode.All);
filter.PinItem(GroupMode.RecentlyPlayed); filter.PinItem(GroupMode.RecentlyPlayed);
filter.ItemChanged += (sender, mode) => filter.Current.ValueChanged += newFilter =>
{ {
text.Text = "Currently Selected: " + mode.ToString(); text.Text = "Currently Selected: " + newFilter.ToString();
}; };
} }
} }

View File

@ -1,13 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Graphics; using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
@ -24,13 +23,12 @@ namespace osu.Desktop.VisualTests.Tests
AddToggleStep("Kiai", b => AddToggleStep("Kiai", b =>
{ {
kiai = !kiai; kiai = !kiai;
Reset(); updateKiaiState();
}); });
Add(new CirclePiece Add(new CirclePiece
{ {
Position = new Vector2(100, 100), Position = new Vector2(100, 100),
Width = 0,
AccentColour = Color4.DarkRed, AccentColour = Color4.DarkRed,
KiaiMode = kiai, KiaiMode = kiai,
Children = new[] Children = new[]
@ -39,10 +37,9 @@ namespace osu.Desktop.VisualTests.Tests
} }
}); });
Add(new StrongCirclePiece Add(new CirclePiece(true)
{ {
Position = new Vector2(350, 100), Position = new Vector2(350, 100),
Width = 0,
AccentColour = Color4.DarkRed, AccentColour = Color4.DarkRed,
KiaiMode = kiai, KiaiMode = kiai,
Children = new[] Children = new[]
@ -54,7 +51,6 @@ namespace osu.Desktop.VisualTests.Tests
Add(new CirclePiece Add(new CirclePiece
{ {
Position = new Vector2(100, 300), Position = new Vector2(100, 300),
Width = 0,
AccentColour = Color4.DarkBlue, AccentColour = Color4.DarkBlue,
KiaiMode = kiai, KiaiMode = kiai,
Children = new[] Children = new[]
@ -63,10 +59,9 @@ namespace osu.Desktop.VisualTests.Tests
} }
}); });
Add(new StrongCirclePiece Add(new CirclePiece(true)
{ {
Position = new Vector2(350, 300), Position = new Vector2(350, 300),
Width = 0,
AccentColour = Color4.DarkBlue, AccentColour = Color4.DarkBlue,
KiaiMode = kiai, KiaiMode = kiai,
Children = new[] Children = new[]
@ -78,7 +73,6 @@ namespace osu.Desktop.VisualTests.Tests
Add(new CirclePiece Add(new CirclePiece
{ {
Position = new Vector2(100, 500), Position = new Vector2(100, 500),
Width = 0,
AccentColour = Color4.Orange, AccentColour = Color4.Orange,
KiaiMode = kiai, KiaiMode = kiai,
Children = new[] Children = new[]
@ -87,37 +81,29 @@ namespace osu.Desktop.VisualTests.Tests
} }
}); });
Add(new DrumRollCircle(new CirclePiece Add(new ElongatedCirclePiece
{ {
KiaiMode = kiai Position = new Vector2(575, 100),
}) AccentColour = Color4.Orange,
{ KiaiMode = kiai,
Width = 250, Length = 0.10f,
Position = new Vector2(575, 100) PlayfieldLengthReference = () => DrawSize.X
}); });
Add(new DrumRollCircle(new StrongCirclePiece Add(new ElongatedCirclePiece(true)
{ {
KiaiMode = kiai Position = new Vector2(575, 300),
}) AccentColour = Color4.Orange,
{ KiaiMode = kiai,
Width = 250, Length = 0.10f,
Position = new Vector2(575, 300) PlayfieldLengthReference = () => DrawSize.X
}); });
} }
private class DrumRollCircle : BaseCircle private void updateKiaiState()
{ {
public DrumRollCircle(CirclePiece piece) foreach (var c in Children.OfType<CirclePiece>())
: base(piece) c.KiaiMode = kiai;
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Piece.AccentColour = colours.YellowDark;
}
} }
private abstract class BaseCircle : Container private abstract class BaseCircle : Container

View File

@ -1,24 +1,33 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects; using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.Taiko.Objects.Drawable; using osu.Game.Modes.Taiko.Objects.Drawables;
using osu.Game.Modes.Taiko.UI; using osu.Game.Modes.Taiko.UI;
using System;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
internal class TestCaseTaikoPlayfield : TestCase internal class TestCaseTaikoPlayfield : TestCase
{ {
private const double default_duration = 300;
private const float scroll_time = 1000;
public override string Description => "Taiko playfield"; public override string Description => "Taiko playfield";
protected override double TimePerAction => default_duration * 2;
private readonly Random rng = new Random(1337);
private TaikoPlayfield playfield; private TaikoPlayfield playfield;
private Container playfieldContainer;
public override void Reset() public override void Reset()
{ {
@ -26,17 +35,31 @@ namespace osu.Desktop.VisualTests.Tests
AddStep("Hit!", addHitJudgement); AddStep("Hit!", addHitJudgement);
AddStep("Miss :(", addMissJudgement); AddStep("Miss :(", addMissJudgement);
AddStep("Swell", addSwell); AddStep("DrumRoll", () => addDrumRoll(false));
AddStep("Strong DrumRoll", () => addDrumRoll(true));
AddStep("Swell", () => addSwell());
AddStep("Centre", () => addCentreHit(false)); AddStep("Centre", () => addCentreHit(false));
AddStep("Strong Centre", () => addCentreHit(true)); AddStep("Strong Centre", () => addCentreHit(true));
AddStep("Rim", () => addRimHit(false)); AddStep("Rim", () => addRimHit(false));
AddStep("Strong Rim", () => addRimHit(true)); AddStep("Strong Rim", () => addRimHit(true));
AddStep("Add bar line", () => addBarLine(false));
AddStep("Add major bar line", () => addBarLine(true));
AddStep("Height test 1", () => changePlayfieldSize(1));
AddStep("Height test 2", () => changePlayfieldSize(2));
AddStep("Height test 3", () => changePlayfieldSize(3));
AddStep("Height test 4", () => changePlayfieldSize(4));
AddStep("Height test 5", () => changePlayfieldSize(5));
AddStep("Reset height", () => changePlayfieldSize(6));
Add(new Container var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
Add(playfieldContainer = new Container
{ {
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Y = 200, Height = TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT,
Padding = new MarginPadding { Left = 200 }, Clock = new FramedClock(rateAdjustClock),
Children = new[] Children = new[]
{ {
playfield = new TaikoPlayfield() playfield = new TaikoPlayfield()
@ -44,21 +67,63 @@ namespace osu.Desktop.VisualTests.Tests
}); });
} }
private void changePlayfieldSize(int step)
{
// Add new hits
switch (step)
{
case 1:
addCentreHit(false);
break;
case 2:
addCentreHit(true);
break;
case 3:
addDrumRoll(false);
break;
case 4:
addDrumRoll(true);
break;
case 5:
addSwell(1000);
playfieldContainer.Delay(scroll_time - 100);
break;
}
// Tween playfield height
switch (step)
{
default:
playfieldContainer.ResizeTo(new Vector2(1, rng.Next(25, 400)), 500);
break;
case 6:
playfieldContainer.ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT), 500);
break;
}
}
private void addHitJudgement() private void addHitJudgement()
{ {
TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great; TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great;
playfield.OnJudgement(new DrawableTestHit(new Hit()) var h = new DrawableTestHit(new Hit())
{ {
X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f), X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f),
Judgement = new TaikoJudgement Judgement = new TaikoJudgement
{ {
Result = HitResult.Hit, Result = HitResult.Hit,
TaikoResult = hitResult, TaikoResult = hitResult,
TimeOffset = 0, TimeOffset = 0
SecondHit = RNG.Next(10) == 0 }
};
playfield.OnJudgement(h);
if (RNG.Next(10) == 0)
{
h.Judgement.SecondHit = true;
playfield.OnJudgement(h);
} }
});
} }
private void addMissJudgement() private void addMissJudgement()
@ -73,26 +138,53 @@ namespace osu.Desktop.VisualTests.Tests
}); });
} }
private void addSwell() private void addBarLine(bool major, double delay = scroll_time)
{
BarLine bl = new BarLine
{
StartTime = playfield.Time.Current + delay,
ScrollTime = scroll_time
};
playfield.AddBarLine(major ? new DrawableBarLineMajor(bl) : new DrawableBarLine(bl));
}
private void addSwell(double duration = default_duration)
{ {
playfield.Add(new DrawableSwell(new Swell playfield.Add(new DrawableSwell(new Swell
{ {
StartTime = Time.Current + 1000, StartTime = playfield.Time.Current + scroll_time,
EndTime = Time.Current + 5000, Duration = duration,
PreEmpt = 1000 ScrollTime = scroll_time
})); }));
} }
private void addDrumRoll(bool strong, double duration = default_duration)
{
addBarLine(true);
addBarLine(true, scroll_time + duration);
var d = new DrumRoll
{
StartTime = playfield.Time.Current + scroll_time,
IsStrong = strong,
Duration = duration,
ScrollTime = scroll_time,
};
playfield.Add(new DrawableDrumRoll(d));
}
private void addCentreHit(bool strong) private void addCentreHit(bool strong)
{ {
Hit h = new Hit Hit h = new Hit
{ {
StartTime = Time.Current + 1000, StartTime = playfield.Time.Current + scroll_time,
PreEmpt = 1000 ScrollTime = scroll_time
}; };
if (strong) if (strong)
playfield.Add(new DrawableStrongCentreHit(h)); playfield.Add(new DrawableCentreHitStrong(h));
else else
playfield.Add(new DrawableCentreHit(h)); playfield.Add(new DrawableCentreHit(h));
} }
@ -101,12 +193,12 @@ namespace osu.Desktop.VisualTests.Tests
{ {
Hit h = new Hit Hit h = new Hit
{ {
StartTime = Time.Current + 1000, StartTime = playfield.Time.Current + scroll_time,
PreEmpt = 1000 ScrollTime = scroll_time
}; };
if (strong) if (strong)
playfield.Add(new DrawableStrongRimHit(h)); playfield.Add(new DrawableRimHitStrong(h));
else else
playfield.Add(new DrawableRimHit(h)); playfield.Add(new DrawableRimHit(h));
} }

View File

@ -14,7 +14,7 @@ namespace osu.Desktop.VisualTests
{ {
base.LoadComplete(); base.LoadComplete();
new BackgroundScreenDefault { Depth = 10 }.LoadAsync(this, AddInternal); LoadComponentAsync(new BackgroundScreenDefault { Depth = 10 }, AddInternal);
// Have to construct this here, rather than in the constructor, because // Have to construct this here, rather than in the constructor, because
// we depend on some dependencies to be loaded within OsuGameBase.load(). // we depend on some dependencies to be loaded within OsuGameBase.load().

View File

@ -83,22 +83,20 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="mscorlib" /> <Reference Include="mscorlib" />
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> <Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath> <HintPath>$(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="SharpCompress, Version=0.15.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL"> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>..\packages\SharpCompress.0.15.1\lib\net45\SharpCompress.dll</HintPath> <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private> </Reference>
<Reference Include="SharpCompress, Version=0.15.2.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\SharpCompress.0.15.2\lib\net45\SharpCompress.dll</HintPath>
</Reference> </Reference>
<Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="SQLite.Net, Version=3.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath> <HintPath>$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="Newtonsoft.Json">
<HintPath>$(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="SQLiteNetExtensions"> <Reference Include="SQLiteNetExtensions">
<HintPath>$(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll</HintPath> <HintPath>$(SolutionDir)\packages\SQLiteNetExtensions.1.3.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\SQLiteNetExtensions.dll</HintPath>
</Reference> </Reference>
@ -187,8 +185,11 @@
<Compile Include="AutomatedVisualTestGame.cs" /> <Compile Include="AutomatedVisualTestGame.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Tests\TestCaseChatDisplay.cs" /> <Compile Include="Tests\TestCaseChatDisplay.cs" />
<Compile Include="Tests\TestCaseBeatmapDetails.cs" />
<Compile Include="Tests\TestCaseDrawings.cs" /> <Compile Include="Tests\TestCaseDrawings.cs" />
<Compile Include="Tests\TestCaseGamefield.cs" /> <Compile Include="Tests\TestCaseGamefield.cs" />
<Compile Include="Tests\TestCaseGraph.cs" />
<Compile Include="Tests\TestCaseMenuOverlays.cs" />
<Compile Include="Tests\TestCaseMusicController.cs" /> <Compile Include="Tests\TestCaseMusicController.cs" />
<Compile Include="Tests\TestCaseNotificationManager.cs" /> <Compile Include="Tests\TestCaseNotificationManager.cs" />
<Compile Include="Tests\TestCasePlayer.cs" /> <Compile Include="Tests\TestCasePlayer.cs" />
@ -207,7 +208,6 @@
<Compile Include="VisualTestGame.cs" /> <Compile Include="VisualTestGame.cs" />
<Compile Include="Platform\TestStorage.cs" /> <Compile Include="Platform\TestStorage.cs" />
<Compile Include="Tests\TestCaseOptions.cs" /> <Compile Include="Tests\TestCaseOptions.cs" />
<Compile Include="Tests\TestCasePauseOverlay.cs" />
<Compile Include="Tests\TestCaseModSelectOverlay.cs" /> <Compile Include="Tests\TestCaseModSelectOverlay.cs" />
<Compile Include="Tests\TestCaseDialogOverlay.cs" /> <Compile Include="Tests\TestCaseDialogOverlay.cs" />
<Compile Include="Tests\TestCaseBeatmapOptionsOverlay.cs" /> <Compile Include="Tests\TestCaseBeatmapOptionsOverlay.cs" />

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 Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
--> -->
<packages> <packages>
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" /> <package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
<package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" /> <package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
<package id="SharpCompress" version="0.15.1" targetFramework="net45" /> <package id="SharpCompress" version="0.15.2" targetFramework="net45" />
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" /> <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="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
<package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" /> <package id="SQLiteNetExtensions" version="1.3.0" targetFramework="net45" />

View File

@ -3,10 +3,7 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.IO; using osu.Game.Beatmaps.IO;
using osu.Game.Beatmaps;
using osu.Game.Database;
namespace osu.Desktop.Beatmaps.IO namespace osu.Desktop.Beatmaps.IO
{ {
@ -18,20 +15,17 @@ namespace osu.Desktop.Beatmaps.IO
public static void Register() => AddReader<LegacyFilesystemReader>((storage, path) => Directory.Exists(path)); public static void Register() => AddReader<LegacyFilesystemReader>((storage, path) => Directory.Exists(path));
private string basePath { get; } private string basePath { get; }
private Beatmap firstMap { get; }
public LegacyFilesystemReader(string path) public LegacyFilesystemReader(string path)
{ {
basePath = path; basePath = path;
BeatmapFilenames = Directory.GetFiles(basePath, @"*.osu").Select(Path.GetFileName).ToArray(); BeatmapFilenames = Directory.GetFiles(basePath, @"*.osu").Select(Path.GetFileName).ToArray();
if (BeatmapFilenames.Length == 0) if (BeatmapFilenames.Length == 0)
throw new FileNotFoundException(@"This directory contains no beatmaps"); throw new FileNotFoundException(@"This directory contains no beatmaps");
StoryboardFilename = Directory.GetFiles(basePath, @"*.osb").Select(Path.GetFileName).FirstOrDefault(); StoryboardFilename = Directory.GetFiles(basePath, @"*.osb").Select(Path.GetFileName).FirstOrDefault();
using (var stream = new StreamReader(GetStream(BeatmapFilenames[0])))
{
var decoder = BeatmapDecoder.GetDecoder(stream);
firstMap = decoder.Decode(stream);
}
} }
public override Stream GetStream(string name) public override Stream GetStream(string name)
@ -39,11 +33,6 @@ namespace osu.Desktop.Beatmaps.IO
return File.OpenRead(Path.Combine(basePath, name)); return File.OpenRead(Path.Combine(basePath, name));
} }
public override BeatmapMetadata ReadMetadata()
{
return firstMap.BeatmapInfo.Metadata;
}
public override void Dispose() public override void Dispose()
{ {
// no-op // no-op

View File

@ -29,7 +29,7 @@ namespace osu.Desktop
{ {
base.LoadComplete(); base.LoadComplete();
versionManager.LoadAsync(this); LoadComponentAsync(versionManager);
ScreenChanged += s => ScreenChanged += s =>
{ {
if (!versionManager.IsAlive && s is Intro) if (!versionManager.IsAlive && s is Intro)

View File

@ -189,19 +189,24 @@ namespace osu.Desktop.Overlays
private class UpdateProgressNotification : ProgressNotification private class UpdateProgressNotification : ProgressNotification
{ {
private OsuGame game;
protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification() protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification()
{ {
Text = @"Update ready to install. Click to restart!", Text = @"Update ready to install. Click to restart!",
Activated = () => Activated = () =>
{ {
UpdateManager.RestartApp(); UpdateManager.RestartAppWhenExited();
game.GracefullyExit();
return true; return true;
} }
}; };
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours, OsuGame game)
{ {
this.game = game;
IconContent.Add(new Drawable[] IconContent.Add(new Drawable[]
{ {
new Box new Box

View File

@ -124,8 +124,7 @@
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath> <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Splat, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath> <HintPath>$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll</HintPath>

View File

@ -7,7 +7,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" /> <package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" 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="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
<package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" /> <package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
<package id="Splat" version="2.0.0" targetFramework="net45" /> <package id="Splat" version="2.0.0" targetFramework="net45" />
<package id="squirrel.windows" version="1.5.2" targetFramework="net45" /> <package id="squirrel.windows" version="1.5.2" targetFramework="net45" />
</packages> </packages>

View File

@ -14,8 +14,7 @@ namespace osu.Game.Modes.Catch.UI
{ {
public CatchPlayfield() public CatchPlayfield()
{ {
RelativeSizeAxes = Axes.Y; Size = new Vector2(1, 0.9f);
Size = new Vector2(512, 0.9f);
Anchor = Anchor.BottomCentre; Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre; Origin = Anchor.BottomCentre;

View File

@ -33,8 +33,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath> <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />

View File

@ -5,5 +5,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
--> -->
<packages> <packages>
<package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" /> <package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
</packages> </packages>

View File

@ -15,8 +15,7 @@ namespace osu.Game.Modes.Mania.UI
{ {
public ManiaPlayfield(int columns) public ManiaPlayfield(int columns)
{ {
RelativeSizeAxes = Axes.Both; Size = new Vector2(0.8f, 1f);
Size = new Vector2(columns / 20f, 1f);
Anchor = Anchor.BottomCentre; Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre; Origin = Anchor.BottomCentre;

View File

@ -33,8 +33,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath> <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />

View File

@ -5,5 +5,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
--> -->
<packages> <packages>
<package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" /> <package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
</packages> </packages>

View File

@ -43,7 +43,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
return new Slider return new Slider
{ {
StartTime = original.StartTime, StartTime = original.StartTime,
Sample = original.Sample, Samples = original.Samples,
CurveObject = curveData, CurveObject = curveData,
Position = positionData?.Position ?? Vector2.Zero, Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false NewCombo = comboData?.NewCombo ?? false
@ -55,7 +55,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
return new Spinner return new Spinner
{ {
StartTime = original.StartTime, StartTime = original.StartTime,
Sample = original.Sample, Samples = original.Samples,
Position = new Vector2(512, 384) / 2, Position = new Vector2(512, 384) / 2,
EndTime = endTimeData.EndTime EndTime = endTimeData.EndTime
}; };
@ -64,7 +64,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
return new HitCircle return new HitCircle
{ {
StartTime = original.StartTime, StartTime = original.StartTime,
Sample = original.Sample, Samples = original.Samples,
Position = positionData?.Position ?? Vector2.Zero, Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false NewCombo = comboData?.NewCombo ?? false
}; };

View File

@ -67,7 +67,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
ComboIndex = s.ComboIndex, ComboIndex = s.ComboIndex,
Scale = s.Scale, Scale = s.Scale,
ComboColour = s.ComboColour, ComboColour = s.ComboColour,
Sample = s.Sample, Samples = s.Samples,
}), }),
}; };
@ -111,7 +111,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
if (repeat > currentRepeat) if (repeat > currentRepeat)
{ {
if (repeat < slider.RepeatCount && ball.Tracking) if (repeat < slider.RepeatCount && ball.Tracking)
PlaySample(); PlaySamples();
currentRepeat = repeat; currentRepeat = repeat;
} }

View File

@ -2,12 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps.Samples;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements; using osu.Game.Modes.Osu.Judgements;
using OpenTK; using OpenTK;
@ -53,22 +49,6 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
}; };
} }
private SampleChannel sample;
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
string sampleSet = (HitObject.Sample?.Set ?? SampleSet.Normal).ToString().ToLower();
sample = audio.Sample.Get($@"Gameplay/{sampleSet}-slidertick");
}
protected override void PlaySample()
{
sample?.Play();
}
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
if (Judgement.TimeOffset >= 0) if (Judgement.TimeOffset >= 0)

View File

@ -2,18 +2,24 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK; using OpenTK;
using osu.Game.Beatmaps.Samples;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Modes.Objects.Types; using osu.Game.Modes.Objects.Types;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using osu.Game.Database; using osu.Game.Database;
using System.Linq;
using osu.Game.Audio;
namespace osu.Game.Modes.Osu.Objects namespace osu.Game.Modes.Osu.Objects
{ {
public class Slider : OsuHitObject, IHasCurve public class Slider : OsuHitObject, IHasCurve
{ {
/// <summary>
/// Scoring distance with a speed-adjusted beat length of 1 second.
/// </summary>
private const float base_scoring_distance = 100;
public IHasCurve CurveObject { get; set; } public IHasCurve CurveObject { get; set; }
public SliderCurve Curve => CurveObject.Curve; public SliderCurve Curve => CurveObject.Curve;
@ -51,13 +57,10 @@ namespace osu.Game.Modes.Osu.Objects
{ {
base.ApplyDefaults(timing, difficulty); base.ApplyDefaults(timing, difficulty);
ControlPoint overridePoint; double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier / timing.SpeedMultiplierAt(StartTime);
ControlPoint timingPoint = timing.TimingPointAt(StartTime, out overridePoint);
var velocityAdjustment = overridePoint?.VelocityAdjustment ?? 1;
var baseVelocity = 100 * difficulty.SliderMultiplier / velocityAdjustment;
Velocity = baseVelocity / timingPoint.BeatLength; Velocity = scoringDistance / timing.BeatLengthAt(StartTime);
TickDistance = baseVelocity / difficulty.SliderTickRate; TickDistance = scoringDistance / difficulty.SliderTickRate;
} }
public IEnumerable<SliderTick> Ticks public IEnumerable<SliderTick> Ticks
@ -93,11 +96,12 @@ namespace osu.Game.Modes.Osu.Objects
StackHeight = StackHeight, StackHeight = StackHeight,
Scale = Scale, Scale = Scale,
ComboColour = ComboColour, ComboColour = ComboColour,
Sample = new HitSampleInfo Samples = Samples.Select(s => new SampleInfo
{ {
Type = SampleType.None, Bank = s.Bank,
Set = SampleSet.Soft, Name = @"slidertick",
}, Volume = s.Volume
}).ToList()
}; };
} }
} }

View File

@ -42,14 +42,14 @@ namespace osu.Game.Modes.Osu
{ {
if (mouseDisabled.Value) if (mouseDisabled.Value)
{ {
mouse.PressedButtons.Remove(MouseButton.Left); mouse.SetPressed(MouseButton.Left, false);
mouse.PressedButtons.Remove(MouseButton.Right); mouse.SetPressed(MouseButton.Right, false);
} }
if (leftViaKeyboard) if (leftViaKeyboard)
mouse.PressedButtons.Add(MouseButton.Left); mouse.SetPressed(MouseButton.Left, true);
if (rightViaKeyboard) if (rightViaKeyboard)
mouse.PressedButtons.Add(MouseButton.Right); mouse.SetPressed(MouseButton.Right, true);
} }
} }
} }

View File

@ -35,11 +35,9 @@ namespace osu.Game.Modes.Osu.Scoring
switch (judgement.Result) switch (judgement.Result)
{ {
case HitResult.Hit: case HitResult.Hit:
Combo.Value++;
Health.Value += 0.1f; Health.Value += 0.1f;
break; break;
case HitResult.Miss: case HitResult.Miss:
Combo.Value = 0;
Health.Value -= 0.2f; Health.Value -= 0.2f;
break; break;
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Beatmaps; using osu.Game.Modes.Osu.Beatmaps;
@ -46,5 +47,7 @@ namespace osu.Game.Modes.Osu.UI
return new DrawableSpinner(spinner); return new DrawableSpinner(spinner);
return null; return null;
} }
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f);
} }
} }

View File

@ -21,6 +21,8 @@ namespace osu.Game.Modes.Osu.UI
private readonly Container judgementLayer; private readonly Container judgementLayer;
private readonly ConnectionRenderer<OsuHitObject> connectionLayer; private readonly ConnectionRenderer<OsuHitObject> connectionLayer;
public override bool ProvidingUserCursor => true;
public override Vector2 Size public override Vector2 Size
{ {
get get
@ -36,8 +38,6 @@ namespace osu.Game.Modes.Osu.UI
{ {
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
Size = new Vector2(0.75f);
Add(new Drawable[] Add(new Drawable[]
{ {

View File

@ -34,8 +34,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath> <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />

View File

@ -5,5 +5,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
--> -->
<packages> <packages>
<package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" /> <package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
</packages> </packages>

View File

@ -2,96 +2,160 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Beatmaps.Samples;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Types; using osu.Game.Modes.Objects.Types;
using osu.Game.Modes.Taiko.Objects; using osu.Game.Modes.Taiko.Objects;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Database;
using osu.Game.IO.Serialization;
using osu.Game.Audio;
namespace osu.Game.Modes.Taiko.Beatmaps namespace osu.Game.Modes.Taiko.Beatmaps
{ {
internal class TaikoBeatmapConverter : IBeatmapConverter<TaikoHitObject> internal class TaikoBeatmapConverter : IBeatmapConverter<TaikoHitObject>
{ {
private const float legacy_velocity_scale = 1.4f; /// <summary>
private const float bash_convert_factor = 1.65f; /// osu! is generally slower than taiko, so a factor is added to increase
/// speed. This must be used everywhere slider length or beat length is used.
/// </summary>
private const float legacy_velocity_multiplier = 1.4f;
/// <summary>
/// Because swells are easier in taiko than spinners are in osu!,
/// legacy taiko multiplies a factor when converting the number of required hits.
/// </summary>
private const float swell_hit_multiplier = 1.65f;
/// <summary>
/// Base osu! slider scoring distance.
/// </summary>
private const float osu_base_scoring_distance = 100;
/// <summary>
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
/// </summary>
private const float taiko_base_distance = 100;
public Beatmap<TaikoHitObject> Convert(Beatmap original) public Beatmap<TaikoHitObject> Convert(Beatmap original)
{ {
if (original is LegacyBeatmap) BeatmapInfo info = original.BeatmapInfo.DeepClone<BeatmapInfo>();
original.TimingInfo.ControlPoints.ForEach(c => c.VelocityAdjustment /= legacy_velocity_scale); info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier;
return new Beatmap<TaikoHitObject>(original) return new Beatmap<TaikoHitObject>(original)
{ {
HitObjects = convertHitObjects(original.HitObjects) BeatmapInfo = info,
HitObjects = original.HitObjects.SelectMany(h => convertHitObject(h, original)).ToList()
}; };
} }
private List<TaikoHitObject> convertHitObjects(List<HitObject> hitObjects) private IEnumerable<TaikoHitObject> convertHitObject(HitObject obj, Beatmap beatmap)
{
return hitObjects.Select(convertHitObject).ToList();
}
private TaikoHitObject convertHitObject(HitObject original)
{ {
// Check if this HitObject is already a TaikoHitObject, and return it if so // Check if this HitObject is already a TaikoHitObject, and return it if so
TaikoHitObject originalTaiko = original as TaikoHitObject; var originalTaiko = obj as TaikoHitObject;
if (originalTaiko != null) if (originalTaiko != null)
return originalTaiko; yield return originalTaiko;
IHasDistance distanceData = original as IHasDistance; var distanceData = obj as IHasDistance;
IHasRepeats repeatsData = original as IHasRepeats; var repeatsData = obj as IHasRepeats;
IHasEndTime endTimeData = original as IHasEndTime; var endTimeData = obj as IHasEndTime;
// Old osu! used hit sounding to determine various hit type information // Old osu! used hit sounding to determine various hit type information
SampleType sample = original.Sample?.Type ?? SampleType.None; List<SampleInfo> samples = obj.Samples;
bool strong = (sample & SampleType.Finish) > 0; bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
if (distanceData != null) if (distanceData != null)
{ {
return new DrumRoll int repeats = repeatsData?.RepeatCount ?? 1;
double speedAdjustment = beatmap.TimingInfo.SpeedMultiplierAt(obj.StartTime);
double speedAdjustedBeatLength = beatmap.TimingInfo.BeatLengthAt(obj.StartTime) * speedAdjustment;
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
double distance = distanceData.Distance * repeats * legacy_velocity_multiplier;
// The velocity of the taiko hit object - calculated as the velocity of a drum roll
double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
// The duration of the taiko hit object
double taikoDuration = distance / taikoVelocity;
// For some reason, old osu! always uses speedAdjustment to determine the taiko velocity, but
// only uses it to determine osu! velocity if beatmap version < 8. Let's account for that here.
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
speedAdjustedBeatLength /= speedAdjustment;
// The velocity of the osu! hit object - calculated as the velocity of a slider
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.Difficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength;
// The duration of the osu! hit object
double osuDuration = distance / osuVelocity;
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.Difficulty.SliderTickRate, taikoDuration / repeats) / 8;
if (tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{ {
StartTime = original.StartTime, for (double j = obj.StartTime; j <= distanceData.EndTime + tickSpacing; j += tickSpacing)
Sample = original.Sample, {
// Todo: This should generate different type of hits (including strongs)
// depending on hitobject sound additions (not implemented fully yet)
yield return new CentreHit
{
StartTime = j,
Samples = obj.Samples,
IsStrong = strong, IsStrong = strong,
Distance = distanceData.Distance * (repeatsData?.RepeatCount ?? 1)
}; };
} }
}
if (endTimeData != null) else
{ {
// We compute the end time manually to add in the Bash convert factor yield return new DrumRoll
return new Swell
{ {
StartTime = original.StartTime, StartTime = obj.StartTime,
Sample = original.Sample, Samples = obj.Samples,
IsStrong = strong, IsStrong = strong,
Duration = taikoDuration,
EndTime = original.StartTime + endTimeData.Duration * bash_convert_factor TickRate = beatmap.BeatmapInfo.Difficulty.SliderTickRate == 3 ? 3 : 4,
}; };
} }
}
bool isCentre = (sample & ~(SampleType.Finish | SampleType.Normal)) == 0; else if (endTimeData != null)
if (isCentre)
{ {
return new CentreHit double hitMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.OverallDifficulty, 3, 5, 7.5) * swell_hit_multiplier;
yield return new Swell
{ {
StartTime = original.StartTime, StartTime = obj.StartTime,
Sample = original.Sample, Samples = obj.Samples,
IsStrong = strong IsStrong = strong,
Duration = endTimeData.Duration,
RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier),
}; };
} }
else
return new RimHit
{ {
StartTime = original.StartTime, bool isRim = samples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
Sample = original.Sample,
if (isRim)
{
yield return new RimHit
{
StartTime = obj.StartTime,
Samples = obj.Samples,
IsStrong = strong,
};
}
else
{
yield return new CentreHit
{
StartTime = obj.StartTime,
Samples = obj.Samples,
IsStrong = strong, IsStrong = strong,
}; };
} }
} }
} }
}
}

View File

@ -15,6 +15,8 @@ namespace osu.Game.Modes.Taiko.Judgements
/// </summary> /// </summary>
public override string MaxResultString => string.Empty; public override string MaxResultString => string.Empty;
public override bool AffectsCombo => false;
protected override int NumericResultForScore(TaikoHitResult result) protected override int NumericResultForScore(TaikoHitResult result)
{ {
switch (result) switch (result)

View File

@ -3,6 +3,7 @@
using osu.Game.Modes.Judgements; using osu.Game.Modes.Judgements;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Game.Modes.Objects.Drawables;
namespace osu.Game.Modes.Taiko.Judgements namespace osu.Game.Modes.Taiko.Judgements
{ {
@ -21,12 +22,12 @@ namespace osu.Game.Modes.Taiko.Judgements
/// <summary> /// <summary>
/// The result value for the combo portion of the score. /// The result value for the combo portion of the score.
/// </summary> /// </summary>
public int ResultValueForScore => NumericResultForScore(TaikoResult); public int ResultValueForScore => Result == HitResult.Miss ? 0 : NumericResultForScore(TaikoResult);
/// <summary> /// <summary>
/// The result value for the accuracy portion of the score. /// The result value for the accuracy portion of the score.
/// </summary> /// </summary>
public int ResultValueForAccuracy => NumericResultForAccuracy(TaikoResult); public int ResultValueForAccuracy => Result == HitResult.Miss ? 0 : NumericResultForAccuracy(TaikoResult);
/// <summary> /// <summary>
/// The maximum result value for the combo portion of the score. /// The maximum result value for the combo portion of the score.
@ -43,7 +44,7 @@ namespace osu.Game.Modes.Taiko.Judgements
public override string MaxResultString => MAX_HIT_RESULT.GetDescription(); public override string MaxResultString => MAX_HIT_RESULT.GetDescription();
/// <summary> /// <summary>
/// Whether this Judgement has a secondary hit in the case of finishers. /// Whether this Judgement has a secondary hit in the case of strong hits.
/// </summary> /// </summary>
public virtual bool SecondHit { get; set; } public virtual bool SecondHit { get; set; }

View File

@ -1,10 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Beatmaps.Samples namespace osu.Game.Modes.Taiko.Objects
{ {
public class HitSampleInfo : SampleInfo public class BarLine : TaikoHitObject
{ {
public SampleType Type { get; set; }
} }
} }

View File

@ -1,57 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using System.Linq;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public class DrawableDrumRoll : DrawableTaikoHitObject
{
private readonly DrumRoll drumRoll;
public DrawableDrumRoll(DrumRoll drumRoll)
: base(drumRoll)
{
this.drumRoll = drumRoll;
int tickIndex = 0;
foreach (var tick in drumRoll.Ticks)
{
var newTick = new DrawableDrumRollTick(tick)
{
Depth = tickIndex,
X = (float)((tick.StartTime - HitObject.StartTime) / drumRoll.Duration)
};
AddNested(newTick);
tickIndex++;
}
}
protected override void UpdateState(ArmedState state)
{
}
protected override void CheckJudgement(bool userTriggered)
{
if (userTriggered)
return;
if (Judgement.TimeOffset < 0)
return;
int countHit = NestedHitObjects.Count(o => o.Judgement.Result == HitResult.Hit);
if (countHit > drumRoll.RequiredGoodHits)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = countHit >= drumRoll.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good;
}
else
Judgement.Result = HitResult.Miss;
}
}
}

View File

@ -1,35 +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 OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public class DrawableStrongCentreHit : DrawableStrongHit
{
protected override Key[] HitKeys { get; } = { Key.F, Key.J };
private readonly CirclePiece circlePiece;
public DrawableStrongCentreHit(Hit hit)
: base(hit)
{
Add(circlePiece = new StrongCirclePiece
{
Children = new []
{
new CentreHitSymbolPiece()
}
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
circlePiece.AccentColour = colours.PinkDarker;
}
}
}

View File

@ -1,35 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
using OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
public class DrawableStrongRimHit : DrawableStrongHit
{
protected override Key[] HitKeys { get; } = { Key.D, Key.K };
private readonly CirclePiece circlePiece;
public DrawableStrongRimHit(Hit hit)
: base(hit)
{
Add(circlePiece = new StrongCirclePiece
{
Children = new[]
{
new RimHitSymbolPiece()
}
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
circlePiece.AccentColour = colours.BlueDarker;
}
}
}

View File

@ -1,161 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Backgrounds;
using OpenTK.Graphics;
using System;
using osu.Game.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces
{
/// <summary>
/// A circle piece which is used uniformly through osu!taiko to visualise hitobjects.
/// <para>
/// The body of this piece will overshoot its parent by <see cref="CirclePiece.Height"/> to form
/// a rounded (_[-Width-]_) figure such that a regular "circle" is the result of a parent with Width = 0.
/// </para>
/// </summary>
public class CirclePiece : Container, IHasAccentColour
{
public const float SYMBOL_SIZE = TaikoHitObject.CIRCLE_RADIUS * 2f * 0.45f;
public const float SYMBOL_BORDER = 8;
public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER;
private Color4 accentColour;
/// <summary>
/// The colour of the inner circle and outer glows.
/// </summary>
public Color4 AccentColour
{
get { return accentColour; }
set
{
accentColour = value;
innerBackground.Colour = AccentColour;
triangles.ColourLight = AccentColour;
triangles.ColourDark = AccentColour.Darken(0.1f);
resetEdgeEffects();
}
}
private bool kiaiMode;
/// <summary>
/// Whether Kiai mode effects are enabled for this circle piece.
/// </summary>
public bool KiaiMode
{
get { return kiaiMode; }
set
{
kiaiMode = value;
resetEdgeEffects();
}
}
public override Anchor Origin
{
get { return Anchor.CentreLeft; }
set { throw new InvalidOperationException($"{nameof(CirclePiece)} must always use CentreLeft origin."); }
}
protected override Container<Framework.Graphics.Drawable> Content => SymbolContainer;
protected readonly Container SymbolContainer;
private readonly Container innerLayer;
private readonly Container innerCircleContainer;
private readonly Box innerBackground;
private readonly Triangles triangles;
public CirclePiece()
{
RelativeSizeAxes = Axes.X;
Height = TaikoHitObject.CIRCLE_RADIUS * 2;
// The "inner layer" is the body of the CirclePiece that overshoots it by Height/2 px on both sides
AddInternal(innerLayer = new Container
{
Name = "Inner Layer",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Children = new Framework.Graphics.Drawable[]
{
innerCircleContainer = new CircularContainer
{
Name = "Inner Circle",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Framework.Graphics.Drawable[]
{
innerBackground = new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
},
triangles = new Triangles
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
}
}
},
new CircularContainer
{
Name = "Ring",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
BorderThickness = 8,
BorderColour = Color4.White,
Masking = true,
Children = new[]
{
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
},
SymbolContainer = new Container
{
Name = "Symbol",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
}
});
}
protected override void Update()
{
// Add the overshoot to compensate for corner radius
innerLayer.Width = DrawWidth + DrawHeight;
}
private void resetEdgeEffects()
{
innerCircleContainer.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Colour = AccentColour,
Radius = KiaiMode ? 50 : 8
};
}
}
}

View File

@ -1,25 +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 OpenTK;
namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces
{
/// <summary>
/// A type of circle piece which is drawn at a higher scale to represent a "strong" piece.
/// </summary>
public class StrongCirclePiece : CirclePiece
{
/// <summary>
/// The amount to scale up the base circle to show it as a "strong" piece.
/// </summary>
private const float strong_scale = 1.5f;
public StrongCirclePiece()
{
SymbolContainer.Scale = new Vector2(strong_scale);
}
public override Vector2 Size => new Vector2(base.Size.X, base.Size.Y * strong_scale);
}
}

View File

@ -0,0 +1,80 @@
// 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.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
/// <summary>
/// A line that scrolls alongside hit objects in the playfield and visualises control points.
/// </summary>
public class DrawableBarLine : Container
{
/// <summary>
/// The width of the line tracker.
/// </summary>
private const float tracker_width = 2f;
/// <summary>
/// Fade out time calibrated to a pre-empt of 1000ms.
/// </summary>
private const float base_fadeout_time = 100f;
/// <summary>
/// The visual line tracker.
/// </summary>
protected Box Tracker;
/// <summary>
/// The bar line.
/// </summary>
protected readonly BarLine BarLine;
public DrawableBarLine(BarLine barLine)
{
BarLine = barLine;
Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre;
RelativePositionAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
Width = tracker_width;
Children = new[]
{
Tracker = new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
EdgeSmoothness = new Vector2(0.5f, 0),
Alpha = 0.75f
}
};
LifetimeStart = BarLine.StartTime - BarLine.ScrollTime * 2;
LifetimeEnd = BarLine.StartTime + BarLine.ScrollTime;
}
protected override void LoadComplete()
{
base.LoadComplete();
Delay(BarLine.StartTime - Time.Current);
FadeOut(base_fadeout_time * BarLine.ScrollTime / 1000);
}
private void updateScrollPosition(double time) => MoveToX((float)((BarLine.StartTime - time) / BarLine.ScrollTime));
protected override void Update()
{
base.Update();
updateScrollPosition(Time.Current);
}
}
}

View File

@ -0,0 +1,57 @@
// 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.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableBarLineMajor : DrawableBarLine
{
/// <summary>
/// The vertical offset of the triangles from the line tracker.
/// </summary>
private const float triangle_offfset = 10f;
/// <summary>
/// The size of the triangles.
/// </summary>
private const float triangle_size = 20f;
public DrawableBarLineMajor(BarLine barLine)
: base(barLine)
{
Add(new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Children = new[]
{
new EquilateralTriangle
{
Name = "Top",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, -triangle_offfset),
Size = new Vector2(-triangle_size),
EdgeSmoothness = new Vector2(1),
},
new EquilateralTriangle
{
Name = "Bottom",
Anchor = Anchor.BottomCentre,
Origin = Anchor.TopCentre,
Position = new Vector2(0, triangle_offfset),
Size = new Vector2(triangle_size),
EdgeSmoothness = new Vector2(1),
}
}
});
Tracker.Alpha = 1f;
}
}
}

View File

@ -1,35 +1,27 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
using osu.Game.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawable namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableCentreHit : DrawableHit public class DrawableCentreHit : DrawableHit
{ {
protected override Key[] HitKeys { get; } = { Key.F, Key.J }; protected override Key[] HitKeys { get; } = { Key.F, Key.J };
private readonly CirclePiece circlePiece;
public DrawableCentreHit(Hit hit) public DrawableCentreHit(Hit hit)
: base(hit) : base(hit)
{ {
Add(circlePiece = new CirclePiece MainPiece.Add(new CentreHitSymbolPiece());
{
Children = new[]
{
new CentreHitSymbolPiece()
}
});
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
circlePiece.AccentColour = colours.PinkDarker; MainPiece.AccentColour = colours.PinkDarker;
} }
} }
} }

View File

@ -0,0 +1,27 @@
// 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.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableCentreHitStrong : DrawableHitStrong
{
protected override Key[] HitKeys { get; } = { Key.F, Key.J };
public DrawableCentreHitStrong(Hit hit)
: base(hit)
{
MainPiece.Add(new CentreHitSymbolPiece());
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
MainPiece.AccentColour = colours.PinkDarker;
}
}
}

View File

@ -0,0 +1,109 @@
// 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.Allocation;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableDrumRoll : DrawableTaikoHitObject<DrumRoll>
{
/// <summary>
/// Number of rolling hits required to reach the dark/final accent colour.
/// </summary>
private const int rolling_hits_for_dark_accent = 5;
private Color4 accentDarkColour;
/// <summary>
/// Rolling number of tick hits. This increases for hits and decreases for misses.
/// </summary>
private int rollingHits;
public DrawableDrumRoll(DrumRoll drumRoll)
: base(drumRoll)
{
foreach (var tick in drumRoll.Ticks)
{
var newTick = new DrawableDrumRollTick(tick)
{
X = (float)((tick.StartTime - HitObject.StartTime) / HitObject.Duration)
};
newTick.OnJudgement += onTickJudgement;
AddNested(newTick);
MainPiece.Add(newTick);
}
}
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = HitObject.IsStrong };
protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(HitObject.IsStrong)
{
Length = (float)(HitObject.Duration / HitObject.ScrollTime),
PlayfieldLengthReference = () => Parent.DrawSize.X
};
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
MainPiece.AccentColour = AccentColour = colours.YellowDark;
accentDarkColour = colours.YellowDarker;
}
protected override void LoadComplete()
{
base.LoadComplete();
// This is naive, however it's based on the reasoning that the hit target
// is further than mid point of the play field, so the time taken to scroll in should always
// be greater than the time taken to scroll out to the left of the screen.
// Thus, using PreEmpt here is enough for the drum roll to completely scroll out.
LifetimeEnd = HitObject.EndTime + HitObject.ScrollTime;
}
private void onTickJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> obj)
{
if (obj.Judgement.Result == HitResult.Hit)
rollingHits++;
else
rollingHits--;
rollingHits = MathHelper.Clamp(rollingHits, 0, rolling_hits_for_dark_accent);
Color4 newAccent = Interpolation.ValueAt((float)rollingHits / rolling_hits_for_dark_accent, AccentColour, accentDarkColour, 0, 1);
MainPiece.FadeAccent(newAccent, 100);
}
protected override void CheckJudgement(bool userTriggered)
{
if (userTriggered)
return;
if (Judgement.TimeOffset < 0)
return;
int countHit = NestedHitObjects.Count(o => o.Judgement.Result == HitResult.Hit);
if (countHit > HitObject.RequiredGoodHits)
{
Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = countHit >= HitObject.RequiredGreatHits ? TaikoHitResult.Great : TaikoHitResult.Good;
}
else
Judgement.Result = HitResult.Miss;
}
protected override void UpdateState(ArmedState state)
{
}
}
}

View File

@ -1,35 +1,39 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Game.Modes.Taiko.Judgements;
using System; using System;
using osu.Framework.Graphics;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawable namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableDrumRollTick : DrawableTaikoHitObject public class DrawableDrumRollTick : DrawableTaikoHitObject<DrumRollTick>
{ {
private readonly DrumRollTick tick;
public DrawableDrumRollTick(DrumRollTick tick) public DrawableDrumRollTick(DrumRollTick tick)
: base(tick) : base(tick)
{ {
this.tick = tick;
} }
protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement(); protected override TaikoPiece CreateMainPiece() => new TickPiece
{
Filled = HitObject.FirstTick
};
protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement { SecondHit = HitObject.IsStrong };
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
if (!userTriggered) if (!userTriggered)
{ {
if (Judgement.TimeOffset > tick.HitWindow) if (Judgement.TimeOffset > HitObject.HitWindow)
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
return; return;
} }
if (Math.Abs(Judgement.TimeOffset) < tick.HitWindow) if (Math.Abs(Judgement.TimeOffset) < HitObject.HitWindow)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great; Judgement.TaikoResult = TaikoHitResult.Great;
@ -38,11 +42,17 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
switch (state)
{
case ArmedState.Hit:
Content.ScaleTo(0, 100, EasingTypes.OutQuint);
break;
}
} }
protected override void UpdateScrollPosition(double time) protected override void UpdateScrollPosition(double time)
{ {
// Drum roll ticks shouldn't move // Ticks don't move
} }
protected override bool HandleKeyPress(Key key) protected override bool HandleKeyPress(Key key)

View File

@ -1,66 +1,53 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using System; using System;
using System.Linq; using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawable namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public abstract class DrawableHit : DrawableTaikoHitObject public abstract class DrawableHit : DrawableTaikoHitObject<Hit>
{ {
/// <summary> /// <summary>
/// A list of keys which can result in hits for this HitObject. /// A list of keys which can result in hits for this HitObject.
/// </summary> /// </summary>
protected abstract Key[] HitKeys { get; } protected abstract Key[] HitKeys { get; }
protected override Container<Framework.Graphics.Drawable> Content => bodyContainer;
private readonly Hit hit;
/// <summary> /// <summary>
/// Whether the last key pressed is a valid hit key. /// Whether the last key pressed is a valid hit key.
/// </summary> /// </summary>
private bool validKeyPressed; private bool validKeyPressed;
private readonly Container bodyContainer;
protected DrawableHit(Hit hit) protected DrawableHit(Hit hit)
: base(hit) : base(hit)
{ {
this.hit = hit;
AddInternal(bodyContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
} }
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
if (!userTriggered) if (!userTriggered)
{ {
if (Judgement.TimeOffset > hit.HitWindowGood) if (Judgement.TimeOffset > HitObject.HitWindowGood)
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
return; return;
} }
double hitOffset = Math.Abs(Judgement.TimeOffset); double hitOffset = Math.Abs(Judgement.TimeOffset);
if (hitOffset > hit.HitWindowMiss) if (hitOffset > HitObject.HitWindowMiss)
return; return;
if (!validKeyPressed) if (!validKeyPressed)
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
else if (hitOffset < hit.HitWindowGood) else if (hitOffset < HitObject.HitWindowGood)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = hitOffset < hit.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good; Judgement.TaikoResult = hitOffset < HitObject.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good;
} }
else else
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
@ -80,21 +67,39 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
{ {
Delay(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true); Delay(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true);
var circlePiece = MainPiece as CirclePiece;
circlePiece?.FlashBox.Flush();
switch (State) switch (State)
{ {
case ArmedState.Idle: case ArmedState.Idle:
Delay(hit.HitWindowMiss); Delay(HitObject.HitWindowMiss);
break; break;
case ArmedState.Miss: case ArmedState.Miss:
FadeOut(100); FadeOut(100);
break; break;
case ArmedState.Hit: case ArmedState.Hit:
bodyContainer.ScaleTo(0.8f, 400, EasingTypes.OutQuad);
bodyContainer.MoveToY(-200, 250, EasingTypes.Out);
bodyContainer.Delay(250);
bodyContainer.MoveToY(0, 500, EasingTypes.In);
FadeOut(600); FadeOut(600);
var flash = circlePiece?.FlashBox;
if (flash != null)
{
flash.FadeTo(0.9f);
flash.FadeOut(300);
}
FadeOut(800);
const float gravity_time = 300;
const float gravity_travel_height = 200;
Content.ScaleTo(0.8f, gravity_time * 2, EasingTypes.OutQuad);
MoveToY(-gravity_travel_height, gravity_time, EasingTypes.Out);
Delay(gravity_time, true);
MoveToY(gravity_travel_height * 2, gravity_time * 2, EasingTypes.In);
break; break;
} }

View File

@ -1,16 +1,17 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input;
using System; using System;
using System.Linq; using System.Linq;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawable namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public abstract class DrawableStrongHit : DrawableHit public abstract class DrawableHitStrong : DrawableHit
{ {
/// <summary> /// <summary>
/// The lenience for the second key press. /// The lenience for the second key press.
@ -22,11 +23,13 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
private bool firstKeyHeld; private bool firstKeyHeld;
private Key firstHitKey; private Key firstHitKey;
protected DrawableStrongHit(Hit hit) protected DrawableHitStrong(Hit hit)
: base(hit) : base(hit)
{ {
} }
protected override TaikoPiece CreateMainPiece() => new CirclePiece(true);
protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement(); protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement();
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)

View File

@ -3,33 +3,25 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input; using OpenTK.Input;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
namespace osu.Game.Modes.Taiko.Objects.Drawable namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableRimHit : DrawableHit public class DrawableRimHit : DrawableHit
{ {
protected override Key[] HitKeys { get; } = { Key.D, Key.K }; protected override Key[] HitKeys { get; } = { Key.D, Key.K };
private readonly CirclePiece circlePiece;
public DrawableRimHit(Hit hit) public DrawableRimHit(Hit hit)
: base(hit) : base(hit)
{ {
Add(circlePiece = new CirclePiece MainPiece.Add(new RimHitSymbolPiece());
{
Children = new[]
{
new RimHitSymbolPiece()
}
});
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
circlePiece.AccentColour = colours.BlueDarker; MainPiece.AccentColour = colours.BlueDarker;
} }
} }
} }

View File

@ -0,0 +1,27 @@
// 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.Allocation;
using osu.Game.Graphics;
using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawables
{
public class DrawableRimHitStrong : DrawableHitStrong
{
protected override Key[] HitKeys { get; } = { Key.D, Key.K };
public DrawableRimHitStrong(Hit hit)
: base(hit)
{
MainPiece.Add(new RimHitSymbolPiece());
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
MainPiece.AccentColour = colours.BlueDarker;
}
}
}

View File

@ -1,9 +1,8 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK; using System;
using OpenTK.Graphics; using System.Linq;
using OpenTK.Input;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -12,13 +11,14 @@ using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects.Drawable.Pieces; using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using System; using OpenTK;
using System.Linq; using OpenTK.Graphics;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawable namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public class DrawableSwell : DrawableTaikoHitObject public class DrawableSwell : DrawableTaikoHitObject<Swell>
{ {
/// <summary> /// <summary>
/// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime. /// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime.
@ -31,8 +31,6 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
private const float target_ring_scale = 5f; private const float target_ring_scale = 5f;
private const float inner_ring_alpha = 0.65f; private const float inner_ring_alpha = 0.65f;
private readonly Swell swell;
private readonly Container bodyContainer; private readonly Container bodyContainer;
private readonly CircularContainer targetRing; private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing; private readonly CircularContainer expandingRing;
@ -54,13 +52,12 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
public DrawableSwell(Swell swell) public DrawableSwell(Swell swell)
: base(swell) : base(swell)
{ {
this.swell = swell; Children = new Drawable[]
Children = new Framework.Graphics.Drawable[]
{ {
bodyContainer = new Container bodyContainer = new Container
{ {
Children = new Framework.Graphics.Drawable[] AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{ {
expandingRing = new CircularContainer expandingRing = new CircularContainer
{ {
@ -68,7 +65,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Alpha = 0, Alpha = 0,
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER),
BlendingMode = BlendingMode.Additive, BlendingMode = BlendingMode.Additive,
Masking = true, Masking = true,
Children = new [] Children = new []
@ -85,11 +82,11 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
Name = "Target ring (thick border)", Name = "Target ring (thick border)",
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER),
Masking = true, Masking = true,
BorderThickness = target_ring_thick_border, BorderThickness = target_ring_thick_border,
BlendingMode = BlendingMode.Additive, BlendingMode = BlendingMode.Additive,
Children = new Framework.Graphics.Drawable[] Children = new Drawable[]
{ {
new Box new Box
{ {
@ -120,6 +117,8 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
}, },
circlePiece = new CirclePiece circlePiece = new CirclePiece
{ {
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new [] Children = new []
{ {
symbol = new SwellSymbolPiece() symbol = new SwellSymbolPiece()
@ -128,6 +127,8 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
} }
} }
}; };
circlePiece.KiaiMode = HitObject.Kiai;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -144,18 +145,18 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
{ {
userHits++; userHits++;
var completion = (float)userHits / swell.RequiredHits; var completion = (float)userHits / HitObject.RequiredHits;
expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50); expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50);
expandingRing.Delay(50); expandingRing.Delay(50);
expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint); expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint);
expandingRing.DelayReset(); expandingRing.DelayReset();
symbol.RotateTo((float)(completion * swell.Duration / 8), 4000, EasingTypes.OutQuint); symbol.RotateTo((float)(completion * HitObject.Duration / 8), 4000, EasingTypes.OutQuint);
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, EasingTypes.OutQuint); expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, EasingTypes.OutQuint);
if (userHits == swell.RequiredHits) if (userHits == HitObject.RequiredHits)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Great; Judgement.TaikoResult = TaikoHitResult.Great;
@ -167,7 +168,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
return; return;
//TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
if (userHits > swell.RequiredHits / 2) if (userHits > HitObject.RequiredHits / 2)
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
Judgement.TaikoResult = TaikoHitResult.Good; Judgement.TaikoResult = TaikoHitResult.Good;
@ -187,7 +188,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
Delay(preempt, true); Delay(preempt, true);
Delay(Judgement.TimeOffset + swell.Duration, true); Delay(Judgement.TimeOffset + HitObject.Duration, true);
const float out_transition_time = 300; const float out_transition_time = 300;

View File

@ -1,16 +1,20 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Input; using System.Collections.Generic;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using System.Collections.Generic; using osu.Game.Modes.Taiko.Objects.Drawables.Pieces;
using osu.Framework.Input; using OpenTK;
using OpenTK.Input;
namespace osu.Game.Modes.Taiko.Objects.Drawable namespace osu.Game.Modes.Taiko.Objects.Drawables
{ {
public abstract class DrawableTaikoHitObject : DrawableHitObject<TaikoHitObject, TaikoJudgement> public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableHitObject<TaikoHitObject, TaikoJudgement>
where TaikoHitType : TaikoHitObject
{ {
/// <summary> /// <summary>
/// A list of keys which this hit object will accept. These are the standard Taiko keys for now. /// A list of keys which this hit object will accept. These are the standard Taiko keys for now.
@ -18,33 +22,52 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
/// </summary> /// </summary>
private readonly List<Key> validKeys = new List<Key>(new[] { Key.D, Key.F, Key.J, Key.K }); private readonly List<Key> validKeys = new List<Key>(new[] { Key.D, Key.F, Key.J, Key.K });
protected DrawableTaikoHitObject(TaikoHitObject hitObject) public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
protected override Container<Drawable> Content => bodyContainer;
protected readonly TaikoPiece MainPiece;
private readonly Container bodyContainer;
public new TaikoHitType HitObject;
protected DrawableTaikoHitObject(TaikoHitType hitObject)
: base(hitObject) : base(hitObject)
{ {
HitObject = hitObject;
Anchor = Anchor.CentreLeft; Anchor = Anchor.CentreLeft;
Origin = Anchor.Centre; Origin = Anchor.Custom;
AutoSizeAxes = Axes.Both;
RelativePositionAxes = Axes.X; RelativePositionAxes = Axes.X;
}
protected override void LoadComplete() AddInternal(bodyContainer = new Container
{ {
LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2; AutoSizeAxes = Axes.Both,
Children = new[]
{
MainPiece = CreateMainPiece()
}
});
base.LoadComplete(); MainPiece.KiaiMode = HitObject.Kiai;
LifetimeStart = HitObject.StartTime - HitObject.ScrollTime * 2;
} }
protected override TaikoJudgement CreateJudgement() => new TaikoJudgement(); protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece(HitObject.IsStrong);
/// <summary> /// <summary>
/// Sets the scroll position of the DrawableHitObject relative to the offset between /// Sets the scroll position of the DrawableHitObject relative to the offset between
/// a time value and the HitObject's StartTime. /// a time value and the HitObject's StartTime.
/// </summary> /// </summary>
/// <param name="time"></param> /// <param name="time"></param>
protected virtual void UpdateScrollPosition(double time) protected virtual void UpdateScrollPosition(double time) => X = (float)((HitObject.StartTime - time) / HitObject.ScrollTime);
{
MoveToX((float)((HitObject.StartTime - time) / HitObject.PreEmpt));
}
protected override void Update() protected override void Update()
{ {

View File

@ -1,12 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using OpenTK; using OpenTK;
namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{ {
/// <summary> /// <summary>
/// The symbol used for centre hit pieces. /// The symbol used for centre hit pieces.

View File

@ -0,0 +1,152 @@
// 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.Sprites;
using osu.Game.Graphics.Backgrounds;
using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
/// <summary>
/// A circle piece which is used uniformly through osu!taiko to visualise hitobjects.
/// <para>
/// Note that this can actually be non-circle if the width is changed. See <see cref="ElongatedCirclePiece"/>
/// for a usage example.
/// </para>
/// </summary>
public class CirclePiece : TaikoPiece
{
public const float SYMBOL_SIZE = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER * 0.45f;
public const float SYMBOL_BORDER = 8;
public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER;
/// <summary>
/// The colour of the inner circle and outer glows.
/// </summary>
public override Color4 AccentColour
{
get { return base.AccentColour; }
set
{
base.AccentColour = value;
background.Colour = AccentColour;
resetEdgeEffects();
}
}
/// <summary>
/// Whether Kiai mode effects are enabled for this circle piece.
/// </summary>
public override bool KiaiMode
{
get { return base.KiaiMode; }
set
{
base.KiaiMode = value;
resetEdgeEffects();
}
}
protected override Container<Drawable> Content => content;
private readonly Container content;
private readonly Container background;
public Box FlashBox;
public CirclePiece(bool isStrong = false)
{
AddInternal(new Drawable[]
{
background = new CircularContainer
{
Name = "Background",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
},
new Triangles
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
ColourLight = Color4.White,
ColourDark = Color4.White.Darken(0.1f)
}
}
},
new CircularContainer
{
Name = "Ring",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
BorderThickness = 8,
BorderColour = Color4.White,
Masking = true,
Children = new[]
{
FlashBox = new Box
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
BlendingMode = BlendingMode.Additive,
Alpha = 0,
AlwaysPresent = true
}
}
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
Name = "Content",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
});
if (isStrong)
{
Size *= TaikoHitObject.STRONG_CIRCLE_DIAMETER_SCALE;
//default for symbols etc.
Content.Scale *= TaikoHitObject.STRONG_CIRCLE_DIAMETER_SCALE;
}
}
protected override void Update()
{
base.Update();
//we want to allow for width of content to remain mapped to the area inside us, regardless of the scale applied above.
Content.Width = 1 / Content.Scale.X;
}
private void resetEdgeEffects()
{
background.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Colour = AccentColour,
Radius = KiaiMode ? 50 : 8
};
}
}
}

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;
using osu.Framework.Graphics.Primitives;
using osu.Game.Modes.Taiko.UI;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
public class ElongatedCirclePiece : CirclePiece
{
/// <summary>
/// As we are being used to define the absolute size of hits, we need to be given a relative reference of our containing <see cref="TaikoPlayfield"/>.
/// </summary>
public Func<float> PlayfieldLengthReference;
/// <summary>
/// The length of this piece as a multiple of the value returned by <see cref="PlayfieldLengthReference"/>
/// </summary>
public float Length;
public ElongatedCirclePiece(bool isStrong = false) : base(isStrong)
{
}
protected override void Update()
{
base.Update();
var padding = Content.DrawHeight * Content.Width / 2;
Content.Padding = new MarginPadding
{
Left = padding,
Right = padding,
};
Width = (PlayfieldLengthReference?.Invoke() ?? 0) * Length + DrawHeight;
}
}
}

View File

@ -1,13 +1,13 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{ {
/// <summary> /// <summary>
/// The symbol used for rim hit pieces. /// The symbol used for rim hit pieces.

View File

@ -1,10 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Graphics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{ {
/// <summary> /// <summary>
/// The symbol used for swell pieces. /// The symbol used for swell pieces.

View File

@ -0,0 +1,45 @@
// 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.Containers;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
public class TaikoPiece : Container, IHasAccentColour
{
private Color4 accentColour;
/// <summary>
/// The colour of the inner circle and outer glows.
/// </summary>
public virtual Color4 AccentColour
{
get { return accentColour; }
set
{
accentColour = value;
}
}
private bool kiaiMode;
/// <summary>
/// Whether Kiai mode effects are enabled for this circle piece.
/// </summary>
public virtual bool KiaiMode
{
get { return kiaiMode; }
set
{
kiaiMode = value;
}
}
public TaikoPiece()
{
//just a default
Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER);
}
}
}

View File

@ -0,0 +1,60 @@
// 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.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.Taiko.Objects.Drawables.Pieces
{
public class TickPiece : TaikoPiece
{
/// <summary>
/// Any tick that is not the first for a drumroll is not filled, but is instead displayed
/// as a hollow circle. This is what controls the border width of that circle.
/// </summary>
private const float tick_border_width = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER / 16;
/// <summary>
/// The size of a tick.
/// </summary>
private const float tick_size = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER / 4;
private bool filled;
public bool Filled
{
get { return filled; }
set
{
filled = value;
fillBox.Alpha = filled ? 1 : 0;
}
}
private readonly Box fillBox;
public TickPiece()
{
Size = new Vector2(tick_size);
Add(new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = tick_border_width,
BorderColour = Color4.White,
Children = new[]
{
fillBox = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
});
}
}
}

View File

@ -1,37 +1,31 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps.Samples;
using osu.Game.Modes.Objects.Types; using osu.Game.Modes.Objects.Types;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Audio;
namespace osu.Game.Modes.Taiko.Objects namespace osu.Game.Modes.Taiko.Objects
{ {
public class DrumRoll : TaikoHitObject, IHasDistance public class DrumRoll : TaikoHitObject, IHasEndTime
{ {
public double EndTime => StartTime + Distance / Velocity; /// <summary>
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
/// </summary>
private const float base_distance = 100;
public double Duration => EndTime - StartTime; public double EndTime => StartTime + Duration;
public double Duration { get; set; }
/// <summary> /// <summary>
/// Raw length of the drum roll in positional length units. /// Numer of ticks per beat length.
/// </summary> /// </summary>
public double Distance { get; set; } public int TickRate = 1;
/// <summary>
/// Velocity of the drum roll in positional length units per millisecond.
/// </summary>
public double Velocity { get; protected set; }
/// <summary>
/// The distance between ticks of this drumroll.
/// <para>Half of this value is the hit window of the ticks.</para>
/// </summary>
public double TickTimeDistance { get; protected set; }
/// <summary> /// <summary>
/// Number of drum roll ticks required for a "Good" hit. /// Number of drum roll ticks required for a "Good" hit.
@ -55,18 +49,17 @@ namespace osu.Game.Modes.Taiko.Objects
private List<DrumRollTick> ticks; private List<DrumRollTick> ticks;
/// <summary>
/// The length (in milliseconds) between ticks of this drumroll.
/// <para>Half of this value is the hit window of the ticks.</para>
/// </summary>
private double tickSpacing = 100;
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{ {
base.ApplyDefaults(timing, difficulty); base.ApplyDefaults(timing, difficulty);
Velocity = timing.SliderVelocityAt(StartTime) * difficulty.SliderMultiplier / 1000; tickSpacing = timing.BeatLengthAt(StartTime) / TickRate;
TickTimeDistance = timing.BeatLengthAt(StartTime);
//TODO: move this to legacy conversion code to allow for direct division without special case.
if (difficulty.SliderTickRate == 3)
TickTimeDistance /= 3;
else
TickTimeDistance /= 4;
RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty); RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty);
RequiredGreatHits = TotalTicks * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty); RequiredGreatHits = TotalTicks * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty);
@ -76,23 +69,25 @@ namespace osu.Game.Modes.Taiko.Objects
{ {
var ret = new List<DrumRollTick>(); var ret = new List<DrumRollTick>();
if (TickTimeDistance == 0) if (tickSpacing == 0)
return ret; return ret;
bool first = true; bool first = true;
for (double t = StartTime; t < EndTime + (int)TickTimeDistance; t += TickTimeDistance) for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing)
{ {
ret.Add(new DrumRollTick ret.Add(new DrumRollTick
{ {
FirstTick = first, FirstTick = first,
PreEmpt = PreEmpt, ScrollTime = ScrollTime,
TickTimeDistance = TickTimeDistance, TickSpacing = tickSpacing,
StartTime = t, StartTime = t,
Sample = new HitSampleInfo IsStrong = IsStrong,
Samples = Samples.Select(s => new SampleInfo
{ {
Type = SampleType.None, Bank = s.Bank,
Set = SampleSet.Soft Name = @"slidertick",
} Volume = s.Volume
}).ToList()
}); });
first = false; first = false;

View File

@ -11,14 +11,14 @@ namespace osu.Game.Modes.Taiko.Objects
public bool FirstTick; public bool FirstTick;
/// <summary> /// <summary>
/// The distance between this tick and the next in milliseconds. /// The length (in milliseconds) between this tick and the next.
/// <para>Half of this value is the hit window of the tick.</para> /// <para>Half of this value is the hit window of the tick.</para>
/// </summary> /// </summary>
public double TickTimeDistance; public double TickSpacing;
/// <summary> /// <summary>
/// The time allowed to hit this tick. /// The time allowed to hit this tick.
/// </summary> /// </summary>
public double HitWindow => TickTimeDistance / 2; public double HitWindow => TickSpacing / 2;
} }
} }

View File

@ -1,30 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
using osu.Game.Modes.Objects.Types; using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Taiko.Objects namespace osu.Game.Modes.Taiko.Objects
{ {
public class Swell : TaikoHitObject, IHasEndTime public class Swell : TaikoHitObject, IHasEndTime
{ {
public double EndTime { get; set; } public double EndTime => StartTime + Duration;
public double Duration => EndTime - StartTime; public double Duration { get; set; }
/// <summary> /// <summary>
/// The number of hits required to complete the swell successfully. /// The number of hits required to complete the swell successfully.
/// </summary> /// </summary>
public int RequiredHits { get; protected set; } = 10; public int RequiredHits = 10;
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
base.ApplyDefaults(timing, difficulty);
double spinnerRotationRatio = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5);
RequiredHits = (int)Math.Max(1, Duration / 1000f * spinnerRotationRatio);
}
} }
} }

View File

@ -4,20 +4,41 @@
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using osu.Game.Modes.Taiko.UI;
namespace osu.Game.Modes.Taiko.Objects namespace osu.Game.Modes.Taiko.Objects
{ {
public abstract class TaikoHitObject : HitObject public abstract class TaikoHitObject : HitObject
{ {
/// <summary> /// <summary>
/// HitCircle radius. /// Diameter of a circle relative to the size of the <see cref="TaikoPlayfield"/>.
/// </summary> /// </summary>
public const float CIRCLE_RADIUS = 42f; public const float PLAYFIELD_RELATIVE_DIAMETER = 0.5f;
/// <summary> /// <summary>
/// The time to scroll in the HitObject. /// Scale multiplier for a strong circle.
/// </summary> /// </summary>
public double PreEmpt; public const float STRONG_CIRCLE_DIAMETER_SCALE = 1.5f;
/// <summary>
/// Default circle diameter.
/// </summary>
public const float DEFAULT_CIRCLE_DIAMETER = TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT * PLAYFIELD_RELATIVE_DIAMETER;
/// <summary>
/// Default strong circle diameter.
/// </summary>
public const float DEFAULT_STRONG_CIRCLE_DIAMETER = DEFAULT_CIRCLE_DIAMETER * STRONG_CIRCLE_DIAMETER_SCALE;
/// <summary>
/// The time taken from the initial (off-screen) spawn position to the centre of the hit target for a <see cref="ControlPoint.BeatLength"/> of 1000ms.
/// </summary>
private const double scroll_time = 6000;
/// <summary>
/// Our adjusted <see cref="scroll_time"/> taking into consideration local <see cref="ControlPoint.BeatLength"/> and other speed multipliers.
/// </summary>
public double ScrollTime;
/// <summary> /// <summary>
/// Whether this HitObject is a "strong" type. /// Whether this HitObject is a "strong" type.
@ -34,7 +55,7 @@ namespace osu.Game.Modes.Taiko.Objects
{ {
base.ApplyDefaults(timing, difficulty); base.ApplyDefaults(timing, difficulty);
PreEmpt = 600 / (timing.SliderVelocityAt(StartTime) * difficulty.SliderMultiplier) * 1000; ScrollTime = scroll_time * (timing.BeatLengthAt(StartTime) * timing.SpeedMultiplierAt(StartTime) / 1000) / difficulty.SliderMultiplier;
ControlPoint overridePoint; ControlPoint overridePoint;
Kiai = timing.TimingPointAt(StartTime, out overridePoint).KiaiMode; Kiai = timing.TimingPointAt(StartTime, out overridePoint).KiaiMode;

View File

@ -72,14 +72,9 @@ namespace osu.Game.Modes.Taiko.Replays
} }
else if (drumRoll != null) else if (drumRoll != null)
{ {
double delay = drumRoll.TickTimeDistance; foreach (var tick in drumRoll.Ticks)
double time = drumRoll.StartTime;
for (int j = 0; j < drumRoll.TotalTicks; j++)
{ {
Frames.Add(new ReplayFrame((int)time, 0, 0, hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2)); Frames.Add(new ReplayFrame(tick.StartTime, 0, 0, hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2));
time += delay;
hitButton = !hitButton; hitButton = !hitButton;
} }
} }

View File

@ -187,10 +187,6 @@ namespace osu.Game.Modes.Taiko.Scoring
if (!isTick) if (!isTick)
totalHits++; totalHits++;
// Apply combo changes, must be done before the hit score is added
if (!isTick && judgement.Result == HitResult.Hit)
Combo.Value++;
// Apply score changes // Apply score changes
addHitScore(judgement); addHitScore(judgement);
@ -261,7 +257,7 @@ namespace osu.Game.Modes.Taiko.Scoring
foreach (var j in Judgements) foreach (var j in Judgements)
{ {
scoreForAccuracy += j.ResultValueForAccuracy; scoreForAccuracy += j.ResultValueForAccuracy;
maxScoreForAccuracy = j.MaxResultValueForAccuracy; maxScoreForAccuracy += j.MaxResultValueForAccuracy;
} }
Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy; Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy;

View File

@ -18,14 +18,18 @@ namespace osu.Game.Modes.Taiko.UI
/// </summary> /// </summary>
internal class HitExplosion : CircularContainer internal class HitExplosion : CircularContainer
{ {
private readonly TaikoJudgement judgement; /// <summary>
/// The judgement this hit explosion visualises.
/// </summary>
public readonly TaikoJudgement Judgement;
private readonly Box innerFill; private readonly Box innerFill;
public HitExplosion(TaikoJudgement judgement) public HitExplosion(TaikoJudgement judgement)
{ {
this.judgement = judgement; Judgement = judgement;
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2); Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER);
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;
@ -50,10 +54,7 @@ namespace osu.Game.Modes.Taiko.UI
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
if (judgement.SecondHit) switch (Judgement.TaikoResult)
Size *= 1.5f;
switch (judgement.TaikoResult)
{ {
case TaikoHitResult.Good: case TaikoHitResult.Good:
innerFill.Colour = colours.Green; innerFill.Colour = colours.Green;
@ -73,5 +74,13 @@ namespace osu.Game.Modes.Taiko.UI
Expire(); Expire();
} }
/// <summary>
/// Transforms this hit explosion to visualise a secondary hit.
/// </summary>
public void VisualiseSecondHit()
{
ResizeTo(Size * TaikoHitObject.STRONG_CIRCLE_DIAMETER_SCALE, 50);
}
} }
} }

View File

@ -15,16 +15,6 @@ namespace osu.Game.Modes.Taiko.UI
/// </summary> /// </summary>
internal class HitTarget : Container internal class HitTarget : Container
{ {
/// <summary>
/// Diameter of normal hit object circles.
/// </summary>
private const float normal_diameter = TaikoHitObject.CIRCLE_RADIUS * 2;
/// <summary>
/// Diameter of finisher hit object circles.
/// </summary>
private const float finisher_diameter = normal_diameter * 1.5f;
/// <summary> /// <summary>
/// The 1px inner border of the taiko playfield. /// The 1px inner border of the taiko playfield.
/// </summary> /// </summary>
@ -37,7 +27,7 @@ namespace osu.Game.Modes.Taiko.UI
public HitTarget() public HitTarget()
{ {
RelativeSizeAxes = Axes.Y; Size = new Vector2(TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT);
Children = new Drawable[] Children = new Drawable[]
{ {
@ -47,15 +37,15 @@ namespace osu.Game.Modes.Taiko.UI
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Y = border_offset, Y = border_offset,
Size = new Vector2(border_thickness, (TaikoPlayfield.PLAYFIELD_HEIGHT - finisher_diameter) / 2f - border_offset), Size = new Vector2(border_thickness, (TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT - TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER) / 2f - border_offset),
Alpha = 0.1f Alpha = 0.1f
}, },
new CircularContainer new CircularContainer
{ {
Name = "Finisher Ring", Name = "Strong Hit Ring",
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(finisher_diameter), Size = new Vector2(TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER),
Masking = true, Masking = true,
BorderColour = Color4.White, BorderColour = Color4.White,
BorderThickness = border_thickness, BorderThickness = border_thickness,
@ -72,10 +62,10 @@ namespace osu.Game.Modes.Taiko.UI
}, },
new CircularContainer new CircularContainer
{ {
Name = "Normal Ring", Name = "Normal Hit Ring",
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(normal_diameter), Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER),
Masking = true, Masking = true,
BorderColour = Color4.White, BorderColour = Color4.White,
BorderThickness = border_thickness, BorderThickness = border_thickness,
@ -96,7 +86,7 @@ namespace osu.Game.Modes.Taiko.UI
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre, Origin = Anchor.BottomCentre,
Y = -border_offset, Y = -border_offset,
Size = new Vector2(border_thickness, (TaikoPlayfield.PLAYFIELD_HEIGHT - finisher_diameter) / 2f - border_offset), Size = new Vector2(border_thickness, (TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT - TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER) / 2f - border_offset),
Alpha = 0.1f Alpha = 0.1f
}, },
}; };

View File

@ -21,7 +21,7 @@ namespace osu.Game.Modes.Taiko.UI
{ {
public InputDrum() public InputDrum()
{ {
Size = new Vector2(TaikoPlayfield.PLAYFIELD_HEIGHT); Size = new Vector2(TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT);
const float middle_split = 10; const float middle_split = 10;
@ -128,17 +128,36 @@ namespace osu.Game.Modes.Taiko.UI
return false; return false;
Drawable target = null; Drawable target = null;
Drawable back = null;
if (args.Key == CentreKey) if (args.Key == CentreKey)
{
target = centreHit; target = centreHit;
back = centre;
}
else if (args.Key == RimKey) else if (args.Key == RimKey)
{
target = rimHit; target = rimHit;
back = rim;
}
if (target != null) if (target != null)
{ {
target.FadeTo(Math.Min(target.Alpha + 0.4f, 1), 40, EasingTypes.OutQuint); const float scale_amount = 0.05f;
target.Delay(40); const float alpha_amount = 0.5f;
target.FadeOut(1000, EasingTypes.OutQuint);
const float down_time = 40;
const float up_time = 1000;
back.ScaleTo(target.Scale.X - scale_amount, down_time, EasingTypes.OutQuint);
back.Delay(down_time);
back.ScaleTo(1, up_time, EasingTypes.OutQuint);
target.ScaleTo(target.Scale.X - scale_amount, down_time, EasingTypes.OutQuint);
target.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, EasingTypes.OutQuint);
target.Delay(down_time);
target.ScaleTo(1, up_time, EasingTypes.OutQuint);
target.FadeOut(up_time, EasingTypes.OutQuint);
} }
return false; return false;

View File

@ -1,16 +1,23 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.MathUtils;
using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Timing;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Objects.Types;
using osu.Game.Modes.Replays; using osu.Game.Modes.Replays;
using osu.Game.Modes.Scoring; using osu.Game.Modes.Scoring;
using osu.Game.Modes.Taiko.Beatmaps; using osu.Game.Modes.Taiko.Beatmaps;
using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects; using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.Taiko.Replays; using osu.Game.Modes.Taiko.Objects.Drawables;
using osu.Game.Modes.Taiko.Scoring; using osu.Game.Modes.Taiko.Scoring;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Modes.Taiko.Replays;
using OpenTK;
namespace osu.Game.Modes.Taiko.UI namespace osu.Game.Modes.Taiko.UI
{ {
@ -21,15 +28,132 @@ namespace osu.Game.Modes.Taiko.UI
{ {
} }
[BackgroundDependencyLoader]
private void load()
{
loadBarLines();
}
private void loadBarLines()
{
var taikoPlayfield = Playfield as TaikoPlayfield;
if (taikoPlayfield == null)
return;
TaikoHitObject lastObject = Beatmap.HitObjects[Beatmap.HitObjects.Count - 1];
double lastHitTime = 1 + (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime;
var timingPoints = Beatmap.TimingInfo.ControlPoints.FindAll(cp => cp.TimingChange);
if (timingPoints.Count == 0)
return;
int currentIndex = 0;
while (currentIndex < timingPoints.Count && Precision.AlmostEquals(timingPoints[currentIndex].BeatLength, 0))
currentIndex++;
double time = timingPoints[currentIndex].Time;
double measureLength = timingPoints[currentIndex].BeatLength * (int)timingPoints[currentIndex].TimeSignature;
// Find the bar line time closest to 0
time -= measureLength * (int)(time / measureLength);
// Always start barlines from a positive time
while (time < 0)
time += measureLength;
int currentBeat = 0;
while (time <= lastHitTime)
{
ControlPoint current = timingPoints[currentIndex];
if (time > current.Time || current.OmitFirstBarLine)
{
bool isMajor = currentBeat % (int)current.TimeSignature == 0;
var barLine = new BarLine
{
StartTime = time,
};
barLine.ApplyDefaults(Beatmap.TimingInfo, Beatmap.BeatmapInfo.Difficulty);
taikoPlayfield.AddBarLine(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
currentBeat++;
}
double bl = current.BeatLength;
if (bl < 800)
bl *= (int)current.TimeSignature;
time += bl;
if (currentIndex + 1 >= timingPoints.Count || time < timingPoints[currentIndex + 1].Time)
continue;
currentBeat = 0;
currentIndex++;
time = timingPoints[currentIndex].Time;
}
}
protected override Vector2 GetPlayfieldAspectAdjust()
{
const float default_relative_height = TaikoPlayfield.DEFAULT_PLAYFIELD_HEIGHT / 768;
const float default_aspect = 16f / 9f;
float aspectAdjust = MathHelper.Clamp(DrawWidth / DrawHeight, 0.4f, 4) / default_aspect;
return new Vector2(1, default_relative_height * aspectAdjust);
}
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
protected override IBeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(); protected override IBeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter();
protected override IBeatmapProcessor<TaikoHitObject> CreateBeatmapProcessor() => new TaikoBeatmapProcessor(); protected override IBeatmapProcessor<TaikoHitObject> CreateBeatmapProcessor() => new TaikoBeatmapProcessor();
protected override Playfield<TaikoHitObject, TaikoJudgement> CreatePlayfield() => new TaikoPlayfield(); protected override Playfield<TaikoHitObject, TaikoJudgement> CreatePlayfield() => new TaikoPlayfield
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
};
protected override DrawableHitObject<TaikoHitObject, TaikoJudgement> GetVisualRepresentation(TaikoHitObject h) => null; protected override DrawableHitObject<TaikoHitObject, TaikoJudgement> GetVisualRepresentation(TaikoHitObject h)
{
var centreHit = h as CentreHit;
if (centreHit != null)
{
if (h.IsStrong)
return new DrawableCentreHitStrong(centreHit);
return new DrawableCentreHit(centreHit);
}
var rimHit = h as RimHit;
if (rimHit != null)
{
if (h.IsStrong)
return new DrawableRimHitStrong(rimHit);
return new DrawableRimHit(rimHit);
}
var drumRoll = h as DrumRoll;
if (drumRoll != null)
{
return new DrawableDrumRoll(drumRoll);
}
var swell = h as Swell;
if (swell != null)
return new DrawableSwell(swell);
return null;
}
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay); protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay);
} }

View File

@ -14,22 +14,23 @@ using osu.Game.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Game.Modes.Taiko.Objects.Drawable; using System.Linq;
using osu.Game.Modes.Taiko.Objects.Drawables;
using System;
namespace osu.Game.Modes.Taiko.UI namespace osu.Game.Modes.Taiko.UI
{ {
public class TaikoPlayfield : Playfield<TaikoHitObject, TaikoJudgement> public class TaikoPlayfield : Playfield<TaikoHitObject, TaikoJudgement>
{ {
/// <summary> /// <summary>
/// The play field height. This is relative to the size of hit objects /// The default play field height.
/// such that the playfield is just a bit larger than finishers.
/// </summary> /// </summary>
public const float PLAYFIELD_HEIGHT = TaikoHitObject.CIRCLE_RADIUS * 2 * 2; public const float DEFAULT_PLAYFIELD_HEIGHT = 168f;
/// <summary> /// <summary>
/// The offset from <see cref="left_area_size"/> which the center of the hit target lies at. /// The offset from <see cref="left_area_size"/> which the center of the hit target lies at.
/// </summary> /// </summary>
private const float hit_target_offset = TaikoHitObject.CIRCLE_RADIUS * 1.5f + 40; private const float hit_target_offset = TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER / 2f + 40;
/// <summary> /// <summary>
/// The size of the left area of the playfield. This area contains the input drum. /// The size of the left area of the playfield. This area contains the input drum.
@ -39,7 +40,7 @@ namespace osu.Game.Modes.Taiko.UI
protected override Container<Drawable> Content => hitObjectContainer; protected override Container<Drawable> Content => hitObjectContainer;
private readonly Container<HitExplosion> hitExplosionContainer; private readonly Container<HitExplosion> hitExplosionContainer;
//private Container<DrawableBarLine> barLineContainer; private readonly Container<DrawableBarLine> barLineContainer;
private readonly Container<DrawableTaikoJudgement> judgementContainer; private readonly Container<DrawableTaikoJudgement> judgementContainer;
private readonly Container hitObjectContainer; private readonly Container hitObjectContainer;
@ -51,13 +52,11 @@ namespace osu.Game.Modes.Taiko.UI
public TaikoPlayfield() public TaikoPlayfield()
{ {
RelativeSizeAxes = Axes.X;
Height = PLAYFIELD_HEIGHT;
AddInternal(new Drawable[] AddInternal(new Drawable[]
{ {
rightBackgroundContainer = new Container rightBackgroundContainer = new Container
{ {
Name = "Transparent playfield background",
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
BorderThickness = 2, BorderThickness = 2,
Masking = true, Masking = true,
@ -76,15 +75,23 @@ namespace osu.Game.Modes.Taiko.UI
}, },
} }
}, },
new ScaleFixContainer
{
RelativeSizeAxes = Axes.X,
Height = DEFAULT_PLAYFIELD_HEIGHT,
Children = new[]
{
new Container new Container
{ {
Name = "Transparent playfield elements",
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = left_area_size }, Padding = new MarginPadding { Left = left_area_size },
Children = new Drawable[] Children = new Drawable[]
{ {
new Container new Container
{ {
Padding = new MarginPadding { Left = hit_target_offset }, Name = "Hit target container",
X = hit_target_offset,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
@ -92,13 +99,13 @@ namespace osu.Game.Modes.Taiko.UI
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), RelativeSizeAxes = Axes.Y,
BlendingMode = BlendingMode.Additive BlendingMode = BlendingMode.Additive
}, },
//barLineContainer = new Container<DrawableBarLine> barLineContainer = new Container<DrawableBarLine>
//{ {
// RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
//}, },
new HitTarget new HitTarget
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
@ -110,7 +117,7 @@ namespace osu.Game.Modes.Taiko.UI
}, },
judgementContainer = new Container<DrawableTaikoJudgement> judgementContainer = new Container<DrawableTaikoJudgement>
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Y,
BlendingMode = BlendingMode.Additive BlendingMode = BlendingMode.Additive
}, },
}, },
@ -119,7 +126,8 @@ namespace osu.Game.Modes.Taiko.UI
}, },
leftBackgroundContainer = new Container leftBackgroundContainer = new Container
{ {
Size = new Vector2(left_area_size, PLAYFIELD_HEIGHT), Name = "Left overlay",
Size = new Vector2(left_area_size, DEFAULT_PLAYFIELD_HEIGHT),
BorderThickness = 1, BorderThickness = 1,
Children = new Drawable[] Children = new Drawable[]
{ {
@ -144,8 +152,11 @@ namespace osu.Game.Modes.Taiko.UI
}, },
} }
}, },
}
},
topLevelHitContainer = new Container topLevelHitContainer = new Container
{ {
Name = "Top level hit objects",
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
} }
}); });
@ -173,12 +184,15 @@ namespace osu.Game.Modes.Taiko.UI
swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy()); swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy());
} }
public void AddBarLine(DrawableBarLine barLine)
{
barLineContainer.Add(barLine);
}
public override void OnJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> judgedObject) public override void OnJudgement(DrawableHitObject<TaikoHitObject, TaikoJudgement> judgedObject)
{ {
bool wasHit = judgedObject.Judgement.Result == HitResult.Hit; bool wasHit = judgedObject.Judgement.Result == HitResult.Hit;
bool secondHit = judgedObject.Judgement.SecondHit;
if (wasHit)
hitExplosionContainer.Add(new HitExplosion(judgedObject.Judgement));
judgementContainer.Add(new DrawableTaikoJudgement(judgedObject.Judgement) judgementContainer.Add(new DrawableTaikoJudgement(judgedObject.Judgement)
{ {
@ -187,6 +201,73 @@ namespace osu.Game.Modes.Taiko.UI
RelativePositionAxes = Axes.X, RelativePositionAxes = Axes.X,
X = wasHit ? judgedObject.Position.X : 0, X = wasHit ? judgedObject.Position.X : 0,
}); });
if (!wasHit)
return;
if (!secondHit)
{
if (judgedObject.X >= -0.05f && !(judgedObject is DrawableSwell))
{
// If we're far enough away from the left stage, we should bring outselves in front of it
topLevelHitContainer.Add(judgedObject.CreateProxy());
}
hitExplosionContainer.Add(new HitExplosion(judgedObject.Judgement));
}
else
hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit();
}
/// <summary>
/// This is a very special type of container. It serves a similar purpose to <see cref="FillMode.Fit"/>, however unlike <see cref="FillMode.Fit"/>,
/// this will only adjust the scale relative to the height of its parent and will maintain the original width relative to its parent.
///
/// <para>
/// By adjusting the scale relative to the height of its parent, the aspect ratio of this container's children is maintained, however this is undesirable
/// in the case where the hit object container should not have its width adjusted by scale. To counteract this, another container is nested inside this
/// container which takes care of reversing the width adjustment while appearing transparent to the user.
/// </para>
/// </summary>
private class ScaleFixContainer : Container
{
protected override Container<Drawable> Content => widthAdjustmentContainer;
private readonly WidthAdjustmentContainer widthAdjustmentContainer;
/// <summary>
/// We only want to apply DrawScale in the Y-axis to preserve aspect ratio and <see cref="TaikoPlayfield"/> doesn't care about having its width adjusted.
/// </summary>
protected override Vector2 DrawScale => Scale * RelativeToAbsoluteFactor.Y / DrawHeight;
public ScaleFixContainer()
{
AddInternal(widthAdjustmentContainer = new WidthAdjustmentContainer { ParentDrawScaleReference = () => DrawScale.X });
}
/// <summary>
/// The container type that reverses the <see cref="Drawable.DrawScale"/> width adjustment.
/// </summary>
private class WidthAdjustmentContainer : Container
{
/// <summary>
/// This container needs to know its parent's <see cref="Drawable.DrawScale"/> so it can reverse the width adjustment caused by <see cref="Drawable.DrawScale"/>.
/// </summary>
public Func<float> ParentDrawScaleReference;
public WidthAdjustmentContainer()
{
// This container doesn't care about height, it should always fill its parent
RelativeSizeAxes = Axes.Y;
}
protected override void Update()
{
base.Update();
// Reverse the DrawScale adjustment
Width = Parent.DrawSize.X / ParentDrawScaleReference();
}
}
} }
} }
} }

View File

@ -33,8 +33,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath> <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
@ -53,22 +52,27 @@
<Compile Include="Judgements\TaikoStrongHitJudgement.cs" /> <Compile Include="Judgements\TaikoStrongHitJudgement.cs" />
<Compile Include="Judgements\TaikoJudgement.cs" /> <Compile Include="Judgements\TaikoJudgement.cs" />
<Compile Include="Judgements\TaikoHitResult.cs" /> <Compile Include="Judgements\TaikoHitResult.cs" />
<Compile Include="Objects\BarLine.cs" />
<Compile Include="Objects\Drawables\DrawableBarLine.cs" />
<Compile Include="Objects\Drawables\DrawableBarLineMajor.cs" />
<Compile Include="Objects\CentreHit.cs" /> <Compile Include="Objects\CentreHit.cs" />
<Compile Include="Objects\Drawable\DrawableRimHit.cs" /> <Compile Include="Objects\Drawables\DrawableRimHit.cs" />
<Compile Include="Objects\Drawable\DrawableStrongRimHit.cs" /> <Compile Include="Objects\Drawables\DrawableRimHitStrong.cs" />
<Compile Include="Objects\Drawable\DrawableCentreHit.cs" /> <Compile Include="Objects\Drawables\DrawableCentreHit.cs" />
<Compile Include="Objects\Drawable\DrawableHit.cs" /> <Compile Include="Objects\Drawables\DrawableHit.cs" />
<Compile Include="Objects\Drawable\DrawableStrongCentreHit.cs" /> <Compile Include="Objects\Drawables\DrawableCentreHitStrong.cs" />
<Compile Include="Objects\Drawable\DrawableStrongHit.cs" /> <Compile Include="Objects\Drawables\DrawableHitStrong.cs" />
<Compile Include="Objects\Drawable\Pieces\CentreHitSymbolPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\ElongatedCirclePiece.cs" />
<Compile Include="Objects\Drawable\DrawableDrumRoll.cs" /> <Compile Include="Objects\Drawables\Pieces\CentreHitSymbolPiece.cs" />
<Compile Include="Objects\Drawable\DrawableDrumRollTick.cs" /> <Compile Include="Objects\Drawables\DrawableDrumRoll.cs" />
<Compile Include="Objects\Drawable\DrawableSwell.cs" /> <Compile Include="Objects\Drawables\DrawableDrumRollTick.cs" />
<Compile Include="Objects\Drawable\DrawableTaikoHitObject.cs" /> <Compile Include="Objects\Drawables\DrawableSwell.cs" />
<Compile Include="Objects\Drawable\Pieces\RimHitSymbolPiece.cs" /> <Compile Include="Objects\Drawables\DrawableTaikoHitObject.cs" />
<Compile Include="Objects\Drawable\Pieces\StrongCirclePiece.cs" /> <Compile Include="Objects\Drawables\Pieces\RimHitSymbolPiece.cs" />
<Compile Include="Objects\Drawable\Pieces\CirclePiece.cs" /> <Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" />
<Compile Include="Objects\Drawable\Pieces\SwellSymbolPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\SwellSymbolPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\TaikoPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\TickPiece.cs" />
<Compile Include="Objects\DrumRoll.cs" /> <Compile Include="Objects\DrumRoll.cs" />
<Compile Include="Objects\DrumRollTick.cs" /> <Compile Include="Objects\DrumRollTick.cs" />
<Compile Include="Objects\Hit.cs" /> <Compile Include="Objects\Hit.cs" />

View File

@ -4,5 +4,5 @@ Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
--> -->
<packages> <packages>
<package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" /> <package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
</packages> </packages>

View File

@ -6,11 +6,12 @@ using NUnit.Framework;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.Samples;
using osu.Game.Modes; using osu.Game.Modes;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
using osu.Game.Modes.Osu; using osu.Game.Modes.Osu;
using osu.Game.Modes.Objects.Legacy; using osu.Game.Modes.Objects.Legacy;
using System.Linq;
using osu.Game.Audio;
namespace osu.Game.Tests.Beatmaps.Formats namespace osu.Game.Tests.Beatmaps.Formats
{ {
@ -55,7 +56,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
var beatmapInfo = decoder.Decode(new StreamReader(stream)).BeatmapInfo; var beatmapInfo = decoder.Decode(new StreamReader(stream)).BeatmapInfo;
Assert.AreEqual(0, beatmapInfo.AudioLeadIn); Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
Assert.AreEqual(false, beatmapInfo.Countdown); Assert.AreEqual(false, beatmapInfo.Countdown);
Assert.AreEqual(SampleSet.Soft, beatmapInfo.SampleSet);
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
Assert.AreEqual(false, beatmapInfo.SpecialStyle); Assert.AreEqual(false, beatmapInfo.SpecialStyle);
Assert.AreEqual(PlayMode.Osu, beatmapInfo.Mode); Assert.AreEqual(PlayMode.Osu, beatmapInfo.Mode);
@ -137,12 +137,12 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsNotNull(slider); Assert.IsNotNull(slider);
Assert.AreEqual(new Vector2(192, 168), slider.Position); Assert.AreEqual(new Vector2(192, 168), slider.Position);
Assert.AreEqual(956, slider.StartTime); Assert.AreEqual(956, slider.StartTime);
Assert.AreEqual(SampleType.None, slider.Sample.Type); Assert.IsTrue(slider.Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
var hit = beatmap.HitObjects[1] as LegacyHit; var hit = beatmap.HitObjects[1] as LegacyHit;
Assert.IsNotNull(hit); Assert.IsNotNull(hit);
Assert.AreEqual(new Vector2(304, 56), hit.Position); Assert.AreEqual(new Vector2(304, 56), hit.Position);
Assert.AreEqual(1285, hit.StartTime); Assert.AreEqual(1285, hit.StartTime);
Assert.AreEqual(SampleType.Clap, hit.Sample.Type); Assert.IsTrue(hit.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
} }
} }
} }

View File

@ -7,6 +7,8 @@ using osu.Game.Beatmaps.IO;
using osu.Game.Modes; using osu.Game.Modes;
using osu.Game.Modes.Osu; using osu.Game.Modes.Osu;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
using osu.Game.Beatmaps.Formats;
using osu.Game.Database;
namespace osu.Game.Tests.Beatmaps.IO namespace osu.Game.Tests.Beatmaps.IO
{ {
@ -53,7 +55,11 @@ namespace osu.Game.Tests.Beatmaps.IO
using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz")) using (var osz = Resource.OpenResource("Beatmaps.241526 Soleily - Renatus.osz"))
{ {
var reader = new OszArchiveReader(osz); var reader = new OszArchiveReader(osz);
var meta = reader.ReadMetadata();
BeatmapMetadata meta;
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
meta = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
Assert.AreEqual("Soleily", meta.Artist); Assert.AreEqual("Soleily", meta.Artist);
Assert.AreEqual("Soleily", meta.ArtistUnicode); Assert.AreEqual("Soleily", meta.ArtistUnicode);

View File

@ -29,13 +29,11 @@
<TreatWarningsAsErrors>false</TreatWarningsAsErrors> <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="nunit.framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=3.6.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll</HintPath> <HintPath>$(SolutionDir)\packages\NUnit.3.6.1\lib\net45\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL"> <Reference Include="OpenTK, Version=2.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll</HintPath> <HintPath>$(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1341\lib\net45\OpenTK.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="SQLite.Net"> <Reference Include="SQLite.Net">

View File

@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
--> -->
<packages> <packages>
<package id="NUnit" version="3.5.0" targetFramework="net45" /> <package id="NUnit" version="3.6.1" targetFramework="net45" />
<package id="ppy.OpenTK" version="2.0.50727.1340" targetFramework="net45" /> <package id="ppy.OpenTK" version="2.0.50727.1341" targetFramework="net45" />
<package id="SQLite.Net.Core-PCL" version="3.1.1" targetFramework="net45" /> <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="SQLite.Net-PCL" version="3.1.1" targetFramework="net45" />
</packages> </packages>

View File

@ -0,0 +1,28 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Audio
{
public class SampleInfo
{
public const string HIT_WHISTLE = @"hitwhistle";
public const string HIT_FINISH = @"hitfinish";
public const string HIT_NORMAL = @"hitnormal";
public const string HIT_CLAP = @"hitclap";
/// <summary>
/// The bank to load the sample from.
/// </summary>
public string Bank;
/// <summary>
/// The name of the sample to load.
/// </summary>
public string Name;
/// <summary>
/// The sample volume.
/// </summary>
public int Volume;
}
}

View File

@ -22,8 +22,9 @@ namespace osu.Game.Beatmaps.Drawables
public Action<BeatmapSetHeader> GainedSelection; public Action<BeatmapSetHeader> GainedSelection;
private readonly SpriteText title; private readonly SpriteText title;
private readonly SpriteText artist; private readonly SpriteText artist;
private OsuConfigManager config;
private Bindable<bool> preferUnicode; private Bindable<bool> preferUnicode;
private readonly WorkingBeatmap beatmap; private readonly WorkingBeatmap beatmap;
private readonly FillFlowContainer difficultyIcons; private readonly FillFlowContainer difficultyIcons;
@ -33,19 +34,15 @@ namespace osu.Game.Beatmaps.Drawables
Children = new Drawable[] Children = new Drawable[]
{ {
new DelayedLoadContainer new DelayedLoadWrapper(
{
RelativeSizeAxes = Axes.Both,
TimeBeforeLoad = 300,
FinishedLoading = d => d.FadeInFromZero(400, EasingTypes.Out),
Children = new[]
{
new PanelBackground(beatmap) new PanelBackground(beatmap)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Depth = 1, OnLoadComplete = d => d.FadeInFromZero(400, EasingTypes.Out),
}
} }
)
{
TimeBeforeLoad = 300,
}, },
new FillFlowContainer new FillFlowContainer
{ {
@ -87,24 +84,13 @@ namespace osu.Game.Beatmaps.Drawables
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
this.config = config;
preferUnicode = config.GetBindable<bool>(OsuConfig.ShowUnicode); preferUnicode = config.GetBindable<bool>(OsuConfig.ShowUnicode);
preferUnicode.ValueChanged += preferUnicode_changed; preferUnicode.ValueChanged += unicode =>
preferUnicode_changed(preferUnicode, null);
}
private void preferUnicode_changed(object sender, EventArgs e)
{ {
title.Text = config.GetUnicodeString(beatmap.BeatmapSetInfo.Metadata.Title, beatmap.BeatmapSetInfo.Metadata.TitleUnicode); title.Text = unicode ? beatmap.BeatmapSetInfo.Metadata.TitleUnicode : beatmap.BeatmapSetInfo.Metadata.Title;
artist.Text = config.GetUnicodeString(beatmap.BeatmapSetInfo.Metadata.Artist, beatmap.BeatmapSetInfo.Metadata.ArtistUnicode); artist.Text = unicode ? beatmap.BeatmapSetInfo.Metadata.ArtistUnicode : beatmap.BeatmapSetInfo.Metadata.Artist;
} };
preferUnicode.TriggerChange();
protected override void Dispose(bool isDisposing)
{
if (preferUnicode != null)
preferUnicode.ValueChanged -= preferUnicode_changed;
base.Dispose(isDisposing);
} }
private class PanelBackground : BufferedContainer private class PanelBackground : BufferedContainer

View File

@ -13,13 +13,13 @@ namespace osu.Game.Beatmaps.Formats
{ {
private static Dictionary<string, Type> decoders { get; } = new Dictionary<string, Type>(); private static Dictionary<string, Type> decoders { get; } = new Dictionary<string, Type>();
public static BeatmapDecoder GetDecoder(TextReader stream) public static BeatmapDecoder GetDecoder(StreamReader stream)
{ {
var line = stream.ReadLine()?.Trim(); string line = stream.ReadLine()?.Trim();
if (line == null || !decoders.ContainsKey(line)) if (line == null || !decoders.ContainsKey(line))
throw new IOException(@"Unknown file format"); throw new IOException(@"Unknown file format");
return (BeatmapDecoder)Activator.CreateInstance(decoders[line]); return (BeatmapDecoder)Activator.CreateInstance(decoders[line], line);
} }
protected static void AddDecoder<T>(string magic) where T : BeatmapDecoder protected static void AddDecoder<T>(string magic) where T : BeatmapDecoder
@ -27,17 +27,17 @@ namespace osu.Game.Beatmaps.Formats
decoders[magic] = typeof(T); decoders[magic] = typeof(T);
} }
public virtual Beatmap Decode(TextReader stream) public virtual Beatmap Decode(StreamReader stream)
{ {
return ParseFile(stream); return ParseFile(stream);
} }
public virtual void Decode(TextReader stream, Beatmap beatmap) public virtual void Decode(StreamReader stream, Beatmap beatmap)
{ {
ParseFile(stream, beatmap); ParseFile(stream, beatmap);
} }
protected virtual Beatmap ParseFile(TextReader stream) protected virtual Beatmap ParseFile(StreamReader stream)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {
@ -48,9 +48,11 @@ namespace osu.Game.Beatmaps.Formats
Difficulty = new BeatmapDifficulty(), Difficulty = new BeatmapDifficulty(),
}, },
}; };
ParseFile(stream, beatmap); ParseFile(stream, beatmap);
return beatmap; return beatmap;
} }
protected abstract void ParseFile(TextReader stream, Beatmap beatmap);
protected abstract void ParseFile(StreamReader stream, Beatmap beatmap);
} }
} }

View File

@ -1,16 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.IO;
namespace osu.Game.Beatmaps.Formats
{
public class ConstructableBeatmapDecoder : BeatmapDecoder
{
protected override void ParseFile(TextReader stream, Beatmap beatmap)
{
throw new NotImplementedException();
}
}
}

View File

@ -6,7 +6,6 @@ using System.Globalization;
using System.IO; using System.IO;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Beatmaps.Events; using osu.Game.Beatmaps.Events;
using osu.Game.Beatmaps.Samples;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Modes; using osu.Game.Modes;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
@ -31,6 +30,20 @@ namespace osu.Game.Beatmaps.Formats
// TODO: Not sure how far back to go, or differences between versions // TODO: Not sure how far back to go, or differences between versions
} }
private LegacySampleBank defaultSampleBank;
private int defaultSampleVolume = 100;
private readonly int beatmapVersion;
public OsuLegacyDecoder()
{
}
public OsuLegacyDecoder(string header)
{
beatmapVersion = int.Parse(header.Substring(17));
}
private enum Section private enum Section
{ {
None, None,
@ -62,7 +75,10 @@ namespace osu.Game.Beatmaps.Formats
beatmap.BeatmapInfo.Countdown = int.Parse(val) == 1; beatmap.BeatmapInfo.Countdown = int.Parse(val) == 1;
break; break;
case @"SampleSet": case @"SampleSet":
beatmap.BeatmapInfo.SampleSet = (SampleSet)Enum.Parse(typeof(SampleSet), val); defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), val);
break;
case @"SampleVolume":
defaultSampleVolume = int.Parse(val);
break; break;
case @"StackLeniency": case @"StackLeniency":
beatmap.BeatmapInfo.StackLeniency = float.Parse(val, NumberFormatInfo.InvariantInfo); beatmap.BeatmapInfo.StackLeniency = float.Parse(val, NumberFormatInfo.InvariantInfo);
@ -192,28 +208,56 @@ namespace osu.Game.Beatmaps.Formats
private void handleTimingPoints(Beatmap beatmap, string val) private void handleTimingPoints(Beatmap beatmap, string val)
{ {
ControlPoint cp = null;
string[] split = val.Split(','); string[] split = val.Split(',');
if (split.Length > 2) double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo);
{
int effectFlags = split.Length > 7 ? Convert.ToInt32(split[7], NumberFormatInfo.InvariantInfo) : 0;
double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo);
cp = new ControlPoint
TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple;
if (split.Length >= 3)
timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]);
LegacySampleBank sampleSet = defaultSampleBank;
if (split.Length >= 4)
sampleSet = (LegacySampleBank)int.Parse(split[3]);
//SampleBank sampleBank = SampleBank.Default;
//if (split.Length >= 5)
// sampleBank = (SampleBank)int.Parse(split[4]);
int sampleVolume = defaultSampleVolume;
if (split.Length >= 6)
sampleVolume = int.Parse(split[5]);
bool timingChange = true;
if (split.Length >= 7)
timingChange = split[6][0] == '1';
bool kiaiMode = false;
bool omitFirstBarSignature = false;
if (split.Length >= 8)
{ {
Time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo), int effectFlags = int.Parse(split[7]);
BeatLength = beatLength > 0 ? beatLength : 0, kiaiMode = (effectFlags & 1) > 0;
VelocityAdjustment = beatLength < 0 ? -beatLength / 100.0 : 1, omitFirstBarSignature = (effectFlags & 8) > 0;
TimingChange = split.Length <= 6 || split[6][0] == '1',
KiaiMode = (effectFlags & 1) > 0,
OmitFirstBarLine = (effectFlags & 8) > 0,
TimeSignature = (TimeSignatures)int.Parse(split[2])
};
} }
if (cp != null) string stringSampleSet = sampleSet.ToString().ToLower();
beatmap.TimingInfo.ControlPoints.Add(cp); if (stringSampleSet == @"none")
stringSampleSet = @"normal";
beatmap.TimingInfo.ControlPoints.Add(new ControlPoint
{
Time = time,
BeatLength = beatLength,
SpeedMultiplier = beatLength < 0 ? -beatLength / 100.0 : 1,
TimingChange = timingChange,
TimeSignature = timeSignature,
SampleBank = stringSampleSet,
SampleVolume = sampleVolume,
KiaiMode = kiaiMode,
OmitFirstBarLine = omitFirstBarSignature
});
} }
private void handleColours(Beatmap beatmap, string key, string val, ref bool hasCustomColours) private void handleColours(Beatmap beatmap, string key, string val, ref bool hasCustomColours)
@ -246,32 +290,36 @@ namespace osu.Game.Beatmaps.Formats
} }
} }
protected override Beatmap ParseFile(TextReader stream) protected override Beatmap ParseFile(StreamReader stream)
{ {
return new LegacyBeatmap(base.ParseFile(stream)); return new LegacyBeatmap(base.ParseFile(stream));
} }
public override Beatmap Decode(TextReader stream) public override Beatmap Decode(StreamReader stream)
{ {
return new LegacyBeatmap(base.Decode(stream)); return new LegacyBeatmap(base.Decode(stream));
} }
protected override void ParseFile(TextReader stream, Beatmap beatmap) protected override void ParseFile(StreamReader stream, Beatmap beatmap)
{ {
HitObjectParser parser = null; beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion;
HitObjectParser parser = new LegacyHitObjectParser();
Section section = Section.None;
bool hasCustomColours = false; bool hasCustomColours = false;
var section = Section.None; string line;
while (true) while ((line = stream.ReadLine()) != null)
{ {
var line = stream.ReadLine();
if (line == null)
break;
if (string.IsNullOrEmpty(line)) if (string.IsNullOrEmpty(line))
continue; continue;
if (line.StartsWith(@"osu file format v")) if (line.StartsWith(@"osu file format v"))
{
beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17));
continue; continue;
}
if (line.StartsWith(@"[") && line.EndsWith(@"]")) if (line.StartsWith(@"[") && line.EndsWith(@"]"))
{ {
@ -290,7 +338,6 @@ namespace osu.Game.Beatmaps.Formats
{ {
case Section.General: case Section.General:
handleGeneral(beatmap, key, val); handleGeneral(beatmap, key, val);
parser = new LegacyHitObjectParser();
break; break;
case Section.Editor: case Section.Editor:
handleEditor(beatmap, key, val); handleEditor(beatmap, key, val);
@ -311,7 +358,7 @@ namespace osu.Game.Beatmaps.Formats
handleColours(beatmap, key, val, ref hasCustomColours); handleColours(beatmap, key, val, ref hasCustomColours);
break; break;
case Section.HitObjects: case Section.HitObjects:
var obj = parser?.Parse(val); var obj = parser.Parse(val);
if (obj != null) if (obj != null)
beatmap.HitObjects.Add(obj); beatmap.HitObjects.Add(obj);
@ -320,5 +367,13 @@ namespace osu.Game.Beatmaps.Formats
} }
} }
} }
internal enum LegacySampleBank
{
None = 0,
Normal = 1,
Soft = 2,
Drum = 3
}
} }
} }

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