diff --git a/NuGet.config b/NuGet.config
new file mode 100644
index 0000000000..95f993e510
--- /dev/null
+++ b/NuGet.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
index b26a895788..21c15724e6 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,3 +1,4 @@
+# 2017-09-14
clone_depth: 1
version: '{branch}-{build}'
configuration: Debug
diff --git a/osu-framework b/osu-framework
index 1a259925b8..7347c386dc 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit 1a259925b82c31ddcebf7b330a6ef9d3a9daf089
+Subproject commit 7347c386dcd10eb799b1ce1512536879328109f9
diff --git a/osu.Desktop.Deploy/App.config b/osu.Desktop.Deploy/App.config
index 6711f9c54e..2fae7a5e1c 100644
--- a/osu.Desktop.Deploy/App.config
+++ b/osu.Desktop.Deploy/App.config
@@ -33,7 +33,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-
+
diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
index c090342a4b..a66c9c8993 100644
--- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
+++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
@@ -66,22 +66,23 @@
True
- $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll
-
-
- $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll
+ $(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll
True
-
- $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll
+
+ $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll
+ True
+
+
+ $(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll
True
$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll
True
-
- $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll
+
+ $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\Squirrel.dll
True
diff --git a/osu.Desktop.Deploy/packages.config b/osu.Desktop.Deploy/packages.config
index 3c5ca9f9a3..7725be5f5e 100644
--- a/osu.Desktop.Deploy/packages.config
+++ b/osu.Desktop.Deploy/packages.config
@@ -6,9 +6,9 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/osu.Desktop.Tests/OpenTK.dll.config b/osu.Desktop.Tests/OpenTK.dll.config
new file mode 100644
index 0000000000..5620e3d9e2
--- /dev/null
+++ b/osu.Desktop.Tests/OpenTK.dll.config
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/osu.Desktop.Tests/Visual/TestCaseStoryboard.cs b/osu.Desktop.Tests/Visual/TestCaseStoryboard.cs
new file mode 100644
index 0000000000..878198e8d2
--- /dev/null
+++ b/osu.Desktop.Tests/Visual/TestCaseStoryboard.cs
@@ -0,0 +1,90 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using OpenTK.Graphics;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Timing;
+using osu.Game;
+using osu.Game.Beatmaps;
+using osu.Game.Overlays;
+using osu.Game.Storyboards.Drawables;
+
+namespace osu.Desktop.Tests.Visual
+{
+ internal class TestCaseStoryboard : OsuTestCase
+ {
+ public override string Description => @"Tests storyboards.";
+
+ private readonly Bindable beatmapBacking = new Bindable();
+
+ private readonly Container storyboardContainer;
+ private DrawableStoryboard storyboard;
+
+ public TestCaseStoryboard()
+ {
+ Clock = new FramedClock();
+
+ Add(new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black,
+ },
+ storyboardContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ },
+ });
+ Add(new MusicController
+ {
+ Origin = Anchor.TopRight,
+ Anchor = Anchor.TopRight,
+ State = Visibility.Visible,
+ });
+
+ AddStep("Restart", restart);
+ AddToggleStep("Passing", passing => { if (storyboard != null) storyboard.Passing = passing; });
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase game)
+ {
+ beatmapBacking.BindTo(game.Beatmap);
+ beatmapBacking.ValueChanged += beatmapChanged;
+ }
+
+ private void beatmapChanged(WorkingBeatmap working)
+ => loadStoryboard(working);
+
+ private void restart()
+ {
+ var track = beatmapBacking.Value.Track;
+
+ track.Reset();
+ loadStoryboard(beatmapBacking.Value);
+ track.Start();
+ }
+
+ private void loadStoryboard(WorkingBeatmap working)
+ {
+ if (storyboard != null)
+ storyboardContainer.Remove(storyboard);
+
+ var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true };
+ decoupledClock.ChangeSource(working.Track);
+ storyboardContainer.Clock = decoupledClock;
+
+ storyboardContainer.Add(storyboard = working.Beatmap.Storyboard.CreateDrawable());
+ storyboard.Passing = false;
+ }
+ }
+}
diff --git a/osu.Desktop.Tests/osu.Desktop.Tests.csproj b/osu.Desktop.Tests/osu.Desktop.Tests.csproj
index 92ddcc65ca..2811fc2910 100644
--- a/osu.Desktop.Tests/osu.Desktop.Tests.csproj
+++ b/osu.Desktop.Tests/osu.Desktop.Tests.csproj
@@ -35,14 +35,15 @@
- $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll
+ $(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll
+ True
-
- $(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll
+
+ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -91,6 +92,7 @@
+
@@ -157,6 +159,7 @@
osu.licenseheader
+
diff --git a/osu.Desktop.Tests/packages.config b/osu.Desktop.Tests/packages.config
index ed487e5cd5..ea33822638 100644
--- a/osu.Desktop.Tests/packages.config
+++ b/osu.Desktop.Tests/packages.config
@@ -4,9 +4,9 @@ Copyright (c) 2007-2017 ppy Pty Ltd .
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
-
-
-
+
+
+
diff --git a/osu.Desktop/OpenTK.dll.config b/osu.Desktop/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Desktop/OpenTK.dll.config
+++ b/osu.Desktop/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
diff --git a/osu.Desktop/app.config b/osu.Desktop/app.config
new file mode 100644
index 0000000000..a704cc3750
--- /dev/null
+++ b/osu.Desktop/app.config
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 661c17699b..5eebad47ef 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -136,23 +136,23 @@
- $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\NuGet.Squirrel.dll
+ $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\NuGet.Squirrel.dll
True
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
-
- $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll
+
+ $(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll
True
$(SolutionDir)\packages\Splat.2.0.0\lib\Net45\Splat.dll
True
-
- $(SolutionDir)\packages\squirrel.windows.1.7.5\lib\Net45\Squirrel.dll
+
+ $(SolutionDir)\packages\squirrel.windows.1.7.8\lib\Net45\Squirrel.dll
True
@@ -173,6 +173,7 @@
osu.licenseheader
+
diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config
index 3ad2106d2b..269b901a97 100644
--- a/osu.Desktop/packages.config
+++ b/osu.Desktop/packages.config
@@ -7,8 +7,8 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-
-
+
+
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index 214cbc7f50..1a9b034cf2 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -9,8 +9,6 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Catch.Scoring;
-using osu.Game.Rulesets.Scoring;
using osu.Framework.Input.Bindings;
namespace osu.Game.Rulesets.Catch
@@ -99,8 +97,6 @@ namespace osu.Game.Rulesets.Catch
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new CatchDifficultyCalculator(beatmap);
- public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor();
-
public override int LegacyID => 2;
public CatchRuleset(RulesetInfo rulesetInfo)
diff --git a/osu.Game.Rulesets.Catch/OpenTK.dll.config b/osu.Game.Rulesets.Catch/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Game.Rulesets.Catch/OpenTK.dll.config
+++ b/osu.Game.Rulesets.Catch/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
index 2637a98fe2..11e95622ca 100644
--- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Catch.Objects;
-using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@@ -18,17 +17,5 @@ namespace osu.Game.Rulesets.Catch.Scoring
: base(rulesetContainer)
{
}
-
- protected override void Reset()
- {
- base.Reset();
-
- Health.Value = 1;
- Accuracy.Value = 1;
- }
-
- protected override void OnNewJudgement(Judgement judgement)
- {
- }
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index 0dac3ec41c..2b6f9bbf5a 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -45,14 +45,15 @@ namespace osu.Game.Rulesets.Catch.UI
public override void Add(DrawableHitObject h)
{
+ h.Depth = (float)h.HitObject.StartTime;
+
base.Add(h);
var fruit = (DrawableFruit)h;
fruit.CheckPosition = catcherArea.CheckIfWeCanCatch;
- fruit.OnJudgement += Fruit_OnJudgement;
}
- private void Fruit_OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
if (judgement.IsHit)
{
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index 64dbe04ba4..5fc2cf9ef7 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -163,7 +163,7 @@ namespace osu.Game.Rulesets.Catch.UI
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
- while (Children.OfType().Any(f => Vector2.DistanceSquared(f.Position, fruit.Position) < distance * distance))
+ while (Children.OfType().Any(f => Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
{
fruit.X += RNG.Next(-5, 5);
fruit.Y -= RNG.Next(0, 5);
diff --git a/osu.Game.Rulesets.Catch/app.config b/osu.Game.Rulesets.Catch/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
index 2ae2262ac7..18e1ee29ca 100644
--- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
+++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
@@ -34,7 +34,7 @@
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -69,6 +69,7 @@
osu.licenseheader
+
diff --git a/osu.Game.Rulesets.Catch/packages.config b/osu.Game.Rulesets.Catch/packages.config
index fa6edb9c8f..0b1838ceee 100644
--- a/osu.Game.Rulesets.Catch/packages.config
+++ b/osu.Game.Rulesets.Catch/packages.config
@@ -1,9 +1,4 @@
-
-
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
index cc7aa63f7e..a8d1b079eb 100644
--- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
@@ -23,17 +23,5 @@ namespace osu.Game.Rulesets.Mania.Judgements
return base.NumericResultFor(HasBroken ? HitResult.Good : result);
}
}
-
- protected override int NumericResultForAccuracy(HitResult result)
- {
- switch (result)
- {
- default:
- return base.NumericResultForAccuracy(result);
- case HitResult.Great:
- case HitResult.Perfect:
- return base.NumericResultForAccuracy(HasBroken ? HitResult.Good : result);
- }
- }
}
}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs
index c00572edff..d326c6fc0a 100644
--- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTickJudgement.cs
@@ -10,6 +10,5 @@ namespace osu.Game.Rulesets.Mania.Judgements
public override bool AffectsCombo => false;
protected override int NumericResultFor(HitResult result) => 20;
- protected override int NumericResultForAccuracy(HitResult result) => 0; // Don't count ticks into accuracy
}
}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs
index e36146aa71..1f3b352da4 100644
--- a/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs
+++ b/osu.Game.Rulesets.Mania/Judgements/ManiaJudgement.cs
@@ -8,11 +8,6 @@ namespace osu.Game.Rulesets.Mania.Judgements
{
public class ManiaJudgement : Judgement
{
- ///
- /// The maximum result value for the accuracy portion of the score.
- ///
- public int MaxNumericAccuracyResult => NumericResultForAccuracy(HitResult.Perfect);
-
protected override int NumericResultFor(HitResult result)
{
switch (result)
@@ -30,29 +25,5 @@ namespace osu.Game.Rulesets.Mania.Judgements
return 300;
}
}
-
- public int NumericAccuracyResult => NumericResultForAccuracy(Result);
-
- ///
- /// The result value for the accuracy portion of the score.
- ///
- protected virtual int NumericResultForAccuracy(HitResult result)
- {
- switch (result)
- {
- default:
- return 0;
- case HitResult.Meh:
- return 50;
- case HitResult.Ok:
- return 100;
- case HitResult.Good:
- return 200;
- case HitResult.Great:
- return 300;
- case HitResult.Perfect:
- return 305;
- }
- }
}
}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 0011d2837f..c0996cadf9 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -10,8 +10,6 @@ using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Mania.Scoring;
-using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania
{
@@ -111,8 +109,6 @@ namespace osu.Game.Rulesets.Mania
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap);
- public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor();
-
public override int LegacyID => 3;
public ManiaRuleset(RulesetInfo rulesetInfo)
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
index 49bca6d1d7..324f4e4e99 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
@@ -78,8 +78,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
- protected ManiaJudgement CreateJudgement() => new HoldNoteTickJudgement();
-
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
@@ -91,7 +89,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (HoldStartTime?.Invoke() > HitObject.StartTime)
return;
- AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
+ AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
}
protected override void UpdateState(ArmedState state)
diff --git a/osu.Game.Rulesets.Mania/OpenTK.dll.config b/osu.Game.Rulesets.Mania/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Game.Rulesets.Mania/OpenTK.dll.config
+++ b/osu.Game.Rulesets.Mania/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index 946caf71cf..a200ba31e2 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2017 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
@@ -15,32 +14,6 @@ namespace osu.Game.Rulesets.Mania.Scoring
{
internal class ManiaScoreProcessor : 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 combo value at which hit objects result in the max score possible.
- ///
- private const int combo_relevance_cap = 400;
-
///
/// The hit HP multiplier at OD = 0.
///
@@ -116,42 +89,6 @@ namespace osu.Game.Rulesets.Mania.Scoring
///
private double hpMultiplier = 1;
- ///
- /// 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, 4) * totalHits / maxTotalHits;
-
- ///
- /// The cumulative bonus score.
- /// This is added on top of , thus the total score can exceed .
- ///
- private double bonusScore;
-
- ///
- /// The achieved by a perfect playthrough.
- ///
- private double maxComboPortion;
-
- ///
- /// The portion of the score dedicated to combo.
- ///
- private double comboPortion;
-
- ///
- /// The achieved by a perfect playthrough.
- ///
- private int maxTotalHits;
-
- ///
- /// The total hits.
- ///
- private int totalHits;
-
public ManiaScoreProcessor()
{
}
@@ -161,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
{
}
- protected override void ComputeTargets(Beatmap beatmap)
+ protected override void SimulateAutoplay(Beatmap beatmap)
{
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.Difficulty;
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
@@ -173,11 +110,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
{
var holdNote = obj as HoldNote;
- if (obj is Note)
- {
- AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
- }
- else if (holdNote != null)
+ if (holdNote != null)
{
// Head
AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
@@ -186,9 +119,9 @@ namespace osu.Game.Rulesets.Mania.Scoring
int tickCount = holdNote.Ticks.Count();
for (int i = 0; i < tickCount; i++)
AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
-
- AddJudgement(new HoldNoteTailJudgement { Result = HitResult.Perfect });
}
+
+ AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
}
if (!HasFailed)
@@ -197,29 +130,23 @@ namespace osu.Game.Rulesets.Mania.Scoring
hpMultiplier *= 1.01;
hpMissMultiplier *= 0.98;
- Reset();
+ Reset(false);
}
-
- maxTotalHits = totalHits;
- maxComboPortion = comboPortion;
}
protected override void OnNewJudgement(Judgement judgement)
{
+ base.OnNewJudgement(judgement);
+
bool isTick = judgement is HoldNoteTickJudgement;
if (isTick)
{
if (judgement.IsHit)
- {
Health.Value += hpMultiplier * hp_increase_tick;
- bonusScore += judgement.NumericResult;
- }
}
else
{
- totalHits++;
-
switch (judgement.Result)
{
case HitResult.Miss:
@@ -241,40 +168,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
Health.Value += hpMultiplier * hp_increase_perfect;
break;
}
-
- if (judgement.IsHit)
- {
- // A factor that is applied to make higher combos more relevant
- double comboRelevance = Math.Min(Math.Max(0.5, Math.Log(Combo.Value, combo_base)), Math.Log(combo_relevance_cap, combo_base));
- comboPortion += judgement.NumericResult * comboRelevance;
- }
}
-
- int scoreForAccuracy = 0;
- int maxScoreForAccuracy = 0;
-
- foreach (var j in Judgements)
- {
- var maniaJudgement = (ManiaJudgement)j;
-
- scoreForAccuracy += maniaJudgement.NumericAccuracyResult;
- maxScoreForAccuracy += maniaJudgement.MaxNumericAccuracyResult;
- }
-
- Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy;
- TotalScore.Value = comboScore + accuracyScore + bonusScore;
- }
-
- protected override void Reset()
- {
- base.Reset();
-
- Health.Value = 1;
- Accuracy.Value = 1;
-
- bonusScore = 0;
- comboPortion = 0;
- totalHits = 0;
}
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 1133c9f2ee..2d553f8639 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -203,6 +203,8 @@ namespace osu.Game.Rulesets.Mania.UI
/// The DrawableHitObject to add.
public override void Add(DrawableHitObject hitObject)
{
+ hitObject.Depth = (float)hitObject.HitObject.StartTime;
+
hitObject.AccentColour = AccentColour;
HitObjects.Add(hitObject);
}
diff --git a/osu.Game.Rulesets.Mania/app.config b/osu.Game.Rulesets.Mania/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
index bd540f72c0..739f1cf48c 100644
--- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
+++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
@@ -34,7 +34,7 @@
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -110,6 +110,7 @@
osu.licenseheader
+
diff --git a/osu.Game.Rulesets.Mania/packages.config b/osu.Game.Rulesets.Mania/packages.config
index 8add43d5d5..0b1838ceee 100644
--- a/osu.Game.Rulesets.Mania/packages.config
+++ b/osu.Game.Rulesets.Mania/packages.config
@@ -1,8 +1,4 @@
-
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
index fce0188cda..621584ce05 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.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 OpenTK;
+using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
@@ -64,8 +64,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
//We are no longer within stacking range of the next object.
break;
- if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
- stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
+ if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
+ stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
{
stackBaseIndex = n;
@@ -130,14 +130,14 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
* o <- hitCircle has stack of -1
* o <- hitCircle has stack of -2
*/
- if (objectN is Slider && Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
+ if (objectN is Slider && Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{
int offset = objectI.StackHeight - objectN.StackHeight + 1;
for (int j = n + 1; j <= i; j++)
{
//For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above).
OsuHitObject objectJ = beatmap.HitObjects[j];
- if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stack_distance)
+ if (Vector2Extensions.Distance(objectN.EndPosition, objectJ.Position) < stack_distance)
objectJ.StackHeight -= offset;
}
@@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
break;
}
- if (Vector2.Distance(objectN.Position, objectI.Position) < stack_distance)
+ if (Vector2Extensions.Distance(objectN.Position, objectI.Position) < stack_distance)
{
//Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out.
//NOTE: Sliders with start positions stacking are a special case that is also handled here.
@@ -170,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
//We are no longer within stacking range of the previous object.
break;
- if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
+ if (Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{
objectN.StackHeight = objectI.StackHeight + 1;
objectI = objectN;
diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
index 3d19d19546..28b6a04376 100644
--- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
@@ -8,12 +8,10 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Judgements
{
- public class SliderTickJudgement : OsuJudgement
- {
- }
-
public class OsuJudgement : Judgement
{
+ public override HitResult MaxResult => HitResult.Great;
+
///
/// The positional hit offset.
///
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 7cb06df679..c5155d1e10 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Scale = s.Scale,
ComboColour = s.ComboColour,
Samples = s.Samples,
- }),
+ })
};
components.Add(body);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index 0938f78843..53b3427fc4 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
- AddJudgement(new SliderTickJudgement { Result = Tracking ? HitResult.Perfect : HitResult.Miss });
+ AddJudgement(new OsuJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
}
protected override void UpdatePreemptState()
diff --git a/osu.Game.Rulesets.Osu/OpenTK.dll.config b/osu.Game.Rulesets.Osu/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Game.Rulesets.Osu/OpenTK.dll.config
+++ b/osu.Game.Rulesets.Osu/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index c943518b0b..7650a91d7a 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -12,8 +12,6 @@ using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Osu.Scoring;
-using osu.Game.Rulesets.Scoring;
using osu.Game.Overlays.Settings;
using osu.Framework.Input.Bindings;
@@ -118,8 +116,6 @@ namespace osu.Game.Rulesets.Osu
public override string Description => "osu!";
- public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor();
-
public override SettingsSubsection CreateSettings() => new OsuSettings();
public override int LegacyID => 0;
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
index 59d4492480..d299faaae2 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
@@ -1,9 +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 System.Collections.Generic;
-using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
@@ -18,8 +16,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
{
internal class OsuScoreProcessor : ScoreProcessor
{
- public readonly Bindable Mode = new Bindable(ScoringMode.Exponential);
-
public OsuScoreProcessor()
{
}
@@ -31,31 +27,33 @@ namespace osu.Game.Rulesets.Osu.Scoring
private float hpDrainRate;
- private int totalAccurateJudgements;
-
private readonly Dictionary scoreResultCounts = new Dictionary();
private readonly Dictionary comboResultCounts = new Dictionary();
- private double comboMaxScore;
-
- protected override void ComputeTargets(Beatmap beatmap)
+ protected override void SimulateAutoplay(Beatmap beatmap)
{
hpDrainRate = beatmap.BeatmapInfo.Difficulty.DrainRate;
- totalAccurateJudgements = beatmap.HitObjects.Count;
- foreach (var unused in beatmap.HitObjects)
+ foreach (var obj in beatmap.HitObjects)
{
- // TODO: add support for other object types.
+ var slider = obj as Slider;
+ if (slider != null)
+ {
+ // Head
+ AddJudgement(new OsuJudgement { Result = HitResult.Great });
+
+ // Ticks
+ foreach (var unused in slider.Ticks)
+ AddJudgement(new OsuJudgement { Result = HitResult.Great });
+ }
+
AddJudgement(new OsuJudgement { Result = HitResult.Great });
}
}
- protected override void Reset()
+ protected override void Reset(bool storeResults)
{
- base.Reset();
-
- Health.Value = 1;
- Accuracy.Value = 1;
+ base.Reset(storeResults);
scoreResultCounts.Clear();
comboResultCounts.Clear();
@@ -73,6 +71,8 @@ namespace osu.Game.Rulesets.Osu.Scoring
protected override void OnNewJudgement(Judgement judgement)
{
+ base.OnNewJudgement(judgement);
+
var osuJudgement = (OsuJudgement)judgement;
if (judgement.Result != HitResult.None)
@@ -103,52 +103,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
Health.Value -= hpDrainRate * 0.04;
break;
}
-
- calculateScore();
- }
-
- private void calculateScore()
- {
- int baseScore = 0;
- double comboScore = 0;
-
- int baseMaxScore = 0;
-
- foreach (var j in Judgements)
- {
- baseScore += j.NumericResult;
- baseMaxScore += j.MaxNumericResult;
-
- comboScore += j.NumericResult * (1 + Combo.Value / 10d);
- }
-
- Accuracy.Value = (double)baseScore / baseMaxScore;
-
- if (comboScore > comboMaxScore)
- comboMaxScore = comboScore;
-
- if (baseScore == 0)
- TotalScore.Value = 0;
- else
- {
- // temporary to make scoring feel more like score v1 without being score v1.
- float exponentialFactor = Mode.Value == ScoringMode.Exponential ? (float)Judgements.Count / 100 : 1;
-
- TotalScore.Value =
- (int)
- (
- exponentialFactor *
- 700000 * comboScore / comboMaxScore +
- 300000 * Math.Pow(Accuracy.Value, 10) * ((double)Judgements.Count / totalAccurateJudgements) +
- 0 /* bonusScore */
- );
- }
- }
-
- public enum ScoringMode
- {
- Standardised,
- Exponential
}
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index 17f4f9f541..1bb4e8493b 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -70,6 +70,8 @@ namespace osu.Game.Rulesets.Osu.UI
public override void Add(DrawableHitObject h)
{
+ h.Depth = (float)h.HitObject.StartTime;
+
var c = h as IDrawableHitObjectWithProxiedApproach;
if (c != null)
approachCircles.Add(c.ProxiedLayer.CreateProxy());
diff --git a/osu.Game.Rulesets.Osu/app.config b/osu.Game.Rulesets.Osu/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
index 857f47f9b9..0963b0ad1b 100644
--- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
+++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
@@ -35,7 +35,7 @@
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -110,6 +110,7 @@
osu.licenseheader
+
diff --git a/osu.Game.Rulesets.Osu/packages.config b/osu.Game.Rulesets.Osu/packages.config
index fa6edb9c8f..0b1838ceee 100644
--- a/osu.Game.Rulesets.Osu/packages.config
+++ b/osu.Game.Rulesets.Osu/packages.config
@@ -1,9 +1,4 @@
-
-
-
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
index 0d61494add..c9daef8c99 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoDrumRollTickJudgement.cs
@@ -19,10 +19,5 @@ namespace osu.Game.Rulesets.Taiko.Judgements
return 200;
}
}
-
- protected override int NumericResultForAccuracy(HitResult result)
- {
- return 0;
- }
}
}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs
index 9d6b5ca535..3cd134f3f7 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs
@@ -8,19 +8,10 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoJudgement : Judgement
{
- ///
- /// The result value for the accuracy portion of the score.
- ///
- public int ResultNumericForAccuracy => Result == HitResult.Miss ? 0 : NumericResultForAccuracy(Result);
-
- ///
- /// The maximum result value for the accuracy portion of the score.
- ///
- public int MaxResultValueForAccuracy => NumericResultForAccuracy(HitResult.Great);
+ public override HitResult MaxResult => HitResult.Great;
///
/// 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 value for.
/// The numeric result value.
@@ -36,24 +27,5 @@ namespace osu.Game.Rulesets.Taiko.Judgements
return 300;
}
}
-
- ///
- /// 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 value for.
- /// The numeric result value.
- protected virtual int NumericResultForAccuracy(HitResult result)
- {
- switch (result)
- {
- default:
- return 0;
- case HitResult.Good:
- return 150;
- case HitResult.Great:
- return 300;
- }
- }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs
index bb67784ab1..f0b57e5c09 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs
@@ -7,6 +7,8 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoStrongHitJudgement : TaikoJudgement
{
+ public override bool AffectsCombo => false;
+
public TaikoStrongHitJudgement()
{
base.Result = HitResult.Perfect;
diff --git a/osu.Game.Rulesets.Taiko/OpenTK.dll.config b/osu.Game.Rulesets.Taiko/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Game.Rulesets.Taiko/OpenTK.dll.config
+++ b/osu.Game.Rulesets.Taiko/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index 6b99b23a12..abdda9676f 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
@@ -1,7 +1,6 @@
// 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.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
@@ -9,33 +8,11 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI;
-using OpenTK;
namespace osu.Game.Rulesets.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.
///
@@ -76,40 +53,13 @@ namespace osu.Game.Rulesets.Taiko.Scoring
///
/// 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;
+ public override bool HasFailed => Hits == MaxHits && Health.Value <= 0.5;
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()
{
}
@@ -119,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
{
}
- protected override void ComputeTargets(Beatmap beatmap)
+ protected override void SimulateAutoplay(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));
@@ -128,13 +78,6 @@ namespace osu.Game.Rulesets.Taiko.Scoring
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)
@@ -163,46 +106,14 @@ namespace osu.Game.Rulesets.Taiko.Scoring
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
}
}
-
- maxTotalHits = totalHits;
- maxComboPortion = comboPortion;
}
protected override void OnNewJudgement(Judgement judgement)
{
- bool isStrong = judgement is TaikoStrongHitJudgement;
+ base.OnNewJudgement(judgement);
+
bool isTick = judgement is TaikoDrumRollTickJudgement;
- // Don't consider ticks and strong hits as a type of hit that counts towards map completion
- if (!isTick && !isStrong)
- totalHits++;
-
- // Apply score changes
- if (judgement.IsHit)
- {
- double baseValue = judgement.NumericResult;
-
- if (isStrong)
- {
- // Add increased score for the previous judgement by hitting a strong hit object with the second key
- var prevJudgement = Judgements[Judgements.Count - 1];
- baseValue = prevJudgement.NumericResult * 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;
- }
- }
-
// Apply HP changes
switch (judgement.Result)
{
@@ -221,32 +132,13 @@ namespace osu.Game.Rulesets.Taiko.Scoring
Health.Value += hpIncreaseGreat;
break;
}
-
- int scoreForAccuracy = 0;
- int maxScoreForAccuracy = 0;
-
- foreach (var j in Judgements)
- {
- var taikoJudgement = (TaikoJudgement)j;
-
- scoreForAccuracy += taikoJudgement.ResultNumericForAccuracy;
- maxScoreForAccuracy += taikoJudgement.MaxResultValueForAccuracy;
- }
-
- Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy;
- TotalScore.Value = comboScore + accuracyScore + bonusScore;
}
- protected override void Reset()
+ protected override void Reset(bool storeResults)
{
- base.Reset();
+ base.Reset(storeResults);
Health.Value = 0;
- Accuracy.Value = 1;
-
- bonusScore = 0;
- comboPortion = 0;
- totalHits = 0;
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index 7b1452766e..ea0f0eae1a 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -9,8 +9,6 @@ using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Scoring;
using osu.Framework.Input.Bindings;
namespace osu.Game.Rulesets.Taiko
@@ -101,8 +99,6 @@ namespace osu.Game.Rulesets.Taiko
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new TaikoDifficultyCalculator(beatmap);
- public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor();
-
public override int LegacyID => 1;
public TaikoRuleset(RulesetInfo rulesetInfo)
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index 2dd06f7179..d9a216bbfc 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -205,6 +205,8 @@ namespace osu.Game.Rulesets.Taiko.UI
public override void Add(DrawableHitObject h)
{
+ h.Depth = (float)h.HitObject.StartTime;
+
base.Add(h);
var barline = h as DrawableBarLine;
diff --git a/osu.Game.Rulesets.Taiko/app.config b/osu.Game.Rulesets.Taiko/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
index a840997214..c8915d233e 100644
--- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
+++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
@@ -34,7 +34,7 @@
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -100,6 +100,7 @@
osu.licenseheader
+
diff --git a/osu.Game.Rulesets.Taiko/packages.config b/osu.Game.Rulesets.Taiko/packages.config
index 8add43d5d5..0b1838ceee 100644
--- a/osu.Game.Rulesets.Taiko/packages.config
+++ b/osu.Game.Rulesets.Taiko/packages.config
@@ -1,8 +1,4 @@
-
-
+
\ No newline at end of file
diff --git a/osu.Game.Tests/OpenTK.dll.config b/osu.Game.Tests/OpenTK.dll.config
index 627e9f6009..5620e3d9e2 100644
--- a/osu.Game.Tests/OpenTK.dll.config
+++ b/osu.Game.Tests/OpenTK.dll.config
@@ -1,7 +1,3 @@
-
diff --git a/osu.Game.Tests/app.config b/osu.Game.Tests/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Game.Tests/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 220b1aac7f..07190bedb0 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -30,12 +30,12 @@
false
-
- $(SolutionDir)\packages\NUnit.3.7.1\lib\net45\nunit.framework.dll
+
+ $(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
@@ -53,6 +53,7 @@
osu.licenseheader
+
diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config
index 9ad76308d7..af47f642e3 100644
--- a/osu.Game.Tests/packages.config
+++ b/osu.Game.Tests/packages.config
@@ -4,8 +4,8 @@ Copyright (c) 2007-2017 ppy Pty Ltd .
Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-->
-
-
+
+
\ No newline at end of file
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 15953fcd82..458c2304f2 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.IO.Serialization;
+using osu.Game.Storyboards;
namespace osu.Game.Beatmaps
{
@@ -40,6 +41,11 @@ namespace osu.Game.Beatmaps
///
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
+ ///
+ /// The Beatmap's Storyboard.
+ ///
+ public Storyboard Storyboard = new Storyboard();
+
///
/// Constructs a new beatmap.
///
@@ -51,6 +57,7 @@ namespace osu.Game.Beatmaps
Breaks = original?.Breaks ?? Breaks;
ComboColors = original?.ComboColors ?? ComboColors;
HitObjects = original?.HitObjects ?? HitObjects;
+ Storyboard = original?.Storyboard ?? Storyboard;
}
}
diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
index ad0b110e48..d8cd58d939 100644
--- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
@@ -7,7 +7,6 @@ using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Beatmaps
@@ -62,11 +61,6 @@ namespace osu.Game.Beatmaps
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => null;
- public override ScoreProcessor CreateScoreProcessor()
- {
- throw new NotImplementedException();
- }
-
public override string Description => "dummy";
public DummyRuleset(RulesetInfo rulesetInfo)
diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs
index b51ea607dd..21fee0f465 100644
--- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs
@@ -10,6 +10,10 @@ using osu.Game.Beatmaps.Timing;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Storyboards;
+using OpenTK;
+using osu.Framework.Graphics;
+using osu.Framework.IO.File;
namespace osu.Game.Beatmaps.Formats
{
@@ -238,42 +242,231 @@ namespace osu.Game.Beatmaps.Formats
}
}
- private void handleEvents(Beatmap beatmap, string line)
+ private void handleEvents(Beatmap beatmap, string line, ref StoryboardSprite storyboardSprite, ref CommandTimelineGroup timelineGroup)
{
+ var depth = 0;
+ while (line.StartsWith(" ") || line.StartsWith("_"))
+ {
+ ++depth;
+ line = line.Substring(1);
+ }
+
decodeVariables(ref line);
string[] split = line.Split(',');
- EventType type;
- if (!Enum.TryParse(split[0], out type))
- throw new InvalidDataException($@"Unknown event type {split[0]}");
-
- // Todo: Implement the rest
- switch (type)
+ if (depth == 0)
{
- case EventType.Video:
- case EventType.Background:
- string filename = split[2].Trim('"');
+ storyboardSprite = null;
- if (type == EventType.Background)
- beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
+ EventType type;
+ if (!Enum.TryParse(split[0], out type))
+ throw new InvalidDataException($@"Unknown event type {split[0]}");
- break;
- case EventType.Break:
- var breakEvent = new BreakPeriod
- {
- StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
- EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo)
- };
+ switch (type)
+ {
+ case EventType.Video:
+ case EventType.Background:
+ string filename = split[2].Trim('"');
- if (!breakEvent.HasEffect)
- return;
+ if (type == EventType.Background)
+ beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
- beatmap.Breaks.Add(breakEvent);
- break;
+ break;
+ case EventType.Break:
+ var breakEvent = new BreakPeriod
+ {
+ StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
+ EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo)
+ };
+
+ if (!breakEvent.HasEffect)
+ return;
+
+ beatmap.Breaks.Add(breakEvent);
+ break;
+ case EventType.Sprite:
+ {
+ var layer = parseLayer(split[1]);
+ var origin = parseOrigin(split[2]);
+ var path = cleanFilename(split[3]);
+ var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
+ var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
+ storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y));
+ beatmap.Storyboard.GetLayer(layer).Add(storyboardSprite);
+ }
+ break;
+ case EventType.Animation:
+ {
+ var layer = parseLayer(split[1]);
+ var origin = parseOrigin(split[2]);
+ var path = cleanFilename(split[3]);
+ var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo);
+ var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo);
+ var frameCount = int.Parse(split[6]);
+ var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo);
+ var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever;
+ storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType);
+ beatmap.Storyboard.GetLayer(layer).Add(storyboardSprite);
+ }
+ break;
+ case EventType.Sample:
+ {
+ var time = double.Parse(split[1], CultureInfo.InvariantCulture);
+ var layer = parseLayer(split[2]);
+ var path = cleanFilename(split[3]);
+ var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100;
+ beatmap.Storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume));
+ }
+ break;
+ }
+ }
+ else
+ {
+ if (depth < 2)
+ timelineGroup = storyboardSprite?.TimelineGroup;
+
+ var commandType = split[0];
+ switch (commandType)
+ {
+ case "T":
+ {
+ var triggerName = split[1];
+ var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue;
+ var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue;
+ var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0;
+ timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber);
+ }
+ break;
+ case "L":
+ {
+ var startTime = double.Parse(split[1], CultureInfo.InvariantCulture);
+ var loopCount = int.Parse(split[2]);
+ timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount);
+ }
+ break;
+ default:
+ {
+ if (string.IsNullOrEmpty(split[3]))
+ split[3] = split[2];
+
+ var easing = (Easing)int.Parse(split[1]);
+ var startTime = double.Parse(split[2], CultureInfo.InvariantCulture);
+ var endTime = double.Parse(split[3], CultureInfo.InvariantCulture);
+
+ switch (commandType)
+ {
+ case "F":
+ {
+ var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
+ timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue);
+ }
+ break;
+ case "S":
+ {
+ var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
+ timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue));
+ }
+ break;
+ case "V":
+ {
+ var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
+ var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
+ var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
+ timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY));
+ }
+ break;
+ case "R":
+ {
+ var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
+ timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue));
+ }
+ break;
+ case "M":
+ {
+ var startX = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var startY = float.Parse(split[5], CultureInfo.InvariantCulture);
+ var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX;
+ var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY;
+ timelineGroup?.X.Add(easing, startTime, endTime, startX, endX);
+ timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY);
+ }
+ break;
+ case "MX":
+ {
+ var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
+ timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue);
+ }
+ break;
+ case "MY":
+ {
+ var startValue = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue;
+ timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue);
+ }
+ break;
+ case "C":
+ {
+ var startRed = float.Parse(split[4], CultureInfo.InvariantCulture);
+ var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture);
+ var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture);
+ var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed;
+ var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen;
+ var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue;
+ timelineGroup?.Colour.Add(easing, startTime, endTime,
+ new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1),
+ new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1));
+ }
+ break;
+ case "P":
+ {
+ var type = split[4];
+ switch (type)
+ {
+ case "A": timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); break;
+ case "H": timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); break;
+ case "V": timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); break;
+ }
+ }
+ break;
+ default:
+ throw new InvalidDataException($@"Unknown command type: {commandType}");
+ }
+ }
+ break;
+ }
}
}
+ private static string cleanFilename(string path)
+ => FileSafety.PathStandardise(path.Trim('\"'));
+
+ private static Anchor parseOrigin(string value)
+ {
+ var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value);
+ switch (origin)
+ {
+ case LegacyOrigins.TopLeft: return Anchor.TopLeft;
+ case LegacyOrigins.TopCentre: return Anchor.TopCentre;
+ case LegacyOrigins.TopRight: return Anchor.TopRight;
+ case LegacyOrigins.CentreLeft: return Anchor.CentreLeft;
+ case LegacyOrigins.Centre: return Anchor.Centre;
+ case LegacyOrigins.CentreRight: return Anchor.CentreRight;
+ case LegacyOrigins.BottomLeft: return Anchor.BottomLeft;
+ case LegacyOrigins.BottomCentre: return Anchor.BottomCentre;
+ case LegacyOrigins.BottomRight: return Anchor.BottomRight;
+ }
+ throw new InvalidDataException($@"Unknown origin: {value}");
+ }
+
+ private static string parseLayer(string value)
+ => Enum.Parse(typeof(StoryLayer), value).ToString();
+
private void handleTimingPoints(Beatmap beatmap, string line)
{
string[] split = line.Split(',');
@@ -414,6 +607,8 @@ namespace osu.Game.Beatmaps.Formats
Section section = Section.None;
bool hasCustomColours = false;
+ StoryboardSprite storyboardSprite = null;
+ CommandTimelineGroup timelineGroup = null;
string line;
while ((line = stream.ReadLine()) != null)
@@ -421,7 +616,7 @@ namespace osu.Game.Beatmaps.Formats
if (string.IsNullOrEmpty(line))
continue;
- if (line.StartsWith(" ") || line.StartsWith("_") || line.StartsWith("//"))
+ if (line.StartsWith("//"))
continue;
if (line.StartsWith(@"osu file format v"))
@@ -452,7 +647,7 @@ namespace osu.Game.Beatmaps.Formats
handleDifficulty(beatmap, line);
break;
case Section.Events:
- handleEvents(beatmap, line);
+ handleEvents(beatmap, line, ref storyboardSprite, ref timelineGroup);
break;
case Section.TimingPoints:
handleTimingPoints(beatmap, line);
@@ -509,5 +704,27 @@ namespace osu.Game.Beatmaps.Formats
Sample = 5,
Animation = 6
}
+
+ internal enum LegacyOrigins
+ {
+ TopLeft,
+ Centre,
+ CentreLeft,
+ TopRight,
+ BottomCentre,
+ TopCentre,
+ Custom,
+ CentreRight,
+ BottomLeft,
+ BottomRight
+ };
+
+ internal enum StoryLayer
+ {
+ Background = 0,
+ Fail = 1,
+ Pass = 2,
+ Foreground = 3
+ }
}
}
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index df8e7f5e3b..bb3122489e 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -100,8 +100,11 @@ namespace osu.Game.Beatmaps
public void TransferTo(WorkingBeatmap other)
{
- if (track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
- other.track = track;
+ lock (trackLock)
+ {
+ if (track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
+ other.track = track;
+ }
if (background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo))
other.background = background;
diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs
index 08745ce6ba..6312db3ad6 100644
--- a/osu.Game/Graphics/Backgrounds/Triangles.cs
+++ b/osu.Game/Graphics/Backgrounds/Triangles.cs
@@ -228,9 +228,9 @@ namespace osu.Game.Graphics.Backgrounds
var size = new Vector2(2 * offset.X, offset.Y);
var triangle = new Triangle(
- particle.Position * Size * DrawInfo.Matrix,
- (particle.Position * Size + offset) * DrawInfo.Matrix,
- (particle.Position * Size + new Vector2(-offset.X, offset.Y)) * DrawInfo.Matrix
+ Vector2Extensions.Transform(particle.Position * Size, DrawInfo.Matrix),
+ Vector2Extensions.Transform(particle.Position * Size + offset, DrawInfo.Matrix),
+ Vector2Extensions.Transform(particle.Position * Size + new Vector2(-offset.X, offset.Y), DrawInfo.Matrix)
);
ColourInfo colourInfo = DrawInfo.Colour;
diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs
index 053ed9c191..36f23d1ae9 100644
--- a/osu.Game/Graphics/Cursor/MenuCursor.cs
+++ b/osu.Game/Graphics/Cursor/MenuCursor.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Graphics.Cursor
// don't start rotating until we're moved a minimum distance away from the mouse down location,
// else it can have an annoying effect.
- startRotation |= Vector2.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30;
+ startRotation |= Vector2Extensions.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30;
if (startRotation)
{
diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs
index 32a37a4910..ccc23e3ff6 100644
--- a/osu.Game/Graphics/UserInterface/OsuButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuButton.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.Collections.Generic;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@@ -112,7 +113,7 @@ namespace osu.Game.Graphics.UserInterface
return base.OnMouseUp(state, args);
}
- public string[] FilterTerms => new[] { Text };
+ public IEnumerable FilterTerms => new[] { Text };
public bool MatchingFilter
{
diff --git a/osu.Game/OpenTK.dll.config b/osu.Game/OpenTK.dll.config
new file mode 100644
index 0000000000..5620e3d9e2
--- /dev/null
+++ b/osu.Game/OpenTK.dll.config
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelListItem.cs
index 8360e793d8..f4cf806044 100644
--- a/osu.Game/Overlays/Chat/ChannelListItem.cs
+++ b/osu.Game/Overlays/Chat/ChannelListItem.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
@@ -34,7 +35,7 @@ namespace osu.Game.Overlays.Chat
private Color4 topicColour;
private Color4 hoverColour;
- public string[] FilterTerms => new[] { channel.Name };
+ public IEnumerable FilterTerms => new[] { channel.Name };
public bool MatchingFilter
{
set
diff --git a/osu.Game/Overlays/Chat/ChannelSection.cs b/osu.Game/Overlays/Chat/ChannelSection.cs
index 1f046aff2a..5068b415bc 100644
--- a/osu.Game/Overlays/Chat/ChannelSection.cs
+++ b/osu.Game/Overlays/Chat/ChannelSection.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Chat
public readonly FillFlowContainer ChannelFlow;
public IEnumerable FilterableChildren => ChannelFlow.Children;
- public string[] FilterTerms => new[] { Header };
+ public IEnumerable FilterTerms => new[] { Header };
public bool MatchingFilter
{
set
diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index b1deae8272..7c28bdea4d 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -12,8 +12,10 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Transforms;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
+using osu.Framework.MathUtils;
using osu.Framework.Threading;
using osu.Game.Configuration;
using osu.Game.Graphics;
@@ -56,7 +58,7 @@ namespace osu.Game.Overlays
private readonly Box chatBackground;
private readonly Box tabBackground;
- private Bindable chatHeight;
+ public Bindable ChatHeight { get; internal set; }
private readonly Container channelSelectionContainer;
private readonly ChannelSelectionOverlay channelSelection;
@@ -177,18 +179,11 @@ namespace osu.Game.Overlays
if (state == Visibility.Visible)
{
textbox.HoldFocus = false;
- if (1f - chatHeight.Value < channel_selection_min_height)
- {
- chatContainer.ResizeHeightTo(1f - channel_selection_min_height, 800, Easing.OutQuint);
- channelSelectionContainer.ResizeHeightTo(channel_selection_min_height, 800, Easing.OutQuint);
- channelSelection.Show();
- chatHeight.Value = 1f - channel_selection_min_height;
- }
+ if (1f - ChatHeight.Value < channel_selection_min_height)
+ transformChatHeightTo(1f - channel_selection_min_height, 800, Easing.OutQuint);
}
else
- {
textbox.HoldFocus = true;
- }
};
}
@@ -202,7 +197,7 @@ namespace osu.Game.Overlays
if (!isDragging)
return base.OnDragStart(state);
- startDragChatHeight = chatHeight.Value;
+ startDragChatHeight = ChatHeight.Value;
return true;
}
@@ -212,7 +207,13 @@ namespace osu.Game.Overlays
{
Trace.Assert(state.Mouse.PositionMouseDown != null);
- chatHeight.Value = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y;
+ double targetChatHeight = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y;
+
+ // If the channel selection screen is shown, mind its minimum height
+ if (channelSelection.State == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height)
+ targetChatHeight = 1f - channel_selection_min_height;
+
+ ChatHeight.Value = targetChatHeight;
}
return true;
@@ -272,14 +273,14 @@ namespace osu.Game.Overlays
this.api = api;
api.Register(this);
- chatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight);
- chatHeight.ValueChanged += h =>
+ ChatHeight = config.GetBindable(OsuSetting.ChatDisplayHeight);
+ ChatHeight.ValueChanged += h =>
{
chatContainer.Height = (float)h;
channelSelectionContainer.Height = 1f - (float)h;
tabBackground.FadeTo(h == 1 ? 1 : 0.8f, 200);
};
- chatHeight.TriggerChange();
+ ChatHeight.TriggerChange();
chatBackground.Colour = colours.ChatBlue;
}
@@ -501,5 +502,26 @@ namespace osu.Game.Overlays
api.Queue(req);
}
+
+ private void transformChatHeightTo(double newChatHeight, double duration = 0, Easing easing = Easing.None)
+ {
+ this.TransformTo(this.PopulateTransform(new TransformChatHeight(), newChatHeight, duration, easing));
+ }
+
+ private class TransformChatHeight : Transform
+ {
+ private double valueAt(double time)
+ {
+ if (time < StartTime) return StartValue;
+ if (time >= EndTime) return EndValue;
+
+ return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
+ }
+
+ public override string TargetMember => "ChatHeight.Value";
+
+ protected override void Apply(ChatOverlay d, double time) => d.ChatHeight.Value = valueAt(time);
+ protected override void ReadIntoStartValue(ChatOverlay d) => StartValue = d.ChatHeight.Value;
+ }
}
}
diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
index 046e56573f..fb84853a0d 100644
--- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
+++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs
@@ -47,7 +47,7 @@ namespace osu.Game.Overlays.KeyBinding
private FillFlowContainer buttons;
- public string[] FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())).ToArray();
+ public IEnumerable FilterTerms => new[] { text.Text }.Concat(bindings.Select(b => b.KeyCombination.ReadableString())).ToArray();
public KeyBindingRow(object action, IEnumerable bindings)
{
@@ -371,4 +371,4 @@ namespace osu.Game.Overlays.KeyBinding
}
}
}
-}
\ No newline at end of file
+}
diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs
index 2aaa182685..723b3f4e96 100644
--- a/osu.Game/Overlays/Music/PlaylistItem.cs
+++ b/osu.Game/Overlays/Music/PlaylistItem.cs
@@ -129,7 +129,7 @@ namespace osu.Game.Overlays.Music
return true;
}
- public string[] FilterTerms { get; private set; }
+ public IEnumerable FilterTerms { get; private set; }
private bool matching = true;
diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs
index 360e2ad843..6f1eaded7f 100644
--- a/osu.Game/Overlays/Music/PlaylistList.cs
+++ b/osu.Game/Overlays/Music/PlaylistList.cs
@@ -229,7 +229,7 @@ namespace osu.Game.Overlays.Music
private class ItemSearchContainer : FillFlowContainer, IHasFilterableChildren
{
- public string[] FilterTerms => new string[] { };
+ public IEnumerable FilterTerms => new string[] { };
public bool MatchingFilter
{
set
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index 77a3449b9c..d6a86fe714 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -414,8 +414,8 @@ namespace osu.Game.Overlays.Profile
scoreNumberText.Add(createScoreNumberText(user.Statistics.TotalHits.ToString(@"#,0")));
scoreText.Add(createScoreText("Max Combo"));
scoreNumberText.Add(createScoreNumberText(user.Statistics.MaxCombo.ToString(@"#,0")));
- scoreText.Add(createScoreText("Replay Watched by Others"));
- scoreNumberText.Add(createScoreNumberText(user.Statistics.ReplayWatched.ToString(@"#,0")));
+ scoreText.Add(createScoreText("Replays Watched by Others"));
+ scoreNumberText.Add(createScoreNumberText(user.Statistics.ReplaysWatched.ToString(@"#,0")));
gradeSS.DisplayCount = user.Statistics.GradesCount.SS;
gradeSS.Show();
diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs
index 0a32b50809..495a2543d1 100644
--- a/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
{
RelativeSizeAxes = Axes.X,
Text = "Force garbage collection",
- Action = () => GC.Collect()
+ Action = GC.Collect
},
};
}
diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs
index 01e32b5a1b..833a5ff966 100644
--- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
{
RelativeSizeAxes = Axes.X,
Text = "Open osu! folder",
- Action = () => storage.OpenInNativeExplorer(),
+ Action = storage.OpenInNativeExplorer,
}
};
}
diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs
index 01e73d0168..b68fd4bc04 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
RelativeSizeAxes = Axes.X,
Text = "Key Configuration",
- Action = () => keyConfig.ToggleVisibility()
+ Action = keyConfig.ToggleVisibility
},
};
}
diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs
index c74f4070e7..f2044f178b 100644
--- a/osu.Game/Overlays/Settings/SettingsItem.cs
+++ b/osu.Game/Overlays/Settings/SettingsItem.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.Collections.Generic;
using OpenTK.Graphics;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
@@ -53,7 +54,7 @@ namespace osu.Game.Overlays.Settings
}
}
- public string[] FilterTerms => new[] { LabelText };
+ public IEnumerable FilterTerms => new[] { LabelText };
public bool MatchingFilter
{
diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs
index f091192d27..eb6e398477 100644
--- a/osu.Game/Overlays/Settings/SettingsSection.cs
+++ b/osu.Game/Overlays/Settings/SettingsSection.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Settings
public abstract string Header { get; }
public IEnumerable FilterableChildren => Children.OfType();
- public string[] FilterTerms => new[] { Header };
+ public IEnumerable FilterTerms => new[] { Header };
private const int header_size = 26;
private const int header_margin = 25;
diff --git a/osu.Game/Overlays/Settings/SettingsSubsection.cs b/osu.Game/Overlays/Settings/SettingsSubsection.cs
index 0fbb5b92f7..4164ceee21 100644
--- a/osu.Game/Overlays/Settings/SettingsSubsection.cs
+++ b/osu.Game/Overlays/Settings/SettingsSubsection.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Settings
protected abstract string Header { get; }
public IEnumerable FilterableChildren => Children.OfType();
- public string[] FilterTerms => new[] { Header };
+ public IEnumerable FilterTerms => new[] { Header };
public bool MatchingFilter
{
set
diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs
index 29cd3ff384..0ae33272a7 100644
--- a/osu.Game/Rulesets/Judgements/Judgement.cs
+++ b/osu.Game/Rulesets/Judgements/Judgement.cs
@@ -25,6 +25,10 @@ namespace osu.Game.Rulesets.Judgements
///
public double TimeOffset { get; internal set; }
+ ///
+ /// Whether the should affect the combo portion of the score.
+ /// If false, the will be considered for the bonus portion of the score.
+ ///
public virtual bool AffectsCombo => true;
///
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 7cf098c3fa..7a26a53c2a 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -55,8 +55,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
: base(hitObject)
{
HitObject = hitObject;
-
- Depth = (float)hitObject.StartTime;
}
private ArmedState state;
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs
index 5ba9c2ff4d..538bb826ad 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableScrollingHitObject.cs
@@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
}
public override bool RemoveWhenNotAlive => false;
+ protected override bool RequiresChildrenUpdate => true;
protected DrawableScrollingHitObject(TObject hitObject)
: base(hitObject)
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index 33621662af..587c2fc659 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -10,7 +10,6 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets
@@ -50,8 +49,6 @@ namespace osu.Game.Rulesets
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap);
- public abstract ScoreProcessor CreateScoreProcessor();
-
public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle };
public abstract string Description { get; }
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 2ca733d82a..deb87e92d8 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
-using System.Collections.Generic;
+using System.Diagnostics;
using osu.Framework.Configuration;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
@@ -15,11 +15,17 @@ namespace osu.Game.Rulesets.Scoring
public abstract class ScoreProcessor
{
///
- /// Invoked when the ScoreProcessor is in a failed state.
+ /// Invoked when the is in a failed state.
+ /// This may occur regardless of whether an event is invoked.
/// Return true if the fail was permitted.
///
public event Func Failed;
+ ///
+ /// Invoked when all s have been judged.
+ ///
+ public event Action AllJudged;
+
///
/// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by the .
///
@@ -33,7 +39,7 @@ namespace osu.Game.Rulesets.Scoring
///
/// The current accuracy.
///
- public readonly BindableDouble Accuracy = new BindableDouble { MinValue = 0, MaxValue = 1 };
+ public readonly BindableDouble Accuracy = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
///
/// The current health.
@@ -50,10 +56,15 @@ namespace osu.Game.Rulesets.Scoring
///
public readonly BindableInt HighestCombo = new BindableInt();
+ ///
+ /// Whether all s have been processed.
+ ///
+ protected virtual bool HasCompleted => false;
+
///
/// Whether the score is in a failed state.
///
- public virtual bool HasFailed => false;
+ public virtual bool HasFailed => Health.Value == Health.MinValue;
///
/// Whether this ScoreProcessor has already triggered the failed state.
@@ -63,8 +74,6 @@ namespace osu.Game.Rulesets.Scoring
protected ScoreProcessor()
{
Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); };
-
- Reset();
}
private ScoreRank rankFrom(double acc)
@@ -85,11 +94,12 @@ namespace osu.Game.Rulesets.Scoring
///
/// Resets this ScoreProcessor to a default state.
///
- protected virtual void Reset()
+ /// Whether to store the current state of the for future use.
+ protected virtual void Reset(bool storeResults)
{
TotalScore.Value = 0;
- Accuracy.Value = 0;
- Health.Value = 0;
+ Accuracy.Value = 1;
+ Health.Value = 1;
Combo.Value = 0;
HighestCombo.Value = 0;
@@ -118,6 +128,9 @@ namespace osu.Game.Rulesets.Scoring
protected void NotifyNewJudgement(Judgement judgement)
{
NewJudgement?.Invoke(judgement);
+
+ if (HasCompleted)
+ AllJudged?.Invoke();
}
///
@@ -135,36 +148,55 @@ namespace osu.Game.Rulesets.Scoring
}
}
- public abstract class ScoreProcessor : ScoreProcessor
+ public class ScoreProcessor : ScoreProcessor
where TObject : HitObject
{
- ///
- /// All judgements held by this ScoreProcessor.
- ///
- protected readonly List Judgements = new List();
+ private const double base_portion = 0.3;
+ private const double combo_portion = 0.7;
+ private const double max_score = 1000000;
- public override bool HasFailed => Health.Value == Health.MinValue;
+ public readonly Bindable Mode = new Bindable();
+
+ protected sealed override bool HasCompleted => Hits == MaxHits;
+
+ protected int MaxHits { get; private set; }
+ protected int Hits { get; private set; }
+
+ private double maxHighestCombo;
+
+ private double maxBaseScore;
+ private double rollingMaxBaseScore;
+ private double baseScore;
protected ScoreProcessor()
{
}
- protected ScoreProcessor(RulesetContainer rulesetContainer)
+ public ScoreProcessor(RulesetContainer rulesetContainer)
{
- Judgements.Capacity = rulesetContainer.Beatmap.HitObjects.Count;
+ Debug.Assert(base_portion + combo_portion == 1.0);
rulesetContainer.OnJudgement += AddJudgement;
- ComputeTargets(rulesetContainer.Beatmap);
+ SimulateAutoplay(rulesetContainer.Beatmap);
+ Reset(true);
- Reset();
+ if (maxBaseScore == 0 || maxHighestCombo == 0)
+ {
+ Mode.Value = ScoringMode.Exponential;
+ Mode.Disabled = true;
+ }
}
///
- /// Computes target scoring values for this ScoreProcessor. This is equivalent to performing an auto-play of the score to find the values.
+ /// Simulates an autoplay of s that will be judged by this
+ /// by adding s for each in the .
+ ///
+ /// This is required for to work, otherwise will be used.
+ ///
///
- /// The Beatmap containing the objects that will be judged by this ScoreProcessor.
- protected virtual void ComputeTargets(Beatmap beatmap) { }
+ /// The containing the s that will be judged by this .
+ protected virtual void SimulateAutoplay(Beatmap beatmap) { }
///
/// Adds a judgement to this ScoreProcessor.
@@ -172,45 +204,72 @@ namespace osu.Game.Rulesets.Scoring
/// The judgement to add.
protected void AddJudgement(Judgement judgement)
{
- bool exists = Judgements.Contains(judgement);
-
- if (!exists)
- {
- if (judgement.AffectsCombo)
- {
- switch (judgement.Result)
- {
- case HitResult.None:
- break;
- case HitResult.Miss:
- Combo.Value = 0;
- break;
- default:
- Combo.Value++;
- break;
- }
- }
-
- Judgements.Add(judgement);
- OnNewJudgement(judgement);
-
- NotifyNewJudgement(judgement);
- }
+ OnNewJudgement(judgement);
+ NotifyNewJudgement(judgement);
UpdateFailed();
}
- protected override void Reset()
+ protected virtual void OnNewJudgement(Judgement judgement)
{
- base.Reset();
+ double bonusScore = 0;
- Judgements.Clear();
+ if (judgement.AffectsCombo)
+ {
+ switch (judgement.Result)
+ {
+ case HitResult.None:
+ break;
+ case HitResult.Miss:
+ Combo.Value = 0;
+ break;
+ default:
+ Combo.Value++;
+ break;
+ }
+
+ baseScore += judgement.NumericResult;
+ rollingMaxBaseScore += judgement.MaxNumericResult;
+
+ Hits++;
+ }
+ else if (judgement.IsHit)
+ bonusScore += judgement.NumericResult;
+
+ if (rollingMaxBaseScore != 0)
+ Accuracy.Value = baseScore / rollingMaxBaseScore;
+
+ switch (Mode.Value)
+ {
+ case ScoringMode.Standardised:
+ TotalScore.Value = max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo / maxHighestCombo) + bonusScore;
+ break;
+ case ScoringMode.Exponential:
+ TotalScore.Value = (baseScore + bonusScore) * Math.Log(HighestCombo + 1, 2);
+ break;
+ }
}
- ///
- /// Updates any values that need post-processing. Invoked when a new judgement has occurred.
- ///
- /// The judgement that triggered this calculation.
- protected abstract void OnNewJudgement(Judgement judgement);
+ protected override void Reset(bool storeResults)
+ {
+ if (storeResults)
+ {
+ MaxHits = Hits;
+ maxHighestCombo = HighestCombo;
+ maxBaseScore = baseScore;
+ }
+
+ base.Reset(storeResults);
+
+ Hits = 0;
+ baseScore = 0;
+ rollingMaxBaseScore = 0;
+ }
+ }
+
+ public enum ScoringMode
+ {
+ Standardised,
+ Exponential
}
}
diff --git a/osu.Game/Rulesets/Timing/ScrollingContainer.cs b/osu.Game/Rulesets/Timing/ScrollingContainer.cs
index 6e77c49e3d..eac596297a 100644
--- a/osu.Game/Rulesets/Timing/ScrollingContainer.cs
+++ b/osu.Game/Rulesets/Timing/ScrollingContainer.cs
@@ -29,6 +29,7 @@ namespace osu.Game.Rulesets.Timing
internal Axes ScrollingAxes;
public override bool RemoveWhenNotAlive => false;
+ protected override bool RequiresChildrenUpdate => true;
///
/// The control point that defines the speed adjustments for this container. This is set by the .
diff --git a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs
index d3bd7685da..81e3a5c70e 100644
--- a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs
+++ b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs
@@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Timing
}
public override bool RemoveWhenNotAlive => false;
+ protected override bool RequiresChildrenUpdate => true;
///
/// The that defines the speed adjustments.
diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs
index f7ece531c3..86ab9a0199 100644
--- a/osu.Game/Rulesets/UI/RulesetContainer.cs
+++ b/osu.Game/Rulesets/UI/RulesetContainer.cs
@@ -29,11 +29,6 @@ namespace osu.Game.Rulesets.UI
///
public abstract class RulesetContainer : Container
{
- ///
- /// Invoked when all the judgeable HitObjects have been judged.
- ///
- public event Action OnAllJudged;
-
///
/// Whether to apply adjustments to the child based on our own size.
///
@@ -61,11 +56,6 @@ namespace osu.Game.Rulesets.UI
public abstract IEnumerable Objects { get; }
- ///
- /// Whether all the HitObjects have been judged.
- ///
- protected abstract bool AllObjectsJudged { get; }
-
protected readonly Ruleset Ruleset;
///
@@ -77,15 +67,6 @@ namespace osu.Game.Rulesets.UI
Ruleset = ruleset;
}
- ///
- /// Checks whether all HitObjects have been judged, and invokes OnAllJudged.
- ///
- protected void CheckAllJudged()
- {
- if (AllObjectsJudged)
- OnAllJudged?.Invoke();
- }
-
public abstract ScoreProcessor CreateScoreProcessor();
///
@@ -152,7 +133,7 @@ namespace osu.Game.Rulesets.UI
public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor;
- protected override bool AllObjectsJudged => drawableObjects.All(h => h.AllJudged);
+ public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this);
///
/// The playfield.
@@ -162,8 +143,6 @@ namespace osu.Game.Rulesets.UI
protected override Container Content => content;
private Container content;
- private readonly List> drawableObjects = new List>();
-
///
/// Whether to assume the beatmap passed into this is for the current ruleset.
/// Creates a hit renderer for a beatmap.
@@ -250,8 +229,6 @@ namespace osu.Game.Rulesets.UI
///
private void loadObjects()
{
- drawableObjects.Capacity = Beatmap.HitObjects.Count;
-
foreach (TObject h in Beatmap.HitObjects)
{
var drawableObject = GetVisualRepresentation(h);
@@ -263,10 +240,8 @@ namespace osu.Game.Rulesets.UI
{
Playfield.OnJudgement(d, j);
OnJudgement?.Invoke(j);
- CheckAllJudged();
};
- drawableObjects.Add(drawableObject);
Playfield.Add(drawableObject);
}
diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs
index bc94f88866..4b8942349d 100644
--- a/osu.Game/Screens/Menu/LogoVisualisation.cs
+++ b/osu.Game/Screens/Menu/LogoVisualisation.cs
@@ -201,10 +201,10 @@ namespace osu.Game.Screens.Menu
var amplitudeOffset = new Vector2(rotationCos * barSize.Y, rotationSin * barSize.Y);
var rectangle = new Quad(
- (barPosition - bottomOffset) * DrawInfo.Matrix,
- (barPosition - bottomOffset + amplitudeOffset) * DrawInfo.Matrix,
- (barPosition + bottomOffset) * DrawInfo.Matrix,
- (barPosition + bottomOffset + amplitudeOffset) * DrawInfo.Matrix
+ Vector2Extensions.Transform(barPosition - bottomOffset, DrawInfo.Matrix),
+ Vector2Extensions.Transform(barPosition - bottomOffset + amplitudeOffset, DrawInfo.Matrix),
+ Vector2Extensions.Transform(barPosition + bottomOffset, DrawInfo.Matrix),
+ Vector2Extensions.Transform(barPosition + bottomOffset + amplitudeOffset, DrawInfo.Matrix)
);
Texture.DrawQuad(
diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs
index 2544cc2837..1c82d15f50 100644
--- a/osu.Game/Screens/Menu/MainMenu.cs
+++ b/osu.Game/Screens/Menu/MainMenu.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Screens.Menu
OnEdit = delegate { Push(new Editor()); },
OnSolo = delegate { Push(consumeSongSelect()); },
OnMulti = delegate { Push(new Lobby()); },
- OnExit = delegate { Exit(); },
+ OnExit = Exit,
}
}
},
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 615e04d6c2..593abb7d26 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -206,10 +206,8 @@ namespace osu.Game.Screens.Play
hudOverlay.ModDisplay.Current.BindTo(working.Mods);
- //bind RulesetContainer to ScoreProcessor and ourselves (for a pass situation)
- RulesetContainer.OnAllJudged += onCompletion;
-
- //bind ScoreProcessor to ourselves (for a fail situation)
+ // Bind ScoreProcessor to ourselves
+ scoreProcessor.AllJudged += onCompletion;
scoreProcessor.Failed += onFail;
}
diff --git a/osu.Game/Screens/Play/SongProgressGraph.cs b/osu.Game/Screens/Play/SongProgressGraph.cs
index 541065e532..38c680902a 100644
--- a/osu.Game/Screens/Play/SongProgressGraph.cs
+++ b/osu.Game/Screens/Play/SongProgressGraph.cs
@@ -3,6 +3,7 @@
using System.Linq;
using System.Collections.Generic;
+using System.Diagnostics;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects;
@@ -34,10 +35,12 @@ namespace osu.Game.Screens.Play
foreach (var h in objects)
{
- IHasEndTime end = h as IHasEndTime;
+ var endTime = (h as IHasEndTime)?.EndTime ?? h.StartTime;
+
+ Debug.Assert(endTime >= h.StartTime);
int startRange = (int)((h.StartTime - firstHit) / interval);
- int endRange = (int)(((end?.EndTime > 0 ? end.EndTime : h.StartTime) - firstHit) / interval);
+ int endRange = (int)((endTime - firstHit) / interval);
for (int i = startRange; i <= endRange; i++)
Values[i]++;
}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index f97c4fe420..84457b77a7 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -106,7 +106,7 @@ namespace osu.Game.Screens.Select
Origin = Anchor.CentreRight,
SelectionChanged = carouselSelectionChanged,
BeatmapsChanged = carouselBeatmapsLoaded,
- DeleteRequested = b => promptDelete(b),
+ DeleteRequested = promptDelete,
RestoreRequested = s => { foreach (var b in s.Beatmaps) manager.Restore(b); },
HideDifficultyRequested = b => manager.Hide(b),
StartRequested = () => carouselRaisedStart(),
diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs
index 7cd81a924d..3d27552212 100644
--- a/osu.Game/Screens/Tournament/Drawings.cs
+++ b/osu.Game/Screens/Tournament/Drawings.cs
@@ -237,7 +237,7 @@ namespace osu.Game.Screens.Tournament
RelativeSizeAxes = Axes.X,
Text = "Reset",
- Action = () => reset(false)
+ Action = () => reset()
}
}
}
diff --git a/osu.Game/Storyboards/CommandLoop.cs b/osu.Game/Storyboards/CommandLoop.cs
new file mode 100644
index 0000000000..02b5eb0122
--- /dev/null
+++ b/osu.Game/Storyboards/CommandLoop.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 System.Collections.Generic;
+
+namespace osu.Game.Storyboards
+{
+ public class CommandLoop : CommandTimelineGroup
+ {
+ public double LoopStartTime;
+ public int LoopCount;
+
+ public override double StartTime => LoopStartTime;
+ public override double EndTime => LoopStartTime + CommandsDuration * LoopCount;
+
+ public CommandLoop(double startTime, int loopCount)
+ {
+ LoopStartTime = startTime;
+ LoopCount = loopCount;
+ }
+
+ public override IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, double offset = 0)
+ {
+ for (var loop = 0; loop < LoopCount; loop++)
+ {
+ var loopOffset = LoopStartTime + loop * CommandsDuration;
+ foreach (var command in base.GetCommands(timelineSelector, offset + loopOffset))
+ yield return command;
+ }
+ }
+
+ public override string ToString()
+ => $"{LoopStartTime} x{LoopCount}";
+ }
+}
diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs
new file mode 100644
index 0000000000..b9bb6629d1
--- /dev/null
+++ b/osu.Game/Storyboards/CommandTimeline.cs
@@ -0,0 +1,77 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Caching;
+using osu.Framework.Graphics;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace osu.Game.Storyboards
+{
+ public class CommandTimeline : ICommandTimeline
+ {
+ private readonly List commands = new List();
+ public IEnumerable Commands => commands.OrderBy(c => c.StartTime);
+ public bool HasCommands => commands.Count > 0;
+
+ private Cached startTimeBacking;
+ public double StartTime => startTimeBacking.IsValid ? startTimeBacking : (startTimeBacking.Value = HasCommands ? commands.Min(c => c.StartTime) : double.MinValue);
+
+ private Cached endTimeBacking;
+ public double EndTime => endTimeBacking.IsValid ? endTimeBacking : (endTimeBacking.Value = HasCommands ? commands.Max(c => c.EndTime) : double.MaxValue);
+
+ public T StartValue => HasCommands ? commands.OrderBy(c => c.StartTime).First().StartValue : default(T);
+ public T EndValue => HasCommands ? commands.OrderByDescending(c => c.EndTime).First().EndValue : default(T);
+
+ public void Add(Easing easing, double startTime, double endTime, T startValue, T endValue)
+ {
+ if (endTime < startTime)
+ return;
+
+ commands.Add(new TypedCommand { Easing = easing, StartTime = startTime, EndTime = endTime, StartValue = startValue, EndValue = endValue, });
+
+ startTimeBacking.Invalidate();
+ endTimeBacking.Invalidate();
+ }
+
+ public override string ToString()
+ => $"{commands.Count} command(s)";
+
+ public class TypedCommand : ICommand
+ {
+ public Easing Easing { get; set; }
+ public double StartTime { get; set; }
+ public double EndTime { get; set; }
+ public double Duration => EndTime - StartTime;
+
+ public T StartValue;
+ public T EndValue;
+
+ public int CompareTo(ICommand other)
+ {
+ var result = StartTime.CompareTo(other.StartTime);
+ if (result != 0) return result;
+ return EndTime.CompareTo(other.EndTime);
+ }
+
+ public override string ToString()
+ => $"{StartTime} -> {EndTime}, {StartValue} -> {EndValue} {Easing}";
+ }
+ }
+
+ public interface ICommandTimeline
+ {
+ double StartTime { get; }
+ double EndTime { get; }
+ bool HasCommands { get; }
+ }
+
+ public interface ICommand : IComparable
+ {
+ Easing Easing { get; set; }
+ double StartTime { get; set; }
+ double EndTime { get; set; }
+ double Duration { get; }
+ }
+}
diff --git a/osu.Game/Storyboards/CommandTimelineGroup.cs b/osu.Game/Storyboards/CommandTimelineGroup.cs
new file mode 100644
index 0000000000..332a6f79cb
--- /dev/null
+++ b/osu.Game/Storyboards/CommandTimelineGroup.cs
@@ -0,0 +1,68 @@
+// 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.Graphics;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace osu.Game.Storyboards
+{
+ public delegate CommandTimeline CommandTimelineSelector(CommandTimelineGroup commandTimelineGroup);
+
+ public class CommandTimelineGroup
+ {
+ public CommandTimeline X = new CommandTimeline();
+ public CommandTimeline Y = new CommandTimeline();
+ public CommandTimeline Scale = new CommandTimeline();
+ public CommandTimeline Rotation = new CommandTimeline();
+ public CommandTimeline Colour = new CommandTimeline();
+ public CommandTimeline Alpha = new CommandTimeline();
+ public CommandTimeline BlendingMode = new CommandTimeline();
+ public CommandTimeline FlipH = new CommandTimeline();
+ public CommandTimeline FlipV = new CommandTimeline();
+
+ public IEnumerable Timelines
+ {
+ get
+ {
+ yield return X;
+ yield return Y;
+ yield return Scale;
+ yield return Rotation;
+ yield return Colour;
+ yield return Alpha;
+ yield return BlendingMode;
+ yield return FlipH;
+ yield return FlipV;
+ }
+ }
+
+ public double CommandsStartTime => Timelines.Where(t => t.HasCommands).Min(t => t.StartTime);
+ public double CommandsEndTime => Timelines.Where(t => t.HasCommands).Max(t => t.EndTime);
+ public double CommandsDuration => CommandsEndTime - CommandsStartTime;
+
+ public virtual double StartTime => CommandsStartTime;
+ public virtual double EndTime => CommandsEndTime;
+ public double Duration => EndTime - StartTime;
+
+ public bool HasCommands => Timelines.Any(t => t.HasCommands);
+
+ public virtual IEnumerable.TypedCommand> GetCommands(CommandTimelineSelector timelineSelector, double offset = 0)
+ {
+ if (offset != 0)
+ return timelineSelector(this).Commands.Select(command =>
+ new CommandTimeline.TypedCommand
+ {
+ Easing = command.Easing,
+ StartTime = offset + command.StartTime,
+ EndTime = offset + command.EndTime,
+ StartValue = command.StartValue,
+ EndValue = command.EndValue,
+ });
+
+ return timelineSelector(this).Commands;
+ }
+ }
+}
diff --git a/osu.Game/Storyboards/CommandTrigger.cs b/osu.Game/Storyboards/CommandTrigger.cs
new file mode 100644
index 0000000000..e2731f9c45
--- /dev/null
+++ b/osu.Game/Storyboards/CommandTrigger.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.Storyboards
+{
+ public class CommandTrigger : CommandTimelineGroup
+ {
+ public string TriggerName;
+ public double TriggerStartTime;
+ public double TriggerEndTime;
+ public int GroupNumber;
+
+ public CommandTrigger(string triggerName, double startTime, double endTime, int groupNumber)
+ {
+ TriggerName = triggerName;
+ TriggerStartTime = startTime;
+ TriggerEndTime = endTime;
+ GroupNumber = groupNumber;
+ }
+
+ public override string ToString()
+ => $"{TriggerName} {TriggerStartTime} -> {TriggerEndTime} ({GroupNumber})";
+ }
+}
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
new file mode 100644
index 0000000000..f88e5d118f
--- /dev/null
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs
@@ -0,0 +1,59 @@
+// 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.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Textures;
+using osu.Game.IO;
+
+namespace osu.Game.Storyboards.Drawables
+{
+ public class DrawableStoryboard : Container
+ {
+ public Storyboard Storyboard { get; private set; }
+
+ protected override Vector2 DrawScale => new Vector2(Parent.DrawHeight / 480);
+ public override bool HandleInput => false;
+
+ private bool passing = true;
+ public bool Passing
+ {
+ get { return passing; }
+ set
+ {
+ if (passing == value) return;
+ passing = value;
+ updateLayerVisibility();
+ }
+ }
+
+ private DependencyContainer dependencies;
+ protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
+ dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+
+ public DrawableStoryboard(Storyboard storyboard)
+ {
+ Storyboard = storyboard;
+ Size = new Vector2(640, 480);
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(FileStore fileStore)
+ {
+ dependencies.Cache(new TextureStore(new RawTextureLoaderStore(fileStore.Store), false) { ScaleAdjust = 1, });
+
+ foreach (var layer in Storyboard.Layers)
+ Add(layer.CreateDrawable());
+ }
+
+ private void updateLayerVisibility()
+ {
+ foreach (var layer in Children)
+ layer.Enabled = passing ? layer.Layer.EnabledWhenPassing : layer.Layer.EnabledWhenFailing;
+ }
+ }
+}
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs
new file mode 100644
index 0000000000..d8b7d05ee9
--- /dev/null
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs
@@ -0,0 +1,87 @@
+// 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.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Animations;
+using osu.Framework.Graphics.Textures;
+using System.Linq;
+
+namespace osu.Game.Storyboards.Drawables
+{
+ public class DrawableStoryboardAnimation : TextureAnimation, IFlippable
+ {
+ public StoryboardAnimation Animation { get; private set; }
+
+ protected override bool ShouldBeAlive => Animation.HasCommands && base.ShouldBeAlive;
+ public override bool RemoveWhenNotAlive => !Animation.HasCommands || base.RemoveWhenNotAlive;
+
+ public bool FlipH { get; set; }
+ public bool FlipV { get; set; }
+
+ protected override Vector2 DrawScale
+ => new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y);
+
+ public override Anchor Origin
+ {
+ get
+ {
+ var origin = base.Origin;
+
+ if (FlipH)
+ {
+ if (origin.HasFlag(Anchor.x0))
+ origin = Anchor.x2 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
+ else if (origin.HasFlag(Anchor.x2))
+ origin = Anchor.x0 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
+ }
+
+ if (FlipV)
+ {
+ if (origin.HasFlag(Anchor.y0))
+ origin = Anchor.y2 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
+ else if (origin.HasFlag(Anchor.y2))
+ origin = Anchor.y0 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
+ }
+
+ return origin;
+ }
+ }
+
+ public override bool IsPresent
+ => !float.IsNaN(DrawPosition.X) && !float.IsNaN(DrawPosition.Y) && base.IsPresent;
+
+ public DrawableStoryboardAnimation(StoryboardAnimation animation)
+ {
+ Animation = animation;
+ Origin = animation.Origin;
+ Position = animation.InitialPosition;
+ Repeat = animation.LoopType == AnimationLoopType.LoopForever;
+
+ if (animation.HasCommands)
+ {
+ LifetimeStart = animation.StartTime;
+ LifetimeEnd = animation.EndTime;
+ }
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase game, TextureStore textureStore)
+ {
+ var basePath = Animation.Path.ToLowerInvariant();
+ for (var frame = 0; frame < Animation.FrameCount; frame++)
+ {
+ var framePath = basePath.Replace(".", frame + ".");
+
+ var path = game.Beatmap.Value.BeatmapSetInfo.Files.FirstOrDefault(f => f.Filename.ToLowerInvariant() == framePath)?.FileInfo.StoragePath;
+ if (path == null)
+ continue;
+
+ var texture = textureStore.Get(path);
+ AddFrame(texture, Animation.FrameDelay);
+ }
+ Animation.ApplyTransforms(this);
+ }
+ }
+}
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.cs
new file mode 100644
index 0000000000..2b5db5b6fa
--- /dev/null
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardLayer.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.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+
+namespace osu.Game.Storyboards.Drawables
+{
+ public class DrawableStoryboardLayer : Container
+ {
+ public StoryboardLayer Layer { get; private set; }
+ public bool Enabled;
+
+ public override bool IsPresent => Enabled && base.IsPresent;
+
+ public DrawableStoryboardLayer(StoryboardLayer layer)
+ {
+ Layer = layer;
+ RelativeSizeAxes = Axes.Both;
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ Enabled = layer.EnabledWhenPassing;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ foreach (var element in Layer.Elements)
+ {
+ var drawable = element.CreateDrawable();
+ if (drawable != null)
+ Add(drawable);
+ }
+ }
+ }
+}
diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs
new file mode 100644
index 0000000000..4b491fa008
--- /dev/null
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSprite.cs
@@ -0,0 +1,80 @@
+// 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.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using System.Linq;
+
+namespace osu.Game.Storyboards.Drawables
+{
+ public class DrawableStoryboardSprite : Sprite, IFlippable
+ {
+ public StoryboardSprite Sprite { get; private set; }
+
+ protected override bool ShouldBeAlive => Sprite.HasCommands && base.ShouldBeAlive;
+ public override bool RemoveWhenNotAlive => !Sprite.HasCommands || base.RemoveWhenNotAlive;
+
+ public bool FlipH { get; set; }
+ public bool FlipV { get; set; }
+
+ protected override Vector2 DrawScale
+ => new Vector2(FlipH ? -base.DrawScale.X : base.DrawScale.X, FlipV ? -base.DrawScale.Y : base.DrawScale.Y);
+
+ public override Anchor Origin
+ {
+ get
+ {
+ var origin = base.Origin;
+
+ if (FlipH)
+ {
+ if (origin.HasFlag(Anchor.x0))
+ origin = Anchor.x2 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
+ else if (origin.HasFlag(Anchor.x2))
+ origin = Anchor.x0 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
+ }
+
+ if (FlipV)
+ {
+ if (origin.HasFlag(Anchor.y0))
+ origin = Anchor.y2 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
+ else if (origin.HasFlag(Anchor.y2))
+ origin = Anchor.y0 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
+ }
+
+ return origin;
+ }
+ }
+
+ public override bool IsPresent
+ => !float.IsNaN(DrawPosition.X) && !float.IsNaN(DrawPosition.Y) && base.IsPresent;
+
+ public DrawableStoryboardSprite(StoryboardSprite sprite)
+ {
+ Sprite = sprite;
+ Origin = sprite.Origin;
+ Position = sprite.InitialPosition;
+
+ if (sprite.HasCommands)
+ {
+ LifetimeStart = sprite.StartTime;
+ LifetimeEnd = sprite.EndTime;
+ }
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGameBase game, TextureStore textureStore)
+ {
+ var spritePath = Sprite.Path.ToLowerInvariant();
+ var path = game.Beatmap.Value.BeatmapSetInfo.Files.FirstOrDefault(f => f.Filename.ToLowerInvariant() == spritePath)?.FileInfo.StoragePath;
+ if (path == null)
+ return;
+
+ Texture = textureStore.Get(path);
+ Sprite.ApplyTransforms(this);
+ }
+ }
+}
diff --git a/osu.Game/Storyboards/Drawables/DrawablesExtensions.cs b/osu.Game/Storyboards/Drawables/DrawablesExtensions.cs
new file mode 100644
index 0000000000..3b21c47b96
--- /dev/null
+++ b/osu.Game/Storyboards/Drawables/DrawablesExtensions.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 osu.Framework.Graphics;
+using osu.Framework.Graphics.Transforms;
+
+namespace osu.Game.Storyboards.Drawables
+{
+ public static class DrawablesExtensions
+ {
+ ///
+ /// Adjusts after a delay.
+ ///
+ /// A to which further transforms can be added.
+ public static TransformSequence TransformBlendingMode(this T drawable, BlendingMode newValue, double delay = 0)
+ where T : Drawable
+ => drawable.TransformTo(drawable.PopulateTransform(new TransformBlendingMode(), newValue, delay));
+ }
+
+ public class TransformBlendingMode : Transform
+ {
+ private BlendingMode valueAt(double time)
+ => time < EndTime ? StartValue : EndValue;
+
+ public override string TargetMember => nameof(Drawable.Blending);
+
+ protected override void Apply(Drawable d, double time) => d.Blending = valueAt(time);
+ protected override void ReadIntoStartValue(Drawable d) => StartValue = d.Blending.Mode;
+ }
+}
diff --git a/osu.Game/Storyboards/Drawables/IFlippable.cs b/osu.Game/Storyboards/Drawables/IFlippable.cs
new file mode 100644
index 0000000000..4d21c9d140
--- /dev/null
+++ b/osu.Game/Storyboards/Drawables/IFlippable.cs
@@ -0,0 +1,55 @@
+// 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.Transforms;
+
+namespace osu.Game.Storyboards.Drawables
+{
+ public interface IFlippable : ITransformable
+ {
+ bool FlipH { get; set; }
+ bool FlipV { get; set; }
+ }
+
+ public class TransformFlipH : Transform
+ {
+ private bool valueAt(double time)
+ => time < EndTime ? StartValue : EndValue;
+
+ public override string TargetMember => nameof(IFlippable.FlipH);
+
+ protected override void Apply(IFlippable d, double time) => d.FlipH = valueAt(time);
+ protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipH;
+ }
+
+ public class TransformFlipV : Transform
+ {
+ private bool valueAt(double time)
+ => time < EndTime ? StartValue : EndValue;
+
+ public override string TargetMember => nameof(IFlippable.FlipV);
+
+ protected override void Apply(IFlippable d, double time) => d.FlipV = valueAt(time);
+ protected override void ReadIntoStartValue(IFlippable d) => StartValue = d.FlipV;
+ }
+
+ public static class FlippableExtensions
+ {
+ ///
+ /// Adjusts after a delay.
+ ///
+ /// A to which further transforms can be added.
+ public static TransformSequence TransformFlipH(this T flippable, bool newValue, double delay = 0)
+ where T : IFlippable
+ => flippable.TransformTo(flippable.PopulateTransform(new TransformFlipH(), newValue, delay));
+
+ ///
+ /// Adjusts after a delay.
+ ///
+ /// A to which further transforms can be added.
+ public static TransformSequence TransformFlipV(this T flippable, bool newValue, double delay = 0)
+ where T : IFlippable
+ => flippable.TransformTo(flippable.PopulateTransform(new TransformFlipV(), newValue, delay));
+ }
+}
diff --git a/osu.Game/Storyboards/IStoryboardElement.cs b/osu.Game/Storyboards/IStoryboardElement.cs
new file mode 100644
index 0000000000..d5fc86b0f7
--- /dev/null
+++ b/osu.Game/Storyboards/IStoryboardElement.cs
@@ -0,0 +1,13 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+
+namespace osu.Game.Storyboards
+{
+ public interface IStoryboardElement
+ {
+ string Path { get; }
+ Drawable CreateDrawable();
+ }
+}
diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs
new file mode 100644
index 0000000000..111cdd5d41
--- /dev/null
+++ b/osu.Game/Storyboards/Storyboard.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.Storyboards.Drawables;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace osu.Game.Storyboards
+{
+ public class Storyboard
+ {
+ private readonly Dictionary layers = new Dictionary();
+ public IEnumerable Layers => layers.Values;
+
+ public Storyboard()
+ {
+ layers.Add("Background", new StoryboardLayer("Background", 3));
+ layers.Add("Fail", new StoryboardLayer("Fail", 2) { EnabledWhenPassing = false, });
+ layers.Add("Pass", new StoryboardLayer("Pass", 1) { EnabledWhenFailing = false, });
+ layers.Add("Foreground", new StoryboardLayer("Foreground", 0));
+ }
+
+ public StoryboardLayer GetLayer(string name)
+ {
+ StoryboardLayer layer;
+ if (!layers.TryGetValue(name, out layer))
+ layers[name] = layer = new StoryboardLayer(name, layers.Values.Min(l => l.Depth) - 1);
+
+ return layer;
+ }
+
+ public DrawableStoryboard CreateDrawable()
+ => new DrawableStoryboard(this);
+ }
+}
diff --git a/osu.Game/Storyboards/StoryboardAnimation.cs b/osu.Game/Storyboards/StoryboardAnimation.cs
new file mode 100644
index 0000000000..98936df9e5
--- /dev/null
+++ b/osu.Game/Storyboards/StoryboardAnimation.cs
@@ -0,0 +1,33 @@
+// 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.Game.Storyboards.Drawables;
+
+namespace osu.Game.Storyboards
+{
+ public class StoryboardAnimation : StoryboardSprite
+ {
+ public int FrameCount;
+ public double FrameDelay;
+ public AnimationLoopType LoopType;
+
+ public StoryboardAnimation(string path, Anchor origin, Vector2 initialPosition, int frameCount, double frameDelay, AnimationLoopType loopType)
+ : base(path, origin, initialPosition)
+ {
+ FrameCount = frameCount;
+ FrameDelay = frameDelay;
+ LoopType = loopType;
+ }
+
+ public override Drawable CreateDrawable()
+ => new DrawableStoryboardAnimation(this);
+ }
+
+ public enum AnimationLoopType
+ {
+ LoopForever,
+ LoopOnce,
+ }
+}
diff --git a/osu.Game/Storyboards/StoryboardLayer.cs b/osu.Game/Storyboards/StoryboardLayer.cs
new file mode 100644
index 0000000000..f565b13eb5
--- /dev/null
+++ b/osu.Game/Storyboards/StoryboardLayer.cs
@@ -0,0 +1,33 @@
+// Copyright (c) 2007-2017 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Storyboards.Drawables;
+using System.Collections.Generic;
+
+namespace osu.Game.Storyboards
+{
+ public class StoryboardLayer
+ {
+ public string Name;
+ public int Depth;
+ public bool EnabledWhenPassing = true;
+ public bool EnabledWhenFailing = true;
+
+ private readonly List elements = new List();
+ public IEnumerable Elements => elements;
+
+ public StoryboardLayer(string name, int depth)
+ {
+ Name = name;
+ Depth = depth;
+ }
+
+ public void Add(IStoryboardElement element)
+ {
+ elements.Add(element);
+ }
+
+ public DrawableStoryboardLayer CreateDrawable()
+ => new DrawableStoryboardLayer(this) { Depth = Depth, };
+ }
+}
diff --git a/osu.Game/Storyboards/StoryboardSample.cs b/osu.Game/Storyboards/StoryboardSample.cs
new file mode 100644
index 0000000000..bcf6a4329d
--- /dev/null
+++ b/osu.Game/Storyboards/StoryboardSample.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.Framework.Graphics;
+
+namespace osu.Game.Storyboards
+{
+ public class StoryboardSample : IStoryboardElement
+ {
+ public string Path { get; set; }
+ public double Time;
+ public float Volume;
+
+ public StoryboardSample(string path, double time, float volume)
+ {
+ Path = path;
+ Time = time;
+ Volume = volume;
+ }
+
+ public Drawable CreateDrawable()
+ => null;
+ }
+}
diff --git a/osu.Game/Storyboards/StoryboardSprite.cs b/osu.Game/Storyboards/StoryboardSprite.cs
new file mode 100644
index 0000000000..598167d720
--- /dev/null
+++ b/osu.Game/Storyboards/StoryboardSprite.cs
@@ -0,0 +1,113 @@
+// 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.Game.Storyboards.Drawables;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace osu.Game.Storyboards
+{
+ public class StoryboardSprite : IStoryboardElement
+ {
+ private readonly List loops = new List();
+ private readonly List triggers = new List();
+
+ public string Path { get; set; }
+ public Anchor Origin;
+ public Vector2 InitialPosition;
+
+ public readonly CommandTimelineGroup TimelineGroup = new CommandTimelineGroup();
+
+ public double StartTime => Math.Min(
+ TimelineGroup.HasCommands ? TimelineGroup.CommandsStartTime : double.MaxValue,
+ loops.Any(l => l.HasCommands) ? loops.Where(l => l.HasCommands).Min(l => l.StartTime) : double.MaxValue);
+
+ public double EndTime => Math.Max(
+ TimelineGroup.HasCommands ? TimelineGroup.CommandsEndTime : double.MinValue,
+ loops.Any(l => l.HasCommands) ? loops.Where(l => l.HasCommands).Max(l => l.EndTime) : double.MinValue);
+
+ public bool HasCommands => TimelineGroup.HasCommands || loops.Any(l => l.HasCommands);
+
+ private delegate void DrawablePropertyInitializer(Drawable drawable, T value);
+ private delegate void DrawableTransformer(Drawable drawable, T value, double duration, Easing easing);
+
+ public StoryboardSprite(string path, Anchor origin, Vector2 initialPosition)
+ {
+ Path = path;
+ Origin = origin;
+ InitialPosition = initialPosition;
+ }
+
+ public CommandLoop AddLoop(double startTime, int loopCount)
+ {
+ var loop = new CommandLoop(startTime, loopCount);
+ loops.Add(loop);
+ return loop;
+ }
+
+ public CommandTrigger AddTrigger(string triggerName, double startTime, double endTime, int groupNumber)
+ {
+ var trigger = new CommandTrigger(triggerName, startTime, endTime, groupNumber);
+ triggers.Add(trigger);
+ return trigger;
+ }
+
+ public virtual Drawable CreateDrawable()
+ => new DrawableStoryboardSprite(this);
+
+ public void ApplyTransforms(Drawable drawable, IEnumerable> triggeredGroups = null)
+ {
+ applyCommands(drawable, getCommands(g => g.X, triggeredGroups), (d, value) => d.X = value, (d, value, duration, easing) => d.MoveToX(value, duration, easing));
+ applyCommands(drawable, getCommands(g => g.Y, triggeredGroups), (d, value) => d.Y = value, (d, value, duration, easing) => d.MoveToY(value, duration, easing));
+ applyCommands(drawable, getCommands(g => g.Scale, triggeredGroups), (d, value) => d.Scale = value, (d, value, duration, easing) => d.ScaleTo(value, duration, easing));
+ applyCommands(drawable, getCommands(g => g.Rotation, triggeredGroups), (d, value) => d.Rotation = value, (d, value, duration, easing) => d.RotateTo(value, duration, easing));
+ applyCommands(drawable, getCommands(g => g.Colour, triggeredGroups), (d, value) => d.Colour = value, (d, value, duration, easing) => d.FadeColour(value, duration, easing));
+ applyCommands(drawable, getCommands(g => g.Alpha, triggeredGroups), (d, value) => d.Alpha = value, (d, value, duration, easing) => d.FadeTo(value, duration, easing));
+ applyCommands(drawable, getCommands(g => g.BlendingMode, triggeredGroups), (d, value) => d.Blending = value, (d, value, duration, easing) => d.TransformBlendingMode(value, duration), false);
+
+ var flippable = drawable as IFlippable;
+ if (flippable != null)
+ {
+ applyCommands(drawable, getCommands(g => g.FlipH, triggeredGroups), (d, value) => flippable.FlipH = value, (d, value, duration, easing) => flippable.TransformFlipH(value, duration), false);
+ applyCommands(drawable, getCommands(g => g.FlipV, triggeredGroups), (d, value) => flippable.FlipV = value, (d, value, duration, easing) => flippable.TransformFlipV(value, duration), false);
+ }
+ }
+
+ private void applyCommands(Drawable drawable, IEnumerable.TypedCommand> commands, DrawablePropertyInitializer initializeProperty, DrawableTransformer transform, bool alwaysInitialize = true)
+ where T : struct
+ {
+ var initialized = false;
+ foreach (var command in commands.OrderBy(l => l))
+ {
+ if (!initialized)
+ {
+ if (alwaysInitialize || command.StartTime == command.EndTime)
+ initializeProperty.Invoke(drawable, command.StartValue);
+ initialized = true;
+ }
+ using (drawable.BeginAbsoluteSequence(command.StartTime))
+ {
+ transform(drawable, command.StartValue, 0, Easing.None);
+ transform(drawable, command.EndValue, command.Duration, command.Easing);
+ }
+ }
+ }
+
+ private IEnumerable.TypedCommand> getCommands(CommandTimelineSelector timelineSelector, IEnumerable> triggeredGroups)
+ {
+ var commands = TimelineGroup.GetCommands(timelineSelector);
+ foreach (var loop in loops)
+ commands = commands.Concat(loop.GetCommands(timelineSelector));
+ if (triggeredGroups != null)
+ foreach (var pair in triggeredGroups)
+ commands = commands.Concat(pair.Item1.GetCommands(timelineSelector, pair.Item2));
+ return commands;
+ }
+
+ public override string ToString()
+ => $"{Path}, {Origin}, {InitialPosition}";
+ }
+}
diff --git a/osu.Game/Users/Avatar.cs b/osu.Game/Users/Avatar.cs
index 5d518f1780..111c901ca0 100644
--- a/osu.Game/Users/Avatar.cs
+++ b/osu.Game/Users/Avatar.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Users
private void load(TextureStore textures)
{
Texture texture = null;
- if (user?.Id > 1) texture = textures.Get($@"https://a.ppy.sh/{user.Id}");
+ if (user != null && user.Id > 1) texture = textures.Get($@"https://a.ppy.sh/{user.Id}");
if (texture == null) texture = textures.Get(@"Online/avatar-guest");
Add(new Sprite
diff --git a/osu.Game/Users/UserStatistics.cs b/osu.Game/Users/UserStatistics.cs
index 05f3d65f30..15b57553a6 100644
--- a/osu.Game/Users/UserStatistics.cs
+++ b/osu.Game/Users/UserStatistics.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Users
public int MaxCombo;
[JsonProperty(@"replays_watched_by_others")]
- public int ReplayWatched;
+ public int ReplaysWatched;
[JsonProperty(@"grade_counts")]
public Grades GradesCount;
diff --git a/osu.Game/app.config b/osu.Game/app.config
new file mode 100644
index 0000000000..faeaf001de
--- /dev/null
+++ b/osu.Game/app.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index b707e2f34d..8da45061bc 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -37,15 +37,16 @@
- $(SolutionDir)\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll
+ $(SolutionDir)\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll
+ True
- $(SolutionDir)\packages\ppy.OpenTK.3.0\lib\net45\OpenTK.dll
+ $(SolutionDir)\packages\OpenTK.3.0.0-git00009\lib\net20\OpenTK.dll
True
-
+
+ $(SolutionDir)\packages\SharpCompress.0.18.1\lib\net45\SharpCompress.dll
True
- $(SolutionDir)\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll
$(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll
@@ -80,6 +81,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -568,6 +585,8 @@
osu.licenseheader
+
+
diff --git a/osu.Game/packages.config b/osu.Game/packages.config
index 434f9328ea..292ba22c06 100644
--- a/osu.Game/packages.config
+++ b/osu.Game/packages.config
@@ -5,9 +5,9 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
-->
-
-
-
+
+
+
diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index 0b8c196ec8..4011e3991f 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -42,6 +42,7 @@
WARNING
WARNING
ERROR
+ HINT
HINT
WARNING
WARNING
@@ -56,6 +57,8 @@
WARNING
HINT
HINT
+ HINT
+ HINT
HINT
WARNING
WARNING
@@ -617,6 +620,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-frame
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />