mirror of
https://github.com/ppy/osu.git
synced 2025-02-15 21:42:55 +08:00
Merge branch 'master' into fix-copy-difficulty-moving-collections
This commit is contained in:
commit
6efab635d8
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@ -121,21 +121,12 @@ jobs:
|
||||
|
||||
build-only-ios:
|
||||
name: Build only (iOS)
|
||||
# change to macos-latest once GitHub finishes migrating all repositories to macOS 12.
|
||||
runs-on: macos-12
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# see https://github.com/actions/runner-images/issues/6771#issuecomment-1354713617
|
||||
# remove once all workflow VMs use Xcode 14.1
|
||||
- name: Set Xcode Version
|
||||
shell: bash
|
||||
run: |
|
||||
sudo xcode-select -s "/Applications/Xcode_14.1.app"
|
||||
echo "MD_APPLE_SDK_ROOT=/Applications/Xcode_14.1.app" >> $GITHUB_ENV
|
||||
|
||||
- name: Install .NET 6.0.x
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
|
@ -113,6 +113,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
new MultiMod(new CatchModDoubleTime(), new CatchModNightcore()),
|
||||
new CatchModHidden(),
|
||||
new CatchModFlashlight(),
|
||||
new ModAccuracyChallenge(),
|
||||
};
|
||||
|
||||
case ModType.Conversion:
|
||||
|
@ -245,6 +245,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
new MultiMod(new ManiaModDoubleTime(), new ManiaModNightcore()),
|
||||
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()),
|
||||
new ManiaModFlashlight(),
|
||||
new ModAccuracyChallenge(),
|
||||
};
|
||||
|
||||
case ModType.Conversion:
|
||||
|
@ -22,6 +22,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
/// </summary>
|
||||
public Vector2 PathStartLocation => body.PathOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Offset in absolute (local) coordinates from the end of the curve.
|
||||
/// </summary>
|
||||
public Vector2 PathEndLocation => body.PathEndOffset;
|
||||
|
||||
public SliderBodyPiece()
|
||||
{
|
||||
InternalChild = body = new ManualSliderBody
|
||||
|
@ -409,6 +409,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
public override Vector2 ScreenSpaceSelectionPoint => DrawableObject.SliderBody?.ToScreenSpace(DrawableObject.SliderBody.PathOffset)
|
||||
?? BodyPiece.ToScreenSpace(BodyPiece.PathStartLocation);
|
||||
|
||||
protected override Vector2[] ScreenSpaceAdditionalNodes => new[]
|
||||
{
|
||||
DrawableObject.SliderBody?.ToScreenSpace(DrawableObject.SliderBody.PathEndOffset) ?? BodyPiece.ToScreenSpace(BodyPiece.PathEndLocation)
|
||||
};
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
|
||||
BodyPiece.ReceivePositionalInputAt(screenSpacePos) || ControlPointVisualiser?.Pieces.Any(p => p.ReceivePositionalInputAt(screenSpacePos)) == true;
|
||||
|
||||
|
14
osu.Game.Rulesets.Osu/Mods/OsuModAccuracyChallenge.cs
Normal file
14
osu.Game.Rulesets.Osu/Mods/OsuModAccuracyChallenge.cs
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModAccuracyChallenge : ModAccuracyChallenge
|
||||
{
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
|
||||
}
|
||||
}
|
@ -164,7 +164,8 @@ namespace osu.Game.Rulesets.Osu
|
||||
new MultiMod(new OsuModDoubleTime(), new OsuModNightcore()),
|
||||
new OsuModHidden(),
|
||||
new MultiMod(new OsuModFlashlight(), new OsuModBlinds()),
|
||||
new OsuModStrictTracking()
|
||||
new OsuModStrictTracking(),
|
||||
new OsuModAccuracyChallenge(),
|
||||
};
|
||||
|
||||
case ModType.Conversion:
|
||||
|
@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
/// </summary>
|
||||
public virtual Vector2 PathOffset => path.PositionInBoundingBox(path.Vertices[0]);
|
||||
|
||||
/// <summary>
|
||||
/// Offset in absolute coordinates from the end of the curve.
|
||||
/// </summary>
|
||||
public virtual Vector2 PathEndOffset => path.PositionInBoundingBox(path.Vertices[^1]);
|
||||
|
||||
/// <summary>
|
||||
/// Used to colour the path.
|
||||
/// </summary>
|
||||
|
@ -43,6 +43,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
|
||||
public override Vector2 PathOffset => snakedPathOffset;
|
||||
|
||||
public override Vector2 PathEndOffset => snakedPathEndOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The top-left position of the path when fully snaked.
|
||||
/// </summary>
|
||||
@ -53,6 +55,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
/// </summary>
|
||||
private Vector2 snakedPathOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The offset of the end of path from <see cref="snakedPosition"/> when fully snaked.
|
||||
/// </summary>
|
||||
private Vector2 snakedPathEndOffset;
|
||||
|
||||
private DrawableSlider drawableSlider = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -109,6 +116,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
|
||||
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
|
||||
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
|
||||
snakedPathEndOffset = Path.PositionInBoundingBox(Path.Vertices[^1]);
|
||||
|
||||
double lastSnakedStart = SnakedStart ?? 0;
|
||||
double lastSnakedEnd = SnakedEnd ?? 0;
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Input.StateChanges;
|
||||
using osu.Game.Configuration;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
@ -38,6 +39,9 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
||||
}
|
||||
|
||||
// Required to handle touches outside of the playfield when screen scaling is enabled.
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||
|
||||
protected override void OnTouchMove(TouchMoveEvent e)
|
||||
{
|
||||
base.OnTouchMove(e);
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Diagnostics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
@ -9,30 +8,15 @@ using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset<TaikoHitObject>, IUpdatableByPlayfield
|
||||
public class TaikoModClassic : ModClassic, IApplicableToDrawableRuleset<TaikoHitObject>
|
||||
{
|
||||
private DrawableTaikoRuleset? drawableTaikoRuleset;
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
||||
{
|
||||
drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset;
|
||||
var drawableTaikoRuleset = (DrawableTaikoRuleset)drawableRuleset;
|
||||
drawableTaikoRuleset.LockPlayfieldMaxAspect.Value = false;
|
||||
|
||||
var playfield = (TaikoPlayfield)drawableRuleset.Playfield;
|
||||
playfield.ClassicHitTargetPosition.Value = true;
|
||||
}
|
||||
|
||||
public void Update(Playfield playfield)
|
||||
{
|
||||
Debug.Assert(drawableTaikoRuleset != null);
|
||||
|
||||
// Classic taiko scrolls at a constant 100px per 1000ms. More notes become visible as the playfield is lengthened.
|
||||
const float scroll_rate = 10;
|
||||
|
||||
// Since the time range will depend on a positional value, it is referenced to the x480 pixel space.
|
||||
float ratio = drawableTaikoRuleset.DrawHeight / 480;
|
||||
|
||||
drawableTaikoRuleset.TimeRange.Value = (playfield.HitObjectContainer.DrawWidth / ratio) * scroll_rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,6 +144,7 @@ namespace osu.Game.Rulesets.Taiko
|
||||
new MultiMod(new TaikoModDoubleTime(), new TaikoModNightcore()),
|
||||
new TaikoModHidden(),
|
||||
new TaikoModFlashlight(),
|
||||
new ModAccuracyChallenge(),
|
||||
};
|
||||
|
||||
case ModType.Conversion:
|
||||
|
@ -43,7 +43,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
Direction.Value = ScrollingDirection.Left;
|
||||
TimeRange.Value = 7000;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -60,6 +59,19 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
KeyBindingInputManager.Add(new DrumTouchInputArea());
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
// Taiko scrolls at a constant 100px per 1000ms. More notes become visible as the playfield is lengthened.
|
||||
const float scroll_rate = 10;
|
||||
|
||||
// Since the time range will depend on a positional value, it is referenced to the x480 pixel space.
|
||||
float ratio = DrawHeight / 480;
|
||||
|
||||
TimeRange.Value = (Playfield.HitObjectContainer.DrawWidth / ratio) * scroll_rate;
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
@ -262,7 +262,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
AddAssert("Nothing happened", () => this.ChildrenOfType<ReportCommentPopover>().Any());
|
||||
AddStep("Set report data", () =>
|
||||
{
|
||||
var field = this.ChildrenOfType<OsuTextBox>().Single();
|
||||
var field = this.ChildrenOfType<ReportCommentPopover>().Single().ChildrenOfType<OsuTextBox>().Single();
|
||||
field.Current.Value = report_text;
|
||||
var reason = this.ChildrenOfType<OsuEnumDropdown<CommentReportReason>>().Single();
|
||||
reason.Current.Value = CommentReportReason.Other;
|
||||
|
54
osu.Game.Tests/Visual/Online/TestSceneLevelBadge.cs
Normal file
54
osu.Game.Tests/Visual/Online/TestSceneLevelBadge.cs
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Overlays.Profile.Header.Components;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
[TestFixture]
|
||||
public partial class TestSceneLevelBadge : OsuTestScene
|
||||
{
|
||||
public TestSceneLevelBadge()
|
||||
{
|
||||
var levels = new List<UserStatistics.LevelInfo>();
|
||||
|
||||
for (int i = 0; i < 11; i++)
|
||||
{
|
||||
levels.Add(new UserStatistics.LevelInfo
|
||||
{
|
||||
Current = i * 10
|
||||
});
|
||||
}
|
||||
|
||||
levels.Add(new UserStatistics.LevelInfo { Current = 101 });
|
||||
levels.Add(new UserStatistics.LevelInfo { Current = 105 });
|
||||
levels.Add(new UserStatistics.LevelInfo { Current = 110 });
|
||||
levels.Add(new UserStatistics.LevelInfo { Current = 115 });
|
||||
levels.Add(new UserStatistics.LevelInfo { Current = 120 });
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer<LevelBadge>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Full,
|
||||
Spacing = new Vector2(5),
|
||||
ChildrenEnumerable = levels.Select(level => new LevelBadge
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(60),
|
||||
LevelInfo = { Value = level }
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -89,6 +89,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Groups = new[]
|
||||
{
|
||||
new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" },
|
||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "mania" } },
|
||||
new APIUserGroup { Colour = "#A347EB", ShortName = "BN", Name = "Beatmap Nominators", Playmodes = new[] { "osu", "taiko" } }
|
||||
},
|
||||
ProfileOrder = new[]
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
@ -187,6 +188,41 @@ namespace osu.Game.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves colour for a <see cref="RankingTier"/>.
|
||||
/// See https://www.figma.com/file/YHWhp9wZ089YXgB7pe6L1k/Tier-Colours
|
||||
/// </summary>
|
||||
public ColourInfo ForRankingTier(RankingTier tier)
|
||||
{
|
||||
switch (tier)
|
||||
{
|
||||
default:
|
||||
case RankingTier.Iron:
|
||||
return Color4Extensions.FromHex(@"BAB3AB");
|
||||
|
||||
case RankingTier.Bronze:
|
||||
return ColourInfo.GradientVertical(Color4Extensions.FromHex(@"B88F7A"), Color4Extensions.FromHex(@"855C47"));
|
||||
|
||||
case RankingTier.Silver:
|
||||
return ColourInfo.GradientVertical(Color4Extensions.FromHex(@"E0E0EB"), Color4Extensions.FromHex(@"A3A3C2"));
|
||||
|
||||
case RankingTier.Gold:
|
||||
return ColourInfo.GradientVertical(Color4Extensions.FromHex(@"F0E4A8"), Color4Extensions.FromHex(@"E0C952"));
|
||||
|
||||
case RankingTier.Platinum:
|
||||
return ColourInfo.GradientVertical(Color4Extensions.FromHex(@"A8F0EF"), Color4Extensions.FromHex(@"52E0DF"));
|
||||
|
||||
case RankingTier.Rhodium:
|
||||
return ColourInfo.GradientVertical(Color4Extensions.FromHex(@"D9F8D3"), Color4Extensions.FromHex(@"A0CF96"));
|
||||
|
||||
case RankingTier.Radiant:
|
||||
return ColourInfo.GradientVertical(Color4Extensions.FromHex(@"97DCFF"), Color4Extensions.FromHex(@"ED82FF"));
|
||||
|
||||
case RankingTier.Lustrous:
|
||||
return ColourInfo.GradientVertical(Color4Extensions.FromHex(@"FFE600"), Color4Extensions.FromHex(@"ED82FF"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a foreground text colour that is supposed to contrast well with
|
||||
/// the supplied <paramref name="backgroundColour"/>.
|
||||
|
@ -250,13 +250,16 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
protected override void OnFocus(FocusEvent e)
|
||||
{
|
||||
BorderThickness = 3;
|
||||
if (Masking)
|
||||
BorderThickness = 3;
|
||||
|
||||
base.OnFocus(e);
|
||||
}
|
||||
|
||||
protected override void OnFocusLost(FocusLostEvent e)
|
||||
{
|
||||
BorderThickness = 0;
|
||||
if (Masking)
|
||||
BorderThickness = 0;
|
||||
|
||||
base.OnFocusLost(e);
|
||||
}
|
||||
|
@ -152,22 +152,6 @@ namespace osu.Game.Graphics.UserInterface
|
||||
segments.Sort();
|
||||
}
|
||||
|
||||
private ColourInfo getSegmentColour(SegmentInfo segment)
|
||||
{
|
||||
var segmentColour = new ColourInfo
|
||||
{
|
||||
TopLeft = DrawColourInfo.Colour.Interpolate(new Vector2(segment.Start, 0f)),
|
||||
TopRight = DrawColourInfo.Colour.Interpolate(new Vector2(segment.End, 0f)),
|
||||
BottomLeft = DrawColourInfo.Colour.Interpolate(new Vector2(segment.Start, 1f)),
|
||||
BottomRight = DrawColourInfo.Colour.Interpolate(new Vector2(segment.End, 1f))
|
||||
};
|
||||
|
||||
var tierColour = segment.Tier >= 0 ? tierColours[segment.Tier] : new Colour4(0, 0, 0, 0);
|
||||
segmentColour.ApplyChild(tierColour);
|
||||
|
||||
return segmentColour;
|
||||
}
|
||||
|
||||
protected override DrawNode CreateDrawNode() => new SegmentedGraphDrawNode(this);
|
||||
|
||||
protected struct SegmentInfo
|
||||
@ -215,6 +199,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
private IShader shader = null!;
|
||||
private readonly List<SegmentInfo> segments = new List<SegmentInfo>();
|
||||
private Vector2 drawSize;
|
||||
private readonly List<Colour4> tierColours = new List<Colour4>();
|
||||
|
||||
public SegmentedGraphDrawNode(SegmentedGraph<T> source)
|
||||
: base(source)
|
||||
@ -228,8 +213,12 @@ namespace osu.Game.Graphics.UserInterface
|
||||
texture = Source.texture;
|
||||
shader = Source.shader;
|
||||
drawSize = Source.DrawSize;
|
||||
|
||||
segments.Clear();
|
||||
segments.AddRange(Source.segments.Where(s => s.Length * drawSize.X > 1));
|
||||
|
||||
tierColours.Clear();
|
||||
tierColours.AddRange(Source.tierColours);
|
||||
}
|
||||
|
||||
public override void Draw(IRenderer renderer)
|
||||
@ -252,11 +241,27 @@ namespace osu.Game.Graphics.UserInterface
|
||||
Vector2Extensions.Transform(topRight, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(bottomLeft, DrawInfo.Matrix),
|
||||
Vector2Extensions.Transform(bottomRight, DrawInfo.Matrix)),
|
||||
Source.getSegmentColour(segment));
|
||||
getSegmentColour(segment));
|
||||
}
|
||||
|
||||
shader.Unbind();
|
||||
}
|
||||
|
||||
private ColourInfo getSegmentColour(SegmentInfo segment)
|
||||
{
|
||||
var segmentColour = new ColourInfo
|
||||
{
|
||||
TopLeft = DrawColourInfo.Colour.Interpolate(new Vector2(segment.Start, 0f)),
|
||||
TopRight = DrawColourInfo.Colour.Interpolate(new Vector2(segment.End, 0f)),
|
||||
BottomLeft = DrawColourInfo.Colour.Interpolate(new Vector2(segment.Start, 1f)),
|
||||
BottomRight = DrawColourInfo.Colour.Interpolate(new Vector2(segment.End, 1f))
|
||||
};
|
||||
|
||||
var tierColour = segment.Tier >= 0 ? tierColours[segment.Tier] : new Colour4(0, 0, 0, 0);
|
||||
segmentColour.ApplyChild(tierColour);
|
||||
|
||||
return segmentColour;
|
||||
}
|
||||
}
|
||||
|
||||
protected class SegmentManager : IEnumerable<SegmentInfo>
|
||||
|
@ -160,9 +160,12 @@ namespace osu.Game
|
||||
|
||||
protected Bindable<WorkingBeatmap> Beatmap { get; private set; } // cached via load() method
|
||||
|
||||
/// <summary>
|
||||
/// The current ruleset selection for the local user.
|
||||
/// </summary>
|
||||
[Cached]
|
||||
[Cached(typeof(IBindable<RulesetInfo>))]
|
||||
protected readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||
protected internal readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// The current mod selection for the local user.
|
||||
@ -553,8 +556,8 @@ namespace osu.Game
|
||||
case JoystickHandler jh:
|
||||
return new JoystickSettings(jh);
|
||||
|
||||
case TouchHandler:
|
||||
return new InputSection.HandlerSection(handler);
|
||||
case TouchHandler th:
|
||||
return new TouchSettings(th);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,14 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Online.API.Requests;
|
||||
@ -22,8 +24,6 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
public partial class ChangelogOverlay : OnlineOverlay<ChangelogHeader>
|
||||
{
|
||||
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
|
||||
|
||||
public readonly Bindable<APIChangelogBuild> Current = new Bindable<APIChangelogBuild>();
|
||||
|
||||
private List<APIChangelogBuild> builds;
|
||||
@ -81,6 +81,8 @@ namespace osu.Game.Overlays
|
||||
ArgumentNullException.ThrowIfNull(updateStream);
|
||||
ArgumentNullException.ThrowIfNull(version);
|
||||
|
||||
Show();
|
||||
|
||||
performAfterFetch(() =>
|
||||
{
|
||||
var build = builds.Find(b => b.Version == version && b.UpdateStream.Name == updateStream)
|
||||
@ -89,8 +91,6 @@ namespace osu.Game.Overlays
|
||||
if (build != null)
|
||||
ShowBuild(build);
|
||||
});
|
||||
|
||||
Show();
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
@ -127,11 +127,16 @@ namespace osu.Game.Overlays
|
||||
|
||||
private Task initialFetchTask;
|
||||
|
||||
private void performAfterFetch(Action action) => Schedule(() =>
|
||||
private void performAfterFetch(Action action)
|
||||
{
|
||||
fetchListing()?.ContinueWith(_ =>
|
||||
Schedule(action), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||
});
|
||||
Debug.Assert(State.Value == Visibility.Visible);
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
fetchListing()?.ContinueWith(_ =>
|
||||
Schedule(action), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||
});
|
||||
}
|
||||
|
||||
private Task fetchListing()
|
||||
{
|
||||
|
@ -147,7 +147,7 @@ namespace osu.Game.Overlays.Comments
|
||||
private void updateCommitButtonState() =>
|
||||
commitButton.Enabled.Value = loadingSpinner.State.Value == Visibility.Hidden && !string.IsNullOrEmpty(Current.Value);
|
||||
|
||||
private partial class EditorTextBox : BasicTextBox
|
||||
private partial class EditorTextBox : OsuTextBox
|
||||
{
|
||||
protected override float LeftRightPadding => side_padding;
|
||||
|
||||
@ -173,12 +173,6 @@ namespace osu.Game.Overlays.Comments
|
||||
{
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular),
|
||||
};
|
||||
|
||||
protected override Drawable GetDrawableCharacter(char c) => new FallingDownContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Child = new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: CalculatedTextSize) }
|
||||
};
|
||||
}
|
||||
|
||||
protected partial class EditorButton : RoundedButton
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
public partial class GroupBadge : Container, IHasTooltip
|
||||
{
|
||||
public LocalisableString TooltipText { get; }
|
||||
public LocalisableString TooltipText { get; private set; }
|
||||
|
||||
public int TextSize { get; set; } = 12;
|
||||
|
||||
@ -78,6 +78,11 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
icon.Size = new Vector2(TextSize - 1);
|
||||
})).ToList()
|
||||
);
|
||||
|
||||
var badgeModesList = group.Playmodes.Select(p => rulesets.GetRuleset(p)?.Name).ToList();
|
||||
|
||||
string modesDisplay = string.Join(", ", badgeModesList);
|
||||
TooltipText += $" ({modesDisplay})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@ -12,6 +13,7 @@ using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Overlays.Profile.Header.Components
|
||||
@ -23,6 +25,10 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
public LocalisableString TooltipText { get; private set; }
|
||||
|
||||
private OsuSpriteText levelText = null!;
|
||||
private Sprite sprite = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour osuColour { get; set; } = null!;
|
||||
|
||||
public LevelBadge()
|
||||
{
|
||||
@ -34,7 +40,7 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Sprite
|
||||
sprite = new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Texture = textures.Get("Profile/levelbadge"),
|
||||
@ -58,9 +64,34 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
|
||||
private void updateLevel(UserStatistics.LevelInfo? levelInfo)
|
||||
{
|
||||
string level = levelInfo?.Current.ToString() ?? "0";
|
||||
levelText.Text = level;
|
||||
TooltipText = UsersStrings.ShowStatsLevel(level);
|
||||
int level = levelInfo?.Current ?? 0;
|
||||
|
||||
levelText.Text = level.ToString();
|
||||
TooltipText = UsersStrings.ShowStatsLevel(level.ToString());
|
||||
|
||||
sprite.Colour = mapLevelToTierColour(level);
|
||||
}
|
||||
|
||||
private ColourInfo mapLevelToTierColour(int level)
|
||||
{
|
||||
var tier = RankingTier.Iron;
|
||||
|
||||
if (level > 0)
|
||||
{
|
||||
tier = (RankingTier)(level / 20);
|
||||
}
|
||||
|
||||
if (level >= 105)
|
||||
{
|
||||
tier = RankingTier.Radiant;
|
||||
}
|
||||
|
||||
if (level >= 110)
|
||||
{
|
||||
tier = RankingTier.Lustrous;
|
||||
}
|
||||
|
||||
return osuColour.ForRankingTier(tier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = SkinSettingsStrings.GameplayCursorDuringTouch,
|
||||
Keywords = new[] { @"touchscreen" },
|
||||
Current = config.GetBindable<bool>(OsuSetting.GameplayCursorDuringTouch)
|
||||
},
|
||||
};
|
||||
|
@ -70,6 +70,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
||||
Add(new SettingsButton
|
||||
{
|
||||
Text = GeneralSettingsStrings.OpenOsuFolder,
|
||||
Keywords = new[] { @"logs", @"files", @"access", "directory" },
|
||||
Action = () => storage.PresentExternally(),
|
||||
});
|
||||
|
||||
|
@ -34,6 +34,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
new SettingsButton
|
||||
{
|
||||
Text = GeneralSettingsStrings.RunSetupWizard,
|
||||
Keywords = new[] { @"first run", @"initial", @"getting started" },
|
||||
TooltipText = FirstRunSetupOverlayStrings.FirstRunSetupDescription,
|
||||
Action = () => firstRunSetupOverlay?.Show(),
|
||||
},
|
||||
|
@ -133,6 +133,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
new SettingsSlider<float>
|
||||
{
|
||||
LabelText = GraphicsSettingsStrings.HorizontalPosition,
|
||||
Keywords = new[] { "screen", "scaling" },
|
||||
Current = scalingPositionX,
|
||||
KeyboardStep = 0.01f,
|
||||
DisplayAsPercentage = true
|
||||
@ -140,6 +141,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
new SettingsSlider<float>
|
||||
{
|
||||
LabelText = GraphicsSettingsStrings.VerticalPosition,
|
||||
Keywords = new[] { "screen", "scaling" },
|
||||
Current = scalingPositionY,
|
||||
KeyboardStep = 0.01f,
|
||||
DisplayAsPercentage = true
|
||||
@ -147,6 +149,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
new SettingsSlider<float>
|
||||
{
|
||||
LabelText = GraphicsSettingsStrings.HorizontalScale,
|
||||
Keywords = new[] { "screen", "scaling" },
|
||||
Current = scalingSizeX,
|
||||
KeyboardStep = 0.01f,
|
||||
DisplayAsPercentage = true
|
||||
@ -154,6 +157,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
||||
new SettingsSlider<float>
|
||||
{
|
||||
LabelText = GraphicsSettingsStrings.VerticalScale,
|
||||
Keywords = new[] { "screen", "scaling" },
|
||||
Current = scalingSizeY,
|
||||
KeyboardStep = 0.01f,
|
||||
DisplayAsPercentage = true
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
{
|
||||
protected override LocalisableString Header => BindingSettingsStrings.ShortcutAndGameplayBindings;
|
||||
|
||||
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "keybindings" });
|
||||
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { @"keybindings", @"controls", @"keyboard", @"keys" });
|
||||
|
||||
public BindingSettings(KeyBindingPanel keyConfig)
|
||||
{
|
||||
|
40
osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs
Normal file
40
osu.Game/Overlays/Settings/Sections/Input/TouchSettings.cs
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Handlers.Touch;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Localisation;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
{
|
||||
public partial class TouchSettings : SettingsSubsection
|
||||
{
|
||||
private readonly TouchHandler handler;
|
||||
|
||||
public TouchSettings(TouchHandler handler)
|
||||
{
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = CommonStrings.Enabled,
|
||||
Current = handler.Enabled
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { @"touchscreen" });
|
||||
|
||||
protected override LocalisableString Header => handler.Description;
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -22,6 +22,8 @@ namespace osu.Game.Overlays.Settings
|
||||
|
||||
public LocalisableString TooltipText { get; set; }
|
||||
|
||||
public IEnumerable<string> Keywords { get; set; } = Array.Empty<string>();
|
||||
|
||||
public BindableBool CanBeShown { get; } = new BindableBool(true);
|
||||
IBindable<bool> IConditionalFilterable.CanBeShown => CanBeShown;
|
||||
|
||||
@ -30,9 +32,13 @@ namespace osu.Game.Overlays.Settings
|
||||
get
|
||||
{
|
||||
if (TooltipText != default)
|
||||
return base.FilterTerms.Append(TooltipText);
|
||||
yield return TooltipText;
|
||||
|
||||
return base.FilterTerms;
|
||||
foreach (string s in Keywords)
|
||||
yield return s;
|
||||
|
||||
foreach (LocalisableString s in base.FilterTerms)
|
||||
yield return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Overlays.Settings
|
||||
Text = game.Name,
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold),
|
||||
},
|
||||
new BuildDisplay(game.Version, DebugUtils.IsDebugBuild)
|
||||
new BuildDisplay(game.Version)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
@ -81,15 +81,13 @@ namespace osu.Game.Overlays.Settings
|
||||
private partial class BuildDisplay : OsuAnimatedButton
|
||||
{
|
||||
private readonly string version;
|
||||
private readonly bool isDebug;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
public BuildDisplay(string version, bool isDebug)
|
||||
public BuildDisplay(string version)
|
||||
{
|
||||
this.version = version;
|
||||
this.isDebug = isDebug;
|
||||
|
||||
Content.RelativeSizeAxes = Axes.Y;
|
||||
Content.AutoSizeAxes = AutoSizeAxes = Axes.X;
|
||||
@ -99,8 +97,7 @@ namespace osu.Game.Overlays.Settings
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(ChangelogOverlay changelog)
|
||||
{
|
||||
if (!isDebug)
|
||||
Action = () => changelog?.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version);
|
||||
Action = () => changelog?.ShowBuild(OsuGameBase.CLIENT_STREAM_NAME, version);
|
||||
|
||||
Add(new OsuSpriteText
|
||||
{
|
||||
@ -110,7 +107,7 @@ namespace osu.Game.Overlays.Settings
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Padding = new MarginPadding(5),
|
||||
Colour = isDebug ? colours.Red : Color4.White,
|
||||
Colour = DebugUtils.IsDebugBuild ? colours.Red : Color4.White,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -125,10 +126,21 @@ namespace osu.Game.Rulesets.Edit
|
||||
public virtual MenuItem[] ContextMenuItems => Array.Empty<MenuItem>();
|
||||
|
||||
/// <summary>
|
||||
/// The screen-space point that causes this <see cref="HitObjectSelectionBlueprint"/> to be selected via a drag.
|
||||
/// The screen-space main point that causes this <see cref="HitObjectSelectionBlueprint"/> to be selected via a drag.
|
||||
/// </summary>
|
||||
public virtual Vector2 ScreenSpaceSelectionPoint => ScreenSpaceDrawQuad.Centre;
|
||||
|
||||
/// <summary>
|
||||
/// Any points that should be used for snapping purposes in addition to <see cref="ScreenSpaceSelectionPoint"/>. Exposed via <see cref="ScreenSpaceSnapPoints"/>.
|
||||
/// </summary>
|
||||
protected virtual Vector2[] ScreenSpaceAdditionalNodes => Array.Empty<Vector2>();
|
||||
|
||||
/// <summary>
|
||||
/// The screen-space collection of base points on this <see cref="HitObjectSelectionBlueprint"/> that other objects can be snapped to.
|
||||
/// The first element of this collection is <see cref="ScreenSpaceSelectionPoint"/>
|
||||
/// </summary>
|
||||
public Vector2[] ScreenSpaceSnapPoints => ScreenSpaceAdditionalNodes.Prepend(ScreenSpaceSelectionPoint).ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// The screen-space quad that outlines this <see cref="HitObjectSelectionBlueprint"/> for selections.
|
||||
/// </summary>
|
||||
|
80
osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs
Normal file
80
osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public class ModAccuracyChallenge : ModFailCondition, IApplicableToScoreProcessor
|
||||
{
|
||||
public override string Name => "Accuracy Challenge";
|
||||
|
||||
public override string Acronym => "AC";
|
||||
|
||||
public override LocalisableString Description => "Fail if your accuracy drops too low!";
|
||||
|
||||
public override ModType Type => ModType.DifficultyIncrease;
|
||||
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(ModEasyWithExtraLives), typeof(ModPerfect) }).ToArray();
|
||||
|
||||
public override bool RequiresConfiguration => false;
|
||||
|
||||
public override string SettingDescription => base.SettingDescription.Replace(MinimumAccuracy.ToString(), MinimumAccuracy.Value.ToString("##%", NumberFormatInfo.InvariantInfo));
|
||||
|
||||
[SettingSource("Minimum accuracy", "Trigger a failure if your accuracy goes below this value.", SettingControlType = typeof(SettingsSlider<double, PercentSlider>))]
|
||||
public BindableNumber<double> MinimumAccuracy { get; } = new BindableDouble
|
||||
{
|
||||
MinValue = 0.60,
|
||||
MaxValue = 0.99,
|
||||
Precision = 0.01,
|
||||
Default = 0.9,
|
||||
Value = 0.9,
|
||||
};
|
||||
|
||||
private ScoreProcessor scoreProcessor = null!;
|
||||
|
||||
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) => this.scoreProcessor = scoreProcessor;
|
||||
|
||||
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
||||
|
||||
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
||||
{
|
||||
if (!result.Type.AffectsAccuracy())
|
||||
return false;
|
||||
|
||||
return getAccuracyWithImminentResultAdded(result) < MinimumAccuracy.Value;
|
||||
}
|
||||
|
||||
private double getAccuracyWithImminentResultAdded(JudgementResult result)
|
||||
{
|
||||
var score = new ScoreInfo { Ruleset = scoreProcessor.Ruleset.RulesetInfo };
|
||||
|
||||
// This is super ugly, but if we don't do it this way we will not have the most recent result added to the accuracy value.
|
||||
// Hopefully we can improve this in the future.
|
||||
scoreProcessor.PopulateScore(score);
|
||||
score.Statistics[result.Type]++;
|
||||
|
||||
return scoreProcessor.ComputeAccuracy(score);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class PercentSlider : OsuSliderBar<double>
|
||||
{
|
||||
public PercentSlider()
|
||||
{
|
||||
DisplayAsPercentage = true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Humanizer;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -19,6 +21,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
};
|
||||
|
||||
public override string SettingDescription => Retries.IsDefault ? string.Empty : $"{"lives".ToQuantity(Retries.Value)}";
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModAccuracyChallenge)).ToArray();
|
||||
|
||||
private int retries;
|
||||
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override LocalisableString Description => "SS or quit.";
|
||||
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModSuddenDeath)).ToArray();
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(ModSuddenDeath), typeof(ModAccuracyChallenge) }).ToArray();
|
||||
|
||||
protected ModPerfect()
|
||||
{
|
||||
|
@ -97,7 +97,11 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// </summary>
|
||||
protected virtual double ClassicScoreMultiplier => 36;
|
||||
|
||||
private readonly Ruleset ruleset;
|
||||
/// <summary>
|
||||
/// The ruleset this score processor is valid for.
|
||||
/// </summary>
|
||||
public readonly Ruleset Ruleset;
|
||||
|
||||
private readonly double accuracyPortion;
|
||||
private readonly double comboPortion;
|
||||
|
||||
@ -145,7 +149,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
|
||||
public ScoreProcessor(Ruleset ruleset)
|
||||
{
|
||||
this.ruleset = ruleset;
|
||||
Ruleset = ruleset;
|
||||
|
||||
accuracyPortion = DefaultAccuracyPortion;
|
||||
comboPortion = DefaultComboPortion;
|
||||
@ -291,8 +295,8 @@ namespace osu.Game.Rulesets.Scoring
|
||||
[Pure]
|
||||
public double ComputeAccuracy(ScoreInfo scoreInfo)
|
||||
{
|
||||
if (!ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
|
||||
throw new ArgumentException($"Unexpected score ruleset. Expected \"{ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
|
||||
if (!Ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
|
||||
throw new ArgumentException($"Unexpected score ruleset. Expected \"{Ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
|
||||
|
||||
// We only extract scoring values from the score's statistics. This is because accuracy is always relative to the point of pass or fail rather than relative to the whole beatmap.
|
||||
extractScoringValues(scoreInfo.Statistics, out var current, out var maximum);
|
||||
@ -312,8 +316,8 @@ namespace osu.Game.Rulesets.Scoring
|
||||
[Pure]
|
||||
public long ComputeScore(ScoringMode mode, ScoreInfo scoreInfo)
|
||||
{
|
||||
if (!ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
|
||||
throw new ArgumentException($"Unexpected score ruleset. Expected \"{ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
|
||||
if (!Ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
|
||||
throw new ArgumentException($"Unexpected score ruleset. Expected \"{Ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
|
||||
|
||||
extractScoringValues(scoreInfo, out var current, out var maximum);
|
||||
|
||||
@ -552,7 +556,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
break;
|
||||
|
||||
default:
|
||||
maxResult = maxBasicResult ??= ruleset.GetHitResults().MaxBy(kvp => Judgement.ToNumericResult(kvp.result)).result;
|
||||
maxResult = maxBasicResult ??= Ruleset.GetHitResults().MaxBy(kvp => Judgement.ToNumericResult(kvp.result)).result;
|
||||
break;
|
||||
}
|
||||
|
||||
|
17
osu.Game/Scoring/RankingTier.cs
Normal file
17
osu.Game/Scoring/RankingTier.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Scoring
|
||||
{
|
||||
public enum RankingTier
|
||||
{
|
||||
Iron,
|
||||
Bronze,
|
||||
Silver,
|
||||
Gold,
|
||||
Platinum,
|
||||
Rhodium,
|
||||
Radiant,
|
||||
Lustrous
|
||||
}
|
||||
}
|
@ -439,7 +439,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
#region Selection Movement
|
||||
|
||||
private Vector2[] movementBlueprintOriginalPositions;
|
||||
private Vector2[][] movementBlueprintsOriginalPositions;
|
||||
private SelectionBlueprint<T>[] movementBlueprints;
|
||||
private bool isDraggingBlueprint;
|
||||
|
||||
@ -459,7 +459,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
// Movement is tracked from the blueprint of the earliest item, since it only makes sense to distance snap from that item
|
||||
movementBlueprints = SortForMovement(SelectionHandler.SelectedBlueprints).ToArray();
|
||||
movementBlueprintOriginalPositions = movementBlueprints.Select(m => m.ScreenSpaceSelectionPoint).ToArray();
|
||||
movementBlueprintsOriginalPositions = movementBlueprints.Select(m => m.ScreenSpaceSnapPoints).ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -480,26 +480,15 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
if (movementBlueprints == null)
|
||||
return false;
|
||||
|
||||
Debug.Assert(movementBlueprintOriginalPositions != null);
|
||||
Debug.Assert(movementBlueprintsOriginalPositions != null);
|
||||
|
||||
Vector2 distanceTravelled = e.ScreenSpaceMousePosition - e.ScreenSpaceMouseDownPosition;
|
||||
|
||||
if (snapProvider != null)
|
||||
{
|
||||
// check for positional snap for every object in selection (for things like object-object snapping)
|
||||
for (int i = 0; i < movementBlueprintOriginalPositions.Length; i++)
|
||||
for (int i = 0; i < movementBlueprints.Length; i++)
|
||||
{
|
||||
Vector2 originalPosition = movementBlueprintOriginalPositions[i];
|
||||
var testPosition = originalPosition + distanceTravelled;
|
||||
|
||||
var positionalResult = snapProvider.FindSnappedPositionAndTime(testPosition, SnapType.NearbyObjects);
|
||||
|
||||
if (positionalResult.ScreenSpacePosition == testPosition) continue;
|
||||
|
||||
var delta = positionalResult.ScreenSpacePosition - movementBlueprints[i].ScreenSpaceSelectionPoint;
|
||||
|
||||
// attempt to move the objects, and abort any time based snapping if we can.
|
||||
if (SelectionHandler.HandleMovement(new MoveSelectionEvent<T>(movementBlueprints[i], delta)))
|
||||
if (checkSnappingBlueprintToNearbyObjects(movementBlueprints[i], distanceTravelled, movementBlueprintsOriginalPositions[i]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -508,7 +497,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
// item in the selection.
|
||||
|
||||
// The final movement position, relative to movementBlueprintOriginalPosition.
|
||||
Vector2 movePosition = movementBlueprintOriginalPositions.First() + distanceTravelled;
|
||||
Vector2 movePosition = movementBlueprintsOriginalPositions.First().First() + distanceTravelled;
|
||||
|
||||
// Retrieve a snapped position.
|
||||
var result = snapProvider?.FindSnappedPositionAndTime(movePosition, ~SnapType.NearbyObjects);
|
||||
@ -521,6 +510,36 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
return ApplySnapResult(movementBlueprints, result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for positional snap for given blueprint.
|
||||
/// </summary>
|
||||
/// <param name="blueprint">The blueprint to check for snapping.</param>
|
||||
/// <param name="distanceTravelled">Distance travelled since start of dragging action.</param>
|
||||
/// <param name="originalPositions">The snap positions of blueprint before start of dragging action.</param>
|
||||
/// <returns>Whether an object to snap to was found.</returns>
|
||||
private bool checkSnappingBlueprintToNearbyObjects(SelectionBlueprint<T> blueprint, Vector2 distanceTravelled, Vector2[] originalPositions)
|
||||
{
|
||||
var currentPositions = blueprint.ScreenSpaceSnapPoints;
|
||||
|
||||
for (int i = 0; i < originalPositions.Length; i++)
|
||||
{
|
||||
Vector2 originalPosition = originalPositions[i];
|
||||
var testPosition = originalPosition + distanceTravelled;
|
||||
|
||||
var positionalResult = snapProvider.FindSnappedPositionAndTime(testPosition, SnapType.NearbyObjects);
|
||||
|
||||
if (positionalResult.ScreenSpacePosition == testPosition) continue;
|
||||
|
||||
var delta = positionalResult.ScreenSpacePosition - currentPositions[i];
|
||||
|
||||
// attempt to move the objects, and abort any time based snapping if we can.
|
||||
if (SelectionHandler.HandleMovement(new MoveSelectionEvent<T>(blueprint, delta)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual bool ApplySnapResult(SelectionBlueprint<T>[] blueprints, SnapResult result) =>
|
||||
SelectionHandler.HandleMovement(new MoveSelectionEvent<T>(blueprints.First(), result.ScreenSpacePosition - blueprints.First().ScreenSpaceSelectionPoint));
|
||||
|
||||
@ -533,7 +552,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
if (movementBlueprints == null)
|
||||
return false;
|
||||
|
||||
movementBlueprintOriginalPositions = null;
|
||||
movementBlueprintsOriginalPositions = null;
|
||||
movementBlueprints = null;
|
||||
|
||||
return true;
|
||||
|
@ -51,6 +51,7 @@ namespace osu.Game.Screens.Play
|
||||
private const float duration = 2500;
|
||||
|
||||
private ISample? failSample;
|
||||
private SampleChannel? failSampleChannel;
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; } = null!;
|
||||
@ -119,13 +120,13 @@ namespace osu.Game.Screens.Play
|
||||
this.TransformBindableTo(trackFreq, 0, duration).OnComplete(_ =>
|
||||
{
|
||||
// Don't reset frequency as the pause screen may appear post transform, causing a second frequency sweep.
|
||||
RemoveFilters(false);
|
||||
removeFilters(false);
|
||||
OnComplete?.Invoke();
|
||||
});
|
||||
|
||||
failHighPassFilter.CutoffTo(300);
|
||||
failLowPassFilter.CutoffTo(300, duration, Easing.OutCubic);
|
||||
failSample?.Play();
|
||||
failSampleChannel = failSample?.Play();
|
||||
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, trackFreq);
|
||||
track.AddAdjustment(AdjustableProperty.Volume, volumeAdjustment);
|
||||
@ -153,7 +154,16 @@ namespace osu.Game.Screens.Play
|
||||
Background?.FadeColour(OsuColour.Gray(0.3f), 60);
|
||||
}
|
||||
|
||||
public void RemoveFilters(bool resetTrackFrequency = true)
|
||||
/// <summary>
|
||||
/// Stops any and all persistent effects added by the ongoing fail animation.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
failSampleChannel?.Stop();
|
||||
removeFilters();
|
||||
}
|
||||
|
||||
private void removeFilters(bool resetTrackFrequency = true)
|
||||
{
|
||||
filtersRemoved = true;
|
||||
|
||||
|
@ -1072,7 +1072,7 @@ namespace osu.Game.Screens.Play
|
||||
public override bool OnExiting(ScreenExitEvent e)
|
||||
{
|
||||
screenSuspension?.RemoveAndDisposeImmediately();
|
||||
failAnimationLayer?.RemoveFilters();
|
||||
failAnimationLayer?.Stop();
|
||||
|
||||
if (LoadedBeatmapSuccessfully)
|
||||
{
|
||||
|
@ -30,14 +30,16 @@ namespace osu.Game.Screens.Select.Details
|
||||
{
|
||||
public partial class AdvancedStats : Container
|
||||
{
|
||||
[Resolved]
|
||||
private BeatmapDifficultyCache difficultyCache { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||
private OsuGameBase game { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapDifficultyCache difficultyCache { get; set; }
|
||||
private IBindable<RulesetInfo> gameRuleset;
|
||||
|
||||
protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate;
|
||||
private readonly StatisticRow starDifficulty;
|
||||
@ -84,7 +86,13 @@ namespace osu.Game.Screens.Select.Details
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ruleset.BindValueChanged(_ => updateStatistics());
|
||||
// the cached ruleset bindable might be a decoupled bindable provided by SongSelect,
|
||||
// which we can't rely on in combination with the game-wide selected mods list,
|
||||
// since mods could be updated to the new ruleset instances while the decoupled bindable is held behind,
|
||||
// therefore resulting in performing difficulty calculation with invalid states.
|
||||
gameRuleset = game.Ruleset.GetBoundCopy();
|
||||
gameRuleset.BindValueChanged(_ => updateStatistics());
|
||||
|
||||
mods.BindValueChanged(modsChanged, true);
|
||||
}
|
||||
|
||||
@ -142,7 +150,14 @@ namespace osu.Game.Screens.Select.Details
|
||||
|
||||
private CancellationTokenSource starDifficultyCancellationSource;
|
||||
|
||||
private void updateStarDifficulty()
|
||||
/// <summary>
|
||||
/// Updates the displayed star difficulty statistics with the values provided by the currently-selected beatmap, ruleset, and selected mods.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is scheduled to avoid scenarios wherein a ruleset changes first before selected mods do,
|
||||
/// potentially resulting in failure during difficulty calculation due to incomplete bindable state updates.
|
||||
/// </remarks>
|
||||
private void updateStarDifficulty() => Scheduler.AddOnce(() =>
|
||||
{
|
||||
starDifficultyCancellationSource?.Cancel();
|
||||
|
||||
@ -151,8 +166,8 @@ namespace osu.Game.Screens.Select.Details
|
||||
|
||||
starDifficultyCancellationSource = new CancellationTokenSource();
|
||||
|
||||
var normalStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, ruleset.Value, null, starDifficultyCancellationSource.Token);
|
||||
var moddedStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, ruleset.Value, mods.Value, starDifficultyCancellationSource.Token);
|
||||
var normalStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, gameRuleset.Value, null, starDifficultyCancellationSource.Token);
|
||||
var moddedStarDifficultyTask = difficultyCache.GetDifficultyAsync(BeatmapInfo, gameRuleset.Value, mods.Value, starDifficultyCancellationSource.Token);
|
||||
|
||||
Task.WhenAll(normalStarDifficultyTask, moddedStarDifficultyTask).ContinueWith(_ => Schedule(() =>
|
||||
{
|
||||
@ -164,7 +179,7 @@ namespace osu.Game.Screens.Select.Details
|
||||
|
||||
starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars);
|
||||
}), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current);
|
||||
}
|
||||
});
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
|
@ -104,6 +104,9 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
|
||||
protected override APIRequest? FetchScores(CancellationToken cancellationToken)
|
||||
{
|
||||
scoreRetrievalRequest?.Cancel();
|
||||
scoreRetrievalRequest = null;
|
||||
|
||||
var fetchBeatmapInfo = BeatmapInfo;
|
||||
|
||||
if (fetchBeatmapInfo == null)
|
||||
@ -152,8 +155,6 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
else if (filterMods)
|
||||
requestMods = mods.Value;
|
||||
|
||||
scoreRetrievalRequest?.Cancel();
|
||||
|
||||
var newRequest = new GetScoresRequest(fetchBeatmapInfo, fetchRuleset, Scope, requestMods);
|
||||
newRequest.Success += response => Schedule(() =>
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user