diff --git a/osu-framework b/osu-framework
index 41e2a0a430..d8d4f55e10 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit 41e2a0a4304544fb67779c21cad1435c105982d5
+Subproject commit d8d4f55e10ac553223db75874bae6ae4894b739a
diff --git a/osu.Desktop/osu.nuspec b/osu.Desktop/osu.nuspec
index bb7d382cee..316a5443ef 100644
--- a/osu.Desktop/osu.nuspec
+++ b/osu.Desktop/osu.nuspec
@@ -16,11 +16,9 @@
en-AU
-
-
-
-
-
+
+
+
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index a6ab18bbf7..1a0ccc9b1e 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -8,7 +8,7 @@ using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Catch.Objects
{
- public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboIndex
+ public abstract class CatchHitObject : HitObject, IHasXPosition, IHasComboInformation
{
public const double OBJECT_RADIUS = 44;
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
index 8d56fc1081..582946ff00 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
@@ -8,6 +8,8 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
using osu.Game.Rulesets.Scoring;
+using osu.Game.Skinning;
+using OpenTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
@@ -57,6 +59,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss });
}
+ protected override void SkinChanged(ISkinSource skin, bool allowFallback)
+ {
+ base.SkinChanged(skin, allowFallback);
+
+ if (HitObject is IHasComboInformation combo)
+ AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
+ }
+
private const float preempt = 1000;
protected override void UpdateState(ArmedState state)
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index 1e4051c5aa..29ad3c3956 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -64,52 +64,57 @@ namespace osu.Game.Rulesets.Catch.Objects
X = X
});
- for (var span = 0; span < this.SpanCount(); span++)
+ double lastDropletTime = StartTime;
+
+ for (int span = 0; span < this.SpanCount(); span++)
{
var spanStartTime = StartTime + span * spanDuration;
var reversed = span % 2 == 1;
- for (var d = tickDistance; d <= length; d += tickDistance)
+ for (double d = 0; d <= length; d += tickDistance)
{
- if (d > length - minDistanceFromEnd)
- break;
-
var timeProgress = d / length;
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
- var lastTickTime = spanStartTime + timeProgress * spanDuration;
- AddNested(new Droplet
+ double time = spanStartTime + timeProgress * spanDuration;
+
+ double tinyTickInterval = time - lastDropletTime;
+ while (tinyTickInterval > 100)
+ tinyTickInterval /= 2;
+
+ for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
{
- StartTime = lastTickTime,
- X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
- Samples = new List(Samples.Select(s => new SampleInfo
+ double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
+
+ AddNested(new TinyDroplet
{
- Bank = s.Bank,
- Name = @"slidertick",
- Volume = s.Volume
- }))
- });
- }
+ StartTime = t,
+ X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
+ Samples = new List(Samples.Select(s => new SampleInfo
+ {
+ Bank = s.Bank,
+ Name = @"slidertick",
+ Volume = s.Volume
+ }))
+ });
+ }
- double tinyTickInterval = tickDistance / length * spanDuration;
- while (tinyTickInterval > 100)
- tinyTickInterval /= 2;
-
- for (double t = 0; t < spanDuration; t += tinyTickInterval)
- {
- double progress = reversed ? 1 - t / spanDuration : t / spanDuration;
-
- AddNested(new TinyDroplet
+ if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
{
- StartTime = spanStartTime + t,
- X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
- Samples = new List(Samples.Select(s => new SampleInfo
+ AddNested(new Droplet
{
- Bank = s.Bank,
- Name = @"slidertick",
- Volume = s.Volume
- }))
- });
+ StartTime = time,
+ X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
+ Samples = new List(Samples.Select(s => new SampleInfo
+ {
+ Bank = s.Bank,
+ Name = @"slidertick",
+ Volume = s.Volume
+ }))
+ });
+ }
+
+ lastDropletTime = time;
}
AddNested(new Fruit
diff --git a/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs
index 826c900140..e40510b71b 100644
--- a/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs
+++ b/osu.Game.Rulesets.Catch/Tests/CatchBeatmapConversionTest.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
- [TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2149")]
+ [TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")]
public new void Test(string name)
{
base.Test(name);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 2e59e2dc60..d4d89c2aa3 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -5,6 +5,9 @@ using System.ComponentModel;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using System.Linq;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Skinning;
+using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -34,6 +37,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
+ protected override void SkinChanged(ISkinSource skin, bool allowFallback)
+ {
+ base.SkinChanged(skin, allowFallback);
+
+ if (HitObject is IHasComboInformation combo)
+ AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
+ }
+
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein);
protected virtual void UpdateCurrentState(ArmedState state)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
index 26186a0049..c59c22c771 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
@@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public double? SnakedStart { get; private set; }
public double? SnakedEnd { get; private set; }
- private Color4 accentColour;
+ private Color4 accentColour = Color4.White;
///
/// Used to colour the path.
///
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 5d1908fa6e..c00c30ced9 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -10,7 +10,7 @@ using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Osu.Objects
{
- public abstract class OsuHitObject : HitObject, IHasComboIndex, IHasPosition
+ public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition
{
public const double OBJECT_RADIUS = 64;
diff --git a/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs b/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs
new file mode 100644
index 0000000000..04a662426f
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs
@@ -0,0 +1,28 @@
+// 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 osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Game.Screens.Edit.Screens.Compose;
+using OpenTK;
+
+namespace osu.Game.Tests.Visual
+{
+ public class TestCaseBeatDivisorControl : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[] { typeof(BindableBeatDivisor) };
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Child = new BeatDivisorControl(new BindableBeatDivisor())
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(90, 90)
+ };
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs
index 5fd0f96f4a..cd25bc1683 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs
@@ -1,6 +1,8 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Timing;
@@ -13,6 +15,8 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public class TestCaseEditorCompose : OsuTestCase
{
+ public override IReadOnlyList RequiredTypes => new[] { typeof(Compose) };
+
private DependencyContainer dependencies;
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
diff --git a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs
index bfdb39dd5e..e9e966a826 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorSeekSnapping.cs
@@ -18,6 +18,7 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Screens.Edit.Screens.Compose;
using osu.Game.Tests.Beatmaps;
using OpenTK;
using OpenTK.Graphics;
@@ -31,6 +32,8 @@ namespace osu.Game.Tests.Visual
private Track track;
private HitObjectComposer composer;
+ private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor(4);
+
private DecoupleableInterpolatingFramedClock clock;
private DependencyContainer dependencies;
@@ -44,6 +47,7 @@ namespace osu.Game.Tests.Visual
clock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
dependencies.CacheAs(clock);
dependencies.CacheAs(clock);
+ dependencies.Cache(beatDivisor);
var testBeatmap = new Beatmap
{
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 80efb0672e..e85bbd6f10 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -116,6 +116,7 @@
+
diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
index 58b51085a4..5874314f75 100644
--- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs
@@ -110,7 +110,7 @@ namespace osu.Game.Beatmaps
Skin skin;
try
{
- skin = new BeatmapSkin(BeatmapInfo, store, audioManager);
+ skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager);
}
catch (Exception e)
{
diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs
index 83b2867df7..f2cc419043 100644
--- a/osu.Game/Beatmaps/BeatmapProcessor.cs
+++ b/osu.Game/Beatmaps/BeatmapProcessor.cs
@@ -23,9 +23,9 @@ namespace osu.Game.Beatmaps
/// The Beatmap to process.
public virtual void PostProcess(Beatmap beatmap)
{
- IHasComboIndex lastObj = null;
+ IHasComboInformation lastObj = null;
- foreach (var obj in beatmap.HitObjects.OfType())
+ foreach (var obj in beatmap.HitObjects.OfType())
{
if (obj.NewCombo)
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 67d497ba83..131c010c5c 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using osu.Framework.Logging;
using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Formats
@@ -31,7 +32,11 @@ namespace osu.Game.Beatmaps.Formats
if (line.StartsWith(@"[") && line.EndsWith(@"]"))
{
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
- throw new InvalidDataException($@"Unknown osu section {line}");
+ {
+ Logger.Log($"Unknown section \"{line}\" in {beatmap}");
+ section = Section.None;
+ }
+
continue;
}
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index a65593ff82..f0e67a7185 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -13,6 +13,7 @@ using osu.Game.IO;
using osu.Game.IO.Archives;
using osu.Game.IPC;
using osu.Game.Overlays.Notifications;
+using SharpCompress.Common;
using FileInfo = osu.Game.IO.FileInfo;
namespace osu.Game.Database
@@ -79,7 +80,6 @@ namespace osu.Game.Database
var notification = new ProgressNotification
{
Text = "Import is initialising...",
- CompletionText = "Import successful!",
Progress = 0,
State = ProgressNotificationState.Active,
};
@@ -88,7 +88,8 @@ namespace osu.Game.Database
List imported = new List();
- int i = 0;
+ int current = 0;
+ int errors = 0;
foreach (string path in paths)
{
if (notification.State == ProgressNotificationState.Cancelled)
@@ -97,11 +98,11 @@ namespace osu.Game.Database
try
{
- notification.Text = $"Importing ({i} of {paths.Length})\n{Path.GetFileName(path)}";
+ notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}";
using (ArchiveReader reader = getReaderFrom(path))
imported.Add(Import(reader));
- notification.Progress = (float)++i / paths.Length;
+ notification.Progress = (float)current / paths.Length;
// We may or may not want to delete the file depending on where it is stored.
// e.g. reconstructing/repairing database with items from default storage.
@@ -121,9 +122,11 @@ namespace osu.Game.Database
{
e = e.InnerException ?? e;
Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})");
+ errors++;
}
}
+ notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!";
notification.State = ProgressNotificationState.Completed;
}
@@ -218,9 +221,11 @@ namespace osu.Game.Database
// user requested abort
return;
- notification.Text = $"Deleting ({i} of {items.Count})";
- notification.Progress = (float)++i / items.Count;
+ notification.Text = $"Deleting ({++i} of {items.Count})";
+
Delete(b);
+
+ notification.Progress = (float)i / items.Count;
}
}
@@ -254,9 +259,11 @@ namespace osu.Game.Database
// user requested abort
return;
- notification.Text = $"Restoring ({i} of {items.Count})";
- notification.Progress = (float)++i / items.Count;
+ notification.Text = $"Restoring ({++i} of {items.Count})";
+
Undelete(item);
+
+ notification.Progress = (float)i / items.Count;
}
}
@@ -331,7 +338,9 @@ namespace osu.Game.Database
{
if (ZipFile.IsZipFile(path))
return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path));
- return new LegacyFilesystemReader(path);
+ if (Directory.Exists(path))
+ return new LegacyFilesystemReader(path);
+ throw new InvalidFormatException($"{path} is not a valid archive");
}
}
}
diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs
index 6f9d83473f..89ed8044e6 100644
--- a/osu.Game/Graphics/Backgrounds/Triangles.cs
+++ b/osu.Game/Graphics/Backgrounds/Triangles.cs
@@ -242,7 +242,7 @@ namespace osu.Game.Graphics.Backgrounds
triangle,
colourInfo,
null,
- Shared.VertexBatch.Add,
+ Shared.VertexBatch.AddAction,
Vector2.Divide(localInflationAmount, size));
}
diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
index 6d9bf231c3..33786252ab 100644
--- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
@@ -30,6 +30,9 @@ namespace osu.Game.Graphics.UserInterface
}
}
+ // We may not be focused yet, but we need to handle keyboard input to be able to request focus
+ public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput;
+
protected override void OnFocus(InputState state)
{
base.OnFocus(state);
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index e656c7256e..4a40a6b5df 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -302,6 +302,21 @@ namespace osu.Game
};
}
+ var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications };
+ foreach (var overlay in singleDisplaySideOverlays)
+ {
+ overlay.StateChanged += state =>
+ {
+ if (state == Visibility.Hidden) return;
+
+ foreach (var c in singleDisplaySideOverlays)
+ {
+ if (c == overlay) continue;
+ c.State = Visibility.Hidden;
+ }
+ };
+ }
+
// eventually informational overlays should be displayed in a stack, but for now let's only allow one to stay open at a time.
var informationalOverlays = new OverlayContainer[] { beatmapSetOverlay, userProfile };
foreach (var overlay in informationalOverlays)
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index ae1c8af1a4..c076b53f51 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
@@ -16,6 +17,7 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
+using osu.Game.Screens.Edit.Screens.Compose;
using osu.Game.Screens.Edit.Screens.Compose.Layers;
using osu.Game.Screens.Edit.Screens.Compose.RadioButtons;
@@ -31,6 +33,7 @@ namespace osu.Game.Rulesets.Edit
private readonly List layerContainers = new List();
private readonly Bindable beatmap = new Bindable();
+ private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
private IAdjustableClock adjustableClock;
@@ -41,11 +44,14 @@ namespace osu.Game.Rulesets.Edit
RelativeSizeAxes = Axes.Both;
}
- [BackgroundDependencyLoader]
- private void load(OsuGameBase osuGame, IAdjustableClock adjustableClock, IFrameBasedClock framedClock)
+ [BackgroundDependencyLoader(true)]
+ private void load([NotNull] OsuGameBase osuGame, [NotNull] IAdjustableClock adjustableClock, [NotNull] IFrameBasedClock framedClock, [CanBeNull] BindableBeatDivisor beatDivisor)
{
this.adjustableClock = adjustableClock;
+ if (beatDivisor != null)
+ this.beatDivisor.BindTo(beatDivisor);
+
beatmap.BindTo(osuGame.Beatmap);
try
@@ -167,9 +173,6 @@ namespace osu.Game.Rulesets.Edit
private void seek(int direction, bool snapped)
{
- // Todo: This should not be a constant, but feels good for now
- const int beat_snap_divisor = 4;
-
var cpi = beatmap.Value.Beatmap.ControlPointInfo;
var timingPoint = cpi.TimingPointAt(adjustableClock.CurrentTime);
@@ -181,7 +184,7 @@ namespace osu.Game.Rulesets.Edit
timingPoint = cpi.TimingPoints[--activeIndex];
}
- double seekAmount = timingPoint.BeatLength / beat_snap_divisor;
+ double seekAmount = timingPoint.BeatLength / beatDivisor;
double seekTime = adjustableClock.CurrentTime + seekAmount * direction;
if (!snapped || cpi.TimingPoints.Count == 0)
@@ -222,9 +225,6 @@ namespace osu.Game.Rulesets.Edit
public void SeekTo(double seekTime, bool snapped = false)
{
- // Todo: This should not be a constant, but feels good for now
- const int beat_snap_divisor = 4;
-
if (!snapped)
{
adjustableClock.Seek(seekTime);
@@ -232,7 +232,7 @@ namespace osu.Game.Rulesets.Edit
}
var timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(seekTime);
- double beatSnapLength = timingPoint.BeatLength / beat_snap_divisor;
+ double beatSnapLength = timingPoint.BeatLength / beatDivisor;
// We will be snapping to beats within the timing point
seekTime -= timingPoint.Time;
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 945cd928d4..348364a2bf 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -102,14 +102,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
}
}
- protected override void SkinChanged(ISkinSource skin, bool allowFallback)
- {
- base.SkinChanged(skin, allowFallback);
-
- if (HitObject is IHasComboIndex combo)
- AccentColour = skin.GetComboColour(combo) ?? Color4.White;
- }
-
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs
new file mode 100644
index 0000000000..1d4f4e0f90
--- /dev/null
+++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs
@@ -0,0 +1,26 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Rulesets.Objects.Types
+{
+ ///
+ /// A HitObject that is part of a combo and has extended information about its position relative to other combo objects.
+ ///
+ public interface IHasComboInformation : IHasCombo
+ {
+ ///
+ /// The offset of this hitobject in the current combo.
+ ///
+ int IndexInCurrentCombo { get; set; }
+
+ ///
+ /// The offset of this combo in relation to the beatmap.
+ ///
+ int ComboIndex { get; set; }
+
+ ///
+ /// Whether this is the last object in the current combo.
+ ///
+ bool LastInCombo { get; set; }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs
new file mode 100644
index 0000000000..a7be3c1eb5
--- /dev/null
+++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs
@@ -0,0 +1,398 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Colour;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.UserInterface;
+using osu.Framework.Input;
+using osu.Game.Graphics;
+using osu.Game.Graphics.UserInterface;
+using OpenTK;
+using OpenTK.Graphics;
+using OpenTK.Input;
+
+namespace osu.Game.Screens.Edit.Screens.Compose
+{
+ public class BeatDivisorControl : CompositeDrawable
+ {
+ private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
+ private int currentDivisorIndex;
+
+ public BeatDivisorControl(BindableBeatDivisor beatDivisor)
+ {
+ this.beatDivisor.BindTo(beatDivisor);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Masking = true;
+ CornerRadius = 5;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ Name = "Gray Background",
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.Gray4
+ },
+ new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ Name = "Black Background",
+ RelativeSizeAxes = Axes.Both,
+ Colour = Color4.Black
+ },
+ new TickSliderBar(beatDivisor, BindableBeatDivisor.VALID_DIVISORS)
+ {
+ RelativeSizeAxes = Axes.Both,
+ }
+ }
+ }
+ },
+ new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.Gray4
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Horizontal = 5 },
+ Child = new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[]
+ {
+ new DivisorButton
+ {
+ Icon = FontAwesome.fa_chevron_left,
+ Action = beatDivisor.Previous
+ },
+ new DivisorText(beatDivisor),
+ new DivisorButton
+ {
+ Icon = FontAwesome.fa_chevron_right,
+ Action = beatDivisor.Next
+ }
+ },
+ },
+ ColumnDimensions = new[]
+ {
+ new Dimension(GridSizeMode.Absolute, 20),
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 20)
+ }
+ }
+ }
+ }
+ }
+ },
+ new Drawable[]
+ {
+ new TextFlowContainer(s => s.TextSize = 14)
+ {
+ Padding = new MarginPadding { Horizontal = 15 },
+ Text = "beat snap divisor",
+ RelativeSizeAxes = Axes.X,
+ TextAnchor = Anchor.TopCentre
+ },
+ }
+ },
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.Absolute, 30),
+ new Dimension(GridSizeMode.Absolute, 25),
+ }
+ }
+ };
+ }
+
+ private class DivisorText : SpriteText
+ {
+ private readonly Bindable beatDivisor = new Bindable();
+
+ public DivisorText(BindableBeatDivisor beatDivisor)
+ {
+ this.beatDivisor.BindTo(beatDivisor);
+
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Colour = colours.BlueLighter;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ beatDivisor.ValueChanged += v => updateText();
+ updateText();
+ }
+
+ private void updateText() => Text = $"1/{beatDivisor.Value}";
+ }
+
+ private class DivisorButton : IconButton
+ {
+ public DivisorButton()
+ {
+ Anchor = Anchor.Centre;
+ Origin = Anchor.Centre;
+
+ // Small offset to look a bit better centered along with the divisor text
+ Y = 1;
+
+ ButtonSize = new Vector2(20);
+ IconScale = new Vector2(0.6f);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ IconColour = Color4.Black;
+ HoverColour = colours.Gray7;
+ FlashColour = colours.Gray9;
+ }
+ }
+
+ private class TickSliderBar : SliderBar
+ {
+ private Marker marker;
+
+ private readonly BindableBeatDivisor beatDivisor;
+ private readonly int[] availableDivisors;
+
+ public TickSliderBar(BindableBeatDivisor beatDivisor, params int[] divisors)
+ {
+ CurrentNumber.BindTo(this.beatDivisor = beatDivisor);
+ availableDivisors = divisors;
+
+ Padding = new MarginPadding { Horizontal = 5 };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ foreach (var t in availableDivisors)
+ {
+ AddInternal(new Tick(t)
+ {
+ Anchor = Anchor.TopLeft,
+ Origin = Anchor.TopCentre,
+ RelativePositionAxes = Axes.X,
+ X = getMappedPosition(t)
+ });
+ }
+
+ AddInternal(marker = new Marker());
+
+ CurrentNumber.ValueChanged += v =>
+ {
+ marker.MoveToX(getMappedPosition(v), 100, Easing.OutQuint);
+ marker.Flash();
+ };
+ }
+
+ protected override void UpdateValue(float value)
+ {
+ }
+
+ public override bool HandleKeyboardInput => IsHovered && !CurrentNumber.Disabled;
+
+ protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ {
+ switch (args.Key)
+ {
+ case Key.Right:
+ beatDivisor.Next();
+ OnUserChange();
+ return true;
+ case Key.Left:
+ beatDivisor.Previous();
+ OnUserChange();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ {
+ marker.Active = true;
+ return base.OnMouseDown(state, args);
+ }
+
+ protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ {
+ marker.Active = false;
+ return base.OnMouseUp(state, args);
+ }
+
+ protected override bool OnClick(InputState state)
+ {
+ handleMouseInput(state);
+ return true;
+ }
+
+ protected override bool OnDrag(InputState state)
+ {
+ handleMouseInput(state);
+ return true;
+ }
+
+ private void handleMouseInput(InputState state)
+ {
+ // copied from SliderBar so we can do custom spacing logic.
+ var xPosition = (ToLocalSpace(state?.Mouse.NativeState.Position ?? Vector2.Zero).X - RangePadding) / UsableWidth;
+
+ CurrentNumber.Value = availableDivisors.OrderBy(d => Math.Abs(getMappedPosition(d) - xPosition)).First();
+ OnUserChange();
+ }
+
+ private float getMappedPosition(float divisor) => (float)Math.Pow((divisor - 1) / (availableDivisors.Last() - 1), 0.90f);
+
+ private class Tick : CompositeDrawable
+ {
+ private readonly int divisor;
+
+ public Tick(int divisor)
+ {
+ this.divisor = divisor;
+ Size = new Vector2(2.5f, 10);
+
+ InternalChild = new Box { RelativeSizeAxes = Axes.Both };
+
+ CornerRadius = 0.5f;
+ Masking = true;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Colour = getColourForDivisor(divisor, colours);
+ }
+
+ private ColourInfo getColourForDivisor(int divisor, OsuColour colours)
+ {
+ switch (divisor)
+ {
+ case 2:
+ return colours.BlueLight;
+ case 4:
+ return colours.Blue;
+ case 8:
+ return colours.BlueDarker;
+ case 16:
+ return colours.PurpleDark;
+ case 3:
+ return colours.YellowLight;
+ case 6:
+ return colours.Yellow;
+ case 12:
+ return colours.YellowDarker;
+ default:
+ return Color4.White;
+ }
+ }
+ }
+
+ private class Marker : CompositeDrawable
+ {
+ private Color4 defaultColour;
+
+ private const float size = 7;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Colour = defaultColour = colours.Gray4;
+ Anchor = Anchor.TopLeft;
+ Origin = Anchor.TopCentre;
+
+ Width = size;
+ RelativeSizeAxes = Axes.Y;
+ RelativePositionAxes = Axes.X;
+
+ InternalChildren = new Drawable[]
+ {
+ new Box
+ {
+ Width = 2,
+ RelativeSizeAxes = Axes.Y,
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.BottomCentre,
+ Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.2f), Color4.White),
+ Blending = BlendingMode.Additive,
+ },
+ new EquilateralTriangle
+ {
+ Origin = Anchor.BottomCentre,
+ Anchor = Anchor.BottomCentre,
+ Height = size,
+ EdgeSmoothness = new Vector2(1),
+ Colour = Color4.White,
+ }
+ };
+ }
+
+ private bool active;
+
+ public bool Active
+ {
+ get => active;
+ set
+ {
+ this.FadeColour(value ? Color4.White : defaultColour, 500, Easing.OutQuint);
+ active = value;
+ }
+ }
+
+ public void Flash()
+ {
+ bool wasActive = active;
+
+ Active = true;
+
+ if (wasActive) return;
+
+ using (BeginDelayedSequence(50))
+ Active = false;
+ }
+ }
+ }
+ }
+}
diff --git a/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs
new file mode 100644
index 0000000000..8eb3f1347e
--- /dev/null
+++ b/osu.Game/Screens/Edit/Screens/Compose/BindableBeatDivisor.cs
@@ -0,0 +1,39 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Linq;
+using osu.Framework.Configuration;
+
+namespace osu.Game.Screens.Edit.Screens.Compose
+{
+ public class BindableBeatDivisor : BindableNumber
+ {
+ public static readonly int[] VALID_DIVISORS = { 1, 2, 3, 4, 6, 8, 12, 16 };
+
+ public BindableBeatDivisor(int value = 1)
+ : base(value)
+ {
+ }
+
+ public void Next() => Value = VALID_DIVISORS[Math.Min(VALID_DIVISORS.Length - 1, Array.IndexOf(VALID_DIVISORS, Value) + 1)];
+
+ public void Previous() => Value = VALID_DIVISORS[Math.Max(0, Array.IndexOf(VALID_DIVISORS, Value) - 1)];
+
+ public override int Value
+ {
+ get { return base.Value; }
+ set
+ {
+ if (!VALID_DIVISORS.Contains(value))
+ throw new ArgumentOutOfRangeException($"Provided divisor is not in {nameof(VALID_DIVISORS)}");
+
+ base.Value = value;
+ }
+ }
+
+ protected override int DefaultMinValue => VALID_DIVISORS.First();
+ protected override int DefaultMaxValue => VALID_DIVISORS.Last();
+ protected override int DefaultPrecision => 1;
+ }
+}
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs
index 861a08fb07..91adc8324a 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs
@@ -17,11 +17,20 @@ namespace osu.Game.Screens.Edit.Screens.Compose
private const float vertical_margins = 10;
private const float horizontal_margins = 20;
+ private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
+
private Container composerContainer;
+ private DependencyContainer dependencies;
+
+ protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
+ => dependencies = new DependencyContainer(parent);
+
[BackgroundDependencyLoader]
private void load()
{
+ dependencies.Cache(beatDivisor);
+
ScrollableTimeline timeline;
Children = new Drawable[]
{
@@ -48,15 +57,28 @@ namespace osu.Game.Screens.Edit.Screens.Compose
Name = "Timeline content",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins },
- Children = new Drawable[]
+ Child = new GridContainer
{
- new Container
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
{
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Right = 115 },
- Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both }
+ new Drawable[]
+ {
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Right = 5 },
+ Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both }
+ },
+ new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both }
+ },
+ },
+ ColumnDimensions = new[]
+ {
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 90),
}
- }
+ },
}
}
}
diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs
index 3a3f3d4650..fc747acbb4 100644
--- a/osu.Game/Screens/Menu/LogoVisualisation.cs
+++ b/osu.Game/Screens/Menu/LogoVisualisation.cs
@@ -211,7 +211,7 @@ namespace osu.Game.Screens.Menu
rectangle,
colourInfo,
null,
- Shared.VertexBatch.Add,
+ Shared.VertexBatch.AddAction,
//barSize by itself will make it smooth more in the X axis than in the Y axis, this reverts that.
Vector2.Divide(inflation, barSize.Yx));
}
diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs
index 3fcb885655..b7d2ed2e1f 100644
--- a/osu.Game/Screens/Menu/OsuLogo.cs
+++ b/osu.Game/Screens/Menu/OsuLogo.cs
@@ -337,12 +337,10 @@ namespace osu.Game.Screens.Menu
}
}
- private bool interactive => Action != null && Alpha > 0.2f;
+ public override bool HandleMouseInput => base.HandleMouseInput && Action != null && Alpha > 0.2f;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
- if (!interactive) return false;
-
logoBounceContainer.ScaleTo(0.9f, 1000, Easing.Out);
return true;
}
@@ -355,8 +353,6 @@ namespace osu.Game.Screens.Menu
protected override bool OnClick(InputState state)
{
- if (!interactive) return false;
-
if (Action?.Invoke() ?? true)
sampleClick.Play();
@@ -368,8 +364,6 @@ namespace osu.Game.Screens.Menu
protected override bool OnHover(InputState state)
{
- if (!interactive) return false;
-
logoHoverContainer.ScaleTo(1.1f, 500, Easing.OutElastic);
return true;
}
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index 31e7313c0b..89082cfbd5 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -139,8 +139,7 @@ namespace osu.Game.Screens.Play
{
// as the pushDebounce below has a delay, we need to keep checking and cancel a future debounce
// if we become unready for push during the delay.
- pushDebounce?.Cancel();
- pushDebounce = null;
+ cancelLoad();
return;
}
@@ -172,10 +171,23 @@ namespace osu.Game.Screens.Play
}
}
+ private void cancelLoad()
+ {
+ pushDebounce?.Cancel();
+ pushDebounce = null;
+ }
+
+ protected override void OnSuspending(Screen next)
+ {
+ base.OnSuspending(next);
+ cancelLoad();
+ }
+
protected override bool OnExiting(Screen next)
{
Content.ScaleTo(0.7f, 150, Easing.InQuint);
this.FadeOut(150);
+ cancelLoad();
return base.OnExiting(next);
}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index ca8a1cae41..f01616ade2 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -259,6 +259,8 @@ namespace osu.Game.Screens.Select
private void workingBeatmapChanged(WorkingBeatmap beatmap)
{
+ if (beatmap is DummyWorkingBeatmap) return;
+
if (IsCurrentScreen && !Carousel.SelectBeatmap(beatmap?.BeatmapInfo, false))
// If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch
if (beatmap?.BeatmapInfo?.Ruleset != null && beatmap.BeatmapInfo.Ruleset != Ruleset.Value)
diff --git a/osu.Game/Skinning/BeatmapSkin.cs b/osu.Game/Skinning/BeatmapSkin.cs
deleted file mode 100644
index 815aac2f64..0000000000
--- a/osu.Game/Skinning/BeatmapSkin.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System.IO;
-using osu.Framework.Audio;
-using osu.Framework.Graphics.Textures;
-using osu.Framework.IO.Stores;
-using osu.Game.Beatmaps;
-
-namespace osu.Game.Skinning
-{
- public class BeatmapSkin : LegacySkin
- {
- public BeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, AudioManager audioManager)
- : base(new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() })
- {
- storage = new LegacySkinResourceStore(beatmap.BeatmapSet, storage);
-
- Samples = audioManager.GetSampleManager(storage);
-
- Textures = new TextureStore(new RawTextureLoaderStore(storage));
-
- var decoder = new LegacySkinDecoder();
-
- using (StreamReader reader = new StreamReader(storage.GetStream(beatmap.Path)))
- {
- Configuration = decoder.Decode(reader);
- }
- }
- }
-}
diff --git a/osu.Game/Skinning/ISkinSource.cs b/osu.Game/Skinning/ISkinSource.cs
index 924fdbd8c1..d8f259b4ea 100644
--- a/osu.Game/Skinning/ISkinSource.cs
+++ b/osu.Game/Skinning/ISkinSource.cs
@@ -5,8 +5,6 @@ using System;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
-using osu.Game.Rulesets.Objects.Types;
-using OpenTK.Graphics;
namespace osu.Game.Skinning
{
@@ -23,6 +21,8 @@ namespace osu.Game.Skinning
SampleChannel GetSample(string sampleName);
- Color4? GetComboColour(IHasComboIndex comboObject);
+ TValue GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : class;
+
+ TValue? GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : struct;
}
}
diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs
new file mode 100644
index 0000000000..01beb8db32
--- /dev/null
+++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Audio;
+using osu.Framework.IO.Stores;
+using osu.Game.Beatmaps;
+
+namespace osu.Game.Skinning
+{
+ public class LegacyBeatmapSkin : LegacySkin
+ {
+ public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, AudioManager audioManager)
+ : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), audioManager, beatmap.Path)
+ {
+ }
+
+ private static SkinInfo createSkinInfo(BeatmapInfo beatmap) =>
+ new SkinInfo { Name = beatmap.ToString(), Creator = beatmap.Metadata.Author.ToString() };
+ }
+}
diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs
index c543537f32..66e8cb8f9f 100644
--- a/osu.Game/Skinning/LegacySkin.cs
+++ b/osu.Game/Skinning/LegacySkin.cs
@@ -11,6 +11,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.IO.Stores;
using osu.Game.Database;
+using OpenTK;
namespace osu.Game.Skinning
{
@@ -21,22 +22,21 @@ namespace osu.Game.Skinning
protected SampleManager Samples;
public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager)
- : this(skin)
+ : this(skin, new LegacySkinResourceStore(skin, storage), audioManager, "skin.ini")
{
- storage = new LegacySkinResourceStore(skin, storage);
- Samples = audioManager.GetSampleManager(storage);
- Textures = new TextureStore(new RawTextureLoaderStore(storage));
+ }
- Stream stream = storage.GetStream("skin.ini");
+ protected LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager, string filename) : base(skin)
+ {
+ Stream stream = storage.GetStream(filename);
if (stream != null)
using (StreamReader reader = new StreamReader(stream))
Configuration = new LegacySkinDecoder().Decode(reader);
else
Configuration = new SkinConfiguration();
- }
- protected LegacySkin(SkinInfo skin) : base(skin)
- {
+ Samples = audioManager.GetSampleManager(storage);
+ Textures = new TextureStore(new RawTextureLoaderStore(storage));
}
public override Drawable GetDrawableComponent(string componentName)
@@ -57,10 +57,22 @@ namespace osu.Game.Skinning
break;
}
- var texture = GetTexture(componentName);
+ float ratio = 0.72f; // brings sizing roughly in-line with stable
+
+ var texture = GetTexture($"{componentName}@2x");
+ if (texture == null)
+ {
+ ratio *= 2;
+ GetTexture(componentName);
+ }
+
if (texture == null) return null;
- return new Sprite { Texture = texture };
+ return new Sprite
+ {
+ Texture = texture,
+ Scale = new Vector2(ratio),
+ };
}
public override Texture GetTexture(string componentName) => Textures.Get(componentName);
diff --git a/osu.Game/Skinning/LegacySkinDecoder.cs b/osu.Game/Skinning/LegacySkinDecoder.cs
index 9a881f9241..853abceddf 100644
--- a/osu.Game/Skinning/LegacySkinDecoder.cs
+++ b/osu.Game/Skinning/LegacySkinDecoder.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Skinning
break;
}
- return;
+ break;
}
base.ParseLine(output, section, line);
diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs
index a0cc11a324..b7e2bd0daf 100644
--- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs
+++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs
@@ -7,8 +7,6 @@ using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
-using osu.Game.Rulesets.Objects.Types;
-using OpenTK.Graphics;
namespace osu.Game.Skinning
{
@@ -22,7 +20,25 @@ namespace osu.Game.Skinning
public SampleChannel GetSample(string sampleName) => source.GetSample(sampleName) ?? fallbackSource?.GetSample(sampleName);
- public Color4? GetComboColour(IHasComboIndex comboObject) => source.GetComboColour(comboObject) ?? fallbackSource?.GetComboColour(comboObject);
+ public TValue? GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : struct
+ {
+ TValue? val = null;
+ var conf = (source as Skin)?.Configuration as TConfiguration;
+ if (conf != null)
+ val = query?.Invoke(conf);
+
+ return val ?? fallbackSource?.GetValue(query);
+ }
+
+ public TValue GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : class
+ {
+ TValue val = null;
+ var conf = (source as Skin)?.Configuration as TConfiguration;
+ if (conf != null)
+ val = query?.Invoke(conf);
+
+ return val ?? fallbackSource?.GetValue(query);
+ }
private readonly ISkinSource source;
private ISkinSource fallbackSource;
diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs
index 5d2c640244..02fb84a4a2 100644
--- a/osu.Game/Skinning/Skin.cs
+++ b/osu.Game/Skinning/Skin.cs
@@ -5,8 +5,6 @@ using System;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
-using osu.Game.Rulesets.Objects.Types;
-using OpenTK.Graphics;
namespace osu.Game.Skinning
{
@@ -24,8 +22,11 @@ namespace osu.Game.Skinning
public abstract Texture GetTexture(string componentName);
- public virtual Color4? GetComboColour(IHasComboIndex comboObject) =>
- Configuration.ComboColours.Count == 0 ? (Color4?)null : Configuration.ComboColours[comboObject.ComboIndex % Configuration.ComboColours.Count];
+ public TValue GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : class
+ => Configuration is TConfiguration conf ? query?.Invoke(conf) : null;
+
+ public TValue? GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : struct
+ => Configuration is TConfiguration conf ? query?.Invoke(conf) : null;
protected Skin(SkinInfo skin)
{
diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs
index 9ae6eef49f..f965a77cce 100644
--- a/osu.Game/Skinning/SkinManager.cs
+++ b/osu.Game/Skinning/SkinManager.cs
@@ -14,8 +14,6 @@ using osu.Framework.Graphics.Textures;
using osu.Framework.Platform;
using osu.Game.Database;
using osu.Game.IO.Archives;
-using osu.Game.Rulesets.Objects.Types;
-using OpenTK.Graphics;
namespace osu.Game.Skinning
{
@@ -120,10 +118,12 @@ namespace osu.Game.Skinning
public Drawable GetDrawableComponent(string componentName) => CurrentSkin.Value.GetDrawableComponent(componentName);
- public Texture GetTexture(string componentName)=> CurrentSkin.Value.GetTexture(componentName);
+ public Texture GetTexture(string componentName) => CurrentSkin.Value.GetTexture(componentName);
public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName);
- public Color4? GetComboColour(IHasComboIndex comboObject) => CurrentSkin.Value.GetComboColour(comboObject);
+ public TValue GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : class => CurrentSkin.Value.GetValue(query);
+
+ public TValue? GetValue(Func query) where TConfiguration : SkinConfiguration where TValue : struct => CurrentSkin.Value.GetValue(query);
}
}
diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs
index 9314d16c39..09d2e6a3ed 100644
--- a/osu.Game/Skinning/SkinnableDrawable.cs
+++ b/osu.Game/Skinning/SkinnableDrawable.cs
@@ -49,6 +49,7 @@ namespace osu.Game.Skinning
{
drawable.RelativeSizeAxes = Axes.Both;
drawable.Size = Vector2.One;
+ drawable.Scale = Vector2.One;
drawable.FillMode = FillMode.Fit;
}
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 1c902a158f..72bc70de51 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -374,13 +374,15 @@
-
+
+
+
@@ -872,7 +874,7 @@
-
+