From 8270e4d758728125c2ebaa24aaccdb57a303cf99 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 20:51:28 +0900 Subject: [PATCH 01/47] Add BorderColour to SliderBody --- .../Objects/Drawables/Pieces/SliderBody.cs | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 89af67ba2a..f5910c8a02 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -51,6 +51,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } + private Color4 borderColour = Color4.White; + /// + /// Used to colour the path border. + /// + public new Color4 BorderColour + { + get { return borderColour; } + set + { + if (borderColour == value) + return; + borderColour = value; + + if (LoadState == LoadState.Ready) + Schedule(reloadTexture); + } + } + public Quad PathDrawQuad => container.ScreenSpaceDrawQuad; private int textureWidth => (int)PathWidth * 2; @@ -130,10 +148,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (progress <= border_portion) { - bytes[i * 4] = 255; - bytes[i * 4 + 1] = 255; - bytes[i * 4 + 2] = 255; - bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * 255); + bytes[i * 4] = (byte)(BorderColour.R * 255); + bytes[i * 4 + 1] = (byte)(BorderColour.G * 255); + bytes[i * 4 + 2] = (byte)(BorderColour.B * 255); + bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (BorderColour.A * 255)); } else { From ee055b8e5c36d0a92fbef03c0e7779410d2a7fe3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 20:52:12 +0900 Subject: [PATCH 02/47] Allow customizing the snake-ability of sliders --- .../Objects/Drawables/DrawableSlider.cs | 9 +++++++++ .../Objects/Drawables/Pieces/SliderBody.cs | 18 +++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 41df7ae4a4..a15c6ccd79 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -7,9 +7,11 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using System.Collections.Generic; using System.Linq; +using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Judgements; using osu.Framework.Graphics.Primitives; +using osu.Game.Configuration; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; @@ -87,6 +89,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.SnakingInSliders, Body.SnakingIn); + config.BindWith(OsuSetting.SnakingOutSliders, Body.SnakingOut); + } + private int currentSpan; public bool Tracking; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index f5910c8a02..31ef1c05e6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Textures; -using osu.Game.Configuration; using OpenTK; using OpenTK.Graphics.ES30; using OpenTK.Graphics; @@ -30,6 +29,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces set { path.PathWidth = value; } } + public readonly Bindable SnakingIn = new Bindable(); + public readonly Bindable SnakingOut = new Bindable(); + public double? SnakedStart { get; private set; } public double? SnakedEnd { get; private set; } @@ -115,15 +117,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - private Bindable snakingIn; - private Bindable snakingOut; - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load() { - snakingIn = config.GetBindable(OsuSetting.SnakingInSliders); - snakingOut = config.GetBindable(OsuSetting.SnakingOutSliders); - reloadTexture(); } @@ -188,18 +184,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void UpdateProgress(double progress, int span) { double start = 0; - double end = snakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1; + double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1; if (span >= slider.SpanCount() - 1) { if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1) { start = 0; - end = snakingOut ? progress : 1; + end = SnakingOut ? progress : 1; } else { - start = snakingOut ? progress : 0; + start = SnakingOut ? progress : 0; } } From 7fd7dc153846d4d8eb549d65f544ee84c35ab473 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 13:50:31 +0900 Subject: [PATCH 03/47] Extract playfield scaling into a separate class And make it more general. --- osu.Game/Rulesets/UI/Playfield.cs | 52 +++-------- osu.Game/Rulesets/UI/ScalableContainer.cs | 86 +++++++++++++++++++ .../UI/Scrolling/ScrollingPlayfield.cs | 11 ++- osu.Game/osu.Game.csproj | 1 + 4 files changed, 105 insertions(+), 45 deletions(-) create mode 100644 osu.Game/Rulesets/UI/ScalableContainer.cs diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index a7fed7059b..bbf20c2c26 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -3,52 +3,37 @@ using System.Collections.Generic; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; using osu.Framework.Allocation; namespace osu.Game.Rulesets.UI { - public abstract class Playfield : Container + public abstract class Playfield : ScalableContainer { /// /// The HitObjects contained in this Playfield. /// public HitObjectContainer HitObjects { get; private set; } - public Container ScaledContent; - - protected override Container Content => content; - private readonly Container content; - - private List nestedPlayfields; - /// /// All the s nested inside this playfield. /// public IReadOnlyList NestedPlayfields => nestedPlayfields; + private List nestedPlayfields; /// /// A container for keeping track of DrawableHitObjects. /// - /// Whether we want our internal coordinate system to be scaled to a specified width. - protected Playfield(float? customWidth = null) + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + protected Playfield(float? customWidth = null, float? customHeight = null) + : base(customWidth, customHeight) { RelativeSizeAxes = Axes.Both; - - AddInternal(ScaledContent = new ScaledContainer - { - CustomWidth = customWidth, - RelativeSizeAxes = Axes.Both, - Children = new[] - { - content = new Container - { - RelativeSizeAxes = Axes.Both, - } - } - }); } [BackgroundDependencyLoader] @@ -94,22 +79,5 @@ namespace osu.Game.Rulesets.UI /// Creates the container that will be used to contain the s. /// protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer(); - - private class ScaledContainer : Container - { - /// - /// A value (in game pixels that we should scale our content to match). - /// - public float? CustomWidth; - - //dividing by the customwidth will effectively scale our content to the required container size. - protected override Vector2 DrawScale => CustomWidth.HasValue ? new Vector2(DrawSize.X / CustomWidth.Value) : base.DrawScale; - - protected override void Update() - { - base.Update(); - RelativeChildSize = new Vector2(DrawScale.X, RelativeChildSize.Y); - } - } } } diff --git a/osu.Game/Rulesets/UI/ScalableContainer.cs b/osu.Game/Rulesets/UI/ScalableContainer.cs new file mode 100644 index 0000000000..e1c1427470 --- /dev/null +++ b/osu.Game/Rulesets/UI/ScalableContainer.cs @@ -0,0 +1,86 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Rulesets.UI +{ + /// + /// A which can have its internal coordinate system scaled to a specific size. + /// + public class ScalableContainer : Container + { + /// + /// The scaled content. + /// + public readonly Container ScaledContent; + + protected override Container Content => content; + private readonly Container content; + + /// + /// A which can have its internal coordinate system scaled to a specific size. + /// + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + public ScalableContainer(float? customWidth = null, float? customHeight = null) + { + AddInternal(ScaledContent = new ScaledContainer + { + CustomWidth = customWidth, + CustomHeight = customHeight, + RelativeSizeAxes = Axes.Both, + Child = content = new Container { RelativeSizeAxes = Axes.Both } + }); + } + + public class ScaledContainer : Container + { + /// + /// The value to scale the width of the content to match. + /// If null, is used. + /// + public float? CustomWidth; + + /// + /// The value to scale the height of the content to match. + /// if null, is used. + /// + public float? CustomHeight; + + /// + /// The scale that is required for the size of the content to match and . + /// + private Vector2 sizeScale + { + get + { + if (CustomWidth.HasValue && CustomHeight.HasValue) + return Vector2.Divide(DrawSize, new Vector2(CustomWidth.Value, CustomHeight.Value)); + if (CustomWidth.HasValue) + return new Vector2(DrawSize.X / CustomWidth.Value); + if (CustomHeight.HasValue) + return new Vector2(DrawSize.Y / CustomHeight.Value); + return Vector2.One; + } + } + + /// + /// Scale the content to the required container size by multiplying by . + /// + protected override Vector2 DrawScale => sizeScale * base.DrawScale; + + protected override void Update() + { + base.Update(); + RelativeChildSize = sizeScale; + } + } + } +} diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index e168f6daec..1c1c8f7f61 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -62,9 +62,14 @@ namespace osu.Game.Rulesets.UI.Scrolling /// Creates a new . /// /// The direction in which s in this container should scroll. - /// Whether we want our internal coordinate system to be scaled to a specified width - protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null) - : base(customWidth) + /// The width to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + /// The height to scale the internal coordinate space to. + /// May be null if scaling based on is desired. If is also null, no scaling will occur. + /// + protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null, float? customHeight = null) + : base(customWidth, customHeight) { this.direction = direction; } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 39261fbe57..afeb791029 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -357,6 +357,7 @@ + From 6b8b39abc51bb07062ef22b92fa18ba9cbc3468a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 13:50:55 +0900 Subject: [PATCH 04/47] Rewrite SelectionLayer testcase to construct an entire HitObjectComposer --- .../Visual/TestCaseEditorSelectionLayer.cs | 61 +++++++------------ 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 50a39e6c33..5e0c0e165c 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -5,15 +5,13 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Timing; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit.Layers.Selection; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Tests.Beatmaps; namespace osu.Game.Tests.Visual { @@ -27,44 +25,31 @@ namespace osu.Game.Tests.Visual }; [BackgroundDependencyLoader] - private void load() + private void load(OsuGameBase osuGame) { - var playfield = new OsuEditPlayfield(); - - Children = new Drawable[] + osuGame.Beatmap.Value = new TestWorkingBeatmap(new Beatmap { - new Container + HitObjects = new List { - RelativeSizeAxes = Axes.Both, - Clock = new FramedClock(new StopwatchClock()), - Child = playfield + new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }, + new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, + new Slider + { + ControlPoints = new List + { + new Vector2(128, 256), + new Vector2(344, 256), + }, + Distance = 400, + Position = new Vector2(128, 256), + Velocity = 1, + TickDistance = 100, + Scale = 0.5f, + } }, - new SelectionLayer(playfield) - }; + }); - var hitCircle1 = new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }; - var hitCircle2 = new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }; - var slider = new Slider - { - ControlPoints = new List - { - new Vector2(128, 256), - new Vector2(344, 256), - }, - Distance = 400, - Position = new Vector2(128, 256), - Velocity = 1, - TickDistance = 100, - Scale = 0.5f, - }; - - hitCircle1.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - hitCircle2.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); - - playfield.Add(new DrawableHitCircle(hitCircle1)); - playfield.Add(new DrawableHitCircle(hitCircle2)); - playfield.Add(new DrawableSlider(slider)); + Child = new OsuHitObjectComposer(new OsuRuleset()); } } } From 4934ef742941b864efddf5d00d55ecf5ca71a135 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 14:01:33 +0900 Subject: [PATCH 05/47] Add playfield aspect ratio + scaling modifications to composer layers --- .../Edit/OsuHitObjectComposer.cs | 4 ++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 50 +++++++++++++++---- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 6652a5fde2..ae19706da3 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -2,10 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Edit @@ -25,5 +27,7 @@ namespace osu.Game.Rulesets.Osu.Edit new HitObjectCompositionTool(), new HitObjectCompositionTool() }; + + protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both }; } } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 7f22b3764c..62669150aa 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using OpenTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -25,6 +26,9 @@ namespace osu.Game.Rulesets.Edit protected ICompositionTool CurrentTool { get; private set; } + private RulesetContainer rulesetContainer; + private readonly Container[] layerContainers = new Container[2]; + protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; @@ -35,7 +39,6 @@ namespace osu.Game.Rulesets.Edit [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { - RulesetContainer rulesetContainer; try { rulesetContainer = CreateRulesetContainer(ruleset, osuGame.Beatmap.Value); @@ -46,6 +49,20 @@ namespace osu.Game.Rulesets.Edit return; } + layerContainers[0] = CreateLayerContainer(); + layerContainers[0].Child = new Container + { + Name = "Border", + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderColour = Color4.White, + BorderThickness = 2, + Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true } + }; + + layerContainers[1] = CreateLayerContainer(); + layerContainers[1].Child = new SelectionLayer(rulesetContainer.Playfield); + RadioButtonCollection toolboxCollection; InternalChild = new GridContainer { @@ -66,20 +83,13 @@ namespace osu.Game.Rulesets.Edit }, new Container { + Name = "Content", RelativeSizeAxes = Axes.Both, - Masking = true, - BorderColour = Color4.White, - BorderThickness = 2, Children = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true, - }, + layerContainers[0], rulesetContainer, - new SelectionLayer(rulesetContainer.Playfield) + layerContainers[1] } } }, @@ -102,10 +112,28 @@ namespace osu.Game.Rulesets.Edit toolboxCollection.Items[0].Select(); } + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + layerContainers.ForEach(l => + { + l.Anchor = rulesetContainer.Playfield.Anchor; + l.Origin = rulesetContainer.Playfield.Origin; + l.Position = rulesetContainer.Playfield.Position; + l.Size = rulesetContainer.Playfield.Size; + }); + } + private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); protected abstract IReadOnlyList CompositionTools { get; } + + /// + /// Creates a which provides a layer above or below the . + /// + protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer(); } } From 9a9f53ddfd76731d76462bdcc287190749667a8e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 20 Feb 2018 14:13:52 +0900 Subject: [PATCH 06/47] Remove 0.75 scale from osu! playfield in the editor --- osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs index 56efc25fa5..dd65cd470d 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics.Cursor; using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.UI; +using OpenTK; namespace osu.Game.Rulesets.Osu.Edit { @@ -17,6 +18,12 @@ namespace osu.Game.Rulesets.Osu.Edit protected override Playfield CreatePlayfield() => new OsuEditPlayfield(); + protected override Vector2 GetAspectAdjustedSize() + { + var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y); + return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y); + } + protected override CursorContainer CreateCursor() => null; } } From f877b642da8a65310a52c0f98bac198a0bdda9ff Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Feb 2018 17:10:18 +0900 Subject: [PATCH 07/47] Clean up and document better what "progress" means --- .../Objects/Drawables/DrawableSlider.cs | 12 ++---------- .../Objects/Drawables/Pieces/SliderBall.cs | 4 ++-- .../Objects/Drawables/Pieces/SliderBody.cs | 9 ++++++--- osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs | 6 +++++- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 41df7ae4a4..14650235c3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -10,7 +10,6 @@ using System.Linq; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Judgements; using osu.Framework.Graphics.Primitives; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -87,7 +86,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - private int currentSpan; public bool Tracking; protected override void Update() @@ -96,19 +94,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Tracking = Ball.Tracking; - double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); - - int span = slider.SpanAt(progress); - progress = slider.ProgressAt(progress); - - if (span > currentSpan) - currentSpan = span; + double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. if (!HeadCircle.IsHit) HeadCircle.Position = slider.Curve.PositionAt(progress); - foreach (var c in components.OfType()) c.UpdateProgress(progress, span); + foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); foreach (var t in components.OfType()) t.Tracking = Ball.Tracking; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 2fda299389..61db10b694 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -139,9 +139,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - public void UpdateProgress(double progress, int span) + public void UpdateProgress(double completionProgress) { - Position = slider.Curve.PositionAt(progress); + Position = slider.StackedPositionAt(completionProgress); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 89af67ba2a..fd63a3d954 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -167,8 +167,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return true; } - public void UpdateProgress(double progress, int span) + public void UpdateProgress(double completionProgress) { + var span = slider.SpanAt(completionProgress); + var spanProgress = slider.ProgressAt(completionProgress); + double start = 0; double end = snakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1; @@ -177,11 +180,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1) { start = 0; - end = snakingOut ? progress : 1; + end = snakingOut ? spanProgress : 1; } else { - start = snakingOut ? progress : 0; + start = snakingOut ? spanProgress : 0; } } diff --git a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs index 54f783b664..a0566eaf17 100644 --- a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs +++ b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs @@ -5,6 +5,10 @@ namespace osu.Game.Rulesets.Osu.Objects { public interface ISliderProgress { - void UpdateProgress(double progress, int span); + /// + /// Updates the progress of this element along the slider. + /// + /// Amount of the slider completed. + void UpdateProgress(double completionProgress); } } From f012cce6682568331c8b34f3a6b12c38722f7175 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Feb 2018 17:33:22 +0900 Subject: [PATCH 08/47] Rewrite some xmldocs to make methods easier to understand --- osu.Game/Rulesets/Objects/Types/IHasCurve.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs index 7f03854ea9..c03bdb240e 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs @@ -30,21 +30,19 @@ namespace osu.Game.Rulesets.Objects.Types public static class HasCurveExtensions { /// - /// Computes the position on the curve at a given progress, accounting for repeat logic. - /// - /// Ranges from [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - /// + /// Computes the position on the curve relative to how much of the has been completed. /// /// The curve. - /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. + /// [0, 1] where 0 is the start time of the and 1 is the end time of the . + /// The position on the curve. public static Vector2 PositionAt(this IHasCurve obj, double progress) => obj.Curve.PositionAt(obj.ProgressAt(progress)); /// - /// Finds the progress along the curve, accounting for repeat logic. + /// Computes the progress along the curve relative to how much of the has been completed. /// /// The curve. - /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. + /// [0, 1] where 0 is the start time of the and 1 is the end time of the . /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. public static double ProgressAt(this IHasCurve obj, double progress) { From f903e6d241035330d7c7cc1ffaa3df207695738f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Feb 2018 17:46:45 +0900 Subject: [PATCH 09/47] Fix stacking not working with sliders Fixes #2093. --- .../Objects/Drawables/DrawableRepeatPoint.cs | 7 ++++--- .../Objects/Drawables/DrawableSlider.cs | 6 +++--- osu.Game.Rulesets.Osu/Objects/Slider.cs | 12 ------------ osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs | 10 ++++++++-- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 79a4714e33..db704b0553 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -78,7 +78,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0; List curve = drawableSlider.Body.CurrentCurve; - Position = isRepeatAtEnd ? end : start; + var positionOnCurve = isRepeatAtEnd ? end : start; + Position = positionOnCurve + drawableSlider.HitObject.StackOffset; if (curve.Count < 2) return; @@ -89,10 +90,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // find the next vector2 in the curve which is not equal to our current position to infer a rotation. for (int i = searchStart; i >= 0 && i < curve.Count; i += direction) { - if (curve[i] == Position) + if (curve[i] == positionOnCurve) continue; - Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X)); + Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - positionOnCurve.Y, curve[i].X - positionOnCurve.X)); break; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 14650235c3..86b9706d9c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { var drawableTick = new DrawableSliderTick(tick) { - Position = tick.Position + Position = tick.StackedPosition }; ticks.Add(drawableTick); @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) { - Position = repeatPoint.Position + Position = repeatPoint.StackedPosition }; repeatPoints.Add(drawableRepeatPoint); @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. if (!HeadCircle.IsHit) - HeadCircle.Position = slider.Curve.PositionAt(progress); + HeadCircle.Position = slider.StackedPositionAt(completionProgress); foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 5dd3d7aa89..ce6c88a340 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -66,18 +66,6 @@ namespace osu.Game.Rulesets.Osu.Objects /// public double SpanDuration => Duration / this.SpanCount(); - private int stackHeight; - - public override int StackHeight - { - get { return stackHeight; } - set - { - stackHeight = value; - Curve.Offset = StackOffset; - } - } - public double Velocity; public double TickDistance; diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs index 55fa37882d..90a0a450a7 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs @@ -88,10 +88,15 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("Catmull Slider", () => testCatmull()); AddStep("Catmull Slider 1 Repeat", () => testCatmull(1)); AddStep("Catmull Slider 2 Repeats", () => testCatmull(2)); + + AddStep("Big Single, Large StackOffset", () => testSimpleBigLargeStackOffset()); + AddStep("Big 1 Repeat, Large StackOffset", () => testSimpleBigLargeStackOffset(1)); } private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats); + private void testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10); + private void testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats); private void testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats); @@ -104,7 +109,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15); - private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2) + private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2, int stackHeight = 0) { var slider = new Slider { @@ -118,7 +123,8 @@ namespace osu.Game.Rulesets.Osu.Tests }, Distance = distance, RepeatCount = repeats, - RepeatSamples = createEmptySamples(repeats) + RepeatSamples = createEmptySamples(repeats), + StackHeight = stackHeight }; addSlider(slider, circleSize, speedMultiplier); From 0d60a65c942a2e224188e5fe05620d621e3b7ac6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Feb 2018 17:51:34 +0900 Subject: [PATCH 10/47] Fix OsuAutoGenerator not considering stacking --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index a22ac6aed1..274f7bff62 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -315,11 +315,11 @@ namespace osu.Game.Rulesets.Osu.Replays for (double j = FrameDelay; j < s.Duration; j += FrameDelay) { - Vector2 pos = s.PositionAt(j / s.Duration); + Vector2 pos = s.StackedPositionAt(j / s.Duration); AddFrameToReplay(new ReplayFrame(h.StartTime + j, pos.X, pos.Y, button)); } - AddFrameToReplay(new ReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button)); + AddFrameToReplay(new ReplayFrame(s.EndTime, s.StackedEndPosition.X, s.StackedEndPosition.Y, button)); } // We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed! From 9cd9c83f2cc2c5947d7cc8aeb80e4111c2a26480 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Feb 2018 18:00:46 +0900 Subject: [PATCH 11/47] Allow changing accentcolour/bordercolour post-load --- osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 31ef1c05e6..96afc35ad6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return; accentColour = value; - if (LoadState == LoadState.Ready) + if (LoadState >= LoadState.Ready) Schedule(reloadTexture); } } @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return; borderColour = value; - if (LoadState == LoadState.Ready) + if (LoadState >= LoadState.Ready) Schedule(reloadTexture); } } From 08e52e8c153afd0e2cf73fd39e4afb756e850ed3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Feb 2018 18:02:52 +0900 Subject: [PATCH 12/47] Remove unnecessary schedules --- osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 96afc35ad6..0098ddc20b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces accentColour = value; if (LoadState >= LoadState.Ready) - Schedule(reloadTexture); + reloadTexture(); } } @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces borderColour = value; if (LoadState >= LoadState.Ready) - Schedule(reloadTexture); + reloadTexture(); } } From 6757be200723d143dbb8cf574c08408c3326ed52 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Feb 2018 20:15:42 +0900 Subject: [PATCH 13/47] Fix incorrect RelativeChildSize in Catch --- osu.Game/Rulesets/UI/ScalableContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/ScalableContainer.cs b/osu.Game/Rulesets/UI/ScalableContainer.cs index e1c1427470..43ed770f77 100644 --- a/osu.Game/Rulesets/UI/ScalableContainer.cs +++ b/osu.Game/Rulesets/UI/ScalableContainer.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.UI protected override void Update() { base.Update(); - RelativeChildSize = sizeScale; + RelativeChildSize = new Vector2(CustomWidth.HasValue ? sizeScale.X : RelativeChildSize.X, CustomHeight.HasValue ? sizeScale.Y : RelativeChildSize.Y); } } } From 5faec5c2f1b9e8cac6263124b59c60699136ea00 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 21 Feb 2018 19:52:36 +0900 Subject: [PATCH 14/47] Add a separate property to control playfield area --- .../UI/ManiaRulesetContainer.cs | 2 +- .../Edit/OsuEditRulesetContainer.cs | 6 +----- osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs | 2 +- .../UI/TaikoRulesetContainer.cs | 2 ++ osu.Game/Rulesets/UI/RulesetContainer.cs | 14 ++++++++++---- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index 3c9647117e..732d5f4109 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -101,7 +101,7 @@ namespace osu.Game.Rulesets.Mania.UI return null; } - protected override Vector2 GetAspectAdjustedSize() => new Vector2(1, 0.8f); + protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f); protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this); diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs index dd65cd470d..a8d895bc1d 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs @@ -18,11 +18,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected override Playfield CreatePlayfield() => new OsuEditPlayfield(); - protected override Vector2 GetAspectAdjustedSize() - { - var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y); - return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y); - } + protected override Vector2 PlayfieldArea => Vector2.One; protected override CursorContainer CreateCursor() => null; } diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index 9cb6a13cb2..2af381dd71 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override Vector2 GetAspectAdjustedSize() { var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y); - return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y) * 0.75f; + return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y); } protected override CursorContainer CreateCursor() => new GameplayCursor(); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index 8342009e80..fd31f738ee 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -88,6 +88,8 @@ namespace osu.Game.Rulesets.Taiko.UI return new Vector2(1, default_relative_height * aspectAdjust); } + protected override Vector2 PlayfieldArea => Vector2.One; + public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); protected override BeatmapConverter CreateBeatmapConverter() => new TaikoBeatmapConverter(IsForCurrentRuleset); diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index f4e700a8eb..05cb0f741b 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -319,7 +319,7 @@ namespace osu.Game.Rulesets.UI { base.Update(); - Playfield.Size = GetAspectAdjustedSize(); + Playfield.Size = GetAspectAdjustedSize() * PlayfieldArea; } /// @@ -330,11 +330,17 @@ namespace osu.Game.Rulesets.UI protected virtual BeatmapProcessor CreateBeatmapProcessor() => new BeatmapProcessor(); /// - /// Computes the final size of the in relative coordinate space after all - /// aspect and scale adjustments. + /// Computes the size of the in relative coordinate space after aspect adjustments. /// /// The aspect-adjusted size. - protected virtual Vector2 GetAspectAdjustedSize() => new Vector2(0.75f); // A sane default + protected virtual Vector2 GetAspectAdjustedSize() => Vector2.One; + + /// + /// The area of this that is available for the to use. + /// Must be specified in relative coordinate space to this . + /// This affects the final size of the but does not affect the 's scale. + /// + protected virtual Vector2 PlayfieldArea => new Vector2(0.75f); // A sane default /// /// Creates a converter to convert Beatmap to a specific mode. From 426343f1364efd05152dd81c89a8d1b0990e4793 Mon Sep 17 00:00:00 2001 From: tgi74000 Date: Wed, 21 Feb 2018 15:05:51 +0100 Subject: [PATCH 15/47] Fix legacy Taiko replays having reversed inputs --- .../Replays/TaikoAutoGenerator.cs | 20 +++++++++---------- .../Replays/TaikoFramedReplayInputHandler.cs | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 002159439d..4c0eafe25c 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -63,16 +63,16 @@ namespace osu.Game.Rulesets.Taiko.Replays { default: case 0: - button = ReplayButtonState.Left1; - break; - case 1: button = ReplayButtonState.Right1; break; + case 1: + button = ReplayButtonState.Left1; + break; case 2: - button = ReplayButtonState.Left2; + button = ReplayButtonState.Right2; break; case 3: - button = ReplayButtonState.Right2; + button = ReplayButtonState.Left2; break; } @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Taiko.Replays { foreach (var tick in drumRoll.NestedHitObjects.OfType()) { - Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2)); + Frames.Add(new TaikoReplayFrame(tick.StartTime, hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2)); hitButton = !hitButton; } } @@ -95,16 +95,16 @@ namespace osu.Game.Rulesets.Taiko.Replays if (hit is CentreHit) { if (h.IsStrong) - button = ReplayButtonState.Right1 | ReplayButtonState.Right2; + button = ReplayButtonState.Left1 | ReplayButtonState.Left2; else - button = hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2; + button = hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2; } else { if (h.IsStrong) - button = ReplayButtonState.Left1 | ReplayButtonState.Left2; + button = ReplayButtonState.Right1 | ReplayButtonState.Right2; else - button = hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2; + button = hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2; } Frames.Add(new TaikoReplayFrame(h.StartTime, button)); diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs index 05e10b6fce..1a96b26d34 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoFramedReplayInputHandler.cs @@ -19,13 +19,13 @@ namespace osu.Game.Rulesets.Taiko.Replays var actions = new List(); if (CurrentFrame?.MouseRight1 == true) - actions.Add(TaikoAction.LeftCentre); - if (CurrentFrame?.MouseRight2 == true) - actions.Add(TaikoAction.RightCentre); - if (CurrentFrame?.MouseLeft1 == true) actions.Add(TaikoAction.LeftRim); - if (CurrentFrame?.MouseLeft2 == true) + if (CurrentFrame?.MouseRight2 == true) actions.Add(TaikoAction.RightRim); + if (CurrentFrame?.MouseLeft1 == true) + actions.Add(TaikoAction.LeftCentre); + if (CurrentFrame?.MouseLeft2 == true) + actions.Add(TaikoAction.RightCentre); return new List { new ReplayState { PressedActions = actions } }; } From 7c942eb592d5e06b54e31828ae8cb0291318bcfd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Feb 2018 13:42:31 +0900 Subject: [PATCH 16/47] Tidy up layer container logic --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 41 +++++++++++---------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 62669150aa..1246127257 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using OpenTK.Graphics; using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -27,12 +26,11 @@ namespace osu.Game.Rulesets.Edit protected ICompositionTool CurrentTool { get; private set; } private RulesetContainer rulesetContainer; - private readonly Container[] layerContainers = new Container[2]; + private readonly List layerContainers = new List(); protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; - RelativeSizeAxes = Axes.Both; } @@ -42,6 +40,9 @@ namespace osu.Game.Rulesets.Edit try { rulesetContainer = CreateRulesetContainer(ruleset, osuGame.Beatmap.Value); + + // TODO: should probably be done at a RulesetContainer level to share logic with Player. + rulesetContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)osuGame.Beatmap.Value.Track ?? new StopwatchClock()); } catch (Exception e) { @@ -49,19 +50,13 @@ namespace osu.Game.Rulesets.Edit return; } - layerContainers[0] = CreateLayerContainer(); - layerContainers[0].Child = new Container + ScalableContainer createLayerContainerWithContent(Drawable content) { - Name = "Border", - RelativeSizeAxes = Axes.Both, - Masking = true, - BorderColour = Color4.White, - BorderThickness = 2, - Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true } - }; - - layerContainers[1] = CreateLayerContainer(); - layerContainers[1].Child = new SelectionLayer(rulesetContainer.Playfield); + var container = CreateLayerContainer(); + container.Child = content; + layerContainers.Add(container); + return container; + } RadioButtonCollection toolboxCollection; InternalChild = new GridContainer @@ -87,9 +82,17 @@ namespace osu.Game.Rulesets.Edit RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - layerContainers[0], + createLayerContainerWithContent(new Container + { + Name = "Border", + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderColour = Color4.White, + BorderThickness = 2, + Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true } + }), rulesetContainer, - layerContainers[1] + createLayerContainerWithContent(new SelectionLayer(rulesetContainer.Playfield)) } } }, @@ -100,8 +103,6 @@ namespace osu.Game.Rulesets.Edit } }; - rulesetContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)osuGame.Beatmap.Value.Track ?? new StopwatchClock()); - toolboxCollection.Items = new[] { new RadioButton("Select", () => setCompositionTool(null)) } .Concat( @@ -134,6 +135,6 @@ namespace osu.Game.Rulesets.Edit /// /// Creates a which provides a layer above or below the . /// - protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer(); + protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both }; } } From 3d4bed462b55f792ebfd8a66b4c5b00581334568 Mon Sep 17 00:00:00 2001 From: Joseph Madamba <35318437+Joehuu@users.noreply.github.com> Date: Wed, 21 Feb 2018 20:54:47 -0800 Subject: [PATCH 17/47] Reword sign in text --- .../Overlays/Settings/Sections/General/LoginSettings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index d959da52f3..a5d068adbd 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -208,7 +208,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { username = new OsuTextBox { - PlaceholderText = "Username", + PlaceholderText = "Email address", RelativeSizeAxes = Axes.X, Text = api?.Username ?? string.Empty, TabbableContentContainer = this @@ -222,12 +222,12 @@ namespace osu.Game.Overlays.Settings.Sections.General }, new SettingsCheckbox { - LabelText = "Remember username", + LabelText = "Remember email address", Bindable = config.GetBindable(OsuSetting.SaveUsername), }, new SettingsCheckbox { - LabelText = "Stay logged in", + LabelText = "Stay signed in", Bindable = config.GetBindable(OsuSetting.SavePassword), }, new SettingsButton @@ -237,7 +237,7 @@ namespace osu.Game.Overlays.Settings.Sections.General }, new SettingsButton { - Text = "Register new account", + Text = "Register", //Action = registerLink } }; From f2b3d9a0e89ad1bdbfba21e025f1bbd7f5ea5570 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Feb 2018 14:17:35 +0900 Subject: [PATCH 18/47] Change some missed instances --- osu.Game/Overlays/ChatOverlay.cs | 2 +- osu.Game/Screens/Select/Leaderboards/Leaderboard.cs | 2 +- osu.Game/Tests/Visual/TestCasePerformancePoints.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index beb2b3b746..7c6e563c5b 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -477,7 +477,7 @@ namespace osu.Game.Overlays if (!api.IsLoggedIn) { - target.AddNewMessages(new ErrorMessage("Please login to participate in chat!")); + target.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); return; } diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 6be6523175..273cceeeda 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -133,7 +133,7 @@ namespace osu.Game.Screens.Select.Leaderboards replacePlaceholder(new MessagePlaceholder(@"No records yet!")); break; case PlaceholderState.NotLoggedIn: - replacePlaceholder(new MessagePlaceholder(@"Please login to view online leaderboards!")); + replacePlaceholder(new MessagePlaceholder(@"Please sign in to view online leaderboards!")); break; case PlaceholderState.NotSupporter: replacePlaceholder(new MessagePlaceholder(@"Please invest in a supporter tag to view this leaderboard!")); diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index c531edb893..5b32433467 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -230,7 +230,7 @@ namespace osu.Game.Tests.Visual { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Text = "Please login to see online scores", + Text = "Please sign in to see online scores", }; } From c070c695229a0b8ec13b2f36743178edec0b5ceb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Feb 2018 15:01:53 +0900 Subject: [PATCH 19/47] Update framework --- osu-framework | 2 +- osu.Game/Overlays/Direct/PlayButton.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu-framework b/osu-framework index f6fa5b80ed..16a4bef775 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit f6fa5b80ed06f84c8fd25a2576eea8d51565785c +Subproject commit 16a4bef775a49166f38faa6e952d83d8823fe3e0 diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 1d67bc2d90..0fb988ead7 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -22,6 +22,7 @@ namespace osu.Game.Overlays.Direct public Track Preview { get; private set; } private BeatmapSetInfo beatmapSet; + public BeatmapSetInfo BeatmapSet { get { return beatmapSet; } @@ -199,8 +200,7 @@ namespace osu.Game.Overlays.Direct // add back the user's music volume setting (since we are no longer in the global TrackManager's hierarchy). config.BindWith(FrameworkSetting.VolumeMusic, trackManager.Volume); - if (!string.IsNullOrEmpty(preview)) - Preview = trackManager.Get(preview); + Preview = trackManager.Get(preview); } protected override void Dispose(bool isDisposing) From 4c14b32783ada771f7e7e75dec301688bf98713b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 15 Feb 2018 13:45:39 +0900 Subject: [PATCH 20/47] Add basic skin database model layout and importing --- osu.Game/Database/OsuDbContext.cs | 2 ++ osu.Game/OsuGame.cs | 2 ++ osu.Game/OsuGameBase.cs | 6 +++++ osu.Game/Skinning/SkinFileInfo.cs | 25 ++++++++++++++++++++ osu.Game/Skinning/SkinInfo.cs | 25 ++++++++++++++++++++ osu.Game/Skinning/SkinManager.cs | 39 +++++++++++++++++++++++++++++++ osu.Game/Skinning/SkinStore.cs | 22 +++++++++++++++++ osu.Game/osu.Game.csproj | 4 ++++ 8 files changed, 125 insertions(+) create mode 100644 osu.Game/Skinning/SkinFileInfo.cs create mode 100644 osu.Game/Skinning/SkinInfo.cs create mode 100644 osu.Game/Skinning/SkinManager.cs create mode 100644 osu.Game/Skinning/SkinStore.cs diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index e83b30595e..a4b0c30478 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -13,6 +13,7 @@ using osu.Game.IO; using osu.Game.Rulesets; using DatabasedKeyBinding = osu.Game.Input.Bindings.DatabasedKeyBinding; using LogLevel = Microsoft.Extensions.Logging.LogLevel; +using osu.Game.Skinning; namespace osu.Game.Database { @@ -26,6 +27,7 @@ namespace osu.Game.Database public DbSet DatabasedSetting { get; set; } public DbSet FileInfo { get; set; } public DbSet RulesetInfo { get; set; } + public DbSet SkinInfo { get; set; } private readonly string connectionString; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 14bc31aecf..15ee62c5e5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -187,7 +187,9 @@ namespace osu.Game CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; // hook up notifications to components. + SkinManager.PostNotification = n => notifications?.Post(n); BeatmapManager.PostNotification = n => notifications?.Post(n); + BeatmapManager.GetStableStorage = GetStorageForStableInstall; AddRange(new Drawable[] diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index b70055cc00..94ed696e49 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -30,6 +30,7 @@ using osu.Game.Input.Bindings; using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; namespace osu.Game { @@ -39,6 +40,8 @@ namespace osu.Game protected BeatmapManager BeatmapManager; + protected SkinManager SkinManager; + protected RulesetStore RulesetStore; protected FileStore FileStore; @@ -103,6 +106,8 @@ namespace osu.Game runMigrations(); + dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host)); + dependencies.Cache(API = new APIAccess { Username = LocalConfig.Get(OsuSetting.Username), @@ -120,6 +125,7 @@ namespace osu.Game fileImporters.Add(BeatmapManager); fileImporters.Add(ScoreStore); + fileImporters.Add(SkinManager); //this completely overrides the framework default. will need to change once we make a proper FontStore. dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 }); diff --git a/osu.Game/Skinning/SkinFileInfo.cs b/osu.Game/Skinning/SkinFileInfo.cs new file mode 100644 index 0000000000..e8caf8f44a --- /dev/null +++ b/osu.Game/Skinning/SkinFileInfo.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using osu.Game.Database; +using osu.Game.IO; + +namespace osu.Game.Skinning +{ + public class SkinFileInfo : INamedFileInfo + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int ID { get; set; } + + public int SkinInfoID { get; set; } + + public int FileInfoID { get; set; } + + public FileInfo FileInfo { get; set; } + + [Required] + public string Filename { get; set; } + } +} diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs new file mode 100644 index 0000000000..ee9f63bec9 --- /dev/null +++ b/osu.Game/Skinning/SkinInfo.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using osu.Game.Database; + +namespace osu.Game.Skinning +{ + public class SkinInfo : IHasFiles, IHasPrimaryKey, ISoftDelete + { + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int ID { get; set; } + + public string Name { get; set; } + + public string Creator { get; set; } + + public List Files { get; set; } + + public bool DeletePending { get; set; } + + public static SkinInfo Default { get; } = new SkinInfo { Name = "osu!lazer", Creator = "team osu!" }; + } +} diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs new file mode 100644 index 0000000000..ac3347e1d0 --- /dev/null +++ b/osu.Game/Skinning/SkinManager.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.Collections.Generic; +using System.Linq; +using osu.Framework.Configuration; +using osu.Framework.Platform; +using osu.Game.Database; +using osu.Game.IO.Archives; + +namespace osu.Game.Skinning +{ + public class SkinManager : ArchiveModelManager + { + public readonly Bindable CurrentSkinInfo = new Bindable(SkinInfo.Default) { Default = SkinInfo.Default }; + + public override string[] HandledExtensions => new[] { ".osk" }; + + /// + /// Returns a list of all usable s. + /// + /// A list of available . + public List GetAllUsableSkins() + { + var userSkins = ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); + userSkins.Insert(0, SkinInfo.Default); + return userSkins; + } + + protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name }; + + private SkinStore store; + + public SkinManager(Storage storage, DatabaseContextFactory contextFactory, IIpcHost importHost) + : base(storage, contextFactory, new SkinStore(contextFactory, storage), importHost) + { + } + } +} diff --git a/osu.Game/Skinning/SkinStore.cs b/osu.Game/Skinning/SkinStore.cs new file mode 100644 index 0000000000..ffd9873901 --- /dev/null +++ b/osu.Game/Skinning/SkinStore.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using Microsoft.EntityFrameworkCore; +using osu.Framework.Platform; +using osu.Game.Database; + +namespace osu.Game.Skinning +{ + public class SkinStore : MutableDatabaseBackedStore + { + public SkinStore(DatabaseContextFactory contextFactory, Storage storage = null) + : base(contextFactory, storage) + { + } + + protected override IQueryable AddIncludesForConsumption(IQueryable query) => + base.AddIncludesForConsumption(query) + .Include(s => s.Files).ThenInclude(f => f.FileInfo); + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index afeb791029..4b1dad28db 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -850,6 +850,10 @@ + + + + From 2351b6ab269d7d4623dd9817ecbaf6b4571c691b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Feb 2018 15:44:13 +0900 Subject: [PATCH 21/47] Add migration --- .../20180219060912_AddSkins.Designer.cs | 379 ++++++++++++++++++ .../Migrations/20180219060912_AddSkins.cs | 73 ++++ .../Migrations/OsuDbContextModelSnapshot.cs | 50 +++ osu.Game/osu.Game.csproj | 4 + 4 files changed, 506 insertions(+) create mode 100644 osu.Game/Migrations/20180219060912_AddSkins.Designer.cs create mode 100644 osu.Game/Migrations/20180219060912_AddSkins.cs diff --git a/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs b/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs new file mode 100644 index 0000000000..83b8d6cf8a --- /dev/null +++ b/osu.Game/Migrations/20180219060912_AddSkins.Designer.cs @@ -0,0 +1,379 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using osu.Game.Database; +using System; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20180219060912_AddSkins")] + partial class AddSkins + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MD5Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntKey") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20180219060912_AddSkins.cs b/osu.Game/Migrations/20180219060912_AddSkins.cs new file mode 100644 index 0000000000..741fcf4079 --- /dev/null +++ b/osu.Game/Migrations/20180219060912_AddSkins.cs @@ -0,0 +1,73 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace osu.Game.Migrations +{ + public partial class AddSkins : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "SkinInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Creator = table.Column(type: "TEXT", nullable: true), + DeletePending = table.Column(type: "INTEGER", nullable: false), + Name = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_SkinInfo", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "SkinFileInfo", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + FileInfoID = table.Column(type: "INTEGER", nullable: false), + Filename = table.Column(type: "TEXT", nullable: false), + SkinInfoID = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SkinFileInfo", x => x.ID); + table.ForeignKey( + name: "FK_SkinFileInfo_FileInfo_FileInfoID", + column: x => x.FileInfoID, + principalTable: "FileInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_SkinFileInfo_SkinInfo_SkinInfoID", + column: x => x.SkinInfoID, + principalTable: "SkinInfo", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_SkinFileInfo_FileInfoID", + table: "SkinFileInfo", + column: "FileInfoID"); + + migrationBuilder.CreateIndex( + name: "IX_SkinFileInfo_SkinInfoID", + table: "SkinFileInfo", + column: "SkinInfoID"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SkinFileInfo"); + + migrationBuilder.DropTable( + name: "SkinInfo"); + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 157125102f..1627627790 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -281,6 +281,43 @@ namespace osu.Game.Migrations b.ToTable("RulesetInfo"); }); + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.ToTable("SkinInfo"); + }); + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => { b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") @@ -322,6 +359,19 @@ namespace osu.Game.Migrations .WithMany("BeatmapSets") .HasForeignKey("MetadataID"); }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); #pragma warning restore 612, 618 } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4b1dad28db..829addc360 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -322,6 +322,10 @@ 20171209034410_AddRulesetInfoShortName.cs + + + 20180219060912_AddSkins.cs + From 659cf629b6d62e614bc5bc87eefcaf6075b78182 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Feb 2018 15:44:25 +0900 Subject: [PATCH 22/47] Add skin seleciton dropdown to settings --- .../Overlays/Settings/Sections/SkinSection.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index f6915896d7..1cd1cd0f7f 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -1,26 +1,33 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using osu.Game.Skinning; using OpenTK; namespace osu.Game.Overlays.Settings.Sections { public class SkinSection : SettingsSection { + private SettingsDropdown skinDropdown; + public override string Header => "Skin"; + public override FontAwesome Icon => FontAwesome.fa_paint_brush; [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load(OsuConfigManager config, SkinManager skins) { FlowContent.Spacing = new Vector2(0, 5); Children = new Drawable[] { + skinDropdown = new SettingsDropdown(), new SettingsSlider { LabelText = "Menu cursor size", @@ -39,6 +46,14 @@ namespace osu.Game.Overlays.Settings.Sections Bindable = config.GetBindable(OsuSetting.AutoCursorSize) }, }; + + void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.Name, s)); + skins.ItemAdded += _ => reloadSkins(); + skins.ItemRemoved += _ => reloadSkins(); + + reloadSkins(); + + skinDropdown.Bindable = skins.CurrentSkinInfo; } private class SizeSlider : OsuSliderBar From 402d71a8d9677b08f61130b5c0b269925f3cc772 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Feb 2018 16:29:05 +0900 Subject: [PATCH 23/47] Add user skin setting storage --- osu.Game/Configuration/OsuConfigManager.cs | 5 ++++- osu.Game/Database/ArchiveModelManager.cs | 2 +- osu.Game/OsuGame.cs | 11 +++++++++++ osu.Game/Overlays/Settings/Sections/SkinSection.cs | 8 ++++---- osu.Game/Skinning/SkinInfo.cs | 5 ++++- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index c33dd91330..3d927ef67c 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -14,6 +14,8 @@ namespace osu.Game.Configuration { // UI/selection defaults Set(OsuSetting.Ruleset, 0, 0, int.MaxValue); + Set(OsuSetting.Skin, 0, 0, int.MaxValue); + Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details); Set(OsuSetting.ShowConvertedBeatmaps, true); @@ -122,6 +124,7 @@ namespace osu.Game.Configuration ChatDisplayHeight, Version, ShowConvertedBeatmaps, - SpeedChangeVisualisation + SpeedChangeVisualisation, + Skin } } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index a65593ff82..854bee99a5 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -48,7 +48,7 @@ namespace osu.Game.Database protected readonly IDatabaseContextFactory ContextFactory; - protected readonly MutableDatabaseBackedStore ModelStore; + public readonly MutableDatabaseBackedStore ModelStore; // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ArchiveImportIPCChannel ipc; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 15ee62c5e5..17a72d3c87 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -30,6 +30,7 @@ using osu.Game.Rulesets; using osu.Game.Screens.Play; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; +using osu.Game.Skinning; using OpenTK.Graphics; namespace osu.Game @@ -79,6 +80,8 @@ namespace osu.Game private Bindable configRuleset; public Bindable Ruleset = new Bindable(); + private Bindable configSkin; + private readonly string[] args; private SettingsOverlay settings; @@ -122,10 +125,18 @@ namespace osu.Game dependencies.CacheAs(this); + // bind config int to database RulesetInfo configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset); Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First(); Ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0; + // bind config int to database SkinInfo + configSkin = LocalConfig.GetBindable(OsuSetting.Skin); + + SkinManager.CurrentSkinInfo.ValueChanged += s => configSkin.Value = s.ID; + configSkin.ValueChanged += id => SkinManager.CurrentSkinInfo.Value = SkinManager.ModelStore.ConsumableItems.FirstOrDefault(s => s.ID == id) ?? SkinInfo.Default; + configSkin.TriggerChange(); + LocalConfig.BindWith(OsuSetting.VolumeInactive, inactiveVolumeAdjust); } diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 1cd1cd0f7f..bc0b8b4aaa 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections { public class SkinSection : SettingsSection { - private SettingsDropdown skinDropdown; + private SettingsDropdown skinDropdown; public override string Header => "Skin"; @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections FlowContent.Spacing = new Vector2(0, 5); Children = new Drawable[] { - skinDropdown = new SettingsDropdown(), + skinDropdown = new SettingsDropdown(), new SettingsSlider { LabelText = "Menu cursor size", @@ -47,13 +47,13 @@ namespace osu.Game.Overlays.Settings.Sections }, }; - void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.Name, s)); + void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.Name, s.ID)); skins.ItemAdded += _ => reloadSkins(); skins.ItemRemoved += _ => reloadSkins(); reloadSkins(); - skinDropdown.Bindable = skins.CurrentSkinInfo; + skinDropdown.Bindable = config.GetBindable(OsuSetting.Skin); } private class SizeSlider : OsuSliderBar diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index ee9f63bec9..45c8b97f63 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -1,13 +1,14 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using osu.Game.Database; namespace osu.Game.Skinning { - public class SkinInfo : IHasFiles, IHasPrimaryKey, ISoftDelete + public class SkinInfo : IHasFiles, IEquatable, IHasPrimaryKey, ISoftDelete { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } @@ -21,5 +22,7 @@ namespace osu.Game.Skinning public bool DeletePending { get; set; } public static SkinInfo Default { get; } = new SkinInfo { Name = "osu!lazer", Creator = "team osu!" }; + + public bool Equals(SkinInfo other) => other != null && ID == other.ID; } } From e9c583438761b00e2b0332c4198a53afe45df946 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Feb 2018 13:22:33 +0900 Subject: [PATCH 24/47] Add query method for now --- osu.Game/Database/ArchiveModelManager.cs | 2 +- osu.Game/OsuGame.cs | 2 +- osu.Game/Skinning/SkinManager.cs | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 854bee99a5..a65593ff82 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -48,7 +48,7 @@ namespace osu.Game.Database protected readonly IDatabaseContextFactory ContextFactory; - public readonly MutableDatabaseBackedStore ModelStore; + protected readonly MutableDatabaseBackedStore ModelStore; // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ArchiveImportIPCChannel ipc; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 17a72d3c87..95eb88c5c8 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -134,7 +134,7 @@ namespace osu.Game configSkin = LocalConfig.GetBindable(OsuSetting.Skin); SkinManager.CurrentSkinInfo.ValueChanged += s => configSkin.Value = s.ID; - configSkin.ValueChanged += id => SkinManager.CurrentSkinInfo.Value = SkinManager.ModelStore.ConsumableItems.FirstOrDefault(s => s.ID == id) ?? SkinInfo.Default; + configSkin.ValueChanged += id => SkinManager.CurrentSkinInfo.Value = SkinManager.Query(s => s.ID == id) ?? SkinInfo.Default; configSkin.TriggerChange(); LocalConfig.BindWith(OsuSetting.VolumeInactive, inactiveVolumeAdjust); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index ac3347e1d0..0031968b2b 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; using osu.Framework.Configuration; using osu.Framework.Platform; using osu.Game.Database; @@ -35,5 +38,12 @@ namespace osu.Game.Skinning : base(storage, contextFactory, new SkinStore(contextFactory, storage), importHost) { } + + /// + /// Perform a lookup query on available s. + /// + /// The query. + /// The first result for the provided query, or null if no results were found. + public SkinInfo Query(Expression> query) => ModelStore.ConsumableItems.AsNoTracking().FirstOrDefault(query); } } From 3726db53b5537a90adfb85b108fd35e2064d5b49 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Feb 2018 17:16:48 +0900 Subject: [PATCH 25/47] Allow instantiation of skins --- osu.Game/OsuGameBase.cs | 4 +-- osu.Game/Skinning/DefaultSkin.cs | 17 +++++++++++ osu.Game/Skinning/LegacySkin.cs | 49 ++++++++++++++++++++++++++++++++ osu.Game/Skinning/Skin.cs | 22 ++++++++++++++ osu.Game/Skinning/SkinManager.cs | 34 ++++++++++++++++++++-- osu.Game/osu.Game.csproj | 5 +++- 6 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Skinning/DefaultSkin.cs create mode 100644 osu.Game/Skinning/LegacySkin.cs create mode 100644 osu.Game/Skinning/Skin.cs diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 94ed696e49..f3c46269d5 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . +// Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; @@ -106,7 +106,7 @@ namespace osu.Game runMigrations(); - dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host)); + dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); dependencies.Cache(API = new APIAccess { diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs new file mode 100644 index 0000000000..794ed58fca --- /dev/null +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -0,0 +1,17 @@ +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; + +namespace osu.Game.Skinning +{ + public class DefaultSkin : Skin + { + public DefaultSkin() + : base(SkinInfo.Default) + { + } + + public override Drawable GetDrawableComponent(string componentName) => null; + + public override SampleChannel GetSample(string sampleName) => null; + } +} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs new file mode 100644 index 0000000000..1c56a64048 --- /dev/null +++ b/osu.Game/Skinning/LegacySkin.cs @@ -0,0 +1,49 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using System.Linq; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; + +namespace osu.Game.Skinning +{ + public class LegacySkin : Skin + { + private readonly TextureStore textures; + + private readonly SampleManager samples; + + public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) + : base(skin) + { + var audioStore = new ResourceStore(storage); + + samples = audioManager.GetSampleManager(audioStore); + textures = new TextureStore(new RawTextureLoaderStore(storage)); + } + + private string getPathForFile(string filename) => + SkinInfo.Files.FirstOrDefault(f => string.Equals(Path.GetFileNameWithoutExtension(f.Filename), filename, StringComparison.InvariantCultureIgnoreCase))?.FileInfo.StoragePath; + + public override Drawable GetDrawableComponent(string componentName) + { + var texture = textures.Get(getPathForFile(componentName.Split('/').Last())); + if (texture == null) return null; + + return new Sprite + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Texture = texture, + }; + } + + public override SampleChannel GetSample(string sampleName) => samples.Get(getPathForFile(sampleName.Split('/').Last())); + } +} diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs new file mode 100644 index 0000000000..fafbdec8f0 --- /dev/null +++ b/osu.Game/Skinning/Skin.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; + +namespace osu.Game.Skinning +{ + public abstract class Skin + { + public readonly SkinInfo SkinInfo; + + public abstract Drawable GetDrawableComponent(string componentName); + + public abstract SampleChannel GetSample(string sampleName); + + protected Skin(SkinInfo skin) + { + SkinInfo = skin; + } + } +} diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 0031968b2b..12e34ec0a0 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . +// Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; +using osu.Framework.Audio; using osu.Framework.Configuration; using osu.Framework.Platform; using osu.Game.Database; @@ -15,6 +16,9 @@ namespace osu.Game.Skinning { public class SkinManager : ArchiveModelManager { + private readonly AudioManager audio; + + public readonly Bindable CurrentSkin = new Bindable(new DefaultSkin()); public readonly Bindable CurrentSkinInfo = new Bindable(SkinInfo.Default) { Default = SkinInfo.Default }; public override string[] HandledExtensions => new[] { ".osk" }; @@ -30,13 +34,37 @@ namespace osu.Game.Skinning return userSkins; } - protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name }; + protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo + { + Name = archive.Name + }; + + /// + /// Retrieve a instance for the provided + /// + /// The skin to lookup. + /// A instance correlating to the provided . + public Skin GetSkin(SkinInfo skinInfo) + { + if (skinInfo == SkinInfo.Default) + return new DefaultSkin(); + + return new LegacySkin(skinInfo, Files.Store, audio); + } private SkinStore store; - public SkinManager(Storage storage, DatabaseContextFactory contextFactory, IIpcHost importHost) + public SkinManager(Storage storage, DatabaseContextFactory contextFactory, IIpcHost importHost, AudioManager audio) : base(storage, contextFactory, new SkinStore(contextFactory, storage), importHost) { + this.audio = audio; + + CurrentSkinInfo.ValueChanged += info => { CurrentSkin.Value = GetSkin(info); }; + CurrentSkin.ValueChanged += skin => + { + if (skin.SkinInfo != CurrentSkinInfo.Value) + throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} isntead."); + }; } /// diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 829addc360..dc5914a76f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,4 +1,4 @@ - + @@ -854,6 +854,9 @@ + + + From 84b707f4f8edd74c5c5f372c181cfccf679a15f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Feb 2018 17:33:47 +0900 Subject: [PATCH 26/47] Add basic hitcircle skinning --- .../Drawables/Pieces/ApproachCircle.cs | 12 +--- .../Objects/Drawables/Pieces/CirclePiece.cs | 27 +-------- .../Drawables/Pieces/DefaultCirclePiece.cs | 35 +++++++++++ .../Objects/Drawables/Pieces/GlowPiece.cs | 24 ++++---- .../Objects/Drawables/Pieces/NumberPiece.cs | 10 ++-- .../Objects/Drawables/Pieces/RingPiece.cs | 27 +++++---- .../osu.Game.Rulesets.Osu.csproj | 1 + osu.Game/Skinning/SkinnableDrawable.cs | 58 +++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 9 files changed, 129 insertions(+), 66 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs create mode 100644 osu.Game/Skinning/SkinnableDrawable.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs index 61e9027157..51f8b7026a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ApproachCircle.cs @@ -6,30 +6,24 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class ApproachCircle : Container { - private readonly Sprite approachCircle; - public ApproachCircle() { Anchor = Anchor.Centre; Origin = Anchor.Centre; - AutoSizeAxes = Axes.Both; - - Children = new Drawable[] - { - approachCircle = new Sprite() - }; + RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] private void load(TextureStore textures) { - approachCircle.Texture = textures.Get(@"Play/osu/approachcircle"); + Child = new SkinnableDrawable("Play/osu/approachcircle", name => new Sprite { Texture = textures.Get(name) }); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index 286df14056..e7b6598cf2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -2,20 +2,16 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; +using osu.Game.Skinning; using OpenTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class CirclePiece : Container, IKeyBindingHandler { - private readonly Sprite disc; - public Func Hit; public CirclePiece() @@ -27,26 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Anchor = Anchor.Centre; Origin = Anchor.Centre; - Children = new Drawable[] - { - disc = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }, - new TrianglesPiece - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, - Alpha = 0.5f, - } - }; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - disc.Texture = textures.Get(@"Play/osu/disc"); + InternalChild = new SkinnableDrawable("Play/osu/hitcircle", _ => new DefaultCirclePiece()); } public bool OnPressed(OsuAction action) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs new file mode 100644 index 0000000000..61f73b6d66 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultCirclePiece.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class DefaultCirclePiece : Container + { + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + RelativeSizeAxes = Axes.Both; + Children = new Drawable[] + { + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = textures.Get(@"Play/osu/disc"), + }, + new TrianglesPiece + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingMode.Additive, + Alpha = 0.5f, + } + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs index 9a1208f998..a4e1916659 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/GlowPiece.cs @@ -6,34 +6,30 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class GlowPiece : Container { - private readonly Sprite layer; - public GlowPiece() { Anchor = Anchor.Centre; Origin = Anchor.Centre; - - Children = new[] - { - layer = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Blending = BlendingMode.Additive, - Alpha = 0.5f - } - }; + RelativeSizeAxes = Axes.Both; } [BackgroundDependencyLoader] private void load(TextureStore textures) { - layer.Texture = textures.Get(@"Play/osu/ring-glow"); + Child = new SkinnableDrawable("Play/osu/ring-glow", name => new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = textures.Get(name), + Blending = BlendingMode.Additive, + Alpha = 0.5f + }, false); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs index afbf00f320..4220299c66 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -28,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Children = new Drawable[] { - new CircularContainer + new SkinnableDrawable("Play/osu/number-glow", name => new CircularContainer { Masking = true, Origin = Anchor.Centre, @@ -38,11 +39,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Radius = 60, Colour = Color4.White.Opacity(0.5f), }, - Children = new[] - { - new Box() - } - }, + Child = new Box() + }, false), number = new OsuSpriteText { Text = @"1", diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs index 2347927f2e..12cc0dc5d9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/RingPiece.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics.Containers; using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -15,24 +16,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { Size = new Vector2(128); - Masking = true; - CornerRadius = Size.X / 2; - Anchor = Anchor.Centre; Origin = Anchor.Centre; - BorderThickness = 10; - BorderColour = Color4.White; - - Children = new Drawable[] + InternalChild = new SkinnableDrawable("Play/osu/hitcircleoverlay", _ => new Container { - new Box + Masking = true, + CornerRadius = Size.X / 2, + BorderThickness = 10, + BorderColour = Color4.White, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - AlwaysPresent = true, - Alpha = 0, - RelativeSizeAxes = Axes.Both + new Box + { + AlwaysPresent = true, + Alpha = 0, + RelativeSizeAxes = Axes.Both + } } - }; + }); } } } diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 7838fb7707..04903d11bf 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -92,6 +92,7 @@ + diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs new file mode 100644 index 0000000000..7310d1e476 --- /dev/null +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -0,0 +1,58 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Skinning +{ + public class SkinnableDrawable : SkinnableDrawable + { + public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true) + : base(name, defaultImplementation, fallback) + { + RelativeSizeAxes = Axes.Both; + } + } + + public class SkinnableDrawable : CompositeDrawable + where T : Drawable + { + private Bindable skin; + protected Func CreateDefault; + + public string ComponentName { get; set; } + + public readonly bool DefaultFallback; + + public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true) + { + DefaultFallback = fallback; + ComponentName = name; + CreateDefault = defaultImplementation; + } + + [BackgroundDependencyLoader] + private void load(SkinManager skinManager) + { + skin = skinManager.CurrentSkin.GetBoundCopy(); + skin.ValueChanged += updateComponent; + skin.TriggerChange(); + } + + private void updateComponent(Skin skin) + { + var drawable = skin.GetDrawableComponent(ComponentName); + if (drawable == null && (DefaultFallback || skin.SkinInfo == SkinInfo.Default)) + drawable = CreateDefault(ComponentName); + + if (drawable != null) + InternalChild = drawable; + else + ClearInternal(); + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index dc5914a76f..f61a2a8dc6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -860,6 +860,7 @@ + From 6ceabfe19ebed5b433cc3ec9d15b439164826ca6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 22 Feb 2018 17:34:35 +0900 Subject: [PATCH 27/47] Add basic hitsound skinning --- .../Audio/DrumSampleMapping.cs | 4 +-- osu.Game/Audio/SampleInfo.cs | 6 ++-- .../Objects/Drawables/DrawableHitObject.cs | 34 ++++++++++++------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs index 5493a5029b..85367b8bf6 100644 --- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs +++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs @@ -29,8 +29,8 @@ namespace osu.Game.Rulesets.Taiko.Audio { mappings[s.Time] = new DrumSample { - Centre = s.GetSampleInfo().GetChannel(audio.Sample, "Taiko"), - Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample, "Taiko") + Centre = s.GetSampleInfo().GetChannel(audio.Sample.Get, "Taiko"), + Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample.Get, "Taiko") }; } } diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index e6f4a0b8d1..99d2da7ebc 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -14,16 +14,16 @@ namespace osu.Game.Audio public const string HIT_NORMAL = @"hitnormal"; public const string HIT_CLAP = @"hitclap"; - public SampleChannel GetChannel(SampleManager manager, string resourceNamespace = null) + public SampleChannel GetChannel(Func getChannel, string resourceNamespace = null) { SampleChannel channel = null; if (resourceNamespace != null) - channel = manager.Get($"Gameplay/{resourceNamespace}/{Bank}-{Name}"); + channel = getChannel($"Gameplay/{resourceNamespace}/{Bank}-{Name}"); // try without namespace as a fallback. if (channel == null) - channel = manager.Get($"Gameplay/{Bank}-{Name}"); + channel = getChannel($"Gameplay/{Bank}-{Name}"); if (channel != null) channel.Volume.Value = Volume / 100.0; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 2db02724ed..2ecdccc31f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -17,6 +17,7 @@ using osu.Framework.Configuration; using OpenTK; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Scoring; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Objects.Drawables { @@ -82,8 +83,10 @@ namespace osu.Game.Rulesets.Objects.Drawables HitObject = hitObject; } + private readonly Bindable skin = new Bindable(); + [BackgroundDependencyLoader] - private void load(AudioManager audio) + private void load(AudioManager audio, SkinManager skins) { var samples = GetSamples(); if (samples.Any()) @@ -91,23 +94,30 @@ namespace osu.Game.Rulesets.Objects.Drawables if (HitObject.SampleControlPoint == null) throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}." + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}."); - - foreach (SampleInfo s in samples) + void loadSamples(Skin skin) { - SampleInfo localSampleInfo = new SampleInfo + Samples.Clear(); + + foreach (SampleInfo s in samples) { - Bank = s.Bank ?? HitObject.SampleControlPoint.SampleBank, - Name = s.Name, - Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume - }; + SampleInfo localSampleInfo = new SampleInfo + { + Bank = s.Bank ?? HitObject.SampleControlPoint.SampleBank, + Name = s.Name, + Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume + }; - SampleChannel channel = localSampleInfo.GetChannel(audio.Sample, SampleNamespace); - if (channel == null) - continue; + SampleChannel channel = localSampleInfo.GetChannel(skin.GetSample, SampleNamespace) ?? localSampleInfo.GetChannel(audio.Sample.Get, SampleNamespace); - Samples.Add(channel); + if (channel == null) return; + + Samples.Add(channel); + } } + + skin.ValueChanged += loadSamples; + skin.BindTo(skins.CurrentSkin); } } From 92d20eea8cb5d2cc7165847ba71891579f91ffc9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Feb 2018 12:35:25 +0900 Subject: [PATCH 28/47] Add missing licence header --- osu.Game/Skinning/DefaultSkin.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/DefaultSkin.cs b/osu.Game/Skinning/DefaultSkin.cs index 794ed58fca..e40a43d400 100644 --- a/osu.Game/Skinning/DefaultSkin.cs +++ b/osu.Game/Skinning/DefaultSkin.cs @@ -1,4 +1,7 @@ -using osu.Framework.Audio.Sample; +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; namespace osu.Game.Skinning @@ -10,8 +13,14 @@ namespace osu.Game.Skinning { } - public override Drawable GetDrawableComponent(string componentName) => null; + public override Drawable GetDrawableComponent(string componentName) + { + return null; + } - public override SampleChannel GetSample(string sampleName) => null; + public override SampleChannel GetSample(string sampleName) + { + return null; + } } } From f81eb2de65cf6f162e69836b1a0c0ccca0584f9a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Feb 2018 14:24:32 +0900 Subject: [PATCH 29/47] Remove unnecessary local store --- osu.Game/Skinning/LegacySkin.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 1c56a64048..5f34ddc2b5 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -22,9 +22,7 @@ namespace osu.Game.Skinning public LegacySkin(SkinInfo skin, IResourceStore storage, AudioManager audioManager) : base(skin) { - var audioStore = new ResourceStore(storage); - - samples = audioManager.GetSampleManager(audioStore); + samples = audioManager.GetSampleManager(storage); textures = new TextureStore(new RawTextureLoaderStore(storage)); } From ff75cf6b7596b6d5f5cd5c0ae9103a922a51cc07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Feb 2018 14:27:39 +0900 Subject: [PATCH 30/47] Remove unnecessary braces --- osu.Game/Skinning/SkinManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 12e34ec0a0..7235ccc46e 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . +// Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; @@ -59,7 +59,7 @@ namespace osu.Game.Skinning { this.audio = audio; - CurrentSkinInfo.ValueChanged += info => { CurrentSkin.Value = GetSkin(info); }; + CurrentSkinInfo.ValueChanged += info => CurrentSkin.Value = GetSkin(info); CurrentSkin.ValueChanged += skin => { if (skin.SkinInfo != CurrentSkinInfo.Value) From 0e20c4e6bbbe71d2582080588c0718f0532f0d12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Feb 2018 14:27:53 +0900 Subject: [PATCH 31/47] Fix typo --- osu.Game/Skinning/SkinManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 7235ccc46e..88d51eca10 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -63,7 +63,7 @@ namespace osu.Game.Skinning CurrentSkin.ValueChanged += skin => { if (skin.SkinInfo != CurrentSkinInfo.Value) - throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} isntead."); + throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead."); }; } From a36986ef5af94df2f50ed2232e44a71484fe1165 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Feb 2018 14:28:57 +0900 Subject: [PATCH 32/47] Make ComponentName readonly --- osu.Game/Skinning/SkinnableDrawable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index 7310d1e476..c1c78fdb05 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -24,7 +24,7 @@ namespace osu.Game.Skinning private Bindable skin; protected Func CreateDefault; - public string ComponentName { get; set; } + public readonly string ComponentName; public readonly bool DefaultFallback; From 3fdb6845da70be4047b821fe7a2e5e2a81bfb16a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Feb 2018 14:40:25 +0900 Subject: [PATCH 33/47] Revert unneeded change This was already correct as per osu-stable. --- osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index 4c0eafe25c..cb45ce2dce 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -63,16 +63,16 @@ namespace osu.Game.Rulesets.Taiko.Replays { default: case 0: - button = ReplayButtonState.Right1; - break; - case 1: button = ReplayButtonState.Left1; break; + case 1: + button = ReplayButtonState.Right1; + break; case 2: - button = ReplayButtonState.Right2; + button = ReplayButtonState.Left2; break; case 3: - button = ReplayButtonState.Left2; + button = ReplayButtonState.Right2; break; } From 08bb25347c8746736dd81b299cc18d9d58272011 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Feb 2018 20:27:05 +0900 Subject: [PATCH 34/47] Make DrawableSlider contain the slider body --- .../Objects/Drawables/DrawableRepeatPoint.cs | 2 +- .../Objects/Drawables/DrawableSlider.cs | 19 +- .../Objects/Drawables/DrawableSliderTail.cs | 6 +- .../Objects/Drawables/Pieces/SliderBody.cs | 37 +++- osu.Game.Rulesets.Osu/Objects/Slider.cs | 14 +- .../Tests/TestCaseNewSliderBody.cs | 171 ++++++++++++++++++ .../osu.Game.Rulesets.Osu.csproj | 1 + 7 files changed, 232 insertions(+), 18 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index db704b0553..3e1b64bb86 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables List curve = drawableSlider.Body.CurrentCurve; var positionOnCurve = isRepeatAtEnd ? end : start; - Position = positionOnCurve + drawableSlider.HitObject.StackOffset; + Position = positionOnCurve - curve[0] + drawableSlider.HitObject.StackOffset; if (curve.Count < 2) return; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 391e0ff023..866631468a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { slider = s; + Position = s.StackedPosition; + DrawableSliderTail tail; Container ticks; Container repeatPoints; @@ -39,20 +41,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Body = new SliderBody(s) { AccentColour = AccentColour, - Position = s.StackedPosition, PathWidth = s.Scale * 64, }, - ticks = new Container(), - repeatPoints = new Container(), + ticks = new Container { RelativeSizeAxes = Axes.Both }, + repeatPoints = new Container { RelativeSizeAxes = Axes.Both }, Ball = new SliderBall(s) { + BypassAutoSizeAxes = Axes.Both, Scale = new Vector2(s.Scale), AccentColour = AccentColour, AlwaysPresent = true, Alpha = 0 }, - HeadCircle = new DrawableHitCircle(s.HeadCircle), - tail = new DrawableSliderTail(s.TailCircle) + HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.HeadCircle.StackedPosition }, + tail = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.StackedPosition } }; components.Add(Body); @@ -112,6 +114,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); foreach (var t in components.OfType()) t.Tracking = Ball.Tracking; + + Size = Body.Size; + OriginPosition = Body.PathOffset; + + foreach (var obj in NestedHitObjects) + obj.RelativeAnchorPosition = Vector2.Divide(OriginPosition, Body.DrawSize); + Ball.RelativeAnchorPosition = Vector2.Divide(OriginPosition, Body.DrawSize); } protected override void CheckForJudgements(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index 8835fc2b29..b907aea8c3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -19,8 +19,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public DrawableSliderTail(HitCircle hitCircle) : base(hitCircle) { - AlwaysPresent = true; + Origin = Anchor.Centre; + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fit; + + AlwaysPresent = true; } protected override void CheckForJudgements(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index a83ee3a2e1..8c0eb7ff7d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -29,6 +29,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces set { path.PathWidth = value; } } + /// + /// Offset in absolute coordinates from the start of the curve. + /// + public Vector2 PathOffset { get; private set; } + + public readonly List CurrentCurve = new List(); + public readonly Bindable SnakingIn = new Bindable(); public readonly Bindable SnakingOut = new Bindable(); @@ -75,6 +82,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private int textureWidth => (int)PathWidth * 2; + private Vector2 topLeftOffset; + private readonly Slider slider; public SliderBody(Slider s) { @@ -84,6 +93,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { container = new BufferedContainer { + RelativeSizeAxes = Axes.Both, CacheDrawnFrameBuffer = true, Children = new Drawable[] { @@ -107,11 +117,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (updateSnaking(p0, p1)) { - // Autosizing does not give us the desired behaviour here. - // We want the container to have the same size as the slider, - // and to be positioned such that the slider head is at (0,0). - container.Size = path.Size; - container.Position = -path.PositionInBoundingBox(slider.Curve.PositionAt(0) - CurrentCurve[0]); + // The path is generated such that its size encloses it. This change of size causes the path + // to move around while snaking, so we need to offset it to make sure it maintains the + // same position as when it is fully snaked. + var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + path.Position = topLeftOffset - newTopLeftOffset; container.ForceRedraw(); } @@ -121,6 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private void load() { reloadTexture(); + computeSize(); } private void reloadTexture() @@ -164,7 +175,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces path.Texture = texture; } - public readonly List CurrentCurve = new List(); + private void computeSize() + { + // Generate the entire curve + slider.Curve.GetPathToProgress(CurrentCurve, 0, 1); + foreach (Vector2 p in CurrentCurve) + path.AddVertex(p); + + Size = path.Size; + + topLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + PathOffset = path.PositionInBoundingBox(CurrentCurve[0]); + } + private bool updateSnaking(double p0, double p1) { if (SnakedStart == p0 && SnakedEnd == p1) return false; @@ -176,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces path.ClearVertices(); foreach (Vector2 p in CurrentCurve) - path.AddVertex(p - CurrentCurve[0]); + path.AddVertex(p); return true; } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index ce6c88a340..4905972e6f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects HeadCircle = new HitCircle { StartTime = StartTime, - Position = StackedPosition, + Position = this.PositionAt(0), IndexInCurrentCombo = IndexInCurrentCombo, ComboColour = ComboColour, Samples = Samples, @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Osu.Objects TailCircle = new HitCircle { StartTime = EndTime, - Position = StackedEndPosition, + Position = this.PositionAt(1), IndexInCurrentCombo = IndexInCurrentCombo, ComboColour = ComboColour }; @@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.Objects SpanIndex = span, SpanStartTime = spanStartTime, StartTime = spanStartTime + timeProgress * SpanDuration, - Position = Curve.PositionAt(distanceProgress), + Position = Curve.PositionAt(distanceProgress) - Curve.PositionAt(0), StackHeight = StackHeight, Scale = Scale, ComboColour = ComboColour, @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Osu.Objects RepeatIndex = repeatIndex, SpanDuration = SpanDuration, StartTime = StartTime + repeat * SpanDuration, - Position = Curve.PositionAt(repeat % 2), + Position = Curve.PositionAt(repeat % 2) - Curve.PositionAt(0), StackHeight = StackHeight, Scale = Scale, ComboColour = ComboColour, @@ -184,4 +184,10 @@ namespace osu.Game.Rulesets.Osu.Objects } } } + + public static class SliderExtensions + { + public static Vector2 PositionAt(this Slider slider, double progress) + => ((IHasCurve)slider).PositionAt(progress) - slider.Curve.PositionAt(0); + } } diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs new file mode 100644 index 0000000000..a23bfb11ca --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs @@ -0,0 +1,171 @@ +// 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.Framework.Graphics.Containers; +using osu.Framework.Graphics.Lines; +using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Tests.Visual; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestCaseNewSliderBody : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(Path) }; + + private readonly NewSliderBody body; + + public TestCaseNewSliderBody() + { + Add(body = new NewSliderBody(new SliderCurve + { + ControlPoints = new List + { + new Vector2(-200, 0), + new Vector2(-50, 75), + new Vector2(0, 100), + new Vector2(100, -200), + new Vector2(230, 0) + }, + Distance = 480, + CurveType = CurveType.Bezier + }) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }); + + AddSliderStep("In", 0f, 1f, 0f, v => inLength = v); + AddSliderStep("Out", 0f, 1f, 1f, v => outLength = v); + AddSliderStep("Path Width", 0f, 100f, 10f, v => body.PathWidth = v); + } + + private float _inLength; + + private float inLength + { + set + { + _inLength = value; + body.UpdateSnaking(_inLength, _outLength); + } + } + + private float _outLength; + + private float outLength + { + set + { + _outLength = value; + body.UpdateSnaking(_inLength, _outLength); + } + } + + private class NewSliderBody : CompositeDrawable + { + private readonly Path path; + private readonly SliderCurve curve; + + public NewSliderBody(SliderCurve curve) + { + this.curve = curve; + + InternalChild = path = new Path(); + } + + [BackgroundDependencyLoader] + private void load() + { + reloadTexture(); + computeSize(); + } + + public float PathWidth + { + get => path.PathWidth; + set { path.PathWidth = value; reloadTexture(); } + } + + private void reloadTexture() + { + var textureWidth = (int)PathWidth * 2; + + //initialise background + var texture = new Texture(textureWidth, 1); + var upload = new TextureUpload(textureWidth * 4); + var bytes = upload.Data; + + const float aa_portion = 0.02f; + const float border_portion = 0.128f; + const float gradient_portion = 1 - border_portion; + + const float opacity_at_centre = 0.3f; + const float opacity_at_edge = 0.8f; + + for (int i = 0; i < textureWidth; i++) + { + float progress = (float)i / (textureWidth - 1); + + if (progress <= border_portion) + { + bytes[i * 4] = (byte)(Color4.White.R * 255); + bytes[i * 4 + 1] = (byte)(Color4.White.G * 255); + bytes[i * 4 + 2] = (byte)(Color4.White.B * 255); + bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (Color4.White.A * 255)); + } + else + { + progress -= border_portion; + + bytes[i * 4] = (byte)(Color4.Blue.R * 255); + bytes[i * 4 + 1] = (byte)(Color4.Blue.G * 255); + bytes[i * 4 + 2] = (byte)(Color4.Blue.B * 255); + bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (Color4.Blue.A * 255)); + } + } + + texture.SetData(upload); + path.Texture = texture; + } + + private Vector2 topLeftOffset; + + private void computeSize() + { + // Compute the final size + var fullPath = new List(); + curve.GetPathToProgress(fullPath, 0, 1); + + foreach (Vector2 p in fullPath) + path.AddVertex(p); + + Size = path.Size; + + topLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + OriginPosition = path.PositionInBoundingBox(fullPath[0]); + } + + public void UpdateSnaking(float t0, float t1) + { + var curvePath = new List(); + curve.GetPathToProgress(curvePath, t0, t1); + + path.ClearVertices(); + foreach (Vector2 p in curvePath) + path.AddVertex(p); + + var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero); + path.Position = topLeftOffset - newTopLeftOffset; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 7838fb7707..53075728ad 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -124,6 +124,7 @@ + From 8c90abe0dc826bdc3487cfc2a064dd6348000a87 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Feb 2018 20:51:26 +0900 Subject: [PATCH 35/47] Make slider control points relative to start position --- .../Objects/Drawables/DrawableRepeatPoint.cs | 7 +++---- .../Objects/Drawables/DrawableSlider.cs | 17 ++++++----------- .../Objects/Drawables/Pieces/SliderBall.cs | 3 ++- osu.Game.Rulesets.Osu/Objects/Slider.cs | 18 ++++++------------ .../Objects/Legacy/ConvertHitObjectParser.cs | 8 +++++--- 5 files changed, 22 insertions(+), 31 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 3e1b64bb86..79a4714e33 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -78,8 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0; List curve = drawableSlider.Body.CurrentCurve; - var positionOnCurve = isRepeatAtEnd ? end : start; - Position = positionOnCurve - curve[0] + drawableSlider.HitObject.StackOffset; + Position = isRepeatAtEnd ? end : start; if (curve.Count < 2) return; @@ -90,10 +89,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // find the next vector2 in the curve which is not equal to our current position to infer a rotation. for (int i = searchStart; i >= 0 && i < curve.Count; i += direction) { - if (curve[i] == positionOnCurve) + if (curve[i] == Position) continue; - Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - positionOnCurve.Y, curve[i].X - positionOnCurve.X)); + Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X)); break; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 866631468a..c2f3d4a314 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Judgements; using osu.Framework.Graphics.Primitives; using osu.Game.Configuration; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -53,8 +54,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true, Alpha = 0 }, - HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.HeadCircle.StackedPosition }, - tail = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.StackedPosition } + HeadCircle = new DrawableHitCircle(s.HeadCircle) { Position = s.HeadCircle.Position - s.Position }, + tail = new DrawableSliderTail(s.TailCircle) { Position = s.TailCircle.Position - s.Position } }; components.Add(Body); @@ -67,10 +68,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (var tick in s.NestedHitObjects.OfType()) { - var drawableTick = new DrawableSliderTick(tick) - { - Position = tick.StackedPosition - }; + var drawableTick = new DrawableSliderTick(tick) { Position = tick.Position - s.Position }; ticks.Add(drawableTick); components.Add(drawableTick); @@ -79,10 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (var repeatPoint in s.NestedHitObjects.OfType()) { - var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) - { - Position = repeatPoint.StackedPosition - }; + var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this) { Position = repeatPoint.Position - s.Position }; repeatPoints.Add(drawableRepeatPoint); components.Add(drawableRepeatPoint); @@ -109,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. if (!HeadCircle.IsHit) - HeadCircle.Position = slider.StackedPositionAt(completionProgress); + HeadCircle.Position = slider.PositionAt(completionProgress); foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 61db10b694..4cfe3c3445 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input; +using osu.Game.Rulesets.Objects.Types; using OpenTK; using OpenTK.Graphics; @@ -141,7 +142,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void UpdateProgress(double completionProgress) { - Position = slider.StackedPositionAt(completionProgress); + Position = slider.PositionAt(completionProgress); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 4905972e6f..308fc37270 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -23,8 +23,8 @@ namespace osu.Game.Rulesets.Osu.Objects public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; public double Duration => EndTime - StartTime; - public Vector2 StackedPositionAt(double t) => this.PositionAt(t) + StackOffset; - public override Vector2 EndPosition => this.PositionAt(1); + public Vector2 StackedPositionAt(double t) => StackedPosition + this.PositionAt(t); + public override Vector2 EndPosition => Position + this.PositionAt(1); public SliderCurve Curve { get; } = new SliderCurve(); @@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects HeadCircle = new HitCircle { StartTime = StartTime, - Position = this.PositionAt(0), + Position = Position, IndexInCurrentCombo = IndexInCurrentCombo, ComboColour = ComboColour, Samples = Samples, @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Osu.Objects TailCircle = new HitCircle { StartTime = EndTime, - Position = this.PositionAt(1), + Position = EndPosition, IndexInCurrentCombo = IndexInCurrentCombo, ComboColour = ComboColour }; @@ -156,7 +156,7 @@ namespace osu.Game.Rulesets.Osu.Objects SpanIndex = span, SpanStartTime = spanStartTime, StartTime = spanStartTime + timeProgress * SpanDuration, - Position = Curve.PositionAt(distanceProgress) - Curve.PositionAt(0), + Position = Position + Curve.PositionAt(distanceProgress), StackHeight = StackHeight, Scale = Scale, ComboColour = ComboColour, @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Osu.Objects RepeatIndex = repeatIndex, SpanDuration = SpanDuration, StartTime = StartTime + repeat * SpanDuration, - Position = Curve.PositionAt(repeat % 2) - Curve.PositionAt(0), + Position = Position + Curve.PositionAt(repeat % 2), StackHeight = StackHeight, Scale = Scale, ComboColour = ComboColour, @@ -184,10 +184,4 @@ namespace osu.Game.Rulesets.Osu.Objects } } } - - public static class SliderExtensions - { - public static Vector2 PositionAt(this Slider slider, double progress) - => ((IHasCurve)slider).PositionAt(progress) - slider.Curve.PositionAt(0); - } } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 5fdc9a07e1..2fcf3205c1 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -41,9 +41,11 @@ namespace osu.Game.Rulesets.Objects.Legacy } else if ((type & ConvertHitObjectType.Slider) > 0) { + var pos = new Vector2(int.Parse(split[0]), int.Parse(split[1])); + CurveType curveType = CurveType.Catmull; double length = 0; - var points = new List { new Vector2(int.Parse(split[0]), int.Parse(split[1])) }; + var points = new List { Vector2.Zero }; string[] pointsplit = split[5].Split('|'); foreach (string t in pointsplit) @@ -69,7 +71,7 @@ namespace osu.Game.Rulesets.Objects.Legacy } string[] temp = t.Split(':'); - points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture))); + points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos); } int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); @@ -134,7 +136,7 @@ namespace osu.Game.Rulesets.Objects.Legacy for (int i = 0; i < nodes; i++) nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); - result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount, nodeSamples); + result = CreateSlider(pos, combo, points, length, curveType, repeatCount, nodeSamples); } else if ((type & ConvertHitObjectType.Spinner) > 0) { From 1e1df2aafff35fb7bda56cead1d1437bbe0b514a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 23 Feb 2018 21:03:45 +0900 Subject: [PATCH 36/47] Fix up testcases --- osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs | 40 +++++++++---------- .../Visual/TestCaseEditorSelectionLayer.cs | 6 +-- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs index 90a0a450a7..ed212b48cd 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs @@ -118,8 +118,8 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-(distance / 2), 0), - new Vector2(distance / 2, 0), + Vector2.Zero, + new Vector2(distance, 0), }, Distance = distance, RepeatCount = repeats, @@ -139,9 +139,9 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-200, 0), - new Vector2(0, 200), - new Vector2(200, 0) + Vector2.Zero, + new Vector2(200, 200), + new Vector2(400, 0) }, Distance = 600, RepeatCount = repeats, @@ -163,12 +163,12 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-200, 0), - new Vector2(-50, 75), - new Vector2(0, 100), - new Vector2(100, -200), + Vector2.Zero, + new Vector2(150, 75), new Vector2(200, 0), - new Vector2(230, 0) + new Vector2(300, -200), + new Vector2(400, 0), + new Vector2(430, 0) }, Distance = 793.4417, RepeatCount = repeats, @@ -190,11 +190,11 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(-200, 0), - new Vector2(-50, 75), - new Vector2(0, 100), - new Vector2(100, -200), - new Vector2(230, 0) + Vector2.Zero, + new Vector2(150, 75), + new Vector2(200, 100), + new Vector2(300, -200), + new Vector2(430, 0) }, Distance = 480, RepeatCount = repeats, @@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Osu.Tests ComboColour = Color4.LightSeaGreen, ControlPoints = new List { - new Vector2(0, 0), + Vector2.Zero, new Vector2(-200, 0), new Vector2(0, 0), new Vector2(0, -200), @@ -247,10 +247,10 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Catmull, ControlPoints = new List { - new Vector2(-100, 0), - new Vector2(-50, -50), - new Vector2(50, 50), - new Vector2(100, 0) + Vector2.Zero, + new Vector2(50, -50), + new Vector2(150, 50), + new Vector2(200, 0) }, Distance = 300, RepeatCount = repeats, diff --git a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs index 5e0c0e165c..60ddff64ba 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs @@ -35,13 +35,13 @@ namespace osu.Game.Tests.Visual new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }, new Slider { + Position = new Vector2(128, 256), ControlPoints = new List { - new Vector2(128, 256), - new Vector2(344, 256), + Vector2.Zero, + new Vector2(216, 0), }, Distance = 400, - Position = new Vector2(128, 256), Velocity = 1, TickDistance = 100, Scale = 0.5f, From 996e605e61628523cabd5d676753d1918ad872ad Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 24 Feb 2018 02:39:44 +0900 Subject: [PATCH 37/47] Remove temporary testcase --- .../Tests/TestCaseNewSliderBody.cs | 171 ------------------ .../osu.Game.Rulesets.Osu.csproj | 1 - 2 files changed, 172 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs deleted file mode 100644 index a23bfb11ca..0000000000 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseNewSliderBody.cs +++ /dev/null @@ -1,171 +0,0 @@ -// 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.Framework.Graphics.Containers; -using osu.Framework.Graphics.Lines; -using osu.Framework.Graphics.OpenGL.Textures; -using osu.Framework.Graphics.Textures; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Tests.Visual; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Rulesets.Osu.Tests -{ - public class TestCaseNewSliderBody : OsuTestCase - { - public override IReadOnlyList RequiredTypes => new[] { typeof(Path) }; - - private readonly NewSliderBody body; - - public TestCaseNewSliderBody() - { - Add(body = new NewSliderBody(new SliderCurve - { - ControlPoints = new List - { - new Vector2(-200, 0), - new Vector2(-50, 75), - new Vector2(0, 100), - new Vector2(100, -200), - new Vector2(230, 0) - }, - Distance = 480, - CurveType = CurveType.Bezier - }) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }); - - AddSliderStep("In", 0f, 1f, 0f, v => inLength = v); - AddSliderStep("Out", 0f, 1f, 1f, v => outLength = v); - AddSliderStep("Path Width", 0f, 100f, 10f, v => body.PathWidth = v); - } - - private float _inLength; - - private float inLength - { - set - { - _inLength = value; - body.UpdateSnaking(_inLength, _outLength); - } - } - - private float _outLength; - - private float outLength - { - set - { - _outLength = value; - body.UpdateSnaking(_inLength, _outLength); - } - } - - private class NewSliderBody : CompositeDrawable - { - private readonly Path path; - private readonly SliderCurve curve; - - public NewSliderBody(SliderCurve curve) - { - this.curve = curve; - - InternalChild = path = new Path(); - } - - [BackgroundDependencyLoader] - private void load() - { - reloadTexture(); - computeSize(); - } - - public float PathWidth - { - get => path.PathWidth; - set { path.PathWidth = value; reloadTexture(); } - } - - private void reloadTexture() - { - var textureWidth = (int)PathWidth * 2; - - //initialise background - var texture = new Texture(textureWidth, 1); - var upload = new TextureUpload(textureWidth * 4); - var bytes = upload.Data; - - const float aa_portion = 0.02f; - const float border_portion = 0.128f; - const float gradient_portion = 1 - border_portion; - - const float opacity_at_centre = 0.3f; - const float opacity_at_edge = 0.8f; - - for (int i = 0; i < textureWidth; i++) - { - float progress = (float)i / (textureWidth - 1); - - if (progress <= border_portion) - { - bytes[i * 4] = (byte)(Color4.White.R * 255); - bytes[i * 4 + 1] = (byte)(Color4.White.G * 255); - bytes[i * 4 + 2] = (byte)(Color4.White.B * 255); - bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (Color4.White.A * 255)); - } - else - { - progress -= border_portion; - - bytes[i * 4] = (byte)(Color4.Blue.R * 255); - bytes[i * 4 + 1] = (byte)(Color4.Blue.G * 255); - bytes[i * 4 + 2] = (byte)(Color4.Blue.B * 255); - bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (Color4.Blue.A * 255)); - } - } - - texture.SetData(upload); - path.Texture = texture; - } - - private Vector2 topLeftOffset; - - private void computeSize() - { - // Compute the final size - var fullPath = new List(); - curve.GetPathToProgress(fullPath, 0, 1); - - foreach (Vector2 p in fullPath) - path.AddVertex(p); - - Size = path.Size; - - topLeftOffset = path.PositionInBoundingBox(Vector2.Zero); - OriginPosition = path.PositionInBoundingBox(fullPath[0]); - } - - public void UpdateSnaking(float t0, float t1) - { - var curvePath = new List(); - curve.GetPathToProgress(curvePath, t0, t1); - - path.ClearVertices(); - foreach (Vector2 p in curvePath) - path.AddVertex(p); - - var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero); - path.Position = topLeftOffset - newTopLeftOffset; - } - } - } -} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 53075728ad..7838fb7707 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -124,7 +124,6 @@ - From 066abfbdbc37cd184bbc7d9e6a8d35c2e51ca280 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 24 Feb 2018 02:43:36 +0900 Subject: [PATCH 38/47] Rename PositionAt -> CurvePositionAt to represent its new meaning --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 4 ++-- osu.Game/Rulesets/Objects/Types/IHasCurve.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index c2f3d4a314..560d13801a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. if (!HeadCircle.IsHit) - HeadCircle.Position = slider.PositionAt(completionProgress); + HeadCircle.Position = slider.CurvePositionAt(completionProgress); foreach (var c in components.OfType()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 4cfe3c3445..1921c51889 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -142,7 +142,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public void UpdateProgress(double completionProgress) { - Position = slider.PositionAt(completionProgress); + Position = slider.CurvePositionAt(completionProgress); } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 308fc37270..61056832e9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -23,8 +23,8 @@ namespace osu.Game.Rulesets.Osu.Objects public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; public double Duration => EndTime - StartTime; - public Vector2 StackedPositionAt(double t) => StackedPosition + this.PositionAt(t); - public override Vector2 EndPosition => Position + this.PositionAt(1); + public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); + public override Vector2 EndPosition => Position + this.CurvePositionAt(1); public SliderCurve Curve { get; } = new SliderCurve(); diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs index c03bdb240e..251ad3e3cd 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Objects.Types /// The curve. /// [0, 1] where 0 is the start time of the and 1 is the end time of the . /// The position on the curve. - public static Vector2 PositionAt(this IHasCurve obj, double progress) + public static Vector2 CurvePositionAt(this IHasCurve obj, double progress) => obj.Curve.PositionAt(obj.ProgressAt(progress)); /// From 50d1183ec2deb5182484874404ad0c40d47f04d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 24 Feb 2018 02:53:02 +0900 Subject: [PATCH 39/47] Division-by-zero safety + reference our own size --- .../Objects/Drawables/DrawableSlider.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 560d13801a..39908e9fa7 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -113,9 +113,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Size = Body.Size; OriginPosition = Body.PathOffset; - foreach (var obj in NestedHitObjects) - obj.RelativeAnchorPosition = Vector2.Divide(OriginPosition, Body.DrawSize); - Ball.RelativeAnchorPosition = Vector2.Divide(OriginPosition, Body.DrawSize); + if (DrawSize.X > 0 && DrawSize.Y > 0) + { + var childAnchorPosition = Vector2.Divide(OriginPosition, DrawSize); + foreach (var obj in NestedHitObjects) + obj.RelativeAnchorPosition = childAnchorPosition; + Ball.RelativeAnchorPosition = childAnchorPosition; + } } protected override void CheckForJudgements(bool userTriggered, double timeOffset) From f5fc9cdfba7d66d1f85e997d15b205803c9d6603 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 24 Feb 2018 02:59:55 +0900 Subject: [PATCH 40/47] Fix catch now having incorrect offsets --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index be1e360fce..a3e5aba2db 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Catch.Objects { StartTime = lastTickTime, ComboColour = ComboColour, - X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, + X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, Samples = new List(Samples.Select(s => new SampleInfo { Bank = s.Bank, @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Catch.Objects { StartTime = spanStartTime + t, ComboColour = ComboColour, - X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, + X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, Samples = new List(Samples.Select(s => new SampleInfo { Bank = s.Bank, @@ -120,14 +120,14 @@ namespace osu.Game.Rulesets.Catch.Objects Samples = Samples, ComboColour = ComboColour, StartTime = spanStartTime + spanDuration, - X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH + X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH }); } } public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; - public float EndX => Curve.PositionAt(this.ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH; + public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH; public double Duration => EndTime - StartTime; From ef6b207c7e6a536a794751ea2e8f4265f0530ad5 Mon Sep 17 00:00:00 2001 From: "V.Kalyuzhny" Date: Sat, 24 Feb 2018 14:18:53 +0200 Subject: [PATCH 41/47] Fix file case --- osu.Game/osu.Game.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 829addc360..e41ca5f0be 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -323,7 +323,7 @@ 20171209034410_AddRulesetInfoShortName.cs - + 20180219060912_AddSkins.cs @@ -930,4 +930,4 @@ - \ No newline at end of file + From 6d4016e18bf9d1691683217975fe1c86ef579225 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Feb 2018 22:41:04 +0900 Subject: [PATCH 42/47] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 16a4bef775..9a773e62eb 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 16a4bef775a49166f38faa6e952d83d8823fe3e0 +Subproject commit 9a773e62eb246206b918ba4fccf9f2507aaa4595 From 768e0a4e2a912c44d7a139ff8243a8b88435a228 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 23 Feb 2018 20:34:08 +0900 Subject: [PATCH 43/47] Add SkinnableSound class Tidy things up, move logic out of SampleInfo. --- .../Audio/DrumSampleMapping.cs | 29 ++++++-- osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 7 +- osu.Game/Audio/SampleInfo.cs | 21 ++---- .../Objects/Drawables/DrawableHitObject.cs | 66 +++++++------------ osu.Game/Skinning/SkinReloadableDrawable.cs | 53 +++++++++++++++ osu.Game/Skinning/SkinnableDrawable.cs | 38 ++++------- osu.Game/Skinning/SkinnableSound.cs | 62 +++++++++++++++++ osu.Game/osu.Game.csproj | 4 +- 8 files changed, 184 insertions(+), 96 deletions(-) create mode 100644 osu.Game/Skinning/SkinReloadableDrawable.cs create mode 100644 osu.Game/Skinning/SkinnableSound.cs diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs index 85367b8bf6..ef96e4c48a 100644 --- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs +++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs @@ -2,10 +2,9 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Taiko.Audio { @@ -14,7 +13,9 @@ namespace osu.Game.Rulesets.Taiko.Audio private readonly ControlPointInfo controlPoints; private readonly Dictionary mappings = new Dictionary(); - public DrumSampleMapping(ControlPointInfo controlPoints, AudioManager audio) + public readonly List Drawables = new List(); + + public DrumSampleMapping(ControlPointInfo controlPoints) { this.controlPoints = controlPoints; @@ -27,20 +28,34 @@ namespace osu.Game.Rulesets.Taiko.Audio foreach (var s in samplePoints) { + var centre = s.GetSampleInfo(); + var rim = s.GetSampleInfo(SampleInfo.HIT_CLAP); + + // todo: this is ugly + centre.Namespace = "taiko"; + rim.Namespace = "taiko"; + mappings[s.Time] = new DrumSample { - Centre = s.GetSampleInfo().GetChannel(audio.Sample.Get, "Taiko"), - Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample.Get, "Taiko") + Centre = addDrawableSound(centre), + Rim = addDrawableSound(rim) }; } } + private SkinnableSound addDrawableSound(SampleInfo rim) + { + var drawable = new SkinnableSound(rim); + Drawables.Add(drawable); + return drawable; + } + public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time]; public class DrumSample { - public SampleChannel Centre; - public SampleChannel Rim; + public SkinnableSound Centre; + public SkinnableSound Rim; } } } diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index 98f20fd558..ac4c077515 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -4,7 +4,6 @@ using System; using OpenTK; using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -34,9 +33,9 @@ namespace osu.Game.Rulesets.Taiko.UI } [BackgroundDependencyLoader] - private void load(AudioManager audio) + private void load() { - var sampleMappings = new DrumSampleMapping(controlPoints, audio); + var sampleMappings = new DrumSampleMapping(controlPoints); Children = new Drawable[] { @@ -63,6 +62,8 @@ namespace osu.Game.Rulesets.Taiko.UI CentreAction = TaikoAction.RightCentre } }; + + AddRangeInternal(sampleMappings.Drawables); } /// diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 99d2da7ebc..2014db6c61 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using osu.Framework.Audio.Sample; namespace osu.Game.Audio { @@ -14,22 +13,10 @@ namespace osu.Game.Audio public const string HIT_NORMAL = @"hitnormal"; public const string HIT_CLAP = @"hitclap"; - public SampleChannel GetChannel(Func getChannel, string resourceNamespace = null) - { - SampleChannel channel = null; - - if (resourceNamespace != null) - channel = getChannel($"Gameplay/{resourceNamespace}/{Bank}-{Name}"); - - // try without namespace as a fallback. - if (channel == null) - channel = getChannel($"Gameplay/{Bank}-{Name}"); - - if (channel != null) - channel.Volume.Value = Volume / 100.0; - - return channel; - } + /// + /// An optional ruleset namespace. + /// + public string Namespace; /// /// The bank to load the sample from. diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 2ecdccc31f..fcb472995a 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -3,21 +3,19 @@ using System; using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Game.Rulesets.Judgements; -using Container = osu.Framework.Graphics.Containers.Container; -using osu.Game.Rulesets.Objects.Types; -using OpenTK.Graphics; -using osu.Game.Audio; using System.Linq; -using osu.Game.Graphics; +using osu.Framework.Allocation; using osu.Framework.Configuration; -using OpenTK; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; +using osu.Game.Audio; +using osu.Game.Graphics; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; +using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Objects.Drawables { @@ -33,8 +31,12 @@ namespace osu.Game.Rulesets.Objects.Drawables // Todo: Rulesets should be overriding the resources instead, but we need to figure out where/when to apply overrides first protected virtual string SampleNamespace => null; - protected List Samples = new List(); - protected virtual IEnumerable GetSamples() => HitObject.Samples; + protected SkinnableSound Samples; + + protected virtual IEnumerable GetSamples() + { + return HitObject.Samples; + } private List nestedHitObjects; public IReadOnlyList NestedHitObjects => nestedHitObjects; @@ -83,41 +85,23 @@ namespace osu.Game.Rulesets.Objects.Drawables HitObject = hitObject; } - private readonly Bindable skin = new Bindable(); - [BackgroundDependencyLoader] - private void load(AudioManager audio, SkinManager skins) + private void load() { - var samples = GetSamples(); + var samples = GetSamples().ToArray(); + if (samples.Any()) { if (HitObject.SampleControlPoint == null) throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}." + $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}."); - void loadSamples(Skin skin) + AddInternal(Samples = new SkinnableSound(samples.Select(s => new SampleInfo { - Samples.Clear(); - - foreach (SampleInfo s in samples) - { - SampleInfo localSampleInfo = new SampleInfo - { - Bank = s.Bank ?? HitObject.SampleControlPoint.SampleBank, - Name = s.Name, - Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume - }; - - - SampleChannel channel = localSampleInfo.GetChannel(skin.GetSample, SampleNamespace) ?? localSampleInfo.GetChannel(audio.Sample.Get, SampleNamespace); - - if (channel == null) return; - - Samples.Add(channel); - } - } - - skin.ValueChanged += loadSamples; - skin.BindTo(skins.CurrentSkin); + Bank = s.Bank ?? HitObject.SampleControlPoint.SampleBank, + Name = s.Name, + Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume, + Namespace = SampleNamespace + }).ToArray())); } } @@ -149,7 +133,7 @@ namespace osu.Game.Rulesets.Objects.Drawables /// /// Plays all the hitsounds for this . /// - public void PlaySamples() => Samples.ForEach(s => s?.Play()); + public void PlaySamples() => Samples?.Play(); protected override void Update() { @@ -231,10 +215,8 @@ namespace osu.Game.Rulesets.Objects.Drawables return false; if (NestedHitObjects != null) - { foreach (var d in NestedHitObjects) judgementOccurred |= d.UpdateJudgement(userTriggered); - } if (!ProvidesJudgement || judgementFinalized || judgementOccurred) return judgementOccurred; diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs new file mode 100644 index 0000000000..1abfd8976e --- /dev/null +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Skinning +{ + /// + /// A drawable which has a callback when the skin changes. + /// + public abstract class SkinReloadableDrawable : CompositeDrawable + { + private Bindable skin; + + /// + /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. + /// + private readonly bool allowDefaultFallback; + + /// + /// Create a new + /// + /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. + protected SkinReloadableDrawable(bool fallback = true) + { + allowDefaultFallback = fallback; + } + + [BackgroundDependencyLoader] + private void load(SkinManager skinManager) + { + skin = skinManager.CurrentSkin.GetBoundCopy(); + skin.ValueChanged += skin => SkinChanged(skin, allowDefaultFallback || skin.SkinInfo == SkinInfo.Default); + } + + protected override void LoadAsyncComplete() + { + base.LoadAsyncComplete(); + skin.TriggerChange(); + } + + /// + /// Called when a change is made to the skin. + /// + /// The new skin. + /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. + protected virtual void SkinChanged(Skin skin, bool allowFallback) + { + } + } +} diff --git a/osu.Game/Skinning/SkinnableDrawable.cs b/osu.Game/Skinning/SkinnableDrawable.cs index c1c78fdb05..cd669778a6 100644 --- a/osu.Game/Skinning/SkinnableDrawable.cs +++ b/osu.Game/Skinning/SkinnableDrawable.cs @@ -2,10 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using osu.Framework.Allocation; -using osu.Framework.Configuration; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; namespace osu.Game.Skinning { @@ -14,40 +11,29 @@ namespace osu.Game.Skinning public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true) : base(name, defaultImplementation, fallback) { - RelativeSizeAxes = Axes.Both; } } - public class SkinnableDrawable : CompositeDrawable + public class SkinnableDrawable : SkinReloadableDrawable where T : Drawable { - private Bindable skin; - protected Func CreateDefault; + private readonly Func createDefault; - public readonly string ComponentName; + private readonly string componentName; - public readonly bool DefaultFallback; - - public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true) + public SkinnableDrawable(string name, Func defaultImplementation, bool fallback = true) : base(fallback) { - DefaultFallback = fallback; - ComponentName = name; - CreateDefault = defaultImplementation; + componentName = name; + createDefault = defaultImplementation; + + RelativeSizeAxes = Axes.Both; } - [BackgroundDependencyLoader] - private void load(SkinManager skinManager) + protected override void SkinChanged(Skin skin, bool allowFallback) { - skin = skinManager.CurrentSkin.GetBoundCopy(); - skin.ValueChanged += updateComponent; - skin.TriggerChange(); - } - - private void updateComponent(Skin skin) - { - var drawable = skin.GetDrawableComponent(ComponentName); - if (drawable == null && (DefaultFallback || skin.SkinInfo == SkinInfo.Default)) - drawable = CreateDefault(ComponentName); + var drawable = skin.GetDrawableComponent(componentName); + if (drawable == null && allowFallback) + drawable = createDefault(componentName); if (drawable != null) InternalChild = drawable; diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs new file mode 100644 index 0000000000..7cc13519da --- /dev/null +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -0,0 +1,62 @@ +// 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.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Audio; + +namespace osu.Game.Skinning +{ + public class SkinnableSound : SkinReloadableDrawable + { + private readonly SampleInfo[] samples; + private SampleChannel[] channels; + + private AudioManager audio; + + public SkinnableSound(params SampleInfo[] samples) + { + this.samples = samples; + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + this.audio = audio; + } + + public void Play() => channels?.ForEach(c => c.Play()); + + protected override void SkinChanged(Skin skin, bool allowFallback) + { + channels = samples.Select(s => + { + var ch = loadChannel(s, skin.GetSample); + if (ch == null && allowFallback) + ch = loadChannel(s, audio.Sample.Get); + return ch; + }).ToArray(); + } + + private SampleChannel loadChannel(SampleInfo info, Func getSampleFunction) + { + SampleChannel ch = null; + + if (info.Namespace != null) + ch = getSampleFunction($"Gameplay/{info.Namespace}/{info.Bank}-{info.Name}"); + + // try without namespace as a fallback. + if (ch == null) + ch = getSampleFunction($"Gameplay/{info.Bank}-{info.Name}"); + + if (ch != null) + ch.Volume.Value = info.Volume / 100.0; + + return ch; + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 6a2ce82b23..6a06bf540b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,4 +1,4 @@ - + @@ -861,6 +861,8 @@ + + From 4a52df2dd4f2e5b4b8c9efbeb6485132a380b100 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 24 Feb 2018 23:07:02 +0900 Subject: [PATCH 44/47] Apply review --- osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs | 12 ++++++------ osu.Game.Rulesets.Taiko/UI/InputDrum.cs | 2 +- .../Rulesets/Objects/Drawables/DrawableHitObject.cs | 5 +---- osu.Game/Skinning/SkinReloadableDrawable.cs | 2 +- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs index ef96e4c48a..afa3d162f4 100644 --- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs +++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Taiko.Audio private readonly ControlPointInfo controlPoints; private readonly Dictionary mappings = new Dictionary(); - public readonly List Drawables = new List(); + public readonly List Sounds = new List(); public DrumSampleMapping(ControlPointInfo controlPoints) { @@ -37,16 +37,16 @@ namespace osu.Game.Rulesets.Taiko.Audio mappings[s.Time] = new DrumSample { - Centre = addDrawableSound(centre), - Rim = addDrawableSound(rim) + Centre = addSound(centre), + Rim = addSound(rim) }; } } - private SkinnableSound addDrawableSound(SampleInfo rim) + private SkinnableSound addSound(SampleInfo sampleInfo) { - var drawable = new SkinnableSound(rim); - Drawables.Add(drawable); + var drawable = new SkinnableSound(sampleInfo); + Sounds.Add(drawable); return drawable; } diff --git a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs index ac4c077515..b918f495fc 100644 --- a/osu.Game.Rulesets.Taiko/UI/InputDrum.cs +++ b/osu.Game.Rulesets.Taiko/UI/InputDrum.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.UI } }; - AddRangeInternal(sampleMappings.Drawables); + AddRangeInternal(sampleMappings.Sounds); } /// diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index fcb472995a..4c2683b389 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -33,10 +33,7 @@ namespace osu.Game.Rulesets.Objects.Drawables protected SkinnableSound Samples; - protected virtual IEnumerable GetSamples() - { - return HitObject.Samples; - } + protected virtual IEnumerable GetSamples() => HitObject.Samples; private List nestedHitObjects; public IReadOnlyList NestedHitObjects => nestedHitObjects; diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index 1abfd8976e..3e33f952cd 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -20,7 +20,7 @@ namespace osu.Game.Skinning private readonly bool allowDefaultFallback; /// - /// Create a new + /// Create a new /// /// Whether fallback to default skin should be allowed if the custom skin is missing this resource. protected SkinReloadableDrawable(bool fallback = true) From f1ddfa05817b0c54aba47768c1f2bb8ea8fddb87 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Feb 2018 05:48:54 +0900 Subject: [PATCH 45/47] Fix sliders never getting a correct lifetime set --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 391e0ff023..0288e96b3b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -154,6 +154,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables this.FadeOut(fade_out_time, Easing.OutQuint).Expire(); } + + Expire(true); } public Drawable ProxiedLayer => HeadCircle.ApproachCircle; From ab9aed351fc691b83f2da760c71e71562d2aaa80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 25 Feb 2018 17:14:06 +0900 Subject: [PATCH 46/47] Remove null channels from SkinnableSound playable channels --- osu.Game/Skinning/SkinnableSound.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 7cc13519da..fd52d62d59 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -39,7 +39,7 @@ namespace osu.Game.Skinning if (ch == null && allowFallback) ch = loadChannel(s, audio.Sample.Get); return ch; - }).ToArray(); + }).Where(c => c != null).ToArray(); } private SampleChannel loadChannel(SampleInfo info, Func getSampleFunction) From 1218a75b7f2c2e49c821502e5d15867d77298db3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 26 Feb 2018 16:11:26 +0900 Subject: [PATCH 47/47] Improve vector conditional --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index d5c2067fec..ede84e7737 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Size = Body.Size; OriginPosition = Body.PathOffset; - if (DrawSize.X > 0 && DrawSize.Y > 0) + if (DrawSize != Vector2.Zero) { var childAnchorPosition = Vector2.Divide(OriginPosition, DrawSize); foreach (var obj in NestedHitObjects)