diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index e47edc37cc..dcd2c7d321 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -38,7 +38,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double difficulty = 0; double weight = 1; - List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); + // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). + // These sections will not contribute to the difficulty. + var peaks = GetCurrentStrainPeaks().Where(p => p > 0); + + List strains = peaks.OrderByDescending(d => d).ToList(); // We are reducing the highest strains first to account for extreme difficulty spikes for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index bd26a99e51..ba7c6e9d33 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -59,11 +59,6 @@ namespace osu.Game.Rulesets.Osu.Edit { LayerBelowRuleset.AddRange(new Drawable[] { - new PlayfieldBorder - { - RelativeSizeAxes = Axes.Both, - PlayfieldBorderStyle = { Value = PlayfieldBorderStyle.Corners } - }, distanceSnapGridContainer = new Container { RelativeSizeAxes = Axes.Both diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 6afdef3f3c..a9d512f076 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -141,7 +141,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty double colourPeak = colourPeaks[i] * colour_skill_multiplier; double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier; double staminaPeak = (staminaRightPeaks[i] + staminaLeftPeaks[i]) * stamina_skill_multiplier * staminaPenalty; - peaks.Add(norm(2, colourPeak, rhythmPeak, staminaPeak)); + + double peak = norm(2, colourPeak, rhythmPeak, staminaPeak); + + // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). + // These sections will not contribute to the difficulty. + if (peak > 0) + peaks.Add(peak); } double difficulty = 0; diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneShearedSearchTextBox.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedSearchTextBox.cs new file mode 100644 index 0000000000..e1dbf04133 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneShearedSearchTextBox.cs @@ -0,0 +1,46 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneShearedSearchTextBox : OsuTestScene + { + [Test] + public void TestAllColourSchemes() + { + foreach (var scheme in Enum.GetValues(typeof(OverlayColourScheme)).Cast()) + AddStep($"set {scheme} scheme", () => Child = createContent(scheme)); + } + + private Drawable createContent(OverlayColourScheme colourScheme) + { + var colourProvider = new OverlayColourProvider(colourScheme); + + return new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = new (Type, object)[] + { + (typeof(OverlayColourProvider), colourProvider) + }, + Children = new Drawable[] + { + new ShearedSearchTextBox + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Width = 0.5f + } + } + }; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs new file mode 100644 index 0000000000..0c34a04a65 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -0,0 +1,137 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osu.Game.Overlays.Mods; +using osu.Game.Resources.Localisation.Web; +using osuTK; + +namespace osu.Game.Graphics.UserInterface +{ + public class ShearedSearchTextBox : CompositeDrawable, IHasCurrentValue + { + private const float corner_radius = 7; + + private readonly Box background; + private readonly SearchTextBox textBox; + + public Bindable Current + { + get => textBox.Current; + set => textBox.Current = value; + } + + public bool HoldFocus + { + get => textBox.HoldFocus; + set => textBox.HoldFocus = value; + } + + public void TakeFocus() => textBox.TakeFocus(); + + public void KillFocus() => textBox.KillFocus(); + + public ShearedSearchTextBox() + { + Height = 42; + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); + Masking = true; + CornerRadius = corner_radius; + + InternalChildren = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + textBox = new InnerSearchTextBox + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Size = Vector2.One + }, + new SpriteIcon + { + Icon = FontAwesome.Solid.Search, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(16), + Shear = -Shear + } + } + }, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 50), + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + background.Colour = colourProvider.Background3; + } + + public override bool HandleNonPositionalInput => textBox.HandleNonPositionalInput; + + private class InnerSearchTextBox : SearchTextBox + { + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + BackgroundFocused = colourProvider.Background4; + BackgroundUnfocused = colourProvider.Background4; + + Placeholder.Font = OsuFont.GetFont(size: CalculatedTextSize, weight: FontWeight.SemiBold); + PlaceholderText = CommonStrings.InputSearch; + + CornerRadius = corner_radius; + TextContainer.Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0); + } + + protected override SpriteText CreatePlaceholder() => new SearchPlaceholder(); + + internal class SearchPlaceholder : SpriteText + { + public override void Show() + { + this + .MoveToY(0, 250, Easing.OutQuint) + .FadeIn(250, Easing.OutQuint); + } + + public override void Hide() + { + this + .MoveToY(3, 250, Easing.OutQuint) + .FadeOut(250, Easing.OutQuint); + } + } + + protected override Drawable GetDrawableCharacter(char c) => new FallingDownContainer + { + AutoSizeAxes = Axes.Both, + Child = new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold) }, + }; + } + } +} diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index bbd2f079aa..97266562e4 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -100,9 +100,13 @@ namespace osu.Game.Rulesets.Difficulty.Skills double difficulty = 0; double weight = 1; + // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). + // These sections will not contribute to the difficulty. + var peaks = GetCurrentStrainPeaks().Where(p => p > 0); + // Difficulty is the weighted sum of the highest strains from every section. // We're sorting from highest to lowest strain. - foreach (double strain in GetCurrentStrainPeaks().OrderByDescending(d => d)) + foreach (double strain in peaks.OrderByDescending(d => d)) { difficulty += strain * weight; weight *= DecayWeight; diff --git a/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs b/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs index 95b4b2fe53..f0d26c7b6a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs +++ b/osu.Game/Screens/Edit/Compose/Components/RectangularPositionSnapGrid.cs @@ -2,10 +2,13 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Layout; +using osu.Framework.Utils; using osuTK; namespace osu.Game.Screens.Edit.Compose.Components @@ -72,33 +75,47 @@ namespace osu.Game.Screens.Edit.Compose.Components int index = 0; float currentPosition = startPosition; - while ((endPosition - currentPosition) * Math.Sign(step) > 0) + // Make lines the same width independent of display resolution. + float lineWidth = DrawWidth / ScreenSpaceDrawQuad.Width; + + List generatedLines = new List(); + + while (Precision.AlmostBigger((endPosition - currentPosition) * Math.Sign(step), 0)) { var gridLine = new Box { Colour = Colour4.White, - Alpha = index == 0 ? 0.3f : 0.1f, - EdgeSmoothness = new Vector2(0.2f) + Alpha = 0.1f, }; if (direction == Direction.Horizontal) { + gridLine.Origin = Anchor.CentreLeft; gridLine.RelativeSizeAxes = Axes.X; - gridLine.Height = 1; + gridLine.Height = lineWidth; gridLine.Y = currentPosition; } else { + gridLine.Origin = Anchor.TopCentre; gridLine.RelativeSizeAxes = Axes.Y; - gridLine.Width = 1; + gridLine.Width = lineWidth; gridLine.X = currentPosition; } - AddInternal(gridLine); + generatedLines.Add(gridLine); index += 1; currentPosition = startPosition + index * step; } + + if (generatedLines.Count == 0) + return; + + generatedLines.First().Alpha = 0.3f; + generatedLines.Last().Alpha = 0.3f; + + AddRangeInternal(generatedLines); } public Vector2 GetSnappedPosition(Vector2 original)