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 @@ - +