diff --git a/osu-framework b/osu-framework
index 42ec8a62fb..bf6a3dc401 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit 42ec8a62fb697f1d967a927549dc51336cd66968
+Subproject commit bf6a3dc40176ee4f921012808070e014fc4a5779
diff --git a/osu-resources b/osu-resources
index f85c594c18..12bbab717d 160000
--- a/osu-resources
+++ b/osu-resources
@@ -1 +1 @@
-Subproject commit f85c594c182db2b01233e29ca52639b7baa00402
+Subproject commit 12bbab717d372dadbd3220d38da862276ac97e98
diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs
index 00ec215e8a..37776b329f 100644
--- a/osu.Desktop.Deploy/Program.cs
+++ b/osu.Desktop.Deploy/Program.cs
@@ -53,7 +53,7 @@ namespace osu.Desktop.Deploy
private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.nupkg";
private static string nupkgDistroFilename(string ver) => $"{PackageName}-{ver}-full.nupkg";
- private static Stopwatch sw = new Stopwatch();
+ private static readonly Stopwatch sw = new Stopwatch();
private static string codeSigningPassword;
diff --git a/osu.Desktop.Tests/BenchmarkTest.cs b/osu.Desktop.Tests/VisualTests.cs
similarity index 83%
rename from osu.Desktop.Tests/BenchmarkTest.cs
rename to osu.Desktop.Tests/VisualTests.cs
index 6d001655ec..6519dbe917 100644
--- a/osu.Desktop.Tests/BenchmarkTest.cs
+++ b/osu.Desktop.Tests/VisualTests.cs
@@ -13,10 +13,10 @@ using osu.Game.Modes.Taiko;
namespace osu.Desktop.Tests
{
[TestFixture]
- public class BenchmarkTest
+ public class VisualTests
{
[Test]
- public void TestBenchmark()
+ public void TestVisualTests()
{
using (var host = new HeadlessGameHost())
{
@@ -25,7 +25,7 @@ namespace osu.Desktop.Tests
Ruleset.Register(new ManiaRuleset());
Ruleset.Register(new CatchRuleset());
- host.Run(new Benchmark());
+ host.Run(new AutomatedVisualTestGame());
}
}
}
diff --git a/osu.Desktop.Tests/osu.Desktop.Tests.csproj b/osu.Desktop.Tests/osu.Desktop.Tests.csproj
index ad69994592..d1b20bd161 100644
--- a/osu.Desktop.Tests/osu.Desktop.Tests.csproj
+++ b/osu.Desktop.Tests/osu.Desktop.Tests.csproj
@@ -56,7 +56,7 @@
-
+
diff --git a/osu.Desktop.VisualTests/AutomatedVisualTestGame.cs b/osu.Desktop.VisualTests/AutomatedVisualTestGame.cs
new file mode 100644
index 0000000000..b08588b29c
--- /dev/null
+++ b/osu.Desktop.VisualTests/AutomatedVisualTestGame.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Testing;
+using osu.Game;
+
+namespace osu.Desktop.VisualTests
+{
+ public class AutomatedVisualTestGame : OsuGameBase
+ {
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // Have to construct this here, rather than in the constructor, because
+ // we depend on some dependencies to be loaded within OsuGameBase.load().
+ Add(new TestRunner(new TestBrowser()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Desktop.VisualTests/Beatmaps/TestWorkingBeatmap.cs b/osu.Desktop.VisualTests/Beatmaps/TestWorkingBeatmap.cs
index babca8ee06..f135a6affa 100644
--- a/osu.Desktop.VisualTests/Beatmaps/TestWorkingBeatmap.cs
+++ b/osu.Desktop.VisualTests/Beatmaps/TestWorkingBeatmap.cs
@@ -15,7 +15,7 @@ namespace osu.Desktop.VisualTests.Beatmaps
this.beatmap = beatmap;
}
- private Beatmap beatmap;
+ private readonly Beatmap beatmap;
protected override Beatmap GetBeatmap() => beatmap;
protected override Texture GetBackground() => null;
diff --git a/osu.Desktop.VisualTests/Benchmark.cs b/osu.Desktop.VisualTests/Benchmark.cs
deleted file mode 100644
index 3847b6f00f..0000000000
--- a/osu.Desktop.VisualTests/Benchmark.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using osu.Framework.Allocation;
-using osu.Framework.Screens.Testing;
-using osu.Game;
-
-namespace osu.Desktop.VisualTests
-{
- public class Benchmark : OsuGameBase
- {
- private double timePerTest = 200;
-
- [BackgroundDependencyLoader]
- private void load()
- {
- Host.MaximumDrawHz = int.MaxValue;
- Host.MaximumUpdateHz = int.MaxValue;
- Host.MaximumInactiveHz = int.MaxValue;
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- TestBrowser f = new TestBrowser();
- Add(f);
-
- Console.WriteLine($@"{Time}: Running {f.TestCount} tests for {timePerTest}ms each...");
-
- for (int i = 1; i < f.TestCount; i++)
- {
- int loadableCase = i;
- Scheduler.AddDelayed(delegate
- {
- f.LoadTest(loadableCase);
- Console.WriteLine($@"{Time}: Switching to test #{loadableCase}");
- }, loadableCase * timePerTest);
- }
-
- Scheduler.AddDelayed(Host.Exit, f.TestCount * timePerTest);
- }
- }
-}
diff --git a/osu.Desktop.VisualTests/Program.cs b/osu.Desktop.VisualTests/Program.cs
index 6760852cf0..fe1cdfd7f0 100644
--- a/osu.Desktop.VisualTests/Program.cs
+++ b/osu.Desktop.VisualTests/Program.cs
@@ -27,7 +27,7 @@ namespace osu.Desktop.VisualTests
Ruleset.Register(new CatchRuleset());
if (benchmark)
- host.Run(new Benchmark());
+ host.Run(new AutomatedVisualTestGame());
else
host.Run(new VisualTestGame());
}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs
new file mode 100644
index 0000000000..e755924a15
--- /dev/null
+++ b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs
@@ -0,0 +1,27 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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.Screens.Select;
+
+namespace osu.Desktop.VisualTests.Tests
+{
+ internal class TestCaseBeatmapDetailArea : TestCase
+ {
+ public override string Description => @"Beatmap details in song select";
+
+ public override void Reset()
+ {
+ base.Reset();
+
+ Add(new BeatmapDetailArea
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(550f, 450f),
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs
index a13f6005bf..7c211227c6 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs
@@ -3,7 +3,7 @@
using OpenTK.Graphics;
using OpenTK.Input;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Game.Graphics;
using osu.Game.Screens.Select.Options;
@@ -26,7 +26,7 @@ namespace osu.Desktop.VisualTests.Tests
Add(overlay);
- AddButton(@"Toggle", overlay.ToggleVisibility);
+ AddStep(@"Toggle", overlay.ToggleVisibility);
}
}
}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs b/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs
index 08765a9b0f..2cb63ba7a0 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs
@@ -1,7 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Framework.Graphics.Containers;
using osu.Framework.Threading;
using osu.Game.Overlays;
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs
index c9edcb8a54..90e214c3c9 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs
@@ -1,7 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Game.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
@@ -20,7 +20,7 @@ namespace osu.Desktop.VisualTests.Tests
Add(overlay = new DialogOverlay());
- AddButton("dialog #1", () => overlay.Push(new PopupDialog
+ AddStep("dialog #1", () => overlay.Push(new PopupDialog
{
Icon = FontAwesome.fa_trash_o,
HeaderText = @"Confirm deletion of",
@@ -40,7 +40,7 @@ namespace osu.Desktop.VisualTests.Tests
},
}));
- AddButton("dialog #2", () => overlay.Push(new PopupDialog
+ AddStep("dialog #2", () => overlay.Push(new PopupDialog
{
Icon = FontAwesome.fa_gear,
HeaderText = @"What do you want to do with",
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs b/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs
index 00e41de254..a0463516de 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Game.Screens.Tournament;
using osu.Game.Screens.Tournament.Teams;
using osu.Game.Users;
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs
index e876c21a12..3129cade63 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs
@@ -5,7 +5,7 @@ using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Database;
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs
index eeebc7faa8..cb7a3e3f84 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Drawables;
@@ -21,7 +21,7 @@ namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseHitObjects : TestCase
{
- private FramedClock framedClock;
+ private readonly FramedClock framedClock;
private bool auto;
@@ -34,7 +34,7 @@ namespace osu.Desktop.VisualTests.Tests
private HitObjectType mode = HitObjectType.Slider;
- private BindableNumber playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 };
+ private readonly BindableNumber playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 };
private Container playfieldContainer;
private Container approachContainer;
@@ -93,19 +93,28 @@ namespace osu.Desktop.VisualTests.Tests
playbackSpeed.TriggerChange();
- AddButton(@"circles", () => load(HitObjectType.Circle));
- AddButton(@"slider", () => load(HitObjectType.Slider));
- AddButton(@"spinner", () => load(HitObjectType.Spinner));
+ AddStep(@"circles", () => load(HitObjectType.Circle));
+ AddStep(@"slider", () => load(HitObjectType.Slider));
+ AddStep(@"spinner", () => load(HitObjectType.Spinner));
- AddToggle(@"auto", state => { auto = state; load(mode); });
+ AddToggleStep(@"auto", state => { auto = state; load(mode); });
- ButtonsContainer.Add(new SpriteText { Text = "Playback Speed" });
- ButtonsContainer.Add(new BasicSliderBar
+ Add(new Container
{
- Width = 150,
- Height = 10,
- SelectionColor = Color4.Orange,
- Bindable = playbackSpeed
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new SpriteText { Text = "Playback Speed" },
+ new BasicSliderBar
+ {
+ Width = 150,
+ Height = 10,
+ SelectionColor = Color4.Orange,
+ Bindable = playbackSpeed
+ }
+ }
});
framedClock.ProcessFrame();
@@ -136,7 +145,7 @@ namespace osu.Desktop.VisualTests.Tests
if (auto)
{
h.State = ArmedState.Hit;
- h.Judgement = new OsuJudgementInfo { Result = HitResult.Hit };
+ h.Judgement = new OsuJudgement { Result = HitResult.Hit };
}
playfieldContainer.Add(h);
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs b/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs
index 9ad439bfbe..3dba201f5d 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs
@@ -1,16 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Framework.Graphics;
using OpenTK.Input;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Configuration;
+using osu.Framework.Graphics.Containers;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.MathUtils;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Transforms;
using osu.Game.Screens.Play;
namespace osu.Desktop.VisualTests.Tests
@@ -38,19 +38,30 @@ namespace osu.Desktop.VisualTests.Tests
};
BindableInt bindable = new BindableInt { MinValue = 0, MaxValue = 200, Default = 50 };
bindable.ValueChanged += delegate { kc.FadeTime = bindable.Value; };
- AddButton("Add Random", () =>
+ AddStep("Add Random", () =>
{
Key key = (Key)((int)Key.A + RNG.Next(26));
kc.Add(new KeyCounterKeyboard(key));
});
- ButtonsContainer.Add(new SpriteText { Text = "FadeTime" });
- ButtonsContainer.Add(new TestSliderBar
+
+ Add(new Container
{
- Width = 150,
- Height = 10,
- SelectionColor = Color4.Orange,
- Bindable = bindable
+ Anchor = Anchor.TopRight,
+ Origin = Anchor.TopRight,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new SpriteText { Text = "FadeTime" },
+ new TestSliderBar
+ {
+ Width = 150,
+ Height = 10,
+ SelectionColor = Color4.Orange,
+ Bindable = bindable
+ }
+ }
});
+
Add(kc);
}
private class TestSliderBar : SliderBar where T : struct
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs b/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs
index cc30e3de95..44e52c237e 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs
@@ -3,10 +3,10 @@
using OpenTK;
using osu.Framework.Graphics;
-using osu.Framework.Screens.Testing;
-using osu.Game.Modes;
+using osu.Framework.Testing;
using osu.Game.Modes.Mods;
using osu.Game.Modes.Osu.Mods;
+using osu.Game.Modes.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
@@ -218,7 +218,7 @@ namespace osu.Desktop.VisualTests.Tests
Size = new Vector2(550f, 450f),
});
- AddButton(@"New Scores", newScores);
+ AddStep(@"New Scores", newScores);
newScores();
}
}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs b/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs
index 36dc3945e2..ddb62598cf 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs
@@ -1,7 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Sprites;
using osu.Game.Screens.Menu;
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs
index eaaa531691..7677682ac8 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs
@@ -3,7 +3,7 @@
using osu.Framework.Graphics;
using osu.Game.Overlays.Mods;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Game.Modes;
namespace osu.Desktop.VisualTests.Tests
@@ -25,11 +25,11 @@ namespace osu.Desktop.VisualTests.Tests
Anchor = Anchor.BottomCentre,
});
- AddButton("Toggle", modSelect.ToggleVisibility);
- AddButton("osu!", () => modSelect.PlayMode.Value = PlayMode.Osu);
- AddButton("osu!taiko", () => modSelect.PlayMode.Value = PlayMode.Taiko);
- AddButton("osu!catch", () => modSelect.PlayMode.Value = PlayMode.Catch);
- AddButton("osu!mania", () => modSelect.PlayMode.Value = PlayMode.Mania);
+ AddStep("Toggle", modSelect.ToggleVisibility);
+ AddStep("osu!", () => modSelect.PlayMode.Value = PlayMode.Osu);
+ AddStep("osu!taiko", () => modSelect.PlayMode.Value = PlayMode.Taiko);
+ AddStep("osu!catch", () => modSelect.PlayMode.Value = PlayMode.Catch);
+ AddStep("osu!mania", () => modSelect.PlayMode.Value = PlayMode.Mania);
}
}
}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs b/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs
index f44f662321..c0c17cd50e 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs
@@ -1,7 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Framework.Graphics;
using osu.Framework.Timing;
using osu.Game.Overlays;
@@ -30,7 +30,7 @@ namespace osu.Desktop.VisualTests.Tests
Anchor = Anchor.Centre
};
Add(mc);
- AddToggle(@"Show", state => mc.State = state ? Visibility.Visible : Visibility.Hidden);
+ AddToggleStep(@"Show", state => mc.State = state ? Visibility.Visible : Visibility.Hidden);
}
}
}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs b/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs
index c3a9064e69..8972040b06 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs
@@ -3,7 +3,7 @@
using System.Collections.Generic;
using osu.Framework.Graphics;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Framework.MathUtils;
using osu.Game.Overlays;
using System.Linq;
@@ -30,13 +30,13 @@ namespace osu.Desktop.VisualTests.Tests
Origin = Anchor.TopRight,
});
- AddToggle(@"show", state => manager.State = state ? Visibility.Visible : Visibility.Hidden);
+ AddToggleStep(@"show", state => manager.State = state ? Visibility.Visible : Visibility.Hidden);
- AddButton(@"simple #1", sendNotification1);
- AddButton(@"simple #2", sendNotification2);
- AddButton(@"progress #1", sendProgress1);
- AddButton(@"progress #2", sendProgress2);
- AddButton(@"barrage", () => sendBarrage());
+ AddStep(@"simple #1", sendNotification1);
+ AddStep(@"simple #2", sendNotification2);
+ AddStep(@"progress #1", sendProgress1);
+ AddStep(@"progress #2", sendProgress2);
+ AddStep(@"barrage", () => sendBarrage());
}
private void sendBarrage(int remaining = 100)
@@ -95,7 +95,7 @@ namespace osu.Desktop.VisualTests.Tests
progressingNotifications.Add(n);
}
- private List progressingNotifications = new List();
+ private readonly List progressingNotifications = new List();
private void sendProgress1()
{
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseOptions.cs b/osu.Desktop.VisualTests/Tests/TestCaseOptions.cs
index 1b4ecd726a..ff6bdc8a5a 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseOptions.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseOptions.cs
@@ -1,7 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Game.Overlays;
namespace osu.Desktop.VisualTests.Tests
diff --git a/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs
index ad8039bc66..ebf6e0c350 100644
--- a/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Logging;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Game.Screens.Play;
namespace osu.Desktop.VisualTests.Tests
@@ -25,8 +25,8 @@ namespace osu.Desktop.VisualTests.Tests
OnRetry = () => Logger.Log(@"Retry"),
OnQuit = () => Logger.Log(@"Quit")
});
- AddButton("Pause", pauseOverlay.Show);
- AddButton("Add Retry", delegate
+ AddStep("Pause", pauseOverlay.Show);
+ AddStep("Add Retry", delegate
{
retryCount++;
pauseOverlay.Retries = retryCount;
diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs
index c97ea929f3..aedab7e895 100644
--- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs
@@ -3,7 +3,7 @@
using System.Collections.Generic;
using osu.Desktop.VisualTests.Platform;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Framework.MathUtils;
using osu.Game.Database;
using osu.Game.Modes;
@@ -23,12 +23,10 @@ namespace osu.Desktop.VisualTests.Tests
public override void Reset()
{
base.Reset();
- oldDb = Dependencies.Get();
if (db == null)
{
storage = new TestStorage(@"TestCasePlaySongSelect");
db = new BeatmapDatabase(storage);
- Dependencies.Cache(db, true);
var sets = new List();
@@ -40,19 +38,16 @@ namespace osu.Desktop.VisualTests.Tests
Add(songSelect = new PlaySongSelect());
- AddButton(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; });
- AddButton(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; });
- AddButton(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; });
- AddButton(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
+ AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; });
+ AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; });
+ AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; });
+ AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
}
protected override void Dispose(bool isDisposing)
{
if (oldDb != null)
- {
- Dependencies.Cache(oldDb, true);
db = null;
- }
base.Dispose(isDisposing);
}
diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs
index a08cb3e97e..f36889b02a 100644
--- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs
@@ -4,7 +4,7 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Game.Beatmaps;
using OpenTK;
using osu.Framework.Graphics.Sprites;
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs b/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs
index be313efed3..55fc969217 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs
@@ -6,7 +6,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Modes.UI;
@@ -68,7 +68,7 @@ namespace osu.Desktop.VisualTests.Tests
};
Add(starsLabel);
- AddButton(@"Reset all", delegate
+ AddStep(@"Reset all", delegate
{
score.Current.Value = 0;
comboCounter.Current.Value = 0;
@@ -78,7 +78,7 @@ namespace osu.Desktop.VisualTests.Tests
starsLabel.Text = stars.Count.ToString("0.00");
});
- AddButton(@"Hit! :D", delegate
+ AddStep(@"Hit! :D", delegate
{
score.Current.Value += 300 + (ulong)(300.0 * (comboCounter.Current > 0 ? comboCounter.Current - 1 : 0) / 25.0);
comboCounter.Increment();
@@ -86,20 +86,20 @@ namespace osu.Desktop.VisualTests.Tests
accuracyCounter.SetFraction(numerator, denominator);
});
- AddButton(@"miss...", delegate
+ AddStep(@"miss...", delegate
{
comboCounter.Current.Value = 0;
denominator++;
accuracyCounter.SetFraction(numerator, denominator);
});
- AddButton(@"Alter stars", delegate
+ AddStep(@"Alter stars", delegate
{
stars.Count = RNG.NextSingle() * (stars.StarCount + 1);
starsLabel.Text = stars.Count.ToString("0.00");
});
- AddButton(@"Stop counters", delegate
+ AddStep(@"Stop counters", delegate
{
score.StopRolling();
comboCounter.StopRolling();
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs b/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs
index da807d5e53..23e7f8a74d 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseTabControl.cs
@@ -3,7 +3,7 @@
using OpenTK;
using osu.Framework.Graphics.Primitives;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Select.Filter;
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs
new file mode 100644
index 0000000000..4005c94b5a
--- /dev/null
+++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs
@@ -0,0 +1,135 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK;
+using OpenTK.Graphics;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
+using osu.Game.Graphics;
+using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
+
+namespace osu.Desktop.VisualTests.Tests
+{
+ internal class TestCaseTaikoHitObjects : TestCase
+ {
+ public override string Description => "Taiko hit objects";
+
+ private bool kiai;
+
+ public override void Reset()
+ {
+ base.Reset();
+
+ AddToggleStep("Kiai", b =>
+ {
+ kiai = !kiai;
+ Reset();
+ });
+
+ Add(new CirclePiece
+ {
+ Position = new Vector2(100, 100),
+ Width = 0,
+ AccentColour = Color4.DarkRed,
+ KiaiMode = kiai,
+ Children = new[]
+ {
+ new CentreHitSymbolPiece()
+ }
+ });
+
+ Add(new StrongCirclePiece
+ {
+ Position = new Vector2(350, 100),
+ Width = 0,
+ AccentColour = Color4.DarkRed,
+ KiaiMode = kiai,
+ Children = new[]
+ {
+ new CentreHitSymbolPiece()
+ }
+ });
+
+ Add(new CirclePiece
+ {
+ Position = new Vector2(100, 300),
+ Width = 0,
+ AccentColour = Color4.DarkBlue,
+ KiaiMode = kiai,
+ Children = new[]
+ {
+ new RimHitSymbolPiece()
+ }
+ });
+
+ Add(new StrongCirclePiece
+ {
+ Position = new Vector2(350, 300),
+ Width = 0,
+ AccentColour = Color4.DarkBlue,
+ KiaiMode = kiai,
+ Children = new[]
+ {
+ new RimHitSymbolPiece()
+ }
+ });
+
+ Add(new CirclePiece
+ {
+ Position = new Vector2(100, 500),
+ Width = 0,
+ AccentColour = Color4.Orange,
+ KiaiMode = kiai,
+ Children = new[]
+ {
+ new SwellSymbolPiece()
+ }
+ });
+
+ Add(new DrumRollCircle(new CirclePiece
+ {
+ KiaiMode = kiai
+ })
+ {
+ Width = 250,
+ Position = new Vector2(575, 100)
+ });
+
+ Add(new DrumRollCircle(new StrongCirclePiece
+ {
+ KiaiMode = kiai
+ })
+ {
+ Width = 250,
+ Position = new Vector2(575, 300)
+ });
+ }
+
+ private class DrumRollCircle : BaseCircle
+ {
+ public DrumRollCircle(CirclePiece piece)
+ : base(piece)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Piece.AccentColour = colours.YellowDark;
+ }
+ }
+
+ private abstract class BaseCircle : Container
+ {
+ protected readonly CirclePiece Piece;
+
+ protected BaseCircle(CirclePiece piece)
+ {
+ Piece = piece;
+
+ Add(Piece);
+ }
+ }
+ }
+}
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs
index 30350f9a39..99ca025ae4 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs
@@ -1,8 +1,11 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// 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.Primitives;
using osu.Framework.MathUtils;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Judgements;
@@ -27,15 +30,24 @@ namespace osu.Desktop.VisualTests.Tests
{
base.Reset();
- Clock.ProcessFrame();
-
- AddButton("Hit!", addHitJudgement);
- AddButton("Miss :(", addMissJudgement);
+ AddStep("Hit!", addHitJudgement);
+ AddStep("Miss :(", addMissJudgement);
+ AddStep("Swell", addSwell);
+ AddStep("Centre", () => addCentreHit(false));
+ AddStep("Strong Centre", () => addCentreHit(true));
AddButton("Add bar line", addBarLine);
+ AddStep("Rim", () => addRimHit(false));
+ AddStep("Strong Rim", () => addRimHit(true));
- Add(playfield = new TaikoPlayfield
+ Add(new Container
{
- Y = 200
+ RelativeSizeAxes = Axes.X,
+ Y = 200,
+ Padding = new MarginPadding { Left = 200 },
+ Children = new[]
+ {
+ playfield = new TaikoPlayfield()
+ }
});
}
@@ -43,15 +55,14 @@ namespace osu.Desktop.VisualTests.Tests
{
TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great;
- playfield.OnJudgement(new DrawableTestHit(new TaikoHitObject())
+ playfield.OnJudgement(new DrawableTestHit(new Hit())
{
X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f),
- Judgement = new TaikoJudgementInfo
+ Judgement = new TaikoJudgement
{
Result = HitResult.Hit,
TaikoResult = hitResult,
TimeOffset = 0,
- ComboAtHit = 1,
SecondHit = RNG.Next(10) == 0
}
});
@@ -59,13 +70,12 @@ namespace osu.Desktop.VisualTests.Tests
private void addMissJudgement()
{
- playfield.OnJudgement(new DrawableTestHit(new TaikoHitObject())
+ playfield.OnJudgement(new DrawableTestHit(new Hit())
{
- Judgement = new TaikoJudgementInfo
+ Judgement = new TaikoJudgement
{
Result = HitResult.Miss,
- TimeOffset = 0,
- ComboAtHit = 0
+ TimeOffset = 0
}
});
}
@@ -83,14 +93,52 @@ namespace osu.Desktop.VisualTests.Tests
playfield.AddBarLine(isMajor ? new DrawableMajorBarLine(bl) : new DrawableBarLine(bl));
}
- private class DrawableTestHit : DrawableHitObject
+ private void addSwell()
+ {
+ playfield.Add(new DrawableSwell(new Swell
+ {
+ StartTime = Time.Current + 1000,
+ EndTime = Time.Current + 5000,
+ PreEmpt = 1000
+ }));
+ }
+
+ private void addCentreHit(bool strong)
+ {
+ Hit h = new Hit
+ {
+ StartTime = Time.Current + 1000,
+ PreEmpt = 1000
+ };
+
+ if (strong)
+ playfield.Add(new DrawableStrongCentreHit(h));
+ else
+ playfield.Add(new DrawableCentreHit(h));
+ }
+
+ private void addRimHit(bool strong)
+ {
+ Hit h = new Hit
+ {
+ StartTime = Time.Current + 1000,
+ PreEmpt = 1000
+ };
+
+ if (strong)
+ playfield.Add(new DrawableStrongRimHit(h));
+ else
+ playfield.Add(new DrawableRimHit(h));
+ }
+
+ private class DrawableTestHit : DrawableHitObject
{
public DrawableTestHit(TaikoHitObject hitObject)
: base(hitObject)
{
}
- protected override TaikoJudgementInfo CreateJudgementInfo() => new TaikoJudgementInfo();
+ protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
protected override void UpdateState(ArmedState state)
{
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs b/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs
index 3ba657d60a..7182ee7c06 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs b/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs
index 4694a6c6ea..2427b6d12c 100644
--- a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs
+++ b/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs
@@ -1,7 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Play;
diff --git a/osu.Desktop.VisualTests/VisualTestGame.cs b/osu.Desktop.VisualTests/VisualTestGame.cs
index c41bdeef66..0392dc5443 100644
--- a/osu.Desktop.VisualTests/VisualTestGame.cs
+++ b/osu.Desktop.VisualTests/VisualTestGame.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Platform;
-using osu.Framework.Screens.Testing;
+using osu.Framework.Testing;
using osu.Game;
using osu.Game.Screens.Backgrounds;
@@ -24,6 +24,11 @@ namespace osu.Desktop.VisualTests
public override void SetHost(GameHost host)
{
base.SetHost(host);
+
+ host.UpdateThread.InactiveHz = host.UpdateThread.ActiveHz;
+ host.DrawThread.InactiveHz = host.DrawThread.ActiveHz;
+ host.InputThread.InactiveHz = host.InputThread.ActiveHz;
+
host.Window.CursorState = CursorState.Hidden;
}
}
diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj
index da5d8dba43..1baf322750 100644
--- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj
+++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj
@@ -150,6 +150,10 @@
{65dc628f-a640-4111-ab35-3a5652bc1e17}
osu.Framework.Desktop
+
+ {007b2356-ab6f-4bd9-96d5-116fc2dce69a}
+ osu.Framework.Testing
+
{c76bf5b3-985e-4d39-95fe-97c9c879b83a}
osu.Framework
@@ -180,7 +184,7 @@
-
+
@@ -194,6 +198,7 @@
+
@@ -207,12 +212,11 @@
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index da3e7b704a..95870125e3 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -17,7 +17,7 @@ namespace osu.Desktop
{
internal class OsuGameDesktop : OsuGame
{
- private VersionManager versionManager;
+ private readonly VersionManager versionManager;
public OsuGameDesktop(string[] args = null)
: base(args)
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 934004dfde..fbc342d695 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -123,7 +123,10 @@
$(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll
True
-
+
+ $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll
+ True
+
$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll
True
@@ -150,6 +153,7 @@
osu.licenseheader
+
diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config
index bdeaf1de89..be9b65f0c6 100644
--- a/osu.Desktop/packages.config
+++ b/osu.Desktop/packages.config
@@ -7,6 +7,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
+
\ No newline at end of file
diff --git a/osu.Game.Modes.Catch/CatchRuleset.cs b/osu.Game.Modes.Catch/CatchRuleset.cs
index 50224e3fdb..09d8bdb9e5 100644
--- a/osu.Game.Modes.Catch/CatchRuleset.cs
+++ b/osu.Game.Modes.Catch/CatchRuleset.cs
@@ -10,6 +10,8 @@ using osu.Game.Modes.Mods;
using osu.Game.Modes.UI;
using osu.Game.Screens.Play;
using System.Collections.Generic;
+using osu.Game.Modes.Catch.Scoring;
+using osu.Game.Modes.Scoring;
namespace osu.Game.Modes.Catch
{
diff --git a/osu.Game.Modes.Catch/Judgements/CatchJudgementInfo.cs b/osu.Game.Modes.Catch/Judgements/CatchJudgement.cs
similarity index 57%
rename from osu.Game.Modes.Catch/Judgements/CatchJudgementInfo.cs
rename to osu.Game.Modes.Catch/Judgements/CatchJudgement.cs
index 33e84d2f97..eaacedd7e0 100644
--- a/osu.Game.Modes.Catch/Judgements/CatchJudgementInfo.cs
+++ b/osu.Game.Modes.Catch/Judgements/CatchJudgement.cs
@@ -5,7 +5,10 @@ using osu.Game.Modes.Judgements;
namespace osu.Game.Modes.Catch.Judgements
{
- public class CatchJudgementInfo : JudgementInfo
+ public class CatchJudgement : Judgement
{
+ public override string ResultString => string.Empty;
+
+ public override string MaxResultString => string.Empty;
}
}
diff --git a/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs
index 5d19d902b1..885048b938 100644
--- a/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs
+++ b/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Modes.Catch.Objects.Drawable
{
internal class DrawableFruit : Sprite
{
- private CatchBaseHit h;
+ private readonly CatchBaseHit h;
public DrawableFruit(CatchBaseHit h)
{
@@ -29,7 +29,7 @@ namespace osu.Game.Modes.Catch.Objects.Drawable
{
Texture = textures.Get(@"Menu/logo");
- double duration = 0;
+ const double duration = 0;
Transforms.Add(new TransformPosition { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = new Vector2(h.Position, -0.1f), EndValue = new Vector2(h.Position, 0.9f) });
Transforms.Add(new TransformAlpha { StartTime = h.StartTime + duration + 200, EndTime = h.StartTime + duration + 400, StartValue = 1, EndValue = 0 });
diff --git a/osu.Game.Modes.Catch/CatchScoreProcessor.cs b/osu.Game.Modes.Catch/Scoring/CatchScoreProcessor.cs
similarity index 71%
rename from osu.Game.Modes.Catch/CatchScoreProcessor.cs
rename to osu.Game.Modes.Catch/Scoring/CatchScoreProcessor.cs
index be4ae8b799..1b9bedf7fb 100644
--- a/osu.Game.Modes.Catch/CatchScoreProcessor.cs
+++ b/osu.Game.Modes.Catch/Scoring/CatchScoreProcessor.cs
@@ -3,22 +3,23 @@
using osu.Game.Modes.Catch.Judgements;
using osu.Game.Modes.Catch.Objects;
+using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI;
-namespace osu.Game.Modes.Catch
+namespace osu.Game.Modes.Catch.Scoring
{
- internal class CatchScoreProcessor : ScoreProcessor
+ internal class CatchScoreProcessor : ScoreProcessor
{
public CatchScoreProcessor()
{
}
- public CatchScoreProcessor(HitRenderer hitRenderer)
+ public CatchScoreProcessor(HitRenderer hitRenderer)
: base(hitRenderer)
{
}
- protected override void UpdateCalculations(CatchJudgementInfo newJudgement)
+ protected override void OnNewJudgement(CatchJudgement judgement)
{
}
}
diff --git a/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs b/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs
index c02b6b6c49..90bd61a39f 100644
--- a/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs
+++ b/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs
@@ -5,12 +5,14 @@ using osu.Game.Beatmaps;
using osu.Game.Modes.Catch.Beatmaps;
using osu.Game.Modes.Catch.Judgements;
using osu.Game.Modes.Catch.Objects;
+using osu.Game.Modes.Catch.Scoring;
using osu.Game.Modes.Objects.Drawables;
+using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI;
namespace osu.Game.Modes.Catch.UI
{
- public class CatchHitRenderer : HitRenderer
+ public class CatchHitRenderer : HitRenderer
{
public CatchHitRenderer(WorkingBeatmap beatmap)
: base(beatmap)
@@ -23,8 +25,8 @@ namespace osu.Game.Modes.Catch.UI
protected override IBeatmapProcessor CreateBeatmapProcessor() => new CatchBeatmapProcessor();
- protected override Playfield CreatePlayfield() => new CatchPlayfield();
+ protected override Playfield CreatePlayfield() => new CatchPlayfield();
- protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h) => null;
+ protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h) => null;
}
}
diff --git a/osu.Game.Modes.Catch/UI/CatchPlayfield.cs b/osu.Game.Modes.Catch/UI/CatchPlayfield.cs
index eba8734eaf..cf1a665470 100644
--- a/osu.Game.Modes.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Modes.Catch/UI/CatchPlayfield.cs
@@ -10,7 +10,7 @@ using osu.Game.Modes.Catch.Judgements;
namespace osu.Game.Modes.Catch.UI
{
- public class CatchPlayfield : Playfield
+ public class CatchPlayfield : Playfield
{
public CatchPlayfield()
{
diff --git a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj
index a32416173d..593d8db4f6 100644
--- a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj
+++ b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj
@@ -50,8 +50,8 @@
-
-
+
+
diff --git a/osu.Game.Modes.Mania/Judgements/ManiaJudgementInfo.cs b/osu.Game.Modes.Mania/Judgements/ManiaJudgement.cs
similarity index 57%
rename from osu.Game.Modes.Mania/Judgements/ManiaJudgementInfo.cs
rename to osu.Game.Modes.Mania/Judgements/ManiaJudgement.cs
index a75f95abe7..3ef5b0f29b 100644
--- a/osu.Game.Modes.Mania/Judgements/ManiaJudgementInfo.cs
+++ b/osu.Game.Modes.Mania/Judgements/ManiaJudgement.cs
@@ -5,7 +5,10 @@ using osu.Game.Modes.Judgements;
namespace osu.Game.Modes.Mania.Judgements
{
- public class ManiaJudgementInfo : JudgementInfo
+ public class ManiaJudgement : Judgement
{
+ public override string ResultString => string.Empty;
+
+ public override string MaxResultString => string.Empty;
}
}
diff --git a/osu.Game.Modes.Mania/ManiaRuleset.cs b/osu.Game.Modes.Mania/ManiaRuleset.cs
index 27b3fcdf60..bd995d87d6 100644
--- a/osu.Game.Modes.Mania/ManiaRuleset.cs
+++ b/osu.Game.Modes.Mania/ManiaRuleset.cs
@@ -9,6 +9,8 @@ using osu.Game.Modes.Mods;
using osu.Game.Modes.UI;
using osu.Game.Screens.Play;
using System.Collections.Generic;
+using osu.Game.Modes.Mania.Scoring;
+using osu.Game.Modes.Scoring;
namespace osu.Game.Modes.Mania
{
diff --git a/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs b/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs
index 9fd39e2f45..76999cef21 100644
--- a/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs
+++ b/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Modes.Mania.Objects.Drawable
{
Texture = textures.Get(@"Menu/logo");
- double duration = 0;
+ const double duration = 0;
Transforms.Add(new TransformPositionY { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f });
Transforms.Add(new TransformAlpha { StartTime = note.StartTime + duration + 200, EndTime = note.StartTime + duration + 400, StartValue = 1, EndValue = 0 });
diff --git a/osu.Game.Modes.Mania/ManiaScoreProcessor.cs b/osu.Game.Modes.Mania/Scoring/ManiaScoreProcessor.cs
similarity index 71%
rename from osu.Game.Modes.Mania/ManiaScoreProcessor.cs
rename to osu.Game.Modes.Mania/Scoring/ManiaScoreProcessor.cs
index 60ed336c8e..0f87030e25 100644
--- a/osu.Game.Modes.Mania/ManiaScoreProcessor.cs
+++ b/osu.Game.Modes.Mania/Scoring/ManiaScoreProcessor.cs
@@ -3,22 +3,23 @@
using osu.Game.Modes.Mania.Judgements;
using osu.Game.Modes.Mania.Objects;
+using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI;
-namespace osu.Game.Modes.Mania
+namespace osu.Game.Modes.Mania.Scoring
{
- internal class ManiaScoreProcessor : ScoreProcessor
+ internal class ManiaScoreProcessor : ScoreProcessor
{
public ManiaScoreProcessor()
{
}
- public ManiaScoreProcessor(HitRenderer hitRenderer)
+ public ManiaScoreProcessor(HitRenderer hitRenderer)
: base(hitRenderer)
{
}
- protected override void UpdateCalculations(ManiaJudgementInfo newJudgement)
+ protected override void OnNewJudgement(ManiaJudgement judgement)
{
}
}
diff --git a/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs
index 0f26c944a0..0415bc961a 100644
--- a/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs
+++ b/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs
@@ -5,12 +5,14 @@ using osu.Game.Beatmaps;
using osu.Game.Modes.Mania.Beatmaps;
using osu.Game.Modes.Mania.Judgements;
using osu.Game.Modes.Mania.Objects;
+using osu.Game.Modes.Mania.Scoring;
using osu.Game.Modes.Objects.Drawables;
+using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI;
namespace osu.Game.Modes.Mania.UI
{
- public class ManiaHitRenderer : HitRenderer
+ public class ManiaHitRenderer : HitRenderer
{
private readonly int columns;
@@ -26,8 +28,8 @@ namespace osu.Game.Modes.Mania.UI
protected override IBeatmapProcessor CreateBeatmapProcessor() => new ManiaBeatmapProcessor();
- protected override Playfield CreatePlayfield() => new ManiaPlayfield(columns);
+ protected override Playfield CreatePlayfield() => new ManiaPlayfield(columns);
- protected override DrawableHitObject GetVisualRepresentation(ManiaBaseHit h) => null;
+ protected override DrawableHitObject GetVisualRepresentation(ManiaBaseHit h) => null;
}
}
diff --git a/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs b/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs
index 3984fce626..670d18f71f 100644
--- a/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs
+++ b/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs
@@ -11,7 +11,7 @@ using osu.Game.Modes.Mania.Judgements;
namespace osu.Game.Modes.Mania.UI
{
- public class ManiaPlayfield : Playfield
+ public class ManiaPlayfield : Playfield
{
public ManiaPlayfield(int columns)
{
diff --git a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj
index 8b78929510..cc925d417a 100644
--- a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj
+++ b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj
@@ -49,9 +49,9 @@
-
+
-
+
diff --git a/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs
index ea5143b08a..fec675be54 100644
--- a/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs
+++ b/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs
@@ -44,11 +44,8 @@ namespace osu.Game.Modes.Osu.Beatmaps
{
StartTime = original.StartTime,
Sample = original.Sample,
-
CurveObject = curveData,
-
Position = positionData?.Position ?? Vector2.Zero,
-
NewCombo = comboData?.NewCombo ?? false
};
}
@@ -60,7 +57,6 @@ namespace osu.Game.Modes.Osu.Beatmaps
StartTime = original.StartTime,
Sample = original.Sample,
Position = new Vector2(512, 384) / 2,
-
EndTime = endTimeData.EndTime
};
}
@@ -69,9 +65,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
{
StartTime = original.StartTime,
Sample = original.Sample,
-
Position = positionData?.Position ?? Vector2.Zero,
-
NewCombo = comboData?.NewCombo ?? false
};
}
@@ -81,7 +75,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
if (endIndex == -1)
endIndex = hitObjects.Count - 1;
- int stackDistance = 3;
+ const int stack_distance = 3;
float stackThreshold = DrawableOsuHitObject.TIME_PREEMPT * stackLeniency;
// Reset stacking inside the update range
@@ -108,8 +102,8 @@ namespace osu.Game.Modes.Osu.Beatmaps
//We are no longer within stacking range of the next object.
break;
- if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stackDistance ||
- stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stackDistance)
+ if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
+ stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
{
stackBaseIndex = n;
@@ -174,14 +168,14 @@ namespace osu.Game.Modes.Osu.Beatmaps
* o <- hitCircle has stack of -1
* o <- hitCircle has stack of -2
*/
- if (objectN is Slider && Vector2.Distance(objectN.EndPosition, objectI.Position) < stackDistance)
+ if (objectN is Slider && Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{
int offset = objectI.StackHeight - objectN.StackHeight + 1;
for (int j = n + 1; j <= i; j++)
{
//For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above).
OsuHitObject objectJ = hitObjects[j];
- if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stackDistance)
+ if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stack_distance)
objectJ.StackHeight -= offset;
}
@@ -190,7 +184,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
break;
}
- if (Vector2.Distance(objectN.Position, objectI.Position) < stackDistance)
+ if (Vector2.Distance(objectN.Position, objectI.Position) < stack_distance)
{
//Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out.
//NOTE: Sliders with start positions stacking are a special case that is also handled here.
@@ -214,7 +208,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
//We are no longer within stacking range of the previous object.
break;
- if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stackDistance)
+ if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{
objectN.StackHeight = objectI.StackHeight + 1;
objectI = objectN;
diff --git a/osu.Game.Modes.Osu/Judgements/OsuJudgementInfo.cs b/osu.Game.Modes.Osu/Judgements/OsuJudgement.cs
similarity index 83%
rename from osu.Game.Modes.Osu/Judgements/OsuJudgementInfo.cs
rename to osu.Game.Modes.Osu/Judgements/OsuJudgement.cs
index 20d36efe55..e65d3dde3a 100644
--- a/osu.Game.Modes.Osu/Judgements/OsuJudgementInfo.cs
+++ b/osu.Game.Modes.Osu/Judgements/OsuJudgement.cs
@@ -4,10 +4,11 @@
using OpenTK;
using osu.Game.Modes.Judgements;
using osu.Game.Modes.Osu.Objects.Drawables;
+using osu.Framework.Extensions;
namespace osu.Game.Modes.Osu.Judgements
{
- public class OsuJudgementInfo : JudgementInfo
+ public class OsuJudgement : Judgement
{
///
/// The positional hit offset.
@@ -24,6 +25,10 @@ namespace osu.Game.Modes.Osu.Judgements
///
public OsuScoreResult MaxScore = OsuScoreResult.Hit300;
+ public override string ResultString => Score.GetDescription();
+
+ public override string MaxResultString => MaxScore.GetDescription();
+
public int ScoreValue => scoreToInt(Score);
public int MaxScoreValue => scoreToInt(MaxScore);
diff --git a/osu.Game.Modes.Osu/Mods/OsuMod.cs b/osu.Game.Modes.Osu/Mods/OsuMod.cs
index e80975aed1..db2ee26b7a 100644
--- a/osu.Game.Modes.Osu/Mods/OsuMod.cs
+++ b/osu.Game.Modes.Osu/Mods/OsuMod.cs
@@ -7,6 +7,7 @@ using osu.Game.Modes.Mods;
using osu.Game.Modes.Osu.Objects;
using System;
using System.Linq;
+using osu.Game.Modes.Scoring;
namespace osu.Game.Modes.Osu.Mods
{
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPoint.cs
index 935f3e01fd..efbd5b291a 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPoint.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPoint.cs
@@ -7,7 +7,6 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Transforms;
namespace osu.Game.Modes.Osu.Objects.Drawables.Connections
{
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs
index e8c74d4f8d..68c5ec0a45 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -3,7 +3,6 @@
using System;
using osu.Framework.Graphics;
-using osu.Framework.Graphics.Transforms;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Objects.Drawables.Pieces;
using OpenTK;
@@ -13,37 +12,33 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{
public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
{
- private OsuHitObject osuObject;
-
public ApproachCircle ApproachCircle;
- private CirclePiece circle;
- private RingPiece ring;
- private FlashPiece flash;
- private ExplodePiece explode;
- private NumberPiece number;
- private GlowPiece glow;
+ private readonly CirclePiece circle;
+ private readonly RingPiece ring;
+ private readonly FlashPiece flash;
+ private readonly ExplodePiece explode;
+ private readonly NumberPiece number;
+ private readonly GlowPiece glow;
public DrawableHitCircle(OsuHitObject h) : base(h)
{
Origin = Anchor.Centre;
- osuObject = h;
-
- Position = osuObject.StackedPosition;
- Scale = new Vector2(osuObject.Scale);
+ Position = HitObject.StackedPosition;
+ Scale = new Vector2(HitObject.Scale);
Children = new Drawable[]
{
glow = new GlowPiece
{
- Colour = osuObject.ComboColour
+ Colour = AccentColour
},
circle = new CirclePiece
{
- Colour = osuObject.ComboColour,
+ Colour = AccentColour,
Hit = () =>
{
- if (Judgement.Result.HasValue) return false;
+ if (Judgement.Result != HitResult.None) return false;
Judgement.PositionOffset = Vector2.Zero; //todo: set to correct value
UpdateJudgement(true);
@@ -58,11 +53,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
flash = new FlashPiece(),
explode = new ExplodePiece
{
- Colour = osuObject.ComboColour,
+ Colour = AccentColour,
},
ApproachCircle = new ApproachCircle
{
- Colour = osuObject.ComboColour,
+ Colour = AccentColour,
}
};
@@ -115,8 +110,8 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
ApproachCircle.FadeOut();
- double endTime = (osuObject as IHasEndTime)?.EndTime ?? osuObject.StartTime;
- double duration = endTime - osuObject.StartTime;
+ double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
+ double duration = endTime - HitObject.StartTime;
glow.Delay(duration);
glow.FadeOut(400);
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index b2af678cae..816faa0d98 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -7,18 +7,19 @@ using osu.Game.Modes.Osu.Judgements;
namespace osu.Game.Modes.Osu.Objects.Drawables
{
- public class DrawableOsuHitObject : DrawableHitObject
+ public class DrawableOsuHitObject : DrawableHitObject
{
public const float TIME_PREEMPT = 600;
public const float TIME_FADEIN = 400;
public const float TIME_FADEOUT = 500;
- public DrawableOsuHitObject(OsuHitObject hitObject)
+ protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject)
{
+ AccentColour = HitObject.ComboColour;
}
- protected override OsuJudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.Hit300 };
+ protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.Hit300 };
protected override void UpdateState(ArmedState state)
{
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuJudgement.cs
new file mode 100644
index 0000000000..647c8faef8
--- /dev/null
+++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuJudgement.cs
@@ -0,0 +1,26 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Game.Modes.Objects.Drawables;
+using osu.Game.Modes.Osu.Judgements;
+using OpenTK;
+using osu.Game.Modes.Judgements;
+
+namespace osu.Game.Modes.Osu.Objects.Drawables
+{
+ public class DrawableOsuJudgement : DrawableJudgement
+ {
+ public DrawableOsuJudgement(OsuJudgement judgement) : base(judgement)
+ {
+ }
+
+ protected override void LoadComplete()
+ {
+ if (Judgement.Result != HitResult.Miss)
+ JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint);
+
+ base.LoadComplete();
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs
index a2a52c7d94..e8f2154d7f 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs
@@ -13,18 +13,18 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{
public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
{
- private Slider slider;
+ private readonly Slider slider;
- private DrawableHitCircle initialCircle;
+ private readonly DrawableHitCircle initialCircle;
- private List components = new List();
+ private readonly List components = new List();
- private Container ticks;
+ private readonly Container ticks;
- private SliderBody body;
- private SliderBall ball;
+ private readonly SliderBody body;
+ private readonly SliderBall ball;
- private SliderBouncer bouncer2;
+ private readonly SliderBouncer bouncer2;
public DrawableSlider(Slider s) : base(s)
{
@@ -39,6 +39,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{
body = new SliderBody(s)
{
+ AccentColour = AccentColour,
Position = s.StackedPosition,
PathWidth = s.Scale * 64,
},
@@ -56,6 +57,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
ball = new SliderBall(s)
{
Scale = new Vector2(s.Scale),
+ AccentColour = AccentColour
},
initialCircle = new DrawableHitCircle(new HitCircle
{
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs
index fff08b9f60..be66689054 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -7,7 +7,6 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Transforms;
using osu.Game.Beatmaps.Samples;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements;
@@ -18,7 +17,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{
public class DrawableSliderTick : DrawableOsuHitObject
{
- private SliderTick sliderTick;
+ private readonly SliderTick sliderTick;
public double FadeInTime;
public double FadeOutTime;
@@ -27,7 +26,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
public override bool RemoveWhenNotAlive => false;
- protected override OsuJudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.SliderTick };
+ protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.SliderTick };
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{
@@ -48,7 +47,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
new Box
{
RelativeSizeAxes = Axes.Both,
- Colour = sliderTick.ComboColour,
+ Colour = AccentColour,
Alpha = 0.3f,
}
};
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs
index 8098e87b12..d0136f717c 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -4,7 +4,6 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Objects.Drawables.Pieces;
@@ -15,12 +14,12 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{
public class DrawableSpinner : DrawableOsuHitObject
{
- private Spinner spinner;
+ private readonly Spinner spinner;
- private SpinnerDisc disc;
- private SpinnerBackground background;
- private Container circleContainer;
- private DrawableHitCircle circle;
+ private readonly SpinnerDisc disc;
+ private readonly SpinnerBackground background;
+ private readonly Container circleContainer;
+ private readonly DrawableHitCircle circle;
public DrawableSpinner(Spinner s) : base(s)
{
@@ -48,7 +47,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
Alpha = 0,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- DiscColour = s.ComboColour
+ DiscColour = AccentColour
},
circleContainer = new Container
{
@@ -108,9 +107,9 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
private Vector2 scaleToCircle => circle.Scale * circle.DrawWidth / DrawWidth * 0.95f;
- private float spinsPerMinuteNeeded = 100 + 5 * 15; //TODO: read per-map OD and place it on the 5
+ private const float spins_per_minute_needed = 100 + 5 * 15; //TODO: read per-map OD and place it on the 5
- private float rotationsNeeded => (float)(spinsPerMinuteNeeded * (spinner.EndTime - spinner.StartTime) / 60000f);
+ private float rotationsNeeded => (float)(spins_per_minute_needed * (spinner.EndTime - spinner.StartTime) / 60000f);
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / rotationsNeeded, 0, 1);
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs b/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs
deleted file mode 100644
index ab34d49ecf..0000000000
--- a/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Extensions;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Transforms;
-using osu.Game.Graphics.Sprites;
-using osu.Game.Modes.Objects.Drawables;
-using osu.Game.Modes.Osu.Judgements;
-using OpenTK;
-using OpenTK.Graphics;
-
-namespace osu.Game.Modes.Osu.Objects.Drawables
-{
- public class HitExplosion : FillFlowContainer
- {
- private readonly OsuJudgementInfo judgement;
- private SpriteText line1;
- private SpriteText line2;
-
- public HitExplosion(OsuJudgementInfo judgement, OsuHitObject h = null)
- {
- this.judgement = judgement;
- AutoSizeAxes = Axes.Both;
- Origin = Anchor.Centre;
-
- Direction = FillDirection.Vertical;
- Spacing = new Vector2(0, 2);
- Position = (h?.StackedEndPosition ?? Vector2.Zero) + judgement.PositionOffset;
-
- Children = new Drawable[]
- {
- line1 = new OsuSpriteText
- {
- Anchor = Anchor.TopCentre,
- Origin = Anchor.TopCentre,
- Text = judgement.Score.GetDescription(),
- Font = @"Venera",
- TextSize = 16,
- },
- line2 = new OsuSpriteText
- {
- Text = judgement.Combo.GetDescription(),
- Font = @"Venera",
- TextSize = 11,
- }
- };
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- if (judgement.Result == HitResult.Miss)
- {
- FadeInFromZero(60);
-
- ScaleTo(1.6f);
- ScaleTo(1, 100, EasingTypes.In);
-
- MoveToOffset(new Vector2(0, 100), 800, EasingTypes.InQuint);
- RotateTo(40, 800, EasingTypes.InQuint);
-
- Delay(600);
- FadeOut(200);
- }
- else
- {
- line1.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint);
- line2.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint);
- FadeOut(500);
- }
-
- switch (judgement.Result)
- {
- case HitResult.Miss:
- Colour = Color4.Red;
- break;
- }
-
- Expire();
- }
- }
-}
\ No newline at end of file
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/ApproachCircle.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/ApproachCircle.cs
index b0b1e81fca..fd4ef64350 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/ApproachCircle.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/ApproachCircle.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
public class ApproachCircle : Container
{
- private Sprite approachCircle;
+ private readonly Sprite approachCircle;
public ApproachCircle()
{
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs
index 2a503e3dec..704a6b7490 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
public class CirclePiece : Container
{
- private Sprite disc;
+ private readonly Sprite disc;
public Func Hit;
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/GlowPiece.cs
index 1a16eae48c..6cffa370cf 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/GlowPiece.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/GlowPiece.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
public class GlowPiece : Container
{
- private Sprite layer;
+ private readonly Sprite layer;
public GlowPiece()
{
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/NumberPiece.cs
index 750f203a8d..0ebd274246 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/NumberPiece.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/NumberPiece.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
public class NumberPiece : Container
{
- private SpriteText number;
+ private readonly SpriteText number;
public string Text
{
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs
index 0b1a3aa751..becbebf0c7 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -4,7 +4,6 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using OpenTK.Graphics;
@@ -12,11 +11,27 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
public class SliderBall : CircularContainer, ISliderProgress
{
- private readonly Slider slider;
- private Box follow;
-
private const float width = 128;
+ private Color4 accentColour = Color4.Black;
+ ///
+ /// The colour that is used for the slider ball.
+ ///
+ public Color4 AccentColour
+ {
+ get { return accentColour; }
+ set
+ {
+ accentColour = value;
+ if (ball != null)
+ ball.Colour = value;
+ }
+ }
+
+ private readonly Slider slider;
+ private readonly Box follow;
+ private readonly Box ball;
+
public SliderBall(Slider slider)
{
this.slider = slider;
@@ -49,9 +64,9 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
Alpha = 1,
Children = new[]
{
- new Box
+ ball = new Box
{
- Colour = slider.ComboColour,
+ Colour = AccentColour,
Alpha = 0.4f,
Width = width,
Height = width,
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs
index b923d05713..e7837471ee 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs
@@ -13,27 +13,45 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Configuration;
using OpenTK;
using OpenTK.Graphics.ES30;
+using OpenTK.Graphics;
namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
public class SliderBody : Container, ISliderProgress
{
- private Path path;
- private BufferedContainer container;
+ private readonly Path path;
+ private readonly BufferedContainer container;
public float PathWidth
{
get { return path.PathWidth; }
- set
- {
- path.PathWidth = value;
- }
+ set { path.PathWidth = value; }
}
public double? SnakedStart { get; private set; }
public double? SnakedEnd { get; private set; }
- private Slider slider;
+ private Color4 accentColour;
+ ///
+ /// Used to colour the path.
+ ///
+ public Color4 AccentColour
+ {
+ get { return accentColour; }
+ set
+ {
+ if (accentColour == value)
+ return;
+ accentColour = value;
+
+ if (LoadState == LoadState.Loaded)
+ Schedule(reloadTexture);
+ }
+ }
+
+ private int textureWidth => (int)PathWidth * 2;
+
+ private readonly Slider slider;
public SliderBody(Slider s)
{
slider = s;
@@ -82,7 +100,12 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
snakingIn = config.GetBindable(OsuConfig.SnakingInSliders);
snakingOut = config.GetBindable(OsuConfig.SnakingOutSliders);
- int textureWidth = (int)PathWidth * 2;
+ reloadTexture();
+ }
+
+ private void reloadTexture()
+ {
+ var texture = new Texture(textureWidth, 1);
//initialise background
var upload = new TextureUpload(textureWidth * 4);
@@ -110,19 +133,18 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
progress -= border_portion;
- bytes[i * 4] = (byte)(slider.ComboColour.R * 255);
- bytes[i * 4 + 1] = (byte)(slider.ComboColour.G * 255);
- bytes[i * 4 + 2] = (byte)(slider.ComboColour.B * 255);
- bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (slider.ComboColour.A * 255));
+ bytes[i * 4] = (byte)(AccentColour.R * 255);
+ bytes[i * 4 + 1] = (byte)(AccentColour.G * 255);
+ bytes[i * 4 + 2] = (byte)(AccentColour.B * 255);
+ bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (AccentColour.A * 255));
}
}
- var texture = new Texture(textureWidth, 1);
texture.SetData(upload);
path.Texture = texture;
}
- private List currentCurve = new List();
+ private readonly List currentCurve = new List();
private bool updateSnaking(double p0, double p1)
{
if (SnakedStart == p0 && SnakedEnd == p1) return false;
diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBouncer.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBouncer.cs
index 1a78e5d85f..196b9fb521 100644
--- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBouncer.cs
+++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBouncer.cs
@@ -11,7 +11,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
private readonly Slider slider;
private readonly bool isEnd;
- private TextAwesome icon;
+ private readonly TextAwesome icon;
public SliderBouncer(Slider slider, bool isEnd)
{
diff --git a/osu.Game.Modes.Osu/Objects/OsuHitObject.cs b/osu.Game.Modes.Osu/Objects/OsuHitObject.cs
index 2af669b4ef..fa422834db 100644
--- a/osu.Game.Modes.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Modes.Osu/Objects/OsuHitObject.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Modes.Osu.Objects
public float Scale { get; set; } = 1;
- public Color4 ComboColour { get; set; }
+ public Color4 ComboColour { get; set; } = Color4.Gray;
public virtual bool NewCombo { get; set; }
public int ComboIndex { get; set; }
diff --git a/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs b/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs
index 926e5132bd..322f6b077a 100644
--- a/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs
+++ b/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs
@@ -47,11 +47,11 @@ namespace osu.Game.Modes.Osu.Objects
internal int MaxCombo = 1;
- private float scalingFactor;
+ private readonly float scalingFactor;
private float lazySliderLength;
- private Vector2 startPosition;
- private Vector2 endPosition;
+ private readonly Vector2 startPosition;
+ private readonly Vector2 endPosition;
internal OsuHitObjectDifficulty(OsuHitObject baseHitObject)
{
diff --git a/osu.Game.Modes.Osu/OsuAutoReplay.cs b/osu.Game.Modes.Osu/OsuAutoReplay.cs
index 6b543cb98f..ae85bd72d8 100644
--- a/osu.Game.Modes.Osu/OsuAutoReplay.cs
+++ b/osu.Game.Modes.Osu/OsuAutoReplay.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
-using osu.Framework.Graphics.Transforms;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Modes.Osu.Objects;
@@ -10,17 +9,19 @@ using osu.Game.Modes.Osu.Objects.Drawables;
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using osu.Framework.Graphics;
using osu.Game.Modes.Objects.Types;
+using osu.Game.Modes.Replays;
namespace osu.Game.Modes.Osu
{
- public class OsuAutoReplay : LegacyReplay
+ public class OsuAutoReplay : Replay
{
private static readonly Vector2 spinner_centre = new Vector2(256, 192);
private const float spin_radius = 50;
- private Beatmap beatmap;
+ private readonly Beatmap beatmap;
public OsuAutoReplay(Beatmap beatmap)
{
@@ -29,19 +30,22 @@ namespace osu.Game.Modes.Osu
createAutoReplay();
}
- internal class LegacyReplayFrameComparer : IComparer
+ private class ReplayFrameComparer : IComparer
{
- public int Compare(LegacyReplayFrame f1, LegacyReplayFrame f2)
+ public int Compare(ReplayFrame f1, ReplayFrame f2)
{
+ if (f1 == null) throw new NullReferenceException($@"{nameof(f1)} cannot be null");
+ if (f2 == null) throw new NullReferenceException($@"{nameof(f2)} cannot be null");
+
return f1.Time.CompareTo(f2.Time);
}
}
- private static IComparer replayFrameComparer = new LegacyReplayFrameComparer();
+ private static readonly IComparer replay_frame_comparer = new ReplayFrameComparer();
- private int findInsertionIndex(LegacyReplayFrame frame)
+ private int findInsertionIndex(ReplayFrame frame)
{
- int index = Frames.BinarySearch(frame, replayFrameComparer);
+ int index = Frames.BinarySearch(frame, replay_frame_comparer);
if (index < 0)
{
@@ -59,7 +63,7 @@ namespace osu.Game.Modes.Osu
return index;
}
- private void addFrameToReplay(LegacyReplayFrame frame) => Frames.Insert(findInsertionIndex(frame), frame);
+ private void addFrameToReplay(ReplayFrame frame) => Frames.Insert(findInsertionIndex(frame), frame);
private static Vector2 circlePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius));
@@ -74,9 +78,9 @@ namespace osu.Game.Modes.Osu
EasingTypes preferredEasing = DelayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out;
- addFrameToReplay(new LegacyReplayFrame(-100000, 256, 500, LegacyButtonState.None));
- addFrameToReplay(new LegacyReplayFrame(beatmap.HitObjects[0].StartTime - 1500, 256, 500, LegacyButtonState.None));
- addFrameToReplay(new LegacyReplayFrame(beatmap.HitObjects[0].StartTime - 1000, 256, 192, LegacyButtonState.None));
+ addFrameToReplay(new ReplayFrame(-100000, 256, 500, ReplayButtonState.None));
+ addFrameToReplay(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1500, 256, 500, ReplayButtonState.None));
+ addFrameToReplay(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1000, 256, 192, ReplayButtonState.None));
// We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps.
float frameDelay = (float)applyModsToRate(1000.0 / 60.0);
@@ -106,18 +110,18 @@ namespace osu.Game.Modes.Osu
//Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
- if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
- if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, LegacyButtonState.None));
+ if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None));
+ if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
- if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
- if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, LegacyButtonState.None));
+ if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None));
+ if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50)
{
- if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
- if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, LegacyButtonState.None));
+ if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None));
+ if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, ReplayButtonState.None));
}
}
@@ -173,13 +177,13 @@ namespace osu.Game.Modes.Osu
// Do some nice easing for cursor movements
if (Frames.Count > 0)
{
- LegacyReplayFrame lastFrame = Frames[Frames.Count - 1];
+ ReplayFrame lastFrame = Frames[Frames.Count - 1];
// Wait until Auto could "see and react" to the next note.
double waitTime = h.StartTime - Math.Max(0.0, DrawableOsuHitObject.TIME_PREEMPT - reactionTime);
if (waitTime > lastFrame.Time)
{
- lastFrame = new LegacyReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
+ lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState);
addFrameToReplay(lastFrame);
}
@@ -196,7 +200,7 @@ namespace osu.Game.Modes.Osu
for (double time = lastFrame.Time + frameDelay; time < h.StartTime; time += frameDelay)
{
Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPosition, lastFrame.Time, h.StartTime, easing);
- addFrameToReplay(new LegacyReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState));
+ addFrameToReplay(new ReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState));
}
buttonIndex = 0;
@@ -207,12 +211,12 @@ namespace osu.Game.Modes.Osu
}
}
- LegacyButtonState button = buttonIndex % 2 == 0 ? LegacyButtonState.Left1 : LegacyButtonState.Right1;
+ ReplayButtonState button = buttonIndex % 2 == 0 ? ReplayButtonState.Left1 : ReplayButtonState.Right1;
double hEndTime = (h as IHasEndTime)?.EndTime ?? h.StartTime;
- LegacyReplayFrame newFrame = new LegacyReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button);
- LegacyReplayFrame endFrame = new LegacyReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, LegacyButtonState.None);
+ ReplayFrame newFrame = new ReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button);
+ ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, ReplayButtonState.None);
// Decrement because we want the previous frame, not the next one
int index = findInsertionIndex(newFrame) - 1;
@@ -220,19 +224,19 @@ namespace osu.Game.Modes.Osu
// Do we have a previous frame? No need to check for < replay.Count since we decremented!
if (index >= 0)
{
- LegacyReplayFrame previousFrame = Frames[index];
+ ReplayFrame previousFrame = Frames[index];
var previousButton = previousFrame.ButtonState;
// If a button is already held, then we simply alternate
- if (previousButton != LegacyButtonState.None)
+ if (previousButton != ReplayButtonState.None)
{
- Debug.Assert(previousButton != (LegacyButtonState.Left1 | LegacyButtonState.Right1));
+ Debug.Assert(previousButton != (ReplayButtonState.Left1 | ReplayButtonState.Right1));
// Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button.
if (previousButton == button)
{
- button = (LegacyButtonState.Left1 | LegacyButtonState.Right1) & ~button;
- newFrame.SetButtonStates(button);
+ button = (ReplayButtonState.Left1 | ReplayButtonState.Right1) & ~button;
+ newFrame.ButtonState = button;
}
// We always follow the most recent slider / spinner, so remove any other frames that occur while it exists.
@@ -246,7 +250,7 @@ namespace osu.Game.Modes.Osu
{
// Don't affect frames which stop pressing a button!
if (j < Frames.Count - 1 || Frames[j].ButtonState == previousButton)
- Frames[j].SetButtonStates(button);
+ Frames[j].ButtonState = button;
}
}
}
@@ -270,13 +274,13 @@ namespace osu.Game.Modes.Osu
t = applyModsToTime(j - h.StartTime) * spinnerDirection;
Vector2 pos = spinner_centre + circlePosition(t / 20 + angle, spin_radius);
- addFrameToReplay(new LegacyReplayFrame((int)j, pos.X, pos.Y, button));
+ addFrameToReplay(new ReplayFrame((int)j, pos.X, pos.Y, button));
}
t = applyModsToTime(s.EndTime - h.StartTime) * spinnerDirection;
Vector2 endPosition = spinner_centre + circlePosition(t / 20 + angle, spin_radius);
- addFrameToReplay(new LegacyReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button));
+ addFrameToReplay(new ReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button));
endFrame.MouseX = endPosition.X;
endFrame.MouseY = endPosition.Y;
@@ -288,10 +292,10 @@ namespace osu.Game.Modes.Osu
for (double j = frameDelay; j < s.Duration; j += frameDelay)
{
Vector2 pos = s.PositionAt(j / s.Duration);
- addFrameToReplay(new LegacyReplayFrame(h.StartTime + j, pos.X, pos.Y, button));
+ addFrameToReplay(new ReplayFrame(h.StartTime + j, pos.X, pos.Y, button));
}
- addFrameToReplay(new LegacyReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button));
+ addFrameToReplay(new ReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button));
}
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
diff --git a/osu.Game.Modes.Osu/OsuRuleset.cs b/osu.Game.Modes.Osu/OsuRuleset.cs
index bbaf7eed5f..12df7d3f3c 100644
--- a/osu.Game.Modes.Osu/OsuRuleset.cs
+++ b/osu.Game.Modes.Osu/OsuRuleset.cs
@@ -12,6 +12,8 @@ using osu.Game.Modes.UI;
using osu.Game.Screens.Play;
using System.Collections.Generic;
using System.Linq;
+using osu.Game.Modes.Osu.Scoring;
+using osu.Game.Modes.Scoring;
namespace osu.Game.Modes.Osu
{
diff --git a/osu.Game.Modes.Osu/OsuScore.cs b/osu.Game.Modes.Osu/Scoring/OsuScore.cs
similarity index 72%
rename from osu.Game.Modes.Osu/OsuScore.cs
rename to osu.Game.Modes.Osu/Scoring/OsuScore.cs
index dddf826887..a0a639a59e 100644
--- a/osu.Game.Modes.Osu/OsuScore.cs
+++ b/osu.Game.Modes.Osu/Scoring/OsuScore.cs
@@ -1,7 +1,9 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-namespace osu.Game.Modes.Osu
+using osu.Game.Modes.Scoring;
+
+namespace osu.Game.Modes.Osu.Scoring
{
internal class OsuScore : Score
{
diff --git a/osu.Game.Modes.Osu/OsuScoreProcessor.cs b/osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs
similarity index 82%
rename from osu.Game.Modes.Osu/OsuScoreProcessor.cs
rename to osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs
index 50eb685ecd..0bd587e8ea 100644
--- a/osu.Game.Modes.Osu/OsuScoreProcessor.cs
+++ b/osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs
@@ -4,17 +4,18 @@
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements;
using osu.Game.Modes.Osu.Objects;
+using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI;
-namespace osu.Game.Modes.Osu
+namespace osu.Game.Modes.Osu.Scoring
{
- internal class OsuScoreProcessor : ScoreProcessor
+ internal class OsuScoreProcessor : ScoreProcessor
{
public OsuScoreProcessor()
{
}
- public OsuScoreProcessor(HitRenderer hitRenderer)
+ public OsuScoreProcessor(HitRenderer hitRenderer)
: base(hitRenderer)
{
}
@@ -27,7 +28,7 @@ namespace osu.Game.Modes.Osu
Accuracy.Value = 1;
}
- protected override void UpdateCalculations(OsuJudgementInfo judgement)
+ protected override void OnNewJudgement(OsuJudgement judgement)
{
if (judgement != null)
{
@@ -47,9 +48,8 @@ namespace osu.Game.Modes.Osu
int score = 0;
int maxScore = 0;
- foreach (var judgementInfo in Judgements)
+ foreach (var j in Judgements)
{
- var j = judgementInfo;
score += j.ScoreValue;
maxScore += j.MaxScoreValue;
}
diff --git a/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs b/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs
index 7a1e83d3a5..ca9ff6fc61 100644
--- a/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs
+++ b/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs
@@ -7,12 +7,14 @@ using osu.Game.Modes.Osu.Beatmaps;
using osu.Game.Modes.Osu.Judgements;
using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Osu.Objects.Drawables;
+using osu.Game.Modes.Osu.Scoring;
+using osu.Game.Modes.Scoring;
using osu.Game.Modes.UI;
using osu.Game.Screens.Play;
namespace osu.Game.Modes.Osu.UI
{
- public class OsuHitRenderer : HitRenderer
+ public class OsuHitRenderer : HitRenderer
{
public OsuHitRenderer(WorkingBeatmap beatmap)
: base(beatmap)
@@ -25,11 +27,11 @@ namespace osu.Game.Modes.Osu.UI
protected override IBeatmapProcessor CreateBeatmapProcessor() => new OsuBeatmapProcessor();
- protected override Playfield CreatePlayfield() => new OsuPlayfield();
+ protected override Playfield CreatePlayfield() => new OsuPlayfield();
protected override KeyConversionInputManager CreateKeyConversionInputManager() => new OsuKeyConversionInputManager();
- protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h)
+ protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h)
{
var circle = h as HitCircle;
if (circle != null)
diff --git a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs
index 5caaaafb13..8090263fe1 100644
--- a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs
@@ -15,11 +15,11 @@ using osu.Game.Modes.Osu.Judgements;
namespace osu.Game.Modes.Osu.UI
{
- public class OsuPlayfield : Playfield
+ public class OsuPlayfield : Playfield
{
- private Container approachCircles;
- private Container judgementLayer;
- private ConnectionRenderer connectionLayer;
+ private readonly Container approachCircles;
+ private readonly Container judgementLayer;
+ private readonly ConnectionRenderer connectionLayer;
public override Vector2 Size
{
@@ -65,7 +65,7 @@ namespace osu.Game.Modes.Osu.UI
AddInternal(new GameplayCursor());
}
- public override void Add(DrawableHitObject h)
+ public override void Add(DrawableHitObject h)
{
h.Depth = (float)h.HitObject.StartTime;
@@ -83,9 +83,13 @@ namespace osu.Game.Modes.Osu.UI
.OrderBy(h => h.StartTime);
}
- public override void OnJudgement(DrawableHitObject judgedObject)
+ public override void OnJudgement(DrawableHitObject judgedObject)
{
- HitExplosion explosion = new HitExplosion(judgedObject.Judgement, judgedObject.HitObject);
+ DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgedObject.Judgement)
+ {
+ Origin = Anchor.Centre,
+ Position = judgedObject.HitObject.StackedEndPosition + judgedObject.Judgement.PositionOffset
+ };
judgementLayer.Add(explosion);
}
diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj
index 12135a38fb..55322e855e 100644
--- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj
+++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj
@@ -48,7 +48,7 @@
-
+
@@ -58,7 +58,7 @@
-
+
@@ -72,8 +72,8 @@
-
-
+
+
diff --git a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index d78c347f22..cc361628a3 100644
--- a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -2,18 +2,95 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.Legacy;
+using osu.Game.Beatmaps.Samples;
+using osu.Game.Modes.Objects;
+using osu.Game.Modes.Objects.Types;
using osu.Game.Modes.Taiko.Objects;
using System.Collections.Generic;
+using System.Linq;
namespace osu.Game.Modes.Taiko.Beatmaps
{
internal class TaikoBeatmapConverter : IBeatmapConverter
{
+ private const float legacy_velocity_scale = 1.4f;
+ private const float bash_convert_factor = 1.65f;
+
public Beatmap Convert(Beatmap original)
{
+ if (original is LegacyBeatmap)
+ original.TimingInfo.ControlPoints.ForEach(c => c.VelocityAdjustment /= legacy_velocity_scale);
+
return new Beatmap(original)
{
- HitObjects = new List() // Todo: Implement
+ HitObjects = convertHitObjects(original.HitObjects)
+ };
+ }
+
+ private List convertHitObjects(List hitObjects)
+ {
+ return hitObjects.Select(convertHitObject).ToList();
+ }
+
+ private TaikoHitObject convertHitObject(HitObject original)
+ {
+ // Check if this HitObject is already a TaikoHitObject, and return it if so
+ TaikoHitObject originalTaiko = original as TaikoHitObject;
+ if (originalTaiko != null)
+ return originalTaiko;
+
+ IHasDistance distanceData = original as IHasDistance;
+ IHasRepeats repeatsData = original as IHasRepeats;
+ IHasEndTime endTimeData = original as IHasEndTime;
+
+ // Old osu! used hit sounding to determine various hit type information
+ SampleType sample = original.Sample?.Type ?? SampleType.None;
+
+ bool strong = (sample & SampleType.Finish) > 0;
+
+ if (distanceData != null)
+ {
+ return new DrumRoll
+ {
+ StartTime = original.StartTime,
+ Sample = original.Sample,
+ IsStrong = strong,
+
+ Distance = distanceData.Distance * (repeatsData?.RepeatCount ?? 1)
+ };
+ }
+
+ if (endTimeData != null)
+ {
+ // We compute the end time manually to add in the Bash convert factor
+ return new Swell
+ {
+ StartTime = original.StartTime,
+ Sample = original.Sample,
+ IsStrong = strong,
+
+ EndTime = original.StartTime + endTimeData.Duration * bash_convert_factor
+ };
+ }
+
+ bool isCentre = (sample & ~(SampleType.Finish | SampleType.Normal)) == 0;
+
+ if (isCentre)
+ {
+ return new CentreHit
+ {
+ StartTime = original.StartTime,
+ Sample = original.Sample,
+ IsStrong = strong
+ };
+ }
+
+ return new RimHit
+ {
+ StartTime = original.StartTime,
+ Sample = original.Sample,
+ IsStrong = strong,
};
}
}
diff --git a/osu.Game.Modes.Taiko/Judgements/TaikoDrumRollTickJudgement.cs b/osu.Game.Modes.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
new file mode 100644
index 0000000000..b6a727aeb4
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
@@ -0,0 +1,34 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Modes.Taiko.Judgements
+{
+ public class TaikoDrumRollTickJudgement : TaikoJudgement
+ {
+ ///
+ /// Drum roll ticks don't display judgement text.
+ ///
+ public override string ResultString => string.Empty;
+
+ ///
+ /// Drum roll ticks don't display judgement text.
+ ///
+ public override string MaxResultString => string.Empty;
+
+ protected override int NumericResultForScore(TaikoHitResult result)
+ {
+ switch (result)
+ {
+ default:
+ return 0;
+ case TaikoHitResult.Great:
+ return 200;
+ }
+ }
+
+ protected override int NumericResultForAccuracy(TaikoHitResult result)
+ {
+ return 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs b/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs
index d425616b66..cbc3919c4f 100644
--- a/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs
+++ b/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs
@@ -1,11 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.ComponentModel;
+
namespace osu.Game.Modes.Taiko.Judgements
{
public enum TaikoHitResult
{
+ [Description("GOOD")]
Good,
+ [Description("GREAT")]
Great
}
}
diff --git a/osu.Game.Modes.Taiko/Judgements/TaikoJudgementInfo.cs b/osu.Game.Modes.Taiko/Judgements/TaikoJudgement.cs
similarity index 54%
rename from osu.Game.Modes.Taiko/Judgements/TaikoJudgementInfo.cs
rename to osu.Game.Modes.Taiko/Judgements/TaikoJudgement.cs
index d9e81d4d77..f4745730db 100644
--- a/osu.Game.Modes.Taiko/Judgements/TaikoJudgementInfo.cs
+++ b/osu.Game.Modes.Taiko/Judgements/TaikoJudgement.cs
@@ -2,52 +2,57 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Judgements;
+using osu.Framework.Extensions;
namespace osu.Game.Modes.Taiko.Judgements
{
- public class TaikoJudgementInfo : JudgementInfo
+ public class TaikoJudgement : Judgement
{
///
- /// The maximum score value.
+ /// The maximum result.
///
public const TaikoHitResult MAX_HIT_RESULT = TaikoHitResult.Great;
///
- /// The score value.
+ /// The result.
///
public TaikoHitResult TaikoResult;
///
- /// The score value for the combo portion of the score.
+ /// The result value for the combo portion of the score.
///
- public int ScoreValue => NumericResultForScore(TaikoResult);
-
- ///
- /// The score value for the accuracy portion of the score.
- ///
- public int AccuracyScoreValue => NumericResultForAccuracy(TaikoResult);
+ public int ResultValueForScore => NumericResultForScore(TaikoResult);
///
- /// The maximum score value for the combo portion of the score.
+ /// The result value for the accuracy portion of the score.
///
- public int MaxScoreValue => NumericResultForScore(MAX_HIT_RESULT);
-
+ public int ResultValueForAccuracy => NumericResultForAccuracy(TaikoResult);
+
///
- /// The maximum score value for the accuracy portion of the score.
+ /// The maximum result value for the combo portion of the score.
///
- public int MaxAccuracyScoreValue => NumericResultForAccuracy(MAX_HIT_RESULT);
+ public int MaxResultValueForScore => NumericResultForScore(MAX_HIT_RESULT);
+
+ ///
+ /// The maximum result value for the accuracy portion of the score.
+ ///
+ public int MaxResultValueForAccuracy => NumericResultForAccuracy(MAX_HIT_RESULT);
+
+ public override string ResultString => TaikoResult.GetDescription();
+
+ public override string MaxResultString => MAX_HIT_RESULT.GetDescription();
///
/// Whether this Judgement has a secondary hit in the case of finishers.
///
- public bool SecondHit;
+ public virtual bool SecondHit { get; set; }
///
- /// Computes the numeric score value for the combo portion of the score.
+ /// Computes the numeric result value for the combo portion of the score.
/// For the accuracy portion of the score (including accuracy percentage), see .
///
- /// The result to compute the score value for.
- /// The numeric score value.
+ /// The result to compute the value for.
+ /// The numeric result value.
protected virtual int NumericResultForScore(TaikoHitResult result)
{
switch (result)
@@ -62,11 +67,11 @@ namespace osu.Game.Modes.Taiko.Judgements
}
///
- /// Computes the numeric score value for the accuracy portion of the score.
+ /// Computes the numeric result value for the accuracy portion of the score.
/// For the combo portion of the score, see .
///
- /// The result to compute the score value for.
- /// The numeric score value.
+ /// The result to compute the value for.
+ /// The numeric result value.
protected virtual int NumericResultForAccuracy(TaikoHitResult result)
{
switch (result)
diff --git a/osu.Game.Modes.Taiko/Judgements/TaikoStrongHitJudgement.cs b/osu.Game.Modes.Taiko/Judgements/TaikoStrongHitJudgement.cs
new file mode 100644
index 0000000000..ee978d0026
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Judgements/TaikoStrongHitJudgement.cs
@@ -0,0 +1,25 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Modes.Judgements;
+
+namespace osu.Game.Modes.Taiko.Judgements
+{
+ public class TaikoStrongHitJudgement : TaikoJudgement, IPartialJudgement
+ {
+ public bool Changed { get; set; }
+
+ public override bool SecondHit
+ {
+ get { return base.SecondHit; }
+ set
+ {
+ if (base.SecondHit == value)
+ return;
+ base.SecondHit = value;
+
+ Changed = true;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Mods/TaikoMod.cs b/osu.Game.Modes.Taiko/Mods/TaikoMod.cs
index c929ebffdd..422f0ec250 100644
--- a/osu.Game.Modes.Taiko/Mods/TaikoMod.cs
+++ b/osu.Game.Modes.Taiko/Mods/TaikoMod.cs
@@ -1,7 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Beatmaps;
using osu.Game.Modes.Mods;
+using osu.Game.Modes.Scoring;
+using osu.Game.Modes.Taiko.Objects;
+using osu.Game.Modes.Taiko.Replays;
+using osu.Game.Users;
namespace osu.Game.Modes.Taiko.Mods
{
@@ -61,4 +66,13 @@ namespace osu.Game.Modes.Taiko.Mods
{
}
+
+ public class TaikoModAutoplay : ModAutoplay
+ {
+ protected override Score CreateReplayScore(Beatmap beatmap) => new Score
+ {
+ User = new User { Username = "mekkadosu!" },
+ Replay = new TaikoAutoReplay(beatmap)
+ };
+ }
}
diff --git a/osu.Game.Modes.Taiko/Objects/CentreHit.cs b/osu.Game.Modes.Taiko/Objects/CentreHit.cs
new file mode 100644
index 0000000000..258112f045
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/CentreHit.cs
@@ -0,0 +1,9 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Modes.Taiko.Objects
+{
+ public class CentreHit : Hit
+ {
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableCentreHit.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableCentreHit.cs
new file mode 100644
index 0000000000..683d48df10
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableCentreHit.cs
@@ -0,0 +1,35 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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;
+
+namespace osu.Game.Modes.Taiko.Objects.Drawable
+{
+ public class DrawableCentreHit : DrawableHit
+ {
+ protected override Key[] HitKeys { get; } = { Key.F, Key.J };
+
+ private readonly CirclePiece circlePiece;
+
+ public DrawableCentreHit(Hit hit)
+ : base(hit)
+ {
+ Add(circlePiece = new CirclePiece
+ {
+ Children = new[]
+ {
+ new CentreHitSymbolPiece()
+ }
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ circlePiece.AccentColour = colours.PinkDarker;
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableDrumRoll.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableDrumRoll.cs
new file mode 100644
index 0000000000..3551538fe7
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableDrumRoll.cs
@@ -0,0 +1,57 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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;
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableDrumRollTick.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableDrumRollTick.cs
new file mode 100644
index 0000000000..5217fd9085
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableDrumRollTick.cs
@@ -0,0 +1,53 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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 osu.Game.Modes.Objects.Drawables;
+
+namespace osu.Game.Modes.Taiko.Objects.Drawable
+{
+ public class DrawableDrumRollTick : DrawableTaikoHitObject
+ {
+ private readonly DrumRollTick tick;
+
+ public DrawableDrumRollTick(DrumRollTick tick)
+ : base(tick)
+ {
+ this.tick = tick;
+ }
+
+ protected override TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement();
+
+ protected override void CheckJudgement(bool userTriggered)
+ {
+ if (!userTriggered)
+ {
+ if (Judgement.TimeOffset > tick.HitWindow)
+ Judgement.Result = HitResult.Miss;
+ return;
+ }
+
+ if (Math.Abs(Judgement.TimeOffset) < tick.HitWindow)
+ {
+ Judgement.Result = HitResult.Hit;
+ Judgement.TaikoResult = TaikoHitResult.Great;
+ }
+ }
+
+ protected override void UpdateState(ArmedState state)
+ {
+ }
+
+ protected override void UpdateScrollPosition(double time)
+ {
+ // Drum roll ticks shouldn't move
+ }
+
+ protected override bool HandleKeyPress(Key key)
+ {
+ return Judgement.Result == HitResult.None && UpdateJudgement(true);
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableHit.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableHit.cs
new file mode 100644
index 0000000000..c8a7355e3c
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableHit.cs
@@ -0,0 +1,104 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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.Linq;
+
+namespace osu.Game.Modes.Taiko.Objects.Drawable
+{
+ public abstract class DrawableHit : DrawableTaikoHitObject
+ {
+ ///
+ /// A list of keys which can result in hits for this HitObject.
+ ///
+ protected abstract Key[] HitKeys { get; }
+
+ protected override Container Content => bodyContainer;
+
+ private readonly Hit hit;
+
+ ///
+ /// Whether the last key pressed is a valid hit key.
+ ///
+ private bool validKeyPressed;
+
+ private readonly Container bodyContainer;
+
+ protected DrawableHit(Hit hit)
+ : base(hit)
+ {
+ this.hit = hit;
+
+ AddInternal(bodyContainer = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ });
+ }
+
+ protected override void CheckJudgement(bool userTriggered)
+ {
+ if (!userTriggered)
+ {
+ if (Judgement.TimeOffset > hit.HitWindowGood)
+ Judgement.Result = HitResult.Miss;
+ return;
+ }
+
+ double hitOffset = Math.Abs(Judgement.TimeOffset);
+
+ if (hitOffset > hit.HitWindowMiss)
+ return;
+
+ if (!validKeyPressed)
+ Judgement.Result = HitResult.Miss;
+ else if (hitOffset < hit.HitWindowGood)
+ {
+ Judgement.Result = HitResult.Hit;
+ Judgement.TaikoResult = hitOffset < hit.HitWindowGreat ? TaikoHitResult.Great : TaikoHitResult.Good;
+ }
+ else
+ Judgement.Result = HitResult.Miss;
+ }
+
+ protected override bool HandleKeyPress(Key key)
+ {
+ if (Judgement.Result != HitResult.None)
+ return false;
+
+ validKeyPressed = HitKeys.Contains(key);
+
+ return UpdateJudgement(true);
+ }
+
+ protected override void UpdateState(ArmedState state)
+ {
+ Delay(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true);
+
+ switch (State)
+ {
+ case ArmedState.Idle:
+ Delay(hit.HitWindowMiss);
+ break;
+ case ArmedState.Miss:
+ FadeOut(100);
+ break;
+ 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);
+ break;
+ }
+
+ Expire();
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableRimHit.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableRimHit.cs
new file mode 100644
index 0000000000..cab6819300
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableRimHit.cs
@@ -0,0 +1,35 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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 DrawableRimHit : DrawableHit
+ {
+ protected override Key[] HitKeys { get; } = { Key.D, Key.K };
+
+ private readonly CirclePiece circlePiece;
+
+ public DrawableRimHit(Hit hit)
+ : base(hit)
+ {
+ Add(circlePiece = new CirclePiece
+ {
+ Children = new[]
+ {
+ new RimHitSymbolPiece()
+ }
+ });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ circlePiece.AccentColour = colours.BlueDarker;
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableStrongCentreHit.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableStrongCentreHit.cs
new file mode 100644
index 0000000000..b4ec0b108a
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableStrongCentreHit.cs
@@ -0,0 +1,35 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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;
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableStrongHit.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableStrongHit.cs
new file mode 100644
index 0000000000..a6cb6ae7fa
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableStrongHit.cs
@@ -0,0 +1,89 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK.Input;
+using System;
+using System.Linq;
+using osu.Framework.Input;
+using osu.Game.Modes.Objects.Drawables;
+using osu.Game.Modes.Taiko.Judgements;
+
+namespace osu.Game.Modes.Taiko.Objects.Drawable
+{
+ public abstract class DrawableStrongHit : DrawableHit
+ {
+ ///
+ /// The lenience for the second key press.
+ /// This does not adjust by map difficulty in ScoreV2 yet.
+ ///
+ private const double second_hit_window = 30;
+
+ private double firstHitTime;
+ private bool firstKeyHeld;
+ private Key firstHitKey;
+
+ protected DrawableStrongHit(Hit hit)
+ : base(hit)
+ {
+ }
+
+ protected override TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement();
+
+ protected override void CheckJudgement(bool userTriggered)
+ {
+ if (Judgement.Result == HitResult.None)
+ {
+ base.CheckJudgement(userTriggered);
+ return;
+ }
+
+ if (!userTriggered)
+ return;
+
+ // If we get here, we're assured that the key pressed is the correct secondary key
+
+ if (Math.Abs(firstHitTime - Time.Current) < second_hit_window)
+ Judgement.SecondHit = true;
+ }
+
+ protected override bool HandleKeyPress(Key key)
+ {
+ // Check if we've handled the first key
+ if (Judgement.Result == HitResult.None)
+ {
+ // First key hasn't been handled yet, attempt to handle it
+ bool handled = base.HandleKeyPress(key);
+
+ if (handled)
+ {
+ firstHitTime = Time.Current;
+ firstHitKey = key;
+ }
+
+ return handled;
+ }
+
+ // If we've already hit the second key, don't handle this object any further
+ if (Judgement.SecondHit)
+ return false;
+
+ // Don't handle represses of the first key
+ if (firstHitKey == key)
+ return false;
+
+ // Don't handle invalid hit key presses
+ if (!HitKeys.Contains(key))
+ return false;
+
+ // Assume the intention was to hit the strong hit with both keys only if the first key is still being held down
+ return firstKeyHeld && UpdateJudgement(true);
+ }
+
+ protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ {
+ firstKeyHeld = state.Keyboard.Keys.Contains(firstHitKey);
+
+ return base.OnKeyDown(state, args);
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableStrongRimHit.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableStrongRimHit.cs
new file mode 100644
index 0000000000..1f77ad0409
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableStrongRimHit.cs
@@ -0,0 +1,35 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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;
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs
new file mode 100644
index 0000000000..ccd6380542
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs
@@ -0,0 +1,242 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK;
+using OpenTK.Graphics;
+using OpenTK.Input;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Graphics;
+using osu.Game.Modes.Objects.Drawables;
+using osu.Game.Modes.Taiko.Judgements;
+using osu.Game.Modes.Taiko.Objects.Drawable.Pieces;
+using System;
+using System.Linq;
+
+namespace osu.Game.Modes.Taiko.Objects.Drawable
+{
+ public class DrawableSwell : DrawableTaikoHitObject
+ {
+ ///
+ /// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime.
+ /// This is only ever invoked once.
+ ///
+ public event Action OnStart;
+
+ private const float target_ring_thick_border = 1.4f;
+ private const float target_ring_thin_border = 1f;
+ private const float target_ring_scale = 5f;
+ private const float inner_ring_alpha = 0.65f;
+
+ private readonly Swell swell;
+
+ private readonly Container bodyContainer;
+ private readonly CircularContainer targetRing;
+ private readonly CircularContainer expandingRing;
+
+ private readonly CirclePiece circlePiece;
+
+ private readonly Key[] rimKeys = { Key.D, Key.K };
+ private readonly Key[] centreKeys = { Key.F, Key.J };
+ private Key[] lastKeySet;
+
+ ///
+ /// The amount of times the user has hit this swell.
+ ///
+ private int userHits;
+
+ private bool hasStarted;
+ private readonly SwellSymbolPiece symbol;
+
+ public DrawableSwell(Swell swell)
+ : base(swell)
+ {
+ this.swell = swell;
+
+ Children = new Framework.Graphics.Drawable[]
+ {
+ bodyContainer = new Container
+ {
+ Children = new Framework.Graphics.Drawable[]
+ {
+ expandingRing = new CircularContainer
+ {
+ Name = "Expanding ring",
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Alpha = 0,
+ Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2),
+ BlendingMode = BlendingMode.Additive,
+ Masking = true,
+ Children = new []
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = inner_ring_alpha,
+ }
+ }
+ },
+ targetRing = new CircularContainer
+ {
+ Name = "Target ring (thick border)",
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2),
+ Masking = true,
+ BorderThickness = target_ring_thick_border,
+ BlendingMode = BlendingMode.Additive,
+ Children = new Framework.Graphics.Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ AlwaysPresent = true
+ },
+ new CircularContainer
+ {
+ Name = "Target ring (thin border)",
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ BorderThickness = target_ring_thin_border,
+ BorderColour = Color4.White,
+ Children = new[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ AlwaysPresent = true
+ }
+ }
+ }
+ }
+ },
+ circlePiece = new CirclePiece
+ {
+ Children = new []
+ {
+ symbol = new SwellSymbolPiece()
+ }
+ }
+ }
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ circlePiece.AccentColour = colours.YellowDark;
+ expandingRing.Colour = colours.YellowLight;
+ targetRing.BorderColour = colours.YellowDark.Opacity(0.25f);
+ }
+
+ protected override void CheckJudgement(bool userTriggered)
+ {
+ if (userTriggered)
+ {
+ userHits++;
+
+ var completion = (float)userHits / swell.RequiredHits;
+
+ expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50);
+ expandingRing.Delay(50);
+ expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint);
+ expandingRing.DelayReset();
+
+ symbol.RotateTo((float)(completion * swell.Duration / 8), 4000, 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)
+ {
+ Judgement.Result = HitResult.Hit;
+ Judgement.TaikoResult = TaikoHitResult.Great;
+ }
+ }
+ else
+ {
+ if (Judgement.TimeOffset < 0)
+ return;
+
+ //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
+ if (userHits > swell.RequiredHits / 2)
+ {
+ Judgement.Result = HitResult.Hit;
+ Judgement.TaikoResult = TaikoHitResult.Good;
+ }
+ else
+ Judgement.Result = HitResult.Miss;
+ }
+ }
+
+ protected override void UpdateState(ArmedState state)
+ {
+ const float preempt = 100;
+
+ Delay(HitObject.StartTime - Time.Current - preempt, true);
+
+ targetRing.ScaleTo(target_ring_scale, preempt * 4, EasingTypes.OutQuint);
+
+ Delay(preempt, true);
+
+ Delay(Judgement.TimeOffset + swell.Duration, true);
+
+ const float out_transition_time = 300;
+
+ switch (state)
+ {
+ case ArmedState.Hit:
+ bodyContainer.ScaleTo(1.4f, out_transition_time);
+ break;
+ }
+
+ FadeOut(out_transition_time, EasingTypes.Out);
+
+ Expire();
+ }
+
+ protected override void UpdateScrollPosition(double time)
+ {
+ // Make the swell stop at the hit target
+ double t = Math.Min(HitObject.StartTime, time);
+
+ if (t == HitObject.StartTime && !hasStarted)
+ {
+ OnStart?.Invoke();
+ hasStarted = true;
+ }
+
+ base.UpdateScrollPosition(t);
+ }
+
+ protected override bool HandleKeyPress(Key key)
+ {
+ if (Judgement.Result != HitResult.None)
+ return false;
+
+ // Don't handle keys before the swell starts
+ if (Time.Current < HitObject.StartTime)
+ return false;
+
+ // Find the keyset which this key corresponds to
+ var keySet = rimKeys.Contains(key) ? rimKeys : centreKeys;
+
+ // Ensure alternating keysets
+ if (keySet == lastKeySet)
+ return false;
+ lastKeySet = keySet;
+
+ UpdateJudgement(true);
+
+ return true;
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHit.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHit.cs
deleted file mode 100644
index 760977ef5b..0000000000
--- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHit.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Transforms;
-using OpenTK;
-
-namespace osu.Game.Modes.Taiko.Objects.Drawable
-{
- internal class DrawableTaikoHit : Sprite
- {
- private TaikoHitObject h;
-
- public DrawableTaikoHit(TaikoHitObject h)
- {
- this.h = h;
-
- Origin = Anchor.Centre;
- Scale = new Vector2(0.2f);
- RelativePositionAxes = Axes.Both;
- Position = new Vector2(1.1f, 0.5f);
- }
-
- [BackgroundDependencyLoader]
- private void load(TextureStore textures)
- {
- Texture = textures.Get(@"Menu/logo");
-
- double duration = 0;
-
- Transforms.Add(new TransformPositionX { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = 1.1f, EndValue = 0.1f });
- Transforms.Add(new TransformAlpha { StartTime = h.StartTime + duration + 200, EndTime = h.StartTime + duration + 400, StartValue = 1, EndValue = 0 });
- Expire(true);
- }
- }
-}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs
new file mode 100644
index 0000000000..5d6d669dc1
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs
@@ -0,0 +1,70 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK.Input;
+using osu.Framework.Graphics;
+using osu.Game.Modes.Objects.Drawables;
+using osu.Game.Modes.Taiko.Judgements;
+using System.Collections.Generic;
+using osu.Framework.Input;
+
+namespace osu.Game.Modes.Taiko.Objects.Drawable
+{
+ public abstract class DrawableTaikoHitObject : DrawableHitObject
+ {
+ ///
+ /// A list of keys which this hit object will accept. These are the standard Taiko keys for now.
+ /// These should be moved to bindings later.
+ ///
+ private readonly List validKeys = new List(new[] { Key.D, Key.F, Key.J, Key.K });
+
+ protected DrawableTaikoHitObject(TaikoHitObject hitObject)
+ : base(hitObject)
+ {
+ Anchor = Anchor.CentreLeft;
+ Origin = Anchor.Centre;
+
+ RelativePositionAxes = Axes.X;
+ }
+
+ protected override void LoadComplete()
+ {
+ LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2;
+
+ base.LoadComplete();
+ }
+
+ protected override TaikoJudgement CreateJudgement() => new TaikoJudgement();
+
+ ///
+ /// Sets the scroll position of the DrawableHitObject relative to the offset between
+ /// a time value and the HitObject's StartTime.
+ ///
+ ///
+ protected virtual void UpdateScrollPosition(double time)
+ {
+ MoveToX((float)((HitObject.StartTime - time) / HitObject.PreEmpt));
+ }
+
+ protected override void Update()
+ {
+ UpdateScrollPosition(Time.Current);
+ }
+
+ protected virtual bool HandleKeyPress(Key key) => false;
+
+ protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ {
+ // Make sure we don't handle held-down keys
+ if (args.Repeat)
+ return false;
+
+ // Check if we've pressed a valid taiko key
+ if (!validKeys.Contains(args.Key))
+ return false;
+
+ // Handle it!
+ return HandleKeyPress(args.Key);
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/CentreHitSymbolPiece.cs b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/CentreHitSymbolPiece.cs
new file mode 100644
index 0000000000..e62ca6b073
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/CentreHitSymbolPiece.cs
@@ -0,0 +1,31 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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.Sprites;
+using OpenTK;
+
+namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces
+{
+ ///
+ /// The symbol used for centre hit pieces.
+ ///
+ public class CentreHitSymbolPiece : CircularContainer
+ {
+ public CentreHitSymbolPiece()
+ {
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ Size = new Vector2(CirclePiece.SYMBOL_INNER_SIZE);
+ Masking = true;
+ Children = new[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/CirclePiece.cs b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/CirclePiece.cs
new file mode 100644
index 0000000000..2321ad30ee
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/CirclePiece.cs
@@ -0,0 +1,161 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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
+{
+ ///
+ /// A circle piece which is used uniformly through osu!taiko to visualise hitobjects.
+ ///
+ /// The body of this piece will overshoot its parent by to form
+ /// a rounded (_[-Width-]_) figure such that a regular "circle" is the result of a parent with Width = 0.
+ ///
+ ///
+ 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;
+ ///
+ /// The colour of the inner circle and outer glows.
+ ///
+ 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;
+ ///
+ /// Whether Kiai mode effects are enabled for this circle piece.
+ ///
+ 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 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
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/RimHitSymbolPiece.cs b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/RimHitSymbolPiece.cs
new file mode 100644
index 0000000000..6999634108
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/RimHitSymbolPiece.cs
@@ -0,0 +1,36 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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.Sprites;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces
+{
+ ///
+ /// The symbol used for rim hit pieces.
+ ///
+ public class RimHitSymbolPiece : CircularContainer
+ {
+ public RimHitSymbolPiece()
+ {
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ Size = new Vector2(CirclePiece.SYMBOL_SIZE);
+ BorderThickness = CirclePiece.SYMBOL_BORDER;
+ BorderColour = Color4.White;
+ Masking = true;
+ Children = new[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ AlwaysPresent = true
+ }
+ };
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/StrongCirclePiece.cs b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/StrongCirclePiece.cs
new file mode 100644
index 0000000000..319ca17cb8
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/StrongCirclePiece.cs
@@ -0,0 +1,25 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK;
+
+namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces
+{
+ ///
+ /// A type of circle piece which is drawn at a higher scale to represent a "strong" piece.
+ ///
+ public class StrongCirclePiece : CirclePiece
+ {
+ ///
+ /// The amount to scale up the base circle to show it as a "strong" piece.
+ ///
+ 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);
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs
new file mode 100644
index 0000000000..2bd86406a7
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs
@@ -0,0 +1,24 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Graphics;
+using osu.Framework.Graphics;
+
+namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces
+{
+ ///
+ /// The symbol used for swell pieces.
+ ///
+ public class SwellSymbolPiece : TextAwesome
+ {
+ public SwellSymbolPiece()
+ {
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ UseFullGlyphHeight = true;
+ TextSize = CirclePiece.SYMBOL_INNER_SIZE;
+ Icon = FontAwesome.fa_asterisk;
+ Shadow = false;
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/DrumRoll.cs b/osu.Game.Modes.Taiko/Objects/DrumRoll.cs
new file mode 100644
index 0000000000..1f9241268b
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/DrumRoll.cs
@@ -0,0 +1,104 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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 System;
+using System.Collections.Generic;
+using System.Linq;
+using osu.Game.Beatmaps.Timing;
+using osu.Game.Database;
+
+namespace osu.Game.Modes.Taiko.Objects
+{
+ public class DrumRoll : TaikoHitObject, IHasDistance
+ {
+ public double EndTime => StartTime + Distance / Velocity;
+
+ public double Duration => EndTime - StartTime;
+
+ ///
+ /// Raw length of the drum roll in positional length units.
+ ///
+ public double Distance { get; set; }
+
+ ///
+ /// Velocity of the drum roll in positional length units per millisecond.
+ ///
+ public double Velocity { get; protected set; }
+
+ ///
+ /// The distance between ticks of this drumroll.
+ /// Half of this value is the hit window of the ticks.
+ ///
+ public double TickTimeDistance { get; protected set; }
+
+ ///
+ /// Number of drum roll ticks required for a "Good" hit.
+ ///
+ public double RequiredGoodHits { get; protected set; }
+
+ ///
+ /// Number of drum roll ticks required for a "Great" hit.
+ ///
+ public double RequiredGreatHits { get; protected set; }
+
+ ///
+ /// Total number of drum roll ticks.
+ ///
+ public int TotalTicks => Ticks.Count();
+
+ ///
+ /// Initializes the drum roll ticks if not initialized and returns them.
+ ///
+ public IEnumerable Ticks => ticks ?? (ticks = createTicks());
+
+ private List ticks;
+
+ public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
+ {
+ base.ApplyDefaults(timing, difficulty);
+
+ Velocity = timing.SliderVelocityAt(StartTime) * difficulty.SliderMultiplier / 1000;
+ 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);
+ RequiredGreatHits = TotalTicks * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty);
+ }
+
+ private List createTicks()
+ {
+ var ret = new List();
+
+ if (TickTimeDistance == 0)
+ return ret;
+
+ bool first = true;
+ for (double t = StartTime; t < EndTime + (int)TickTimeDistance; t += TickTimeDistance)
+ {
+ ret.Add(new DrumRollTick
+ {
+ FirstTick = first,
+ PreEmpt = PreEmpt,
+ TickTimeDistance = TickTimeDistance,
+ StartTime = t,
+ Sample = new HitSampleInfo
+ {
+ Type = SampleType.None,
+ Set = SampleSet.Soft
+ }
+ });
+
+ first = false;
+ }
+
+ return ret;
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game.Modes.Taiko/Objects/DrumRollTick.cs b/osu.Game.Modes.Taiko/Objects/DrumRollTick.cs
new file mode 100644
index 0000000000..2ca0d71fc1
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/DrumRollTick.cs
@@ -0,0 +1,24 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Modes.Taiko.Objects
+{
+ public class DrumRollTick : TaikoHitObject
+ {
+ ///
+ /// Whether this is the first (initial) tick of the slider.
+ ///
+ public bool FirstTick;
+
+ ///
+ /// The distance between this tick and the next in milliseconds.
+ /// Half of this value is the hit window of the tick.
+ ///
+ public double TickTimeDistance;
+
+ ///
+ /// The time allowed to hit this tick.
+ ///
+ public double HitWindow => TickTimeDistance / 2;
+ }
+}
\ No newline at end of file
diff --git a/osu.Game.Modes.Taiko/Objects/Hit.cs b/osu.Game.Modes.Taiko/Objects/Hit.cs
new file mode 100644
index 0000000000..ad8d07d901
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Hit.cs
@@ -0,0 +1,35 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Beatmaps.Timing;
+using osu.Game.Database;
+
+namespace osu.Game.Modes.Taiko.Objects
+{
+ public class Hit : TaikoHitObject
+ {
+ ///
+ /// The hit window that results in a "GREAT" hit.
+ ///
+ public double HitWindowGreat = 35;
+
+ ///
+ /// The hit window that results in a "GOOD" hit.
+ ///
+ public double HitWindowGood = 80;
+
+ ///
+ /// The hit window that results in a "MISS".
+ ///
+ public double HitWindowMiss = 95;
+
+ public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
+ {
+ base.ApplyDefaults(timing, difficulty);
+
+ HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20);
+ HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50);
+ HitWindowMiss = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70);
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/RimHit.cs b/osu.Game.Modes.Taiko/Objects/RimHit.cs
new file mode 100644
index 0000000000..aae93ec10d
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/RimHit.cs
@@ -0,0 +1,9 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Modes.Taiko.Objects
+{
+ public class RimHit : Hit
+ {
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Objects/Swell.cs b/osu.Game.Modes.Taiko/Objects/Swell.cs
new file mode 100644
index 0000000000..4cbb5904c7
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Objects/Swell.cs
@@ -0,0 +1,30 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// 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;
+
+namespace osu.Game.Modes.Taiko.Objects
+{
+ public class Swell : TaikoHitObject, IHasEndTime
+ {
+ public double EndTime { get; set; }
+
+ public double Duration => EndTime - StartTime;
+
+ ///
+ /// The number of hits required to complete the swell successfully.
+ ///
+ public int RequiredHits { get; protected set; } = 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs
index 61d8ed5f01..ac47a3bc88 100644
--- a/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs
+++ b/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs
@@ -7,37 +7,28 @@ using osu.Game.Modes.Objects;
namespace osu.Game.Modes.Taiko.Objects
{
- public class TaikoHitObject : HitObject
+ public abstract class TaikoHitObject : HitObject
{
///
/// HitCircle radius.
///
- public const float CIRCLE_RADIUS = 64;
-
- ///
- /// The hit window that results in a "GREAT" hit.
- ///
- public double HitWindowGreat = 35;
-
- ///
- /// The hit window that results in a "GOOD" hit.
- ///
- public double HitWindowGood = 80;
-
- ///
- /// The hit window that results in a "MISS".
- ///
- public double HitWindowMiss = 95;
+ public const float CIRCLE_RADIUS = 42f;
///
/// The time to scroll in the HitObject.
///
public double PreEmpt;
+ ///
+ /// Whether this HitObject is a "strong" type.
+ /// Strong hit objects give more points for hitting the hit object with both keys.
+ ///
+ public bool IsStrong;
+
///
/// Whether this HitObject is in Kiai time.
///
- public bool Kiai;
+ public bool Kiai { get; protected set; }
public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
{
@@ -50,10 +41,6 @@ namespace osu.Game.Modes.Taiko.Objects
if (overridePoint != null)
Kiai |= overridePoint.KiaiMode;
-
- HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20);
- HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50);
- HitWindowMiss = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70);
}
}
}
\ No newline at end of file
diff --git a/osu.Game.Modes.Taiko/Replays/TaikoAutoReplay.cs b/osu.Game.Modes.Taiko/Replays/TaikoAutoReplay.cs
new file mode 100644
index 0000000000..c8a93c9068
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Replays/TaikoAutoReplay.cs
@@ -0,0 +1,121 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Game.Beatmaps;
+using osu.Game.Modes.Objects.Types;
+using osu.Game.Modes.Taiko.Objects;
+using osu.Game.Modes.Replays;
+
+namespace osu.Game.Modes.Taiko.Replays
+{
+ public class TaikoAutoReplay : Replay
+ {
+ private readonly Beatmap beatmap;
+
+ public TaikoAutoReplay(Beatmap beatmap)
+ {
+ this.beatmap = beatmap;
+
+ createAutoReplay();
+ }
+
+ private void createAutoReplay()
+ {
+ bool hitButton = true;
+
+ Frames.Add(new ReplayFrame(-100000, 320, 240, ReplayButtonState.None));
+ Frames.Add(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1000, 320, 240, ReplayButtonState.None));
+
+ for (int i = 0; i < beatmap.HitObjects.Count; i++)
+ {
+ TaikoHitObject h = beatmap.HitObjects[i];
+
+ ReplayButtonState button;
+
+ IHasEndTime endTimeData = h as IHasEndTime;
+ double endTime = endTimeData?.EndTime ?? h.StartTime;
+
+ Swell swell = h as Swell;
+ DrumRoll drumRoll = h as DrumRoll;
+ Hit hit = h as Hit;
+
+ if (swell != null)
+ {
+ int d = 0;
+ int count = 0;
+ int req = swell.RequiredHits;
+ double hitRate = swell.Duration / req;
+ for (double j = h.StartTime; j < endTime; j += hitRate)
+ {
+ switch (d)
+ {
+ default:
+ button = ReplayButtonState.Left1;
+ break;
+ case 1:
+ button = ReplayButtonState.Right1;
+ break;
+ case 2:
+ button = ReplayButtonState.Left2;
+ break;
+ case 3:
+ button = ReplayButtonState.Right2;
+ break;
+ }
+
+ Frames.Add(new ReplayFrame(j, 0, 0, button));
+ d = (d + 1) % 4;
+ if (++count > req)
+ break;
+ }
+ }
+ else if (drumRoll != null)
+ {
+ double delay = drumRoll.TickTimeDistance;
+
+ 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));
+ time += delay;
+ hitButton = !hitButton;
+ }
+ }
+ else if (hit != null)
+ {
+ if (hit is CentreHit)
+ {
+ if (h.IsStrong)
+ button = ReplayButtonState.Right1 | ReplayButtonState.Right2;
+ else
+ button = hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2;
+ }
+ else
+ {
+ if (h.IsStrong)
+ button = ReplayButtonState.Left1 | ReplayButtonState.Left2;
+ else
+ button = hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2;
+ }
+
+ Frames.Add(new ReplayFrame(h.StartTime, 0, 0, button));
+ }
+ else
+ throw new Exception("Unknown hit object type.");
+
+ Frames.Add(new ReplayFrame(endTime + 1, 0, 0, ReplayButtonState.None));
+
+ if (i < beatmap.HitObjects.Count - 1)
+ {
+ double waitTime = beatmap.HitObjects[i + 1].StartTime - 1000;
+ if (waitTime > endTime)
+ Frames.Add(new ReplayFrame(waitTime, 0, 0, ReplayButtonState.None));
+ }
+
+ hitButton = !hitButton;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game.Modes.Taiko/Replays/TaikoFramedReplayInputHandler.cs b/osu.Game.Modes.Taiko/Replays/TaikoFramedReplayInputHandler.cs
new file mode 100644
index 0000000000..44fca4abe7
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Replays/TaikoFramedReplayInputHandler.cs
@@ -0,0 +1,37 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Modes.Replays;
+using System.Collections.Generic;
+using osu.Framework.Input;
+using OpenTK.Input;
+
+namespace osu.Game.Modes.Taiko.Replays
+{
+ internal class TaikoFramedReplayInputHandler : FramedReplayInputHandler
+ {
+ public TaikoFramedReplayInputHandler(Replay replay)
+ : base(replay)
+ {
+ }
+
+ public override List GetPendingStates()
+ {
+ var keys = new List();
+
+ if (CurrentFrame?.MouseRight1 == true)
+ keys.Add(Key.F);
+ if (CurrentFrame?.MouseRight2 == true)
+ keys.Add(Key.J);
+ if (CurrentFrame?.MouseLeft1 == true)
+ keys.Add(Key.D);
+ if (CurrentFrame?.MouseLeft2 == true)
+ keys.Add(Key.K);
+
+ return new List
+ {
+ new InputState { Keyboard = new ReplayKeyboardState(keys) }
+ };
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Modes.Taiko/Scoring/TaikoScoreProcessor.cs
new file mode 100644
index 0000000000..fa7e18cadb
--- /dev/null
+++ b/osu.Game.Modes.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -0,0 +1,282 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Game.Beatmaps;
+using osu.Game.Database;
+using osu.Game.Modes.Objects.Drawables;
+using osu.Game.Modes.Scoring;
+using osu.Game.Modes.Taiko.Judgements;
+using osu.Game.Modes.Taiko.Objects;
+using osu.Game.Modes.UI;
+using OpenTK;
+
+namespace osu.Game.Modes.Taiko.Scoring
+{
+ internal class TaikoScoreProcessor : ScoreProcessor
+ {
+ ///
+ /// The maximum score achievable.
+ /// Does _not_ include bonus score - for bonus score see .
+ ///
+ private const int max_score = 1000000;
+
+ ///
+ /// The amount of the score attributed to combo.
+ ///
+ private const double combo_portion_max = max_score * 0.2;
+
+ ///
+ /// The amount of the score attributed to accuracy.
+ ///
+ private const double accuracy_portion_max = max_score * 0.8;
+
+ ///
+ /// The factor used to determine relevance of combos.
+ ///
+ private const double combo_base = 4;
+
+ ///
+ /// The HP awarded by a hit.
+ ///
+ private const double hp_hit_great = 0.03;
+
+ ///
+ /// The HP awarded for a hit.
+ ///
+ private const double hp_hit_good = 0.011;
+
+ ///
+ /// The minimum HP deducted for a .
+ /// This occurs when HP Drain = 0.
+ ///
+ private const double hp_miss_min = -0.0018;
+
+ ///
+ /// The median HP deducted for a .
+ /// This occurs when HP Drain = 5.
+ ///
+ private const double hp_miss_mid = -0.0075;
+
+ ///
+ /// The maximum HP deducted for a .
+ /// This occurs when HP Drain = 10.
+ ///
+ private const double hp_miss_max = -0.12;
+
+ ///
+ /// The HP awarded for a hit.
+ ///
+ /// hits award less HP as they're more spammable, although in hindsight
+ /// this probably awards too little HP and is kept at this value for now for compatibility.
+ ///
+ ///
+ private const double hp_hit_tick = 0.00000003;
+
+ ///
+ /// Taiko fails at the end of the map if the player has not half-filled their HP bar.
+ ///
+ public override bool HasFailed => totalHits == maxTotalHits && Health.Value <= 0.5;
+
+ ///
+ /// The cumulative combo portion of the score.
+ ///
+ private double comboScore => combo_portion_max * comboPortion / maxComboPortion;
+
+ ///
+ /// The cumulative accuracy portion of the score.
+ ///
+ private double accuracyScore => accuracy_portion_max * Math.Pow(Accuracy, 3.6) * totalHits / maxTotalHits;
+
+ ///
+ /// The cumulative bonus score.
+ /// This is added on top of , thus the total score can exceed .
+ ///
+ private double bonusScore;
+
+ ///
+ /// The multiple of the original score added to the combo portion of the score
+ /// for correctly hitting a strong hit object with both keys.
+ ///
+ private double strongHitScale;
+
+ private double hpIncreaseTick;
+ private double hpIncreaseGreat;
+ private double hpIncreaseGood;
+ private double hpIncreaseMiss;
+
+ private double maxComboPortion;
+ private double comboPortion;
+ private int maxTotalHits;
+ private int totalHits;
+
+ public TaikoScoreProcessor()
+ {
+ }
+
+ public TaikoScoreProcessor(HitRenderer hitRenderer)
+ : base(hitRenderer)
+ {
+ }
+
+ protected override void ComputeTargets(Beatmap beatmap)
+ {
+ double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, 0.5, 0.75, 0.98));
+
+ hpIncreaseTick = hp_hit_tick;
+ hpIncreaseGreat = hpMultiplierNormal * hp_hit_great;
+ hpIncreaseGood = hpMultiplierNormal * hp_hit_good;
+ hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);
+
+ var strongHits = beatmap.HitObjects.FindAll(o => o is Hit && o.IsStrong);
+
+ // This is a linear function that awards:
+ // 10 times bonus points for hitting a strong hit object with both keys with 30 strong hit objects in the map
+ // 3 times bonus points for hitting a strong hit object with both keys with 120 strong hit objects in the map
+ strongHitScale = -7d / 90d * MathHelper.Clamp(strongHits.Count, 30, 120) + 111d / 9d;
+
+ foreach (var obj in beatmap.HitObjects)
+ {
+ if (obj is Hit)
+ {
+ AddJudgement(new TaikoJudgement
+ {
+ Result = HitResult.Hit,
+ TaikoResult = TaikoHitResult.Great,
+ SecondHit = obj.IsStrong
+ });
+ }
+ else if (obj is DrumRoll)
+ {
+ for (int i = 0; i < ((DrumRoll)obj).TotalTicks; i++)
+ {
+ AddJudgement(new TaikoDrumRollTickJudgement
+ {
+ Result = HitResult.Hit,
+ TaikoResult = TaikoHitResult.Great,
+ SecondHit = obj.IsStrong
+ });
+ }
+
+ AddJudgement(new TaikoJudgement
+ {
+ Result = HitResult.Hit,
+ TaikoResult = TaikoHitResult.Great,
+ SecondHit = obj.IsStrong
+ });
+ }
+ else if (obj is Swell)
+ {
+ AddJudgement(new TaikoJudgement
+ {
+ Result = HitResult.Hit,
+ TaikoResult = TaikoHitResult.Great
+ });
+ }
+ }
+
+ maxTotalHits = totalHits;
+ maxComboPortion = comboPortion;
+ }
+
+ protected override void OnNewJudgement(TaikoJudgement judgement)
+ {
+ bool isTick = judgement is TaikoDrumRollTickJudgement;
+
+ // Don't consider ticks as a type of hit that counts towards map completion
+ if (!isTick)
+ totalHits++;
+
+ // Apply combo changes, must be done before the hit score is added
+ if (!isTick && judgement.Result == HitResult.Hit)
+ Combo.Value++;
+
+ // Apply score changes
+ addHitScore(judgement);
+
+ // Apply HP changes
+ switch (judgement.Result)
+ {
+ case HitResult.Miss:
+ // Missing ticks shouldn't drop HP
+ if (!isTick)
+ Health.Value += hpIncreaseMiss;
+ break;
+ case HitResult.Hit:
+ switch (judgement.TaikoResult)
+ {
+ case TaikoHitResult.Good:
+ Health.Value += hpIncreaseGood;
+ break;
+ case TaikoHitResult.Great:
+ if (isTick)
+ Health.Value += hpIncreaseTick;
+ else
+ Health.Value += hpIncreaseGreat;
+ break;
+ }
+ break;
+ }
+
+ calculateScore();
+ }
+
+ protected override void OnJudgementChanged(TaikoJudgement judgement)
+ {
+ // Apply score changes
+ addHitScore(judgement);
+
+ calculateScore();
+ }
+
+ private void addHitScore(TaikoJudgement judgement)
+ {
+ if (judgement.Result != HitResult.Hit)
+ return;
+
+ double baseValue = judgement.ResultValueForScore;
+
+ // Add increased score for hitting a strong hit object with the second key
+ if (judgement.SecondHit)
+ baseValue *= strongHitScale;
+
+ // Add score to portions
+ if (judgement is TaikoDrumRollTickJudgement)
+ bonusScore += baseValue;
+ else
+ {
+ // A relevance factor that needs to be applied to make higher combos more relevant
+ // Value is capped at 400 combo
+ double comboRelevance = Math.Min(Math.Log(400, combo_base), Math.Max(0.5, Math.Log(Combo.Value, combo_base)));
+
+ comboPortion += baseValue * comboRelevance;
+ }
+ }
+
+ private void calculateScore()
+ {
+ int scoreForAccuracy = 0;
+ int maxScoreForAccuracy = 0;
+
+ foreach (var j in Judgements)
+ {
+ scoreForAccuracy += j.ResultValueForAccuracy;
+ maxScoreForAccuracy = j.MaxResultValueForAccuracy;
+ }
+
+ Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy;
+ TotalScore.Value = comboScore + accuracyScore + bonusScore;
+ }
+
+ protected override void Reset()
+ {
+ base.Reset();
+
+ Health.Value = 0;
+
+ bonusScore = 0;
+ comboPortion = 0;
+ totalHits = 0;
+ }
+ }
+}
diff --git a/osu.Game.Modes.Taiko/TaikoRuleset.cs b/osu.Game.Modes.Taiko/TaikoRuleset.cs
index ff481e9162..1b3c3fc0eb 100644
--- a/osu.Game.Modes.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Modes.Taiko/TaikoRuleset.cs
@@ -10,6 +10,8 @@ using osu.Game.Modes.Taiko.UI;
using osu.Game.Modes.UI;
using osu.Game.Screens.Play;
using System.Collections.Generic;
+using osu.Game.Modes.Scoring;
+using osu.Game.Modes.Taiko.Scoring;
namespace osu.Game.Modes.Taiko
{
@@ -63,7 +65,7 @@ namespace osu.Game.Modes.Taiko
{
Mods = new Mod[]
{
- new ModAutoplay(),
+ new TaikoModAutoplay(),
new ModCinema(),
},
},
diff --git a/osu.Game.Modes.Taiko/TaikoScoreProcessor.cs b/osu.Game.Modes.Taiko/TaikoScoreProcessor.cs
deleted file mode 100644
index 849c0fa894..0000000000
--- a/osu.Game.Modes.Taiko/TaikoScoreProcessor.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Game.Modes.Taiko.Judgements;
-using osu.Game.Modes.Taiko.Objects;
-using osu.Game.Modes.UI;
-
-namespace osu.Game.Modes.Taiko
-{
- internal class TaikoScoreProcessor : ScoreProcessor
- {
- public TaikoScoreProcessor()
- {
- }
-
- public TaikoScoreProcessor(HitRenderer hitRenderer)
- : base(hitRenderer)
- {
- }
-
- protected override void UpdateCalculations(TaikoJudgementInfo newJudgement)
- {
- }
- }
-}
diff --git a/osu.Game.Modes.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Modes.Taiko/UI/DrawableTaikoJudgement.cs
new file mode 100644
index 0000000000..78c9657b40
--- /dev/null
+++ b/osu.Game.Modes.Taiko/UI/DrawableTaikoJudgement.cs
@@ -0,0 +1,57 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Modes.Taiko.Judgements;
+using osu.Game.Modes.Objects.Drawables;
+using osu.Framework.Allocation;
+using osu.Game.Graphics;
+using osu.Game.Modes.Judgements;
+
+namespace osu.Game.Modes.Taiko.UI
+{
+ ///
+ /// Text that is shown as judgement when a hit object is hit or missed.
+ ///
+ public class DrawableTaikoJudgement : DrawableJudgement
+ {
+ ///
+ /// Creates a new judgement text.
+ ///
+ /// The judgement to visualise.
+ public DrawableTaikoJudgement(TaikoJudgement judgement)
+ : base(judgement)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ switch (Judgement.Result)
+ {
+ case HitResult.Hit:
+ switch (Judgement.TaikoResult)
+ {
+ case TaikoHitResult.Good:
+ Colour = colours.GreenLight;
+ break;
+ case TaikoHitResult.Great:
+ Colour = colours.BlueLight;
+ break;
+ }
+ break;
+ }
+ }
+
+ protected override void LoadComplete()
+ {
+ switch (Judgement.Result)
+ {
+ case HitResult.Hit:
+ MoveToY(-100, 500);
+ break;
+ }
+
+ base.LoadComplete();
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game.Modes.Taiko/UI/RingExplosion.cs b/osu.Game.Modes.Taiko/UI/HitExplosion.cs
similarity index 76%
rename from osu.Game.Modes.Taiko/UI/RingExplosion.cs
rename to osu.Game.Modes.Taiko/UI/HitExplosion.cs
index 2bc1d2d3eb..94938a700d 100644
--- a/osu.Game.Modes.Taiko/UI/RingExplosion.cs
+++ b/osu.Game.Modes.Taiko/UI/HitExplosion.cs
@@ -7,7 +7,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
-using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects;
@@ -15,19 +14,17 @@ using osu.Game.Modes.Taiko.Objects;
namespace osu.Game.Modes.Taiko.UI
{
///
- /// A ring that explodes to indicate a judgement has occurred.
+ /// A circle explodes from the hit target to indicate a hitobject has been hit.
///
- internal class RingExplosion : CircularContainer
+ internal class HitExplosion : CircularContainer
{
- ///
- /// The Judgement to display.
- ///
- public TaikoJudgementInfo Judgement;
+ private readonly TaikoJudgement judgement;
+ private readonly Box innerFill;
- private Box innerFill;
-
- public RingExplosion()
+ public HitExplosion(TaikoJudgement judgement)
{
+ this.judgement = judgement;
+
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2);
Anchor = Anchor.Centre;
@@ -53,10 +50,10 @@ namespace osu.Game.Modes.Taiko.UI
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- if (Judgement.SecondHit)
+ if (judgement.SecondHit)
Size *= 1.5f;
- switch (Judgement.TaikoResult)
+ switch (judgement.TaikoResult)
{
case TaikoHitResult.Good:
innerFill.Colour = colours.Green;
diff --git a/osu.Game.Modes.Taiko/UI/HitTarget.cs b/osu.Game.Modes.Taiko/UI/HitTarget.cs
index cc332abb8d..32b2877545 100644
--- a/osu.Game.Modes.Taiko/UI/HitTarget.cs
+++ b/osu.Game.Modes.Taiko/UI/HitTarget.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Modes.Taiko.UI
///
/// Diameter of normal hit object circles.
///
- private const float normal_diameter = TaikoHitObject.CIRCLE_RADIUS * 2 * TaikoPlayfield.PLAYFIELD_SCALE;
+ private const float normal_diameter = TaikoHitObject.CIRCLE_RADIUS * 2;
///
/// Diameter of finisher hit object circles.
@@ -30,6 +30,11 @@ namespace osu.Game.Modes.Taiko.UI
///
private const float border_offset = 1;
+ ///
+ /// Thickness of all drawn line pieces.
+ ///
+ private const float border_thickness = 2.5f;
+
public HitTarget()
{
RelativeSizeAxes = Axes.Y;
@@ -42,7 +47,7 @@ namespace osu.Game.Modes.Taiko.UI
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Y = border_offset,
- Size = new Vector2(3, (TaikoPlayfield.PlayfieldHeight - finisher_diameter) / 2f - border_offset),
+ Size = new Vector2(border_thickness, (TaikoPlayfield.PLAYFIELD_HEIGHT - finisher_diameter) / 2f - border_offset),
Alpha = 0.1f
},
new CircularContainer
@@ -53,7 +58,7 @@ namespace osu.Game.Modes.Taiko.UI
Size = new Vector2(finisher_diameter),
Masking = true,
BorderColour = Color4.White,
- BorderThickness = 2,
+ BorderThickness = border_thickness,
Alpha = 0.1f,
Children = new[]
{
@@ -73,7 +78,7 @@ namespace osu.Game.Modes.Taiko.UI
Size = new Vector2(normal_diameter),
Masking = true,
BorderColour = Color4.White,
- BorderThickness = 2,
+ BorderThickness = border_thickness,
Alpha = 0.5f,
Children = new[]
{
@@ -91,7 +96,7 @@ namespace osu.Game.Modes.Taiko.UI
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Y = -border_offset,
- Size = new Vector2(3, (TaikoPlayfield.PlayfieldHeight - finisher_diameter) / 2f - border_offset),
+ Size = new Vector2(border_thickness, (TaikoPlayfield.PLAYFIELD_HEIGHT - finisher_diameter) / 2f - border_offset),
Alpha = 0.1f
},
};
diff --git a/osu.Game.Modes.Taiko/UI/InputDrum.cs b/osu.Game.Modes.Taiko/UI/InputDrum.cs
index b763b53aa2..e7470ee913 100644
--- a/osu.Game.Modes.Taiko/UI/InputDrum.cs
+++ b/osu.Game.Modes.Taiko/UI/InputDrum.cs
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using OpenTK;
using OpenTK.Input;
using osu.Framework.Allocation;
@@ -8,10 +9,8 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Game.Graphics;
-using System.Collections.Generic;
namespace osu.Game.Modes.Taiko.UI
{
@@ -22,7 +21,9 @@ namespace osu.Game.Modes.Taiko.UI
{
public InputDrum()
{
- Size = new Vector2(TaikoPlayfield.PlayfieldHeight);
+ Size = new Vector2(TaikoPlayfield.PLAYFIELD_HEIGHT);
+
+ const float middle_split = 10;
Children = new Drawable[]
{
@@ -32,7 +33,9 @@ namespace osu.Game.Modes.Taiko.UI
Anchor = Anchor.Centre,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.Both,
- Keys = new List(new[] { Key.F, Key.D })
+ X = -middle_split / 2,
+ RimKey = Key.D,
+ CentreKey = Key.F
},
new TaikoHalfDrum(true)
{
@@ -40,8 +43,10 @@ namespace osu.Game.Modes.Taiko.UI
Anchor = Anchor.Centre,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both,
+ X = middle_split / 2,
Position = new Vector2(-1f, 0),
- Keys = new List(new[] { Key.J, Key.K })
+ RimKey = Key.K,
+ CentreKey = Key.J
}
};
}
@@ -52,17 +57,19 @@ namespace osu.Game.Modes.Taiko.UI
private class TaikoHalfDrum : Container
{
///
- /// A list of keys which this half-drum accepts.
- ///
- /// [0] => Inner key, [1] => Outer key
- ///
+ /// The key to be used for the rim of the half-drum.
///
- public List Keys = new List();
+ public Key RimKey;
+
+ ///
+ /// The key to be used for the centre of the half-drum.
+ ///
+ public Key CentreKey;
- private Sprite outer;
- private Sprite outerHit;
- private Sprite inner;
- private Sprite innerHit;
+ private readonly Sprite rim;
+ private readonly Sprite rimHit;
+ private readonly Sprite centre;
+ private readonly Sprite centreHit;
public TaikoHalfDrum(bool flipped)
{
@@ -70,28 +77,28 @@ namespace osu.Game.Modes.Taiko.UI
Children = new Drawable[]
{
- outer = new Sprite
+ rim = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both
},
- outerHit = new Sprite
+ rimHit = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0,
- BlendingMode = BlendingMode.Additive
+ BlendingMode = BlendingMode.Additive,
},
- inner = new Sprite
+ centre = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.7f)
},
- innerHit = new Sprite
+ centreHit = new Sprite
{
Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight,
Origin = Anchor.Centre,
@@ -106,13 +113,13 @@ namespace osu.Game.Modes.Taiko.UI
[BackgroundDependencyLoader]
private void load(TextureStore textures, OsuColour colours)
{
- outer.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer");
- outerHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer-hit");
- inner.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner");
- innerHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner-hit");
+ rim.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer");
+ rimHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer-hit");
+ centre.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner");
+ centreHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner-hit");
- outerHit.Colour = colours.Blue;
- innerHit.Colour = colours.Pink;
+ rimHit.Colour = colours.Blue;
+ centreHit.Colour = colours.Pink;
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
@@ -120,16 +127,18 @@ namespace osu.Game.Modes.Taiko.UI
if (args.Repeat)
return false;
- if (args.Key == Keys[0])
- {
- innerHit.FadeIn();
- innerHit.FadeOut(500, EasingTypes.OutQuint);
- }
+ Drawable target = null;
- if (args.Key == Keys[1])
+ if (args.Key == CentreKey)
+ target = centreHit;
+ else if (args.Key == RimKey)
+ target = rimHit;
+
+ if (target != null)
{
- outerHit.FadeIn();
- outerHit.FadeOut(500, EasingTypes.OutQuint);
+ target.FadeTo(Math.Min(target.Alpha + 0.4f, 1), 40, EasingTypes.OutQuint);
+ target.Delay(40);
+ target.FadeOut(1000, EasingTypes.OutQuint);
}
return false;
diff --git a/osu.Game.Modes.Taiko/UI/JudgementText.cs b/osu.Game.Modes.Taiko/UI/JudgementText.cs
deleted file mode 100644
index 8d86e880d6..0000000000
--- a/osu.Game.Modes.Taiko/UI/JudgementText.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright (c) 2007-2017 ppy Pty Ltd .
-// 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.Transforms;
-using osu.Game.Graphics.Sprites;
-using OpenTK;
-using OpenTK.Graphics;
-using osu.Game.Modes.Taiko.Judgements;
-using osu.Game.Modes.Objects.Drawables;
-using osu.Framework.Allocation;
-using osu.Game.Graphics;
-
-namespace osu.Game.Modes.Taiko.UI
-{
- ///
- /// Text that is shown as judgement when a hit object is hit or missed.
- ///
- public class JudgementText : Container
- {
- ///
- /// The Judgement to display.
- ///
- public TaikoJudgementInfo Judgement;
-
- private Container textContainer;
- private OsuSpriteText glowText;
- private OsuSpriteText normalText;
-
- private int movementDirection;
-
- public JudgementText()
- {
- AutoSizeAxes = Axes.Both;
-
- Children = new Drawable[]
- {
- textContainer = new Container
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- AutoSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- new BufferedContainer
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- BlurSigma = new Vector2(10),
- CacheDrawnFrameBuffer = true,
- RelativeSizeAxes = Axes.Both,
- Size = new Vector2(3),
- BlendingMode = BlendingMode.Additive,
- Children = new[]
- {
- glowText = new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Font = "Venera",
- TextSize = 22f,
- }
- }
- },
- normalText = new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Font = "Venera",
- TextSize = 22f,
- }
- }
- }
- };
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- Color4 judgementColour = Color4.White;
- string judgementText = string.Empty;
-
- switch (Judgement.Result)
- {
- case HitResult.Miss:
- judgementColour = colours.Red;
- judgementText = "MISS";
- movementDirection = 1;
- break;
- case HitResult.Hit:
- switch (Judgement.TaikoResult)
- {
- case TaikoHitResult.Good:
- judgementColour = colours.Green;
- judgementText = "GOOD";
- textContainer.Scale = new Vector2(0.45f);
- break;
- case TaikoHitResult.Great:
- judgementColour = colours.Blue;
- judgementText = "GREAT";
- break;
- }
-
- movementDirection = -1;
- break;
- }
-
- glowText.Colour = judgementColour;
- glowText.Text = normalText.Text = judgementText;
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
-
- ScaleTo(1.5f, 250, EasingTypes.OutQuint);
- MoveToY(movementDirection * 100, 500);
-
- Delay(250);
- ScaleTo(0.75f, 250);
- FadeOut(250);
-
- Expire();
- }
- }
-}
\ No newline at end of file
diff --git a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs
index d88ffa7ae2..7086dcfbc6 100644
--- a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs
+++ b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs
@@ -6,16 +6,18 @@ using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Timing;
using osu.Game.Modes.Objects.Drawables;
-using osu.Game.Modes.Objects.Types;
+using osu.Game.Modes.Replays;
+using osu.Game.Modes.Scoring;
using osu.Game.Modes.Taiko.Beatmaps;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects;
-using osu.Game.Modes.Taiko.Objects.Drawable;
+using osu.Game.Modes.Taiko.Replays;
+using osu.Game.Modes.Taiko.Scoring;
using osu.Game.Modes.UI;
namespace osu.Game.Modes.Taiko.UI
{
- public class TaikoHitRenderer : HitRenderer
+ public class TaikoHitRenderer : HitRenderer
{
public TaikoHitRenderer(WorkingBeatmap beatmap)
: base(beatmap)
@@ -102,8 +104,10 @@ namespace osu.Game.Modes.Taiko.UI
protected override IBeatmapProcessor CreateBeatmapProcessor() => new TaikoBeatmapProcessor();
- protected override Playfield CreatePlayfield() => new TaikoPlayfield();
+ protected override Playfield CreatePlayfield() => new TaikoPlayfield();
- protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h) => null;
+ protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h) => null;
+
+ protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay);
}
}
diff --git a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs
index 993eaaabe7..d9eae9a773 100644
--- a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs
@@ -18,27 +18,18 @@ using osu.Game.Modes.Taiko.Objects.Drawable;
namespace osu.Game.Modes.Taiko.UI
{
- public class TaikoPlayfield : Playfield
+ public class TaikoPlayfield : Playfield
{
///
- /// The default play field height.
+ /// The play field height. This is relative to the size of hit objects
+ /// such that the playfield is just a bit larger than finishers.
///
- public const float PLAYFIELD_BASE_HEIGHT = 242;
-
- ///
- /// The play field height scale.
- ///
- public const float PLAYFIELD_SCALE = 0.65f;
-
- ///
- /// The play field height after scaling.
- ///
- public static float PlayfieldHeight => PLAYFIELD_BASE_HEIGHT * PLAYFIELD_SCALE;
+ public const float PLAYFIELD_HEIGHT = TaikoHitObject.CIRCLE_RADIUS * 2 * 2;
///
/// The offset from which the center of the hit target lies at.
///
- private const float hit_target_offset = 80;
+ private const float hit_target_offset = TaikoHitObject.CIRCLE_RADIUS * 1.5f + 40;
///
/// The size of the left area of the playfield. This area contains the input drum.
@@ -47,22 +38,21 @@ namespace osu.Game.Modes.Taiko.UI
protected override Container Content => hitObjectContainer;
- private Container ringExplosionContainer;
+ private readonly Container hitExplosionContainer;
private Container barLineContainer;
- private Container judgementContainer;
+ private readonly Container judgementContainer;
- private Container hitObjectContainer;
- // ReSharper disable once NotAccessedField.Local
- private Container topLevelHitContainer;
- private Container leftBackgroundContainer;
- private Container rightBackgroundContainer;
- private Box leftBackground;
- private Box rightBackground;
+ private readonly Container hitObjectContainer;
+ private readonly Container topLevelHitContainer;
+ private readonly Container leftBackgroundContainer;
+ private readonly Container rightBackgroundContainer;
+ private readonly Box leftBackground;
+ private readonly Box rightBackground;
public TaikoPlayfield()
{
RelativeSizeAxes = Axes.X;
- Height = PlayfieldHeight;
+ Height = PLAYFIELD_HEIGHT;
AddInternal(new Drawable[]
{
@@ -98,12 +88,11 @@ namespace osu.Game.Modes.Taiko.UI
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
- ringExplosionContainer = new Container
+ hitExplosionContainer = new Container
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2),
- Scale = new Vector2(PLAYFIELD_SCALE),
BlendingMode = BlendingMode.Additive
},
barLineContainer = new Container
@@ -119,7 +108,7 @@ namespace osu.Game.Modes.Taiko.UI
{
RelativeSizeAxes = Axes.Both,
},
- judgementContainer = new Container
+ judgementContainer = new Container
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive
@@ -130,7 +119,7 @@ namespace osu.Game.Modes.Taiko.UI
},
leftBackgroundContainer = new Container
{
- Size = new Vector2(left_area_size, PlayfieldHeight),
+ Size = new Vector2(left_area_size, PLAYFIELD_HEIGHT),
BorderThickness = 1,
Children = new Drawable[]
{
@@ -142,10 +131,8 @@ namespace osu.Game.Modes.Taiko.UI
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
-
RelativePositionAxes = Axes.X,
Position = new Vector2(0.10f, 0),
-
Scale = new Vector2(0.9f)
},
new Box
@@ -174,11 +161,16 @@ namespace osu.Game.Modes.Taiko.UI
rightBackground.Colour = colours.Gray0;
}
- public override void Add(DrawableHitObject h)
+ public override void Add(DrawableHitObject h)
{
h.Depth = (float)h.HitObject.StartTime;
base.Add(h);
+
+ // Swells should be moved at the very top of the playfield when they reach the hit target
+ var swell = h as DrawableSwell;
+ if (swell != null)
+ swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy());
}
public void AddBarLine(DrawableBarLine barLine)
@@ -186,27 +178,19 @@ namespace osu.Game.Modes.Taiko.UI
barLineContainer.Add(barLine);
}
- public override void OnJudgement(DrawableHitObject judgedObject)
+ public override void OnJudgement(DrawableHitObject judgedObject)
{
- if (judgedObject.Judgement.Result == HitResult.Hit)
+ bool wasHit = judgedObject.Judgement.Result == HitResult.Hit;
+
+ if (wasHit)
+ hitExplosionContainer.Add(new HitExplosion(judgedObject.Judgement));
+
+ judgementContainer.Add(new DrawableTaikoJudgement(judgedObject.Judgement)
{
- ringExplosionContainer.Add(new RingExplosion
- {
- Judgement = judgedObject.Judgement
- });
- }
-
- float judgementOffset = judgedObject.Judgement.Result == HitResult.Hit ? judgedObject.Position.X : 0;
-
- judgementContainer.Add(new JudgementText
- {
- Anchor = judgedObject.Judgement.Result == HitResult.Hit ? Anchor.TopLeft : Anchor.BottomLeft,
- Origin = judgedObject.Judgement.Result == HitResult.Hit ? Anchor.BottomCentre : Anchor.TopCentre,
-
+ Anchor = wasHit ? Anchor.TopLeft : Anchor.CentreLeft,
+ Origin = wasHit ? Anchor.BottomCentre : Anchor.Centre,
RelativePositionAxes = Axes.X,
- X = judgementOffset,
-
- Judgement = judgedObject.Judgement
+ X = wasHit ? judgedObject.Position.X : 0,
});
}
}
diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj
index 42f0f839c4..7446979fc4 100644
--- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj
+++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj
@@ -49,20 +49,44 @@
-
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
+
+
@@ -80,10 +104,6 @@
{C76BF5B3-985E-4D39-95FE-97C9C879B83A}
osu.Framework
-
- {C92A607B-1FDD-4954-9F92-03FF547D9080}
- osu.Game.Modes.Osu
-
{0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D}
osu.Game
diff --git a/osu.Game.Modes.Taiko/packages.config b/osu.Game.Modes.Taiko/packages.config
index 08fca09c35..4031dd62a8 100644
--- a/osu.Game.Modes.Taiko/packages.config
+++ b/osu.Game.Modes.Taiko/packages.config
@@ -1,5 +1,4 @@
-