diff --git a/osu-framework b/osu-framework
index a7dd1fba84..90bf49a2df 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit a7dd1fba8473f636ee8f6e49075331d9076383ee
+Subproject commit 90bf49a2df3dbad5994d922df63e4891c622dbc3
diff --git a/osu-resources b/osu-resources
index 7724abdf1d..266965f0d7 160000
--- a/osu-resources
+++ b/osu-resources
@@ -1 +1 @@
-Subproject commit 7724abdf1d7c9705ba2e3989a9c604e17ccdc871
+Subproject commit 266965f0d795b94a126e2da302bd2c10eadd642a
diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
index 5f08048bf9..cdce598ce8 100644
--- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
+++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj
@@ -32,6 +32,10 @@
false
+
+ $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll
+ True
+
$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
diff --git a/osu.Game.Rulesets.Catch/packages.config b/osu.Game.Rulesets.Catch/packages.config
index b39a85a382..e67d3e9b34 100644
--- a/osu.Game.Rulesets.Catch/packages.config
+++ b/osu.Game.Rulesets.Catch/packages.config
@@ -1,5 +1,6 @@
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
new file mode 100644
index 0000000000..a4de360870
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
@@ -0,0 +1,34 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Configuration.Tracking;
+using osu.Game.Configuration;
+using osu.Game.Rulesets.Configuration;
+
+namespace osu.Game.Rulesets.Mania.Configuration
+{
+ public class ManiaConfigManager : RulesetConfigManager
+ {
+ public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant)
+ : base(settings, ruleset, variant)
+ {
+ }
+
+ protected override void InitialiseDefaults()
+ {
+ base.InitialiseDefaults();
+
+ Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0);
+ }
+
+ public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
+ {
+ new TrackedSetting(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms"))
+ };
+ }
+
+ public enum ManiaSetting
+ {
+ ScrollTime
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 882628642b..21f00c003b 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -11,13 +11,14 @@ using osu.Framework.Graphics.Colour;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using System;
+using System.Linq;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI
{
- public class Column : ScrollingPlayfield, IHasAccentColour
+ public class Column : ScrollingPlayfield, IKeyBindingHandler, IHasAccentColour
{
private const float key_icon_size = 10;
private const float key_icon_corner_radius = 3;
@@ -259,5 +260,26 @@ namespace osu.Game.Rulesets.Mania.UI
public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false;
public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false;
}
+
+ public bool OnPressed(ManiaAction action)
+ {
+ // Play the sounds of the next hitobject
+ if (HitObjects.AliveObjects.Any())
+ {
+ // If there are alive hitobjects, we can abuse the fact that AliveObjects are sorted by time (see: Add())
+ HitObjects.AliveObjects.First().PlaySamples();
+ }
+ else
+ {
+ // If not, we do a slow search - we might want to do a BinarySearch here if this becomes problematic
+ // We fallback to LastOrDefault() if we're beyond the last note in the map
+ var hitObject = HitObjects.Objects.FirstOrDefault(h => h.HitObject.StartTime > Time.Current) ?? HitObjects.Objects.LastOrDefault();
+ hitObject?.PlaySamples();
+ }
+
+ return false;
+ }
+
+ public bool OnReleased(ManiaAction action) => false;
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
index c008e71819..c3cbf81af1 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
@@ -7,10 +7,12 @@ using osu.Framework.Graphics.Containers;
using System;
using System.Collections.Generic;
using System.Linq;
+using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI
@@ -78,6 +80,12 @@ namespace osu.Game.Rulesets.Mania.UI
return null;
}
+ [BackgroundDependencyLoader]
+ private void load(ManiaConfigManager maniaConfig)
+ {
+ maniaConfig.BindWith(ManiaSetting.ScrollTime, VisibleTimeRange);
+ }
+
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
getStageByColumn(((ManiaHitObject)judgedObject.HitObject).Column).OnJudgement(judgedObject, judgement);
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
index c438fe1abc..436d5c1ea6 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
@@ -10,8 +10,11 @@ using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Configuration;
+using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Mods;
+using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
@@ -77,11 +80,9 @@ namespace osu.Game.Rulesets.Mania.UI
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
- public override PassThroughInputManager CreateInputManager()
- {
- var variantType = Mods.OfType().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single;
- return new ManiaInputManager(Ruleset.RulesetInfo, (int)variantType + Beatmap.TotalColumns);
- }
+ public override int Variant => (int)(Mods.OfType().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single) + Beatmap.TotalColumns;
+
+ public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap);
@@ -103,5 +104,7 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Vector2 GetPlayfieldAspectAdjust() => new Vector2(1, 0.8f);
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this);
+
+ protected override IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => new ManiaConfigManager(settings, Ruleset.RulesetInfo, Variant);
}
}
diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
index c3c0089f14..b9e7f8e60f 100644
--- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
+++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj
@@ -32,6 +32,10 @@
false
+
+ $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll
+ True
+
$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
@@ -58,6 +62,7 @@
+
diff --git a/osu.Game.Rulesets.Mania/packages.config b/osu.Game.Rulesets.Mania/packages.config
index b39a85a382..e67d3e9b34 100644
--- a/osu.Game.Rulesets.Mania/packages.config
+++ b/osu.Game.Rulesets.Mania/packages.config
@@ -1,5 +1,6 @@
+
\ 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 d42338f20e..3dad5b508c 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
continue;
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
- float stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
+ double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
if (objectN.StartTime - endTime > stackThreshold)
//We are no longer within stacking range of the next object.
@@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
OsuHitObject objectI = beatmap.HitObjects[i];
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
- float stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
+ double stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
/* If this object is a hitcircle, then we enter this "special" case.
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
index 3a486e7763..b4dd08eadb 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Description => @"Play with no approach circles and fading notes for a slight score advantage.";
public override double ScoreMultiplier => 1.06;
- private const float fade_in_duration_multiplier = 0.4f;
+ private const double fade_in_duration_multiplier = 0.4;
private const double fade_out_duration_multiplier = 0.3;
public void ApplyToDrawableHitObjects(IEnumerable drawables)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
index 077e97aa95..8285b1b3e1 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
@@ -22,8 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
///
private bool isRepeatAtEnd => repeatPoint.RepeatIndex % 2 == 0;
- public double FadeInTime;
- public double FadeOutTime;
+ private double animDuration;
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
: base(repeatPoint)
@@ -54,11 +53,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdatePreemptState()
{
- var animIn = Math.Min(150, repeatPoint.StartTime - FadeInTime);
+ animDuration = Math.Min(150, repeatPoint.SpanDuration / 2);
- this.FadeIn(animIn).ScaleTo(1.2f, animIn)
+ this.FadeIn(animDuration).ScaleTo(1.2f, animDuration / 2)
.Then()
- .ScaleTo(1, 150, Easing.Out);
+ .ScaleTo(1, animDuration / 2, Easing.Out);
}
protected override void UpdateCurrentState(ArmedState state)
@@ -66,14 +65,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
switch (state)
{
case ArmedState.Idle:
- this.Delay(FadeOutTime - repeatPoint.StartTime).FadeOut();
+ this.Delay(HitObject.TimePreempt).FadeOut();
break;
case ArmedState.Miss:
- this.FadeOut(160);
+ this.FadeOut(animDuration);
break;
case ArmedState.Hit:
- this.FadeOut(120, Easing.OutQuint)
- .ScaleTo(Scale * 1.5f, 120, Easing.OutQuint);
+ this.FadeOut(animDuration, Easing.OutQuint)
+ .ScaleTo(Scale * 1.5f, animDuration, Easing.OutQuint);
break;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 8deb31db2a..d277aca75f 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -75,12 +75,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
AddNested(InitialCircle);
- var spanDuration = s.Curve.Distance / s.Velocity;
foreach (var tick in s.NestedHitObjects.OfType())
{
- var spanStartTime = s.StartTime + tick.SpanIndex * spanDuration;
+ var spanStartTime = s.StartTime + tick.SpanIndex * s.SpanDuration;
var fadeInTime = spanStartTime + (tick.StartTime - spanStartTime) / 2 - (tick.SpanIndex == 0 ? HitObject.TimeFadein : HitObject.TimeFadein / 2);
- var fadeOutTime = spanStartTime + spanDuration;
+ var fadeOutTime = spanStartTime + s.SpanDuration;
var drawableTick = new DrawableSliderTick(tick)
{
@@ -95,15 +94,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
foreach (var repeatPoint in s.NestedHitObjects.OfType())
{
- var repeatStartTime = s.StartTime + (repeatPoint.RepeatIndex + 1) * spanDuration;
- var fadeInTime = repeatStartTime + (repeatPoint.StartTime - repeatStartTime) / 2 - (repeatPoint.RepeatIndex == 0 ? HitObject.TimeFadein : HitObject.TimeFadein / 2);
- var fadeOutTime = repeatStartTime + spanDuration;
-
var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this)
{
- FadeInTime = fadeInTime,
- FadeOutTime = fadeOutTime,
- Position = repeatPoint.Position,
+ Position = repeatPoint.Position
};
repeatPoints.Add(drawableRepeatPoint);
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 0e7c2f3d4d..f217ae89e9 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Osu.Objects
public double HitWindow100 = 80;
public double HitWindow300 = 30;
- public float TimePreempt = 600;
- public float TimeFadein = 400;
+ public double TimePreempt = 600;
+ public double TimeFadein = 400;
public Vector2 Position { get; set; }
public float X => Position.X;
diff --git a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
index abdbb97072..eaaa8d7a7e 100644
--- a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
@@ -1,10 +1,25 @@
// Copyright (c) 2007-2018 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.Beatmaps.ControlPoints;
+
namespace osu.Game.Rulesets.Osu.Objects
{
public class RepeatPoint : OsuHitObject
{
public int RepeatIndex { get; set; }
+ public double SpanDuration { get; set; }
+
+ protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
+ {
+ base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
+
+ // We want to show the first RepeatPoint as the TimePreempt dictates but on short (and possibly fast) sliders
+ // we may need to cut down this time on following RepeatPoints to only show up to two RepeatPoints at any given time.
+ if (RepeatIndex > 0)
+ TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 35635788f2..79bb14a475 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -60,6 +60,11 @@ namespace osu.Game.Rulesets.Osu.Objects
public List> RepeatSamples { get; set; } = new List>();
public int RepeatCount { get; set; }
+ ///
+ /// The length of one span of this .
+ ///
+ public double SpanDuration => Duration / this.SpanCount();
+
private int stackHeight;
public override int StackHeight
@@ -102,13 +107,12 @@ namespace osu.Game.Rulesets.Osu.Objects
var length = Curve.Distance;
var tickDistance = Math.Min(TickDistance, length);
- var spanDuration = length / Velocity;
var minDistanceFromEnd = Velocity * 0.01;
for (var span = 0; span < this.SpanCount(); span++)
{
- var spanStartTime = StartTime + span * spanDuration;
+ var spanStartTime = StartTime + span * SpanDuration;
var reversed = span % 2 == 1;
for (var d = tickDistance; d <= length; d += tickDistance)
@@ -133,7 +137,7 @@ namespace osu.Game.Rulesets.Osu.Objects
AddNested(new SliderTick
{
SpanIndex = span,
- StartTime = spanStartTime + timeProgress * spanDuration,
+ StartTime = spanStartTime + timeProgress * SpanDuration,
Position = Curve.PositionAt(distanceProgress),
StackHeight = StackHeight,
Scale = Scale,
@@ -146,16 +150,13 @@ namespace osu.Game.Rulesets.Osu.Objects
private void createRepeatPoints()
{
- var repeatDuration = Distance / Velocity;
-
for (int repeatIndex = 0, repeat = 1; repeatIndex < RepeatCount; repeatIndex++, repeat++)
{
- var repeatStartTime = StartTime + repeat * repeatDuration;
-
AddNested(new RepeatPoint
{
RepeatIndex = repeatIndex,
- StartTime = repeatStartTime,
+ SpanDuration = SpanDuration,
+ StartTime = StartTime + repeat * SpanDuration,
Position = Curve.PositionAt(repeat % 2),
StackHeight = StackHeight,
Scale = Scale,
diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs
index c9ab39e0b9..d747dfc2e9 100644
--- a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs
+++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs
@@ -63,6 +63,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddStep("Fast Short Slider", () => testShortHighSpeed());
AddStep("Fast Short Slider 1 Repeat", () => testShortHighSpeed(1));
AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2));
+ AddStep("Fast Short Slider 6 Repeats", () => testShortHighSpeed(6));
AddStep("Perfect Curve", () => testPerfect());
AddStep("Perfect Curve 1 Repeat", () => testPerfect(1));
diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
index a59d4607df..74a3883f0a 100644
--- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
+++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj
@@ -33,6 +33,10 @@
false
+
+ $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll
+ True
+
$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
diff --git a/osu.Game.Rulesets.Osu/packages.config b/osu.Game.Rulesets.Osu/packages.config
index b39a85a382..e67d3e9b34 100644
--- a/osu.Game.Rulesets.Osu/packages.config
+++ b/osu.Game.Rulesets.Osu/packages.config
@@ -1,5 +1,6 @@
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
index c1fe2c13a8..002159439d 100644
--- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
+++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs
@@ -35,8 +35,8 @@ namespace osu.Game.Rulesets.Taiko.Replays
{
bool hitButton = true;
- Frames.Add(new ReplayFrame(-100000, null, null, ReplayButtonState.None));
- Frames.Add(new ReplayFrame(Beatmap.HitObjects[0].StartTime - 1000, null, null, ReplayButtonState.None));
+ Frames.Add(new TaikoReplayFrame(-100000, ReplayButtonState.None));
+ Frames.Add(new TaikoReplayFrame(Beatmap.HitObjects[0].StartTime - 1000, ReplayButtonState.None));
for (int i = 0; i < Beatmap.HitObjects.Count; i++)
{
@@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
break;
}
- Frames.Add(new ReplayFrame(j, null, null, button));
+ Frames.Add(new TaikoReplayFrame(j, button));
d = (d + 1) % 4;
if (++count == req)
break;
@@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
{
foreach (var tick in drumRoll.NestedHitObjects.OfType())
{
- Frames.Add(new ReplayFrame(tick.StartTime, null, null, hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2));
+ Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2));
hitButton = !hitButton;
}
}
@@ -107,18 +107,18 @@ namespace osu.Game.Rulesets.Taiko.Replays
button = hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2;
}
- Frames.Add(new ReplayFrame(h.StartTime, null, null, button));
+ Frames.Add(new TaikoReplayFrame(h.StartTime, button));
}
else
throw new InvalidOperationException("Unknown hit object type.");
- Frames.Add(new ReplayFrame(endTime + KEY_UP_DELAY, null, null, ReplayButtonState.None));
+ Frames.Add(new TaikoReplayFrame(endTime + KEY_UP_DELAY, ReplayButtonState.None));
if (i < Beatmap.HitObjects.Count - 1)
{
double waitTime = Beatmap.HitObjects[i + 1].StartTime - 1000;
if (waitTime > endTime)
- Frames.Add(new ReplayFrame(waitTime, null, null, ReplayButtonState.None));
+ Frames.Add(new TaikoReplayFrame(waitTime, ReplayButtonState.None));
}
hitButton = !hitButton;
diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs
new file mode 100644
index 0000000000..0c60cdc109
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Replays/TaikoReplayFrame.cs
@@ -0,0 +1,17 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Replays;
+
+namespace osu.Game.Rulesets.Taiko.Replays
+{
+ public class TaikoReplayFrame : ReplayFrame
+ {
+ public override bool IsImportant => MouseLeft || MouseRight;
+
+ public TaikoReplayFrame(double time, ReplayButtonState buttons)
+ : base(time, null, null, buttons)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
index 36ac9384cf..5795048322 100644
--- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
+++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj
@@ -32,6 +32,10 @@
false
+
+ $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll
+ True
+
$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
@@ -91,6 +95,7 @@
+
diff --git a/osu.Game.Rulesets.Taiko/packages.config b/osu.Game.Rulesets.Taiko/packages.config
index b39a85a382..e67d3e9b34 100644
--- a/osu.Game.Rulesets.Taiko/packages.config
+++ b/osu.Game.Rulesets.Taiko/packages.config
@@ -1,5 +1,6 @@
+
\ No newline at end of file
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
index cd3f6d066f..dce6c0f55b 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
@@ -70,7 +70,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition);
Assert.IsTrue(sprite.IsDrawable);
Assert.AreEqual(Anchor.Centre, sprite.Origin);
- Assert.AreEqual(Path.Combine("SB", "lyric", "ja-21.png"), sprite.Path);
+ Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path);
var animation = background.Elements.ElementAt(12) as StoryboardAnimation;
Assert.NotNull(animation);
@@ -82,7 +82,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsTrue(animation.IsDrawable);
Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType);
Assert.AreEqual(Anchor.Centre, animation.Origin);
- Assert.AreEqual(Path.Combine("SB", "red jitter", "red_0000.jpg"), animation.Path);
+ Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path);
Assert.AreEqual(78993, animation.StartTime);
}
}
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 059adc6d55..d30241fae4 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -33,6 +33,10 @@
$(SolutionDir)\packages\DeepEqual.1.6.0.0\lib\net40\DeepEqual.dll
+
+ $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll
+ True
+
$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll
True
diff --git a/osu.Game.Tests/packages.config b/osu.Game.Tests/packages.config
index 62ddb99609..c16d10bf45 100644
--- a/osu.Game.Tests/packages.config
+++ b/osu.Game.Tests/packages.config
@@ -1,11 +1,12 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index d60297a26c..a4ff060c83 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -301,6 +301,6 @@ namespace osu.Game.Beatmaps.Formats
}
}
- private string cleanFilename(string path) => FileSafety.PathSanitise(path.Trim('\"'));
+ private string cleanFilename(string path) => FileSafety.PathStandardise(FileSafety.PathSanitise(path.Trim('\"')));
}
}
diff --git a/osu.Game/Configuration/DatabasedConfigManager.cs b/osu.Game/Configuration/DatabasedConfigManager.cs
new file mode 100644
index 0000000000..1f7a84c6d3
--- /dev/null
+++ b/osu.Game/Configuration/DatabasedConfigManager.cs
@@ -0,0 +1,71 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Configuration;
+using osu.Game.Rulesets;
+
+namespace osu.Game.Configuration
+{
+ public abstract class DatabasedConfigManager : ConfigManager
+ where T : struct
+ {
+ private readonly SettingsStore settings;
+
+ private readonly int variant;
+
+ private readonly List databasedSettings;
+
+ private readonly RulesetInfo ruleset;
+
+ protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int variant = 0)
+ {
+ this.settings = settings;
+ this.ruleset = ruleset;
+ this.variant = variant;
+
+ databasedSettings = settings.Query(ruleset?.ID, variant);
+
+ InitialiseDefaults();
+ }
+
+ protected override void PerformLoad()
+ {
+ }
+
+ protected override bool PerformSave()
+ {
+ return true;
+ }
+
+ protected override void AddBindable(T lookup, Bindable bindable)
+ {
+ base.AddBindable(lookup, bindable);
+
+ var setting = databasedSettings.FirstOrDefault(s => (int)s.Key == (int)(object)lookup);
+ if (setting != null)
+ {
+ bindable.Parse(setting.Value);
+ }
+ else
+ {
+ settings.Update(setting = new DatabasedSetting
+ {
+ Key = lookup,
+ Value = bindable.Value,
+ RulesetID = ruleset?.ID,
+ Variant = variant,
+ });
+
+ databasedSettings.Add(setting);
+ }
+
+ bindable.ValueChanged += v =>
+ {
+ setting.Value = v;
+ settings.Update(setting);
+ };
+ }
+ }
+}
diff --git a/osu.Game/Configuration/DatabasedSetting.cs b/osu.Game/Configuration/DatabasedSetting.cs
new file mode 100644
index 0000000000..7c2f65c854
--- /dev/null
+++ b/osu.Game/Configuration/DatabasedSetting.cs
@@ -0,0 +1,51 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.ComponentModel.DataAnnotations.Schema;
+using osu.Game.Database;
+
+namespace osu.Game.Configuration
+{
+ [Table("Settings")]
+ public class DatabasedSetting : IHasPrimaryKey
+ {
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public int ID { get; set; }
+
+ public int? RulesetID { get; set; }
+
+ public int? Variant { get; set; }
+
+ [Column("Key")]
+ public int IntKey
+ {
+ get => (int)Key;
+ private set => Key = value;
+ }
+
+ [Column("Value")]
+ public string StringValue
+ {
+ get => Value.ToString();
+ set => Value = value;
+ }
+
+ public object Key;
+ public object Value;
+
+ public DatabasedSetting(object key, object value)
+ {
+ Key = key;
+ Value = value;
+ }
+
+ ///
+ /// Constructor for derived classes that may require serialisation.
+ ///
+ public DatabasedSetting()
+ {
+ }
+
+ public override string ToString() => $"{Key}=>{Value}";
+ }
+}
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index 23f7fd6ac1..33810c9712 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -8,7 +8,7 @@ using osu.Game.Screens.Select;
namespace osu.Game.Configuration
{
- public class OsuConfigManager : ConfigManager
+ public class OsuConfigManager : IniConfigManager
{
protected override void InitialiseDefaults()
{
diff --git a/osu.Game/Configuration/SettingsStore.cs b/osu.Game/Configuration/SettingsStore.cs
new file mode 100644
index 0000000000..9b18151c84
--- /dev/null
+++ b/osu.Game/Configuration/SettingsStore.cs
@@ -0,0 +1,44 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using osu.Game.Database;
+
+namespace osu.Game.Configuration
+{
+ public class SettingsStore : DatabaseBackedStore
+ {
+ public event Action SettingChanged;
+
+ public SettingsStore(Func createContext)
+ : base(createContext)
+ {
+ }
+
+ ///
+ /// Retrieve s for a specified ruleset/variant content.
+ ///
+ /// The ruleset's internal ID.
+ /// An optional variant.
+ ///
+ public List Query(int? rulesetId = null, int? variant = null) =>
+ GetContext().DatabasedSetting.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList();
+
+ public void Update(DatabasedSetting setting)
+ {
+ var context = GetContext();
+
+ var newValue = setting.Value;
+
+ Refresh(ref setting);
+
+ setting.Value = newValue;
+
+ context.SaveChanges();
+
+ SettingChanged?.Invoke();
+ }
+ }
+}
diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs
index 3184696266..ec9967e097 100644
--- a/osu.Game/Database/DatabaseBackedStore.cs
+++ b/osu.Game/Database/DatabaseBackedStore.cs
@@ -34,8 +34,14 @@ namespace osu.Game.Database
if (context.Entry(obj).State != EntityState.Detached) return;
var id = obj.ID;
- obj = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find(id);
- context.Entry(obj).Reload();
+ var foundObject = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find(id);
+ if (foundObject != null)
+ {
+ obj = foundObject;
+ context.Entry(obj).Reload();
+ }
+ else
+ context.Add(obj);
}
///
diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs
index 091ec3487c..0fa1f238a9 100644
--- a/osu.Game/Database/OsuDbContext.cs
+++ b/osu.Game/Database/OsuDbContext.cs
@@ -8,9 +8,10 @@ using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
-using osu.Game.Input.Bindings;
+using osu.Game.Configuration;
using osu.Game.IO;
using osu.Game.Rulesets;
+using DatabasedKeyBinding = osu.Game.Input.Bindings.DatabasedKeyBinding;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace osu.Game.Database
@@ -22,6 +23,7 @@ namespace osu.Game.Database
public DbSet BeatmapMetadata { get; set; }
public DbSet BeatmapSetInfo { get; set; }
public DbSet DatabasedKeyBinding { get; set; }
+ public DbSet DatabasedSetting { get; set; }
public DbSet FileInfo { get; set; }
public DbSet RulesetInfo { get; set; }
@@ -86,9 +88,11 @@ namespace osu.Game.Database
modelBuilder.Entity().HasIndex(b => b.DeletePending);
modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique();
- modelBuilder.Entity().HasIndex(b => b.Variant);
+ modelBuilder.Entity().HasIndex(b => new { b.RulesetID, b.Variant });
modelBuilder.Entity().HasIndex(b => b.IntAction);
+ modelBuilder.Entity().HasIndex(b => new { b.RulesetID, b.Variant });
+
modelBuilder.Entity().HasIndex(b => b.Hash).IsUnique();
modelBuilder.Entity().HasIndex(b => b.ReferenceCount);
diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs
index bcf6ab92b6..5b266d9a59 100644
--- a/osu.Game/Graphics/UserInterface/IconButton.cs
+++ b/osu.Game/Graphics/UserInterface/IconButton.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Graphics.UserInterface
{
public class IconButton : OsuClickableContainer
{
- private const float button_size = 30;
+ public const float BUTTON_SIZE = 30;
private Color4? flashColour;
///
@@ -106,7 +106,7 @@ namespace osu.Game.Graphics.UserInterface
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
- Size = new Vector2(button_size),
+ Size = new Vector2(BUTTON_SIZE),
CornerRadius = 5,
Masking = true,
EdgeEffect = new EdgeEffectParameters
diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs
index 33888e57e0..ccf70af6ed 100644
--- a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs
+++ b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs
@@ -7,6 +7,7 @@ using osu.Framework.Threading;
using OpenTK;
using osu.Framework.Audio;
using osu.Framework.Allocation;
+using osu.Framework.Configuration;
using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface.Volume
@@ -14,6 +15,7 @@ namespace osu.Game.Graphics.UserInterface.Volume
public class VolumeControl : OverlayContainer
{
private readonly VolumeMeter volumeMeterMaster;
+ private readonly IconButton muteIcon;
protected override bool BlockPassThroughMouse => false;
@@ -34,6 +36,17 @@ namespace osu.Game.Graphics.UserInterface.Volume
Spacing = new Vector2(15, 0),
Children = new Drawable[]
{
+ new Container
+ {
+ Size = new Vector2(IconButton.BUTTON_SIZE),
+ Child = muteIcon = new IconButton
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Icon = FontAwesome.fa_volume_up,
+ Action = () => Adjust(GlobalAction.ToggleMute),
+ }
+ },
volumeMeterMaster = new VolumeMeter("Master"),
volumeMeterEffect = new VolumeMeter("Effects"),
volumeMeterMusic = new VolumeMeter("Music")
@@ -46,18 +59,10 @@ namespace osu.Game.Graphics.UserInterface.Volume
{
base.LoadComplete();
- volumeMeterMaster.Bindable.ValueChanged += volumeChanged;
- volumeMeterEffect.Bindable.ValueChanged += volumeChanged;
- volumeMeterMusic.Bindable.ValueChanged += volumeChanged;
- }
-
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
-
- volumeMeterMaster.Bindable.ValueChanged -= volumeChanged;
- volumeMeterEffect.Bindable.ValueChanged -= volumeChanged;
- volumeMeterMusic.Bindable.ValueChanged -= volumeChanged;
+ volumeMeterMaster.Bindable.ValueChanged += _ => settingChanged();
+ volumeMeterEffect.Bindable.ValueChanged += _ => settingChanged();
+ volumeMeterMusic.Bindable.ValueChanged += _ => settingChanged();
+ muted.ValueChanged += _ => settingChanged();
}
public bool Adjust(GlobalAction action)
@@ -76,23 +81,45 @@ namespace osu.Game.Graphics.UserInterface.Volume
else
volumeMeterMaster.Increase();
return true;
+ case GlobalAction.ToggleMute:
+ Show();
+ muted.Toggle();
+ return true;
}
return false;
}
- private void volumeChanged(double newVolume)
+ private void settingChanged()
{
Show();
schedulePopOut();
}
+ private readonly BindableDouble muteAdjustment = new BindableDouble();
+
+ private readonly BindableBool muted = new BindableBool();
+
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
volumeMeterMaster.Bindable.BindTo(audio.Volume);
volumeMeterEffect.Bindable.BindTo(audio.VolumeSample);
volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack);
+
+ muted.ValueChanged += mute =>
+ {
+ if (mute)
+ {
+ audio.AddAdjustment(AdjustableProperty.Volume, muteAdjustment);
+ muteIcon.Icon = FontAwesome.fa_volume_off;
+ }
+ else
+ {
+ audio.RemoveAdjustment(AdjustableProperty.Volume, muteAdjustment);
+ muteIcon.Icon = FontAwesome.fa_volume_up;
+ }
+ };
}
private ScheduledDelegate popOutDelegate;
diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs
index 8323dade44..ef3702fdf3 100644
--- a/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs
+++ b/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs
@@ -70,11 +70,8 @@ namespace osu.Game.Graphics.UserInterface.Volume
public double Volume
{
- get { return Bindable.Value; }
- private set
- {
- Bindable.Value = value;
- }
+ get => Bindable.Value;
+ private set => Bindable.Value = value;
}
public void Increase()
diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs
similarity index 87%
rename from osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs
rename to osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs
index 4632c6c5f0..b6bc348a52 100644
--- a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs
+++ b/osu.Game/Input/Bindings/DatabasedKeyBindingContainer.cs
@@ -14,7 +14,7 @@ namespace osu.Game.Input.Bindings
/// A KeyBindingInputManager with a database backing for custom overrides.
///
/// The type of the custom action.
- public class DatabasedKeyBindingInputManager : KeyBindingContainer
+ public class DatabasedKeyBindingContainer : KeyBindingContainer
where T : struct
{
private readonly RulesetInfo ruleset;
@@ -31,7 +31,7 @@ namespace osu.Game.Input.Bindings
/// A reference to identify the current . Used to lookup mappings. Null for global mappings.
/// An optional variant for the specified . Used when a ruleset has more than one possible keyboard layouts.
/// Specify how to deal with multiple matches of s and s.
- public DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None)
+ public DatabasedKeyBindingContainer(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None)
: base(simultaneousMode)
{
this.ruleset = ruleset;
diff --git a/osu.Game/Input/Bindings/GlobalKeyBindingInputManager.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs
similarity index 78%
rename from osu.Game/Input/Bindings/GlobalKeyBindingInputManager.cs
rename to osu.Game/Input/Bindings/GlobalActionContainer.cs
index dcebe939d4..17ec2af4b9 100644
--- a/osu.Game/Input/Bindings/GlobalKeyBindingInputManager.cs
+++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs
@@ -10,11 +10,11 @@ using osu.Framework.Input.Bindings;
namespace osu.Game.Input.Bindings
{
- public class GlobalKeyBindingInputManager : DatabasedKeyBindingInputManager, IHandleGlobalInput
+ public class GlobalActionContainer : DatabasedKeyBindingContainer, IHandleGlobalInput
{
private readonly Drawable handler;
- public GlobalKeyBindingInputManager(OsuGameBase game)
+ public GlobalActionContainer(OsuGameBase game)
{
if (game is IKeyBindingHandler)
handler = game;
@@ -29,10 +29,11 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings),
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
- new KeyBinding(new[] { InputKey.Up }, GlobalAction.IncreaseVolume),
- new KeyBinding(new[] { InputKey.MouseWheelUp }, GlobalAction.IncreaseVolume),
- new KeyBinding(new[] { InputKey.Down }, GlobalAction.DecreaseVolume),
- new KeyBinding(new[] { InputKey.MouseWheelDown }, GlobalAction.DecreaseVolume),
+ new KeyBinding(InputKey.Up, GlobalAction.IncreaseVolume),
+ new KeyBinding(InputKey.MouseWheelUp, GlobalAction.IncreaseVolume),
+ new KeyBinding(InputKey.Down, GlobalAction.DecreaseVolume),
+ new KeyBinding(InputKey.MouseWheelDown, GlobalAction.DecreaseVolume),
+ new KeyBinding(InputKey.F4, GlobalAction.ToggleMute),
};
public IEnumerable InGameKeyBindings => new[]
@@ -63,6 +64,8 @@ namespace osu.Game.Input.Bindings
IncreaseVolume,
[Description("Decrease Volume")]
DecreaseVolume,
+ [Description("Toggle mute")]
+ ToggleMute,
// In-Game Keybindings
[Description("Skip Cutscene")]
diff --git a/osu.Game/Migrations/20180125143340_Settings.Designer.cs b/osu.Game/Migrations/20180125143340_Settings.Designer.cs
new file mode 100644
index 0000000000..8e045abc6f
--- /dev/null
+++ b/osu.Game/Migrations/20180125143340_Settings.Designer.cs
@@ -0,0 +1,329 @@
+//
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage;
+using osu.Game.Database;
+using System;
+
+namespace osu.Game.Migrations
+{
+ [DbContext(typeof(OsuDbContext))]
+ [Migration("20180125143340_Settings")]
+ partial class Settings
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ApproachRate");
+
+ b.Property("CircleSize");
+
+ b.Property("DrainRate");
+
+ b.Property("OverallDifficulty");
+
+ b.Property("SliderMultiplier");
+
+ b.Property("SliderTickRate");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapDifficulty");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AudioLeadIn");
+
+ b.Property("BaseDifficultyID");
+
+ b.Property("BeatDivisor");
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("Countdown");
+
+ b.Property("DistanceSpacing");
+
+ b.Property("GridSize");
+
+ b.Property("Hash");
+
+ b.Property("Hidden");
+
+ b.Property("LetterboxInBreaks");
+
+ b.Property("MD5Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapID");
+
+ b.Property("Path");
+
+ b.Property("RulesetID");
+
+ b.Property("SpecialStyle");
+
+ b.Property("StackLeniency");
+
+ b.Property("StarDifficulty");
+
+ b.Property("StoredBookmarks");
+
+ b.Property("TimelineZoom");
+
+ b.Property("Version");
+
+ b.Property("WidescreenStoryboard");
+
+ b.HasKey("ID");
+
+ b.HasIndex("BaseDifficultyID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("MD5Hash")
+ .IsUnique();
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapID")
+ .IsUnique();
+
+ b.HasIndex("RulesetID");
+
+ b.ToTable("BeatmapInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Artist");
+
+ b.Property("ArtistUnicode");
+
+ b.Property("AudioFile");
+
+ b.Property("AuthorString")
+ .HasColumnName("Author");
+
+ b.Property("BackgroundFile");
+
+ b.Property("PreviewTime");
+
+ b.Property("Source");
+
+ b.Property("Tags");
+
+ b.Property("Title");
+
+ b.Property("TitleUnicode");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapMetadata");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.HasKey("ID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("FileInfoID");
+
+ b.ToTable("BeatmapSetFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("DeletePending");
+
+ b.Property("Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapSetID");
+
+ b.Property("Protected");
+
+ b.HasKey("ID");
+
+ b.HasIndex("DeletePending");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapSetID")
+ .IsUnique();
+
+ b.ToTable("BeatmapSetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntKey")
+ .HasColumnName("Key");
+
+ b.Property("RulesetID");
+
+ b.Property("StringValue")
+ .HasColumnName("Value");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("Settings");
+ });
+
+ modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntAction")
+ .HasColumnName("Action");
+
+ b.Property("KeysString")
+ .HasColumnName("Keys");
+
+ b.Property("RulesetID");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("IntAction");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("KeyBinding");
+ });
+
+ modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Hash");
+
+ b.Property("ReferenceCount");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("ReferenceCount");
+
+ b.ToTable("FileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Available");
+
+ b.Property("InstantiationInfo");
+
+ b.Property("Name");
+
+ b.Property("ShortName");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Available");
+
+ b.HasIndex("ShortName")
+ .IsUnique();
+
+ b.ToTable("RulesetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
+ .WithMany()
+ .HasForeignKey("BaseDifficultyID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
+ .WithMany("Beatmaps")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("Beatmaps")
+ .HasForeignKey("MetadataID");
+
+ b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
+ .WithMany()
+ .HasForeignKey("RulesetID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
+ .WithMany("Files")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("BeatmapSets")
+ .HasForeignKey("MetadataID");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/osu.Game/Migrations/20180125143340_Settings.cs b/osu.Game/Migrations/20180125143340_Settings.cs
new file mode 100644
index 0000000000..86a6b2dc5e
--- /dev/null
+++ b/osu.Game/Migrations/20180125143340_Settings.cs
@@ -0,0 +1,57 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+using System;
+using System.Collections.Generic;
+
+namespace osu.Game.Migrations
+{
+ public partial class Settings : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropIndex(
+ name: "IX_KeyBinding_Variant",
+ table: "KeyBinding");
+
+ migrationBuilder.CreateTable(
+ name: "Settings",
+ columns: table => new
+ {
+ ID = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ Key = table.Column(type: "INTEGER", nullable: false),
+ RulesetID = table.Column(type: "INTEGER", nullable: true),
+ Value = table.Column(type: "TEXT", nullable: true),
+ Variant = table.Column(type: "INTEGER", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Settings", x => x.ID);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_KeyBinding_RulesetID_Variant",
+ table: "KeyBinding",
+ columns: new[] { "RulesetID", "Variant" });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Settings_RulesetID_Variant",
+ table: "Settings",
+ columns: new[] { "RulesetID", "Variant" });
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "Settings");
+
+ migrationBuilder.DropIndex(
+ name: "IX_KeyBinding_RulesetID_Variant",
+ table: "KeyBinding");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_KeyBinding_Variant",
+ table: "KeyBinding",
+ column: "Variant");
+ }
+ }
+}
diff --git a/osu.Game/Migrations/20180131154205_AddMuteBinding.cs b/osu.Game/Migrations/20180131154205_AddMuteBinding.cs
new file mode 100644
index 0000000000..fc1f2fff55
--- /dev/null
+++ b/osu.Game/Migrations/20180131154205_AddMuteBinding.cs
@@ -0,0 +1,25 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+using System;
+using System.Collections.Generic;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using osu.Game.Database;
+using osu.Game.Input.Bindings;
+
+namespace osu.Game.Migrations
+{
+ [DbContext(typeof(OsuDbContext))]
+ [Migration("20180131154205_AddMuteBinding")]
+ public partial class AddMuteBinding : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql($"UPDATE KeyBinding SET Action = Action + 1 WHERE RulesetID IS NULL AND Variant IS NULL AND Action >= {(int)GlobalAction.ToggleMute}");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql($"DELETE FROM KeyBinding WHERE RulesetID IS NULL AND Variant IS NULL AND Action = {(int)GlobalAction.ToggleMute}");
+ migrationBuilder.Sql($"UPDATE KeyBinding SET Action = Action - 1 WHERE RulesetID IS NULL AND Variant IS NULL AND Action > {(int)GlobalAction.ToggleMute}");
+ }
+ }
+}
diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
index cd4d3c2854..157125102f 100644
--- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
+++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
@@ -193,6 +193,28 @@ namespace osu.Game.Migrations
b.ToTable("BeatmapSetInfo");
});
+ modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntKey")
+ .HasColumnName("Key");
+
+ b.Property("RulesetID");
+
+ b.Property("StringValue")
+ .HasColumnName("Value");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("Settings");
+ });
+
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property("ID")
@@ -212,7 +234,7 @@ namespace osu.Game.Migrations
b.HasIndex("IntAction");
- b.HasIndex("Variant");
+ b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs
index 5559aadeb4..1d657b8664 100644
--- a/osu.Game/Online/API/APIAccess.cs
+++ b/osu.Game/Online/API/APIAccess.cs
@@ -126,6 +126,7 @@ namespace osu.Game.Online.API
userReq.Success += u =>
{
LocalUser.Value = u;
+ Username = LocalUser.Value.Username;
failureCount = 0;
//we're connected!
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 1e175ff8c4..bd71d37f97 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.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 osu.Framework.Configuration;
using osu.Framework.Screens;
using osu.Game.Configuration;
@@ -27,6 +28,7 @@ using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
using osu.Game.Screens.Play;
using osu.Game.Input.Bindings;
+using osu.Game.Rulesets.Mods;
using OpenTK.Graphics;
namespace osu.Game
@@ -71,6 +73,7 @@ namespace osu.Game
private OsuScreen screenStack;
private VolumeControl volume;
+ private OnScreenDisplay onscreenDisplay;
private Bindable configRuleset;
public Bindable Ruleset = new Bindable();
@@ -79,6 +82,9 @@ namespace osu.Game
private SettingsOverlay settings;
+ // todo: move this to SongSelect once Screen has the ability to unsuspend.
+ public readonly Bindable> SelectedMods = new Bindable>(new List());
+
public OsuGame(string[] args = null)
{
this.args = args;
@@ -110,7 +116,7 @@ namespace osu.Game
Task.Run(() => BeatmapManager.Import(paths.ToArray()));
}
- dependencies.CacheAs(this);
+ dependencies.CacheAs(this);
configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset);
Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First();
@@ -195,7 +201,7 @@ namespace osu.Game
}, overlayContent.Add);
loadComponentSingleFile(volume = new VolumeControl(), Add);
- loadComponentSingleFile(new OnScreenDisplay(), Add);
+ loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
//overlay elements
loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
@@ -232,6 +238,7 @@ namespace osu.Game
forwardLoggedErrorsToNotifications();
dependencies.Cache(settings);
+ dependencies.Cache(onscreenDisplay);
dependencies.Cache(social);
dependencies.Cache(direct);
dependencies.Cache(chat);
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 8b317ca59a..937b204c81 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -44,6 +44,8 @@ namespace osu.Game
protected KeyBindingStore KeyBindingStore;
+ protected SettingsStore SettingsStore;
+
protected CursorOverrideContainer CursorOverrideContainer;
protected override string MainResourceFile => @"osu.Game.Resources.dll";
@@ -93,7 +95,7 @@ namespace osu.Game
dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures"))));
- dependencies.CacheAs(this);
+ dependencies.CacheAs(this);
dependencies.Cache(LocalConfig);
runMigrations();
@@ -109,6 +111,7 @@ namespace osu.Game
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory.GetContext, RulesetStore, API, Host));
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory.GetContext, Host, BeatmapManager, RulesetStore));
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory.GetContext, RulesetStore));
+ dependencies.Cache(SettingsStore = new SettingsStore(contextFactory.GetContext));
dependencies.Cache(new OsuColour());
//this completely overrides the framework default. will need to change once we make a proper FontStore.
@@ -209,10 +212,10 @@ namespace osu.Game
{
base.LoadComplete();
- GlobalKeyBindingInputManager globalBinding;
+ GlobalActionContainer globalBinding;
CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both };
- CursorOverrideContainer.Child = globalBinding = new GlobalKeyBindingInputManager(this)
+ CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this)
{
RelativeSizeAxes = Axes.Both,
Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both }
diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
index f5b3096404..a4c1621266 100644
--- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
+++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Overlays.KeyBinding
public override FontAwesome Icon => FontAwesome.fa_osu_hot;
public override string Header => "Global";
- public GlobalKeyBindingsSection(GlobalKeyBindingInputManager manager)
+ public GlobalKeyBindingsSection(GlobalActionContainer manager)
{
Add(new DefaultBindingsSubsection(manager));
Add(new InGameKeyBindingsSubsection(manager));
@@ -23,7 +23,7 @@ namespace osu.Game.Overlays.KeyBinding
{
protected override string Header => string.Empty;
- public DefaultBindingsSubsection(GlobalKeyBindingInputManager manager)
+ public DefaultBindingsSubsection(GlobalActionContainer manager)
: base(null)
{
Defaults = manager.GlobalKeyBindings;
@@ -34,7 +34,7 @@ namespace osu.Game.Overlays.KeyBinding
{
protected override string Header => "In Game";
- public InGameKeyBindingsSubsection(GlobalKeyBindingInputManager manager) : base(null)
+ public InGameKeyBindingsSubsection(GlobalActionContainer manager) : base(null)
{
Defaults = manager.InGameKeyBindings;
}
diff --git a/osu.Game/Overlays/KeyBindingOverlay.cs b/osu.Game/Overlays/KeyBindingOverlay.cs
index 18e43ad39b..b311ee68c0 100644
--- a/osu.Game/Overlays/KeyBindingOverlay.cs
+++ b/osu.Game/Overlays/KeyBindingOverlay.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Overlays
protected override Drawable CreateHeader() => new SettingsHeader("key configuration", "Customise your keys!");
[BackgroundDependencyLoader(permitNulls: true)]
- private void load(RulesetStore rulesets, GlobalKeyBindingInputManager global)
+ private void load(RulesetStore rulesets, GlobalActionContainer global)
{
AddSection(new GlobalKeyBindingsSection(global));
diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs
index 80823c56bf..91063bfa38 100644
--- a/osu.Game/Overlays/Mods/ModButton.cs
+++ b/osu.Game/Overlays/Mods/ModButton.cs
@@ -188,17 +188,19 @@ namespace osu.Game.Overlays.Mods
start = Mods.Length - 1;
for (int i = start; i < Mods.Length && i >= 0; i += direction)
- {
- if (Mods[i].HasImplementation)
- {
- changeSelectedIndex(i);
- return;
- }
- }
+ if (SelectAt(i)) return;
Deselect();
}
+ public bool SelectAt(int index)
+ {
+ if (!Mods[index].HasImplementation) return false;
+
+ changeSelectedIndex(index);
+ return true;
+ }
+
public void Deselect() => changeSelectedIndex(-1);
private void displayMod(Mod mod)
diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs
index a43c54f994..03c1f0468c 100644
--- a/osu.Game/Overlays/Mods/ModSection.cs
+++ b/osu.Game/Overlays/Mods/ModSection.cs
@@ -113,6 +113,23 @@ namespace osu.Game.Overlays.Mods
}
}
+ ///
+ /// Select one or more mods in this section.
+ ///
+ /// The types of s which should be deselected.
+ public void SelectTypes(IEnumerable mods)
+ {
+ foreach (var button in buttons)
+ {
+ for (int i = 0; i < button.Mods.Length; i++)
+ {
+ foreach (var mod in mods)
+ if (mod.GetType().IsInstanceOfType(button.Mods[i]))
+ button.SelectAt(i);
+ }
+ }
+ }
+
protected ModSection()
{
AutoSizeAxes = Axes.Y;
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index 96faa376ba..d7268fb186 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -51,6 +51,8 @@ namespace osu.Game.Overlays.Mods
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets)
{
+ SelectedMods.ValueChanged += selectedModsChanged;
+
LowMultiplierColour = colours.Red;
HighMultiplierColour = colours.Green;
@@ -63,6 +65,37 @@ namespace osu.Game.Overlays.Mods
Ruleset.TriggerChange();
}
+ private void selectedModsChanged(IEnumerable obj)
+ {
+ foreach (ModSection section in ModSectionsContainer.Children)
+ section.SelectTypes(obj);
+
+ updateMods();
+ }
+
+ private void updateMods()
+ {
+ double multiplier = 1.0;
+ bool ranked = true;
+
+ foreach (Mod mod in SelectedMods.Value)
+ {
+ multiplier *= mod.ScoreMultiplier;
+ ranked &= mod.Ranked;
+ }
+
+ MultiplierLabel.Text = $"{multiplier:N2}x";
+ if (!ranked)
+ MultiplierLabel.Text += " (Unranked)";
+
+ if (multiplier > 1.0)
+ MultiplierLabel.FadeColour(HighMultiplierColour, 200);
+ else if (multiplier < 1.0)
+ MultiplierLabel.FadeColour(LowMultiplierColour, 200);
+ else
+ MultiplierLabel.FadeColour(Color4.White, 200);
+ }
+
protected override void PopOut()
{
base.PopOut();
@@ -97,6 +130,7 @@ namespace osu.Game.Overlays.Mods
{
foreach (ModSection section in ModSectionsContainer.Children)
section.DeselectAll();
+
refreshSelectedMods();
}
@@ -119,30 +153,7 @@ namespace osu.Game.Overlays.Mods
refreshSelectedMods();
}
- private void refreshSelectedMods()
- {
- SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
-
- double multiplier = 1.0;
- bool ranked = true;
-
- foreach (Mod mod in SelectedMods.Value)
- {
- multiplier *= mod.ScoreMultiplier;
- ranked &= mod.Ranked;
- }
-
- MultiplierLabel.Text = $"{multiplier:N2}x";
- if (!ranked)
- MultiplierLabel.Text += " (Unranked)";
-
- if (multiplier > 1.0)
- MultiplierLabel.FadeColour(HighMultiplierColour, 200);
- else if (multiplier < 1.0)
- MultiplierLabel.FadeColour(LowMultiplierColour, 200);
- else
- MultiplierLabel.FadeColour(Color4.White, 200);
- }
+ private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
public ModSelectOverlay()
{
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index d9e08a48ba..b3140d8bd0 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -5,8 +5,6 @@ using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
-using OpenTK;
-using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
@@ -20,10 +18,12 @@ using osu.Framework.Localisation;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
-using osu.Game.Graphics.Sprites;
-using osu.Game.Overlays.Music;
-using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.Containers;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays.Music;
+using OpenTK;
+using OpenTK.Graphics;
namespace osu.Game.Overlays
{
@@ -65,6 +65,12 @@ namespace osu.Game.Overlays
AlwaysPresent = true;
}
+ protected override bool OnDragStart(InputState state)
+ {
+ base.OnDragStart(state);
+ return true;
+ }
+
protected override bool OnDrag(InputState state)
{
if (base.OnDrag(state)) return true;
diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs
index 65803b477b..bbb2c476f4 100644
--- a/osu.Game/Overlays/OnScreenDisplay.cs
+++ b/osu.Game/Overlays/OnScreenDisplay.cs
@@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
-using osu.Framework.Extensions;
+using osu.Framework.Configuration.Tracking;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -118,43 +118,62 @@ namespace osu.Game.Overlays
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager frameworkConfig)
{
- trackSetting(frameworkConfig.GetBindable(FrameworkSetting.FrameSync), v => display(v, "Frame Limiter", v.GetDescription(), "Ctrl+F7"));
- trackSetting(frameworkConfig.GetBindable(FrameworkSetting.AudioDevice), v => display(v, "Audio Device", string.IsNullOrEmpty(v) ? "Default" : v, v));
- trackSetting(frameworkConfig.GetBindable(FrameworkSetting.ShowLogOverlay), v => display(v, "Debug Logs", v ? "visible" : "hidden", "Ctrl+F10"));
-
- void displayResolution() => display(null, "Screen Resolution", frameworkConfig.Get(FrameworkSetting.Width) + "x" + frameworkConfig.Get(FrameworkSetting.Height));
-
- trackSetting(frameworkConfig.GetBindable(FrameworkSetting.Width), v => displayResolution());
- trackSetting(frameworkConfig.GetBindable(FrameworkSetting.Height), v => displayResolution());
-
- trackSetting(frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity), v => display(v, "Cursor Sensitivity", v.ToString(@"0.##x"), "Ctrl+Alt+R to reset"));
- trackSetting(frameworkConfig.GetBindable(FrameworkSetting.ActiveInputHandlers),
- delegate (string v)
- {
- bool raw = v.Contains("Raw");
- display(raw, "Raw Input", raw ? "enabled" : "disabled", "Ctrl+Alt+R to reset");
- });
-
- trackSetting(frameworkConfig.GetBindable(FrameworkSetting.WindowMode), v => display(v, "Screen Mode", v.ToString(), "Alt+Enter"));
+ BeginTracking(this, frameworkConfig);
}
- private readonly List references = new List();
+ private readonly Dictionary<(object, IConfigManager), TrackedSettings> trackedConfigManagers = new Dictionary<(object, IConfigManager), TrackedSettings>();
- private void trackSetting(Bindable bindable, Action action)
+ ///
+ /// Registers a to have its settings tracked by this .
+ ///
+ /// The object that is registering the to be tracked.
+ /// The to be tracked.
+ /// If is null.
+ /// If is already being tracked from the same .
+ public void BeginTracking(object source, ITrackableConfigManager configManager)
{
- // we need to keep references as we bind
- references.Add(bindable);
+ if (configManager == null) throw new ArgumentNullException(nameof(configManager));
- bindable.ValueChanged += action;
+ if (trackedConfigManagers.ContainsKey((source, configManager)))
+ throw new InvalidOperationException($"{nameof(configManager)} is already registered.");
+
+ var trackedSettings = configManager.CreateTrackedSettings();
+ if (trackedSettings == null)
+ return;
+
+ configManager.LoadInto(trackedSettings);
+ trackedSettings.SettingChanged += display;
+
+ trackedConfigManagers.Add((source, configManager), trackedSettings);
}
- private void display(object rawValue, string settingName, string settingValue, string shortcut = @"")
+ ///
+ /// Unregisters a from having its settings tracked by this .
+ ///
+ /// The object that registered the to be tracked.
+ /// The that is being tracked.
+ /// If is null.
+ /// If is not being tracked from the same .
+ public void StopTracking(object source, ITrackableConfigManager configManager)
+ {
+ if (configManager == null) throw new ArgumentNullException(nameof(configManager));
+
+ if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing))
+ throw new InvalidOperationException($"{nameof(configManager)} is not registered.");
+
+ existing.Unload();
+ existing.SettingChanged -= display;
+
+ trackedConfigManagers.Remove((source, configManager));
+ }
+
+ private void display(SettingDescription description)
{
Schedule(() =>
{
- textLine1.Text = settingName.ToUpper();
- textLine2.Text = settingValue;
- textLine3.Text = shortcut.ToUpper();
+ textLine1.Text = description.Name.ToUpper();
+ textLine2.Text = description.Value;
+ textLine3.Text = description.Shortcut.ToUpper();
box.Animate(
b => b.FadeIn(500, Easing.OutQuint),
@@ -167,16 +186,16 @@ namespace osu.Game.Overlays
int optionCount = 0;
int selectedOption = -1;
- if (rawValue is bool)
+ if (description.RawValue is bool)
{
optionCount = 1;
- if ((bool)rawValue) selectedOption = 0;
+ if ((bool)description.RawValue) selectedOption = 0;
}
- else if (rawValue is Enum)
+ else if (description.RawValue is Enum)
{
- var values = Enum.GetValues(rawValue.GetType());
+ var values = Enum.GetValues(description.RawValue.GetType());
optionCount = values.Length;
- selectedOption = Convert.ToInt32(rawValue);
+ selectedOption = Convert.ToInt32(description.RawValue);
}
textLine2.Origin = optionCount > 0 ? Anchor.BottomCentre : Anchor.Centre;
diff --git a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs
index 64fa763a0b..1b0c821b54 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Toolbar
}
[BackgroundDependencyLoader(true)]
- private void load(SettingsOverlay settings)
+ private void load(MainSettings settings)
{
StateContainer = settings;
}
diff --git a/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs
new file mode 100644
index 0000000000..56eac730b0
--- /dev/null
+++ b/osu.Game/Rulesets/Configuration/IRulesetConfigManager.cs
@@ -0,0 +1,11 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Configuration.Tracking;
+
+namespace osu.Game.Rulesets.Configuration
+{
+ public interface IRulesetConfigManager : ITrackableConfigManager
+ {
+ }
+}
diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs
new file mode 100644
index 0000000000..9f244f6267
--- /dev/null
+++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs
@@ -0,0 +1,15 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Configuration;
+
+namespace osu.Game.Rulesets.Configuration
+{
+ public abstract class RulesetConfigManager : DatabasedConfigManager, IRulesetConfigManager
+ where T : struct
+ {
+ protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int variant) : base(settings, ruleset, variant)
+ {
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 8680ff4e83..2db02724ed 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -136,7 +136,10 @@ namespace osu.Game.Rulesets.Objects.Drawables
///
public event Action ApplyCustomUpdateState;
- protected void PlaySamples() => Samples.ForEach(s => s?.Play());
+ ///
+ /// Plays all the hitsounds for this .
+ ///
+ public void PlaySamples() => Samples.ForEach(s => s?.Play());
protected override void Update()
{
diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs
index 375af75347..8f72644b28 100644
--- a/osu.Game/Rulesets/UI/RulesetContainer.cs
+++ b/osu.Game/Rulesets/UI/RulesetContainer.cs
@@ -16,6 +16,9 @@ using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
+using osu.Game.Configuration;
+using osu.Game.Overlays;
+using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using OpenTK;
@@ -35,6 +38,11 @@ namespace osu.Game.Rulesets.UI
///
public bool AspectAdjust = true;
+ ///
+ /// The selected variant.
+ ///
+ public virtual int Variant => 0;
+
///
/// The input manager for this RulesetContainer.
///
@@ -65,6 +73,14 @@ namespace osu.Game.Rulesets.UI
protected readonly Ruleset Ruleset;
+ private IRulesetConfigManager rulesetConfig;
+ private OnScreenDisplay onScreenDisplay;
+
+ private DependencyContainer dependencies;
+
+ protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
+
///
/// A visual representation of a .
///
@@ -77,6 +93,20 @@ namespace osu.Game.Rulesets.UI
Cursor = CreateCursor();
}
+ [BackgroundDependencyLoader(true)]
+ private void load(OnScreenDisplay onScreenDisplay, SettingsStore settings)
+ {
+ this.onScreenDisplay = onScreenDisplay;
+
+ rulesetConfig = CreateConfig(Ruleset, settings);
+
+ if (rulesetConfig != null)
+ {
+ dependencies.Cache(rulesetConfig);
+ onScreenDisplay?.BeginTracking(this, rulesetConfig);
+ }
+ }
+
public abstract ScoreProcessor CreateScoreProcessor();
///
@@ -110,11 +140,24 @@ namespace osu.Game.Rulesets.UI
///
protected virtual CursorContainer CreateCursor() => null;
+ protected virtual IRulesetConfigManager CreateConfig(Ruleset ruleset, SettingsStore settings) => null;
+
///
/// Creates a Playfield.
///
/// The Playfield.
protected abstract Playfield CreatePlayfield();
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+
+ if (rulesetConfig != null)
+ {
+ onScreenDisplay?.StopTracking(this, rulesetConfig);
+ rulesetConfig = null;
+ }
+ }
}
///
diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs
index 223586a959..037f9136a8 100644
--- a/osu.Game/Rulesets/UI/RulesetInputManager.cs
+++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs
@@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.UI
public abstract class RulesetInputManager : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler
where T : struct
{
- public class RulesetKeyBindingContainer : DatabasedKeyBindingInputManager
+ public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer
{
public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
: base(ruleset, variant, unique)
@@ -136,9 +136,20 @@ namespace osu.Game.Rulesets.UI
int loops = 0;
while (validState && requireMoreUpdateLoops && loops++ < max_catch_up_updates_per_frame)
+ {
if (!base.UpdateSubTree())
return false;
+ if (isAttached)
+ {
+ // When handling replay input, we need to consider the possibility of fast-forwarding, which may cause the clock to be updated
+ // to a point very far into the future, then playing a frame at that time. In such a case, lifetime MUST be updated before
+ // input is handled. This is why base.Update is not called from the derived Update when handling replay input, and is instead
+ // called manually at the correct time here.
+ base.Update();
+ }
+ }
+
return true;
}
@@ -173,8 +184,11 @@ namespace osu.Game.Rulesets.UI
// to ensure that the its time is valid for our children before input is processed
Clock.ProcessFrame();
- // Process input
- base.Update();
+ if (!isAttached)
+ {
+ // For non-replay input handling, this provides equivalent input ordering as if Update was not overridden
+ base.Update();
+ }
}
#endregion
diff --git a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs
index 735c81aedf..af7c1ef5aa 100644
--- a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs
+++ b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs
@@ -41,6 +41,12 @@ namespace osu.Game.Screens.Play.BreaksOverlay
private readonly InfoContainer info;
private readonly ArrowsOverlay arrowsOverlay;
+ public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor)
+ : this(letterboxing)
+ {
+ bindProcessor(scoreProcessor);
+ }
+
public BreakOverlay(bool letterboxing)
{
this.letterboxing = letterboxing;
@@ -148,7 +154,7 @@ namespace osu.Game.Screens.Play.BreaksOverlay
arrowsOverlay.Hide();
}
- public void BindProcessor(ScoreProcessor processor)
+ private void bindProcessor(ScoreProcessor processor)
{
info.AccuracyDisplay.Current.BindTo(processor.Accuracy);
info.GradeDisplay.Current.BindTo(processor.Rank);
diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs
index 255c071ac1..5fb867e151 100644
--- a/osu.Game/Screens/Play/HUDOverlay.cs
+++ b/osu.Game/Screens/Play/HUDOverlay.cs
@@ -6,6 +6,8 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
+using osu.Framework.Timing;
+using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
@@ -39,7 +41,7 @@ namespace osu.Game.Screens.Play
private static bool hasShownNotificationOnce;
- public HUDOverlay()
+ public HUDOverlay(ScoreProcessor scoreProcessor, RulesetContainer rulesetContainer, DecoupleableInterpolatingFramedClock decoupledClock, WorkingBeatmap working, IAdjustableClock adjustableSourceClock)
{
RelativeSizeAxes = Axes.Both;
@@ -59,6 +61,18 @@ namespace osu.Game.Screens.Play
ReplaySettingsOverlay = CreateReplaySettingsOverlay(),
}
});
+
+ BindProcessor(scoreProcessor);
+ BindRulesetContainer(rulesetContainer);
+
+ Progress.Objects = rulesetContainer.Objects;
+ Progress.AudioClock = decoupledClock;
+ Progress.AllowSeeking = rulesetContainer.HasReplayLoaded;
+ Progress.OnSeek = pos => decoupledClock.Seek(pos);
+
+ ModDisplay.Current.BindTo(working.Mods);
+
+ ReplaySettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock;
}
[BackgroundDependencyLoader(true)]
@@ -115,7 +129,7 @@ namespace osu.Game.Screens.Play
}
}
- public virtual void BindRulesetContainer(RulesetContainer rulesetContainer)
+ protected virtual void BindRulesetContainer(RulesetContainer rulesetContainer)
{
(rulesetContainer.KeyBindingInputManager as ICanAttachKeyCounter)?.Attach(KeyCounter);
@@ -201,7 +215,7 @@ namespace osu.Game.Screens.Play
protected virtual ReplaySettingsOverlay CreateReplaySettingsOverlay() => new ReplaySettingsOverlay();
- public virtual void BindProcessor(ScoreProcessor processor)
+ protected virtual void BindProcessor(ScoreProcessor processor)
{
ScoreCounter?.Current.BindTo(processor.TotalScore);
AccuracyCounter?.Current.BindTo(processor.Accuracy);
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 8d26d63d41..72864482d5 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -1,35 +1,35 @@
// Copyright (c) 2007-2018 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.Audio;
-using osu.Framework.Configuration;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Input;
-using osu.Framework.Logging;
-using osu.Framework.Screens;
-using osu.Framework.Timing;
-using osu.Game.Configuration;
-using osu.Game.Rulesets;
-using osu.Game.Rulesets.UI;
-using osu.Game.Screens.Backgrounds;
using System;
using System.Linq;
using System.Threading.Tasks;
-using osu.Framework.Threading;
-using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Scoring;
-using osu.Game.Screens.Ranking;
+using osu.Framework.Allocation;
+using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
+using osu.Framework.Configuration;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
+using osu.Framework.Input;
+using osu.Framework.Logging;
+using osu.Framework.Screens;
+using osu.Framework.Threading;
+using osu.Framework.Timing;
using osu.Game.Beatmaps;
+using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.API;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.UI;
+using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Play.BreaksOverlay;
+using osu.Game.Screens.Ranking;
using osu.Game.Storyboards.Drawables;
+using OpenTK;
namespace osu.Game.Screens.Play
{
@@ -79,7 +79,6 @@ namespace osu.Game.Screens.Play
#endregion
- private BreakOverlay breakOverlay;
private Container storyboardContainer;
private DrawableStoryboard storyboard;
@@ -155,6 +154,8 @@ namespace osu.Game.Screens.Play
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
userAudioOffset.TriggerChange();
+ scoreProcessor = RulesetContainer.CreateScoreProcessor();
+
Children = new Drawable[]
{
storyboardContainer = new Container
@@ -170,13 +171,12 @@ namespace osu.Game.Screens.Play
OnRetry = Restart,
OnQuit = Exit,
CheckCanPause = () => AllowPause && ValidForResume && !HasFailed && !RulesetContainer.HasReplayLoaded,
- OnPause = () => {
+ OnPause = () =>
+ {
pauseContainer.Retries = RestartCount;
hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
},
- OnResume = () => {
- hudOverlay.KeyCounter.IsCounting = true;
- },
+ OnResume = () => hudOverlay.KeyCounter.IsCounting = true,
Children = new Drawable[]
{
new Container
@@ -186,12 +186,12 @@ namespace osu.Game.Screens.Play
Child = RulesetContainer,
},
new SkipButton(firstObjectTime) { AudioClock = decoupledClock },
- hudOverlay = new HUDOverlay
+ hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, decoupledClock, working, adjustableSourceClock)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
- breakOverlay = new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks)
+ new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -207,7 +207,10 @@ namespace osu.Game.Screens.Play
},
new HotkeyRetryOverlay
{
- Action = () => {
+ Action = () =>
+ {
+ if (!IsCurrentScreen) return;
+
//we want to hide the hitrenderer immediately (looks better).
//we may be able to remove this once the mouse cursor trail is improved.
RulesetContainer?.Hide();
@@ -216,24 +219,9 @@ namespace osu.Game.Screens.Play
}
};
- scoreProcessor = RulesetContainer.CreateScoreProcessor();
-
if (showStoryboard)
initializeStoryboard(false);
- hudOverlay.BindProcessor(scoreProcessor);
- hudOverlay.BindRulesetContainer(RulesetContainer);
-
- hudOverlay.Progress.Objects = RulesetContainer.Objects;
- hudOverlay.Progress.AudioClock = decoupledClock;
- hudOverlay.Progress.OnSeek = pos => decoupledClock.Seek(pos);
-
- hudOverlay.ModDisplay.Current.BindTo(working.Mods);
-
- breakOverlay.BindProcessor(scoreProcessor);
-
- hudOverlay.ReplaySettingsOverlay.PlaybackSettings.AdjustableClock = adjustableSourceClock;
-
// Bind ScoreProcessor to ourselves
scoreProcessor.AllJudged += onCompletion;
scoreProcessor.Failed += onFail;
@@ -288,6 +276,8 @@ namespace osu.Game.Screens.Play
{
onCompletionEvent = Schedule(delegate
{
+ if (!IsCurrentScreen) return;
+
var score = new Score
{
Beatmap = Beatmap.Value.BeatmapInfo,
diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs
index 6fdd38ce30..739bc39269 100644
--- a/osu.Game/Screens/Select/PlaySongSelect.cs
+++ b/osu.Game/Screens/Select/PlaySongSelect.cs
@@ -13,6 +13,7 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
+using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
@@ -47,10 +48,13 @@ namespace osu.Game.Screens.Select
private SampleChannel sampleConfirm;
[BackgroundDependencyLoader(true)]
- private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay)
+ private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, OsuGame game)
{
sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection");
+ if (game != null)
+ modSelect.SelectedMods.BindTo(game.SelectedMods);
+
Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue);
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
@@ -121,6 +125,9 @@ namespace osu.Game.Screens.Select
if (Beatmap.Value.Track != null)
Beatmap.Value.Track.Looping = false;
+ Beatmap.Value.Mods.UnbindBindings();
+ Beatmap.Value.Mods.Value = new Mod[] { };
+
return false;
}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 79f00cc988..2421a4fdfe 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -181,7 +181,7 @@ namespace osu.Game.Screens.Select
[BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours)
{
- dependencies.CacheAs(this);
+ dependencies.CacheAs(this);
if (Footer != null)
{
diff --git a/osu.Game/Screens/Tournament/Components/DrawingsConfigManager.cs b/osu.Game/Screens/Tournament/Components/DrawingsConfigManager.cs
index 0c45729a18..63000d6c54 100644
--- a/osu.Game/Screens/Tournament/Components/DrawingsConfigManager.cs
+++ b/osu.Game/Screens/Tournament/Components/DrawingsConfigManager.cs
@@ -6,7 +6,7 @@ using osu.Framework.Platform;
namespace osu.Game.Screens.Tournament.Components
{
- public class DrawingsConfigManager : ConfigManager
+ public class DrawingsConfigManager : IniConfigManager
{
protected override string Filename => @"drawings.ini";
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 85da54e317..05cf61c23c 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -91,6 +91,10 @@
$(SolutionDir)\packages\Humanizer.Core.2.2.0\lib\netstandard1.0\Humanizer.dll
+
+ $(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll
+ True
+
$(SolutionDir)\packages\Microsoft.Data.Sqlite.Core.2.0.0\lib\netstandard2.0\Microsoft.Data.Sqlite.dll
@@ -265,10 +269,18 @@
+
+
+
+
+
+ 20180125143340_Settings.cs
+
+
@@ -314,6 +326,8 @@
+
+
@@ -433,8 +447,8 @@
-
-
+
+
diff --git a/osu.Game/packages.config b/osu.Game/packages.config
index 2938739eef..0216c8ae67 100644
--- a/osu.Game/packages.config
+++ b/osu.Game/packages.config
@@ -47,6 +47,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
+