mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 15:07:44 +08:00
Merge branch 'master' into add-offset-adjust-tooltip-text
This commit is contained in:
commit
bbc2b36117
@ -50,7 +50,7 @@ Please make sure you have the following prerequisites:
|
||||
|
||||
- A desktop platform with the [.NET 6.0 SDK](https://dotnet.microsoft.com/download) installed.
|
||||
- When developing with mobile, [Xamarin](https://docs.microsoft.com/en-us/xamarin/) is required, which is shipped together with Visual Studio or [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/).
|
||||
- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as [Visual Studio 2019+](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
|
||||
- When working with the codebase, we recommend using an IDE with intelligent code completion and syntax highlighting, such as the latest version of [Visual Studio](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
|
||||
- When running on Linux, please have a system-wide FFmpeg installation available to support video decoding.
|
||||
|
||||
### Downloading the source code
|
||||
@ -72,7 +72,7 @@ git pull
|
||||
|
||||
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this is provided [below](#contributing).
|
||||
|
||||
- Visual Studio / Rider users should load the project via one of the platform-specific `.slnf` files, rather than the main `.sln.` This will allow access to template run configurations.
|
||||
- Visual Studio / Rider users should load the project via one of the platform-specific `.slnf` files, rather than the main `.sln`. This will allow access to template run configurations.
|
||||
|
||||
You can also build and run *osu!* from the command-line with a single command:
|
||||
|
||||
|
@ -51,7 +51,7 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.304.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.223.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Rulesets;
|
||||
@ -108,10 +109,7 @@ namespace osu.Desktop
|
||||
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.GlobalRank > 0 ? $" (rank #{user.Value.Statistics.GlobalRank:N0})" : string.Empty);
|
||||
|
||||
// update ruleset
|
||||
int onlineID = ruleset.Value.OnlineID;
|
||||
bool isLegacyRuleset = onlineID >= 0 && onlineID <= ILegacyRuleset.MAX_LEGACY_RULESET_ID;
|
||||
|
||||
presence.Assets.SmallImageKey = isLegacyRuleset ? $"mode_{onlineID}" : "mode_custom";
|
||||
presence.Assets.SmallImageKey = ruleset.Value.IsLegacyRuleset() ? $"mode_{ruleset.Value.OnlineID}" : "mode_custom";
|
||||
presence.Assets.SmallImageText = ruleset.Value.Name;
|
||||
|
||||
client.SetPresence(presence);
|
||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDisplay()
|
||||
public void TestCalibrationFromZero()
|
||||
{
|
||||
const double average_error = -4.5;
|
||||
|
||||
@ -70,5 +70,33 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("Remove reference score", () => offsetControl.ReferenceScore.Value = null);
|
||||
AddAssert("No calibration button", () => !offsetControl.ChildrenOfType<SettingsButton>().Any());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When a beatmap offset was already set, the calibration should take it into account.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCalibrationFromNonZero()
|
||||
{
|
||||
const double average_error = -4.5;
|
||||
const double initial_offset = -2;
|
||||
|
||||
AddStep("Set offset non-neutral", () => offsetControl.Current.Value = initial_offset);
|
||||
AddAssert("No calibration button", () => !offsetControl.ChildrenOfType<SettingsButton>().Any());
|
||||
AddStep("Set reference score", () =>
|
||||
{
|
||||
offsetControl.ReferenceScore.Value = new ScoreInfo
|
||||
{
|
||||
HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(average_error)
|
||||
};
|
||||
});
|
||||
|
||||
AddAssert("Has calibration button", () => offsetControl.ChildrenOfType<SettingsButton>().Any());
|
||||
AddStep("Press button", () => offsetControl.ChildrenOfType<SettingsButton>().Single().TriggerClick());
|
||||
AddAssert("Offset is adjusted", () => offsetControl.Current.Value == initial_offset - average_error);
|
||||
|
||||
AddAssert("Button is disabled", () => !offsetControl.ChildrenOfType<SettingsButton>().Single().Enabled.Value);
|
||||
AddStep("Remove reference score", () => offsetControl.ReferenceScore.Value = null);
|
||||
AddAssert("No calibration button", () => !offsetControl.ChildrenOfType<SettingsButton>().Any());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,12 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
createTest(CreateDistributedHitEvents());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestManyDistributedEventsOffset()
|
||||
{
|
||||
createTest(CreateDistributedHitEvents(-3.5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAroundCentre()
|
||||
{
|
||||
|
@ -119,7 +119,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
AddStep(@"Network failure", () => leaderboard.SetErrorState(LeaderboardState.NetworkFailure));
|
||||
AddStep(@"No supporter", () => leaderboard.SetErrorState(LeaderboardState.NotSupporter));
|
||||
AddStep(@"Not logged in", () => leaderboard.SetErrorState(LeaderboardState.NotLoggedIn));
|
||||
AddStep(@"Unavailable", () => leaderboard.SetErrorState(LeaderboardState.Unavailable));
|
||||
AddStep(@"Ruleset unavailable", () => leaderboard.SetErrorState(LeaderboardState.RulesetUnavailable));
|
||||
AddStep(@"Beatmap unavailable", () => leaderboard.SetErrorState(LeaderboardState.BeatmapUnavailable));
|
||||
AddStep(@"None selected", () => leaderboard.SetErrorState(LeaderboardState.NoneSelected));
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Rulesets;
|
||||
@ -83,7 +84,7 @@ namespace osu.Game.Beatmaps
|
||||
requestedUserId = api.LocalUser.Value.Id;
|
||||
|
||||
// only query API for built-in rulesets
|
||||
rulesets.AvailableRulesets.Where(ruleset => ruleset.OnlineID >= 0 && ruleset.OnlineID <= ILegacyRuleset.MAX_LEGACY_RULESET_ID).ForEach(rulesetInfo =>
|
||||
rulesets.AvailableRulesets.Where(ruleset => ruleset.IsLegacyRuleset()).ForEach(rulesetInfo =>
|
||||
{
|
||||
var req = new GetUserRequest(api.LocalUser.Value.Id, rulesetInfo);
|
||||
|
||||
|
@ -240,9 +240,9 @@ namespace osu.Game.Configuration
|
||||
};
|
||||
}
|
||||
|
||||
public Func<Guid, string> LookupSkinName { private get; set; }
|
||||
public Func<Guid, string> LookupSkinName { private get; set; } = _ => @"unknown";
|
||||
|
||||
public Func<GlobalAction, LocalisableString> LookupKeyBindings { get; set; }
|
||||
public Func<GlobalAction, LocalisableString> LookupKeyBindings { get; set; } = _ => @"unknown";
|
||||
}
|
||||
|
||||
// IMPORTANT: These are used in user configuration files.
|
||||
|
@ -72,6 +72,11 @@ namespace osu.Game.Extensions
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whether this <see cref="IRulesetInfo"/>'s online ID is within the range that defines it as a legacy ruleset (ie. either osu!, osu!taiko, osu!catch or osu!mania).
|
||||
/// </summary>
|
||||
public static bool IsLegacyRuleset(this IRulesetInfo ruleset) => ruleset.OnlineID >= 0 && ruleset.OnlineID <= ILegacyRuleset.MAX_LEGACY_RULESET_ID;
|
||||
|
||||
/// <summary>
|
||||
/// Check whether the online ID of two <see cref="IBeatmapSetInfo"/>s match.
|
||||
/// </summary>
|
||||
|
@ -5,6 +5,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Screens;
|
||||
@ -38,24 +39,24 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
private BackgroundScreenStack backgroundStack;
|
||||
|
||||
private bool allowScaling = true;
|
||||
private RectangleF? customRect;
|
||||
private bool customRectIsRelativePosition;
|
||||
|
||||
/// <summary>
|
||||
/// Whether user scaling preferences should be applied. Enabled by default.
|
||||
/// Set a custom position and scale which overrides any user specification.
|
||||
/// </summary>
|
||||
public bool AllowScaling
|
||||
/// <param name="rect">A rectangle with positional and sizing information for this container to conform to. <c>null</c> will clear the custom rect and revert to user settings.</param>
|
||||
/// <param name="relativePosition">Whether the position portion of the provided rect is in relative coordinate space or not.</param>
|
||||
public void SetCustomRect(RectangleF? rect, bool relativePosition = false)
|
||||
{
|
||||
get => allowScaling;
|
||||
set
|
||||
{
|
||||
if (value == allowScaling)
|
||||
return;
|
||||
customRect = rect;
|
||||
customRectIsRelativePosition = relativePosition;
|
||||
|
||||
allowScaling = value;
|
||||
if (IsLoaded) Scheduler.AddOnce(updateSize);
|
||||
}
|
||||
if (IsLoaded) Scheduler.AddOnce(updateSize);
|
||||
}
|
||||
|
||||
private const float corner_radius = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance.
|
||||
/// </summary>
|
||||
@ -69,7 +70,7 @@ namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
CornerRadius = 10,
|
||||
CornerRadius = corner_radius,
|
||||
Child = content = new ScalingDrawSizePreservingFillContainer(targetMode != ScalingMode.Gameplay)
|
||||
};
|
||||
}
|
||||
@ -137,7 +138,7 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
private void updateSize()
|
||||
{
|
||||
const float fade_time = 500;
|
||||
const float duration = 500;
|
||||
|
||||
if (targetMode == ScalingMode.Everything)
|
||||
{
|
||||
@ -156,17 +157,31 @@ namespace osu.Game.Graphics.Containers
|
||||
backgroundStack.Push(new ScalingBackgroundScreen());
|
||||
}
|
||||
|
||||
backgroundStack.FadeIn(fade_time);
|
||||
backgroundStack.FadeIn(duration);
|
||||
}
|
||||
else
|
||||
backgroundStack?.FadeOut(fade_time);
|
||||
backgroundStack?.FadeOut(duration);
|
||||
}
|
||||
|
||||
bool scaling = AllowScaling && (targetMode == null || scalingMode.Value == targetMode);
|
||||
RectangleF targetRect = new RectangleF(Vector2.Zero, Vector2.One);
|
||||
|
||||
var targetSize = scaling ? new Vector2(sizeX.Value, sizeY.Value) : Vector2.One;
|
||||
var targetPosition = scaling ? new Vector2(posX.Value, posY.Value) * (Vector2.One - targetSize) : Vector2.Zero;
|
||||
bool requiresMasking = (scaling && targetSize != Vector2.One)
|
||||
if (customRect != null)
|
||||
{
|
||||
sizableContainer.RelativePositionAxes = customRectIsRelativePosition ? Axes.Both : Axes.None;
|
||||
|
||||
targetRect = customRect.Value;
|
||||
}
|
||||
else if (targetMode == null || scalingMode.Value == targetMode)
|
||||
{
|
||||
sizableContainer.RelativePositionAxes = Axes.Both;
|
||||
|
||||
Vector2 scale = new Vector2(sizeX.Value, sizeY.Value);
|
||||
Vector2 pos = new Vector2(posX.Value, posY.Value) * (Vector2.One - scale);
|
||||
|
||||
targetRect = new RectangleF(pos, scale);
|
||||
}
|
||||
|
||||
bool requiresMasking = targetRect.Size != Vector2.One
|
||||
// For the top level scaling container, for now we apply masking if safe areas are in use.
|
||||
// In the future this can likely be removed as more of the actual UI supports overflowing into the safe areas.
|
||||
|| (targetMode == ScalingMode.Everything && safeAreaPadding.Value.Total != Vector2.Zero);
|
||||
@ -174,8 +189,14 @@ namespace osu.Game.Graphics.Containers
|
||||
if (requiresMasking)
|
||||
sizableContainer.Masking = true;
|
||||
|
||||
sizableContainer.MoveTo(targetPosition, 500, Easing.OutQuart);
|
||||
sizableContainer.ResizeTo(targetSize, 500, Easing.OutQuart).OnComplete(_ => { sizableContainer.Masking = requiresMasking; });
|
||||
sizableContainer.MoveTo(targetRect.Location, duration, Easing.OutQuart);
|
||||
sizableContainer.ResizeTo(targetRect.Size, duration, Easing.OutQuart);
|
||||
|
||||
// Of note, this will not work great in the case of nested ScalingContainers where multiple are applying corner radius.
|
||||
// Masking and corner radius should likely only be applied at one point in the full game stack to fix this.
|
||||
// An example of how this can occur is when the skin editor is visible and the game screen scaling is set to "Everything".
|
||||
sizableContainer.TransformTo(nameof(CornerRadius), requiresMasking ? corner_radius : 0, duration, requiresMasking ? Easing.OutQuart : Easing.None)
|
||||
.OnComplete(_ => { sizableContainer.Masking = requiresMasking; });
|
||||
}
|
||||
|
||||
private class ScalingBackgroundScreen : BackgroundScreenDefault
|
||||
|
@ -140,6 +140,7 @@ namespace osu.Game.Graphics.Cursor
|
||||
// Scale to [-0.75, 0.75] so that the sample isn't fully panned left or right (sounds weird)
|
||||
channel.Balance.Value = ((activeCursor.X / DrawWidth) * 2 - 1) * 0.75;
|
||||
channel.Frequency.Value = baseFrequency - (random_range / 2f) + RNG.NextDouble(random_range);
|
||||
channel.Volume.Value = baseFrequency;
|
||||
|
||||
channel.Play();
|
||||
}
|
||||
|
49
osu.Game/Localisation/LeaderboardStrings.cs
Normal file
49
osu.Game/Localisation/LeaderboardStrings.cs
Normal file
@ -0,0 +1,49 @@
|
||||
// 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 osu.Framework.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation
|
||||
{
|
||||
public static class LeaderboardStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.Leaderboard";
|
||||
|
||||
/// <summary>
|
||||
/// "Couldn't fetch scores!"
|
||||
/// </summary>
|
||||
public static LocalisableString CouldntFetchScores => new TranslatableString(getKey(@"couldnt_fetch_scores"), @"Couldn't fetch scores!");
|
||||
|
||||
/// <summary>
|
||||
/// "Please select a beatmap!"
|
||||
/// </summary>
|
||||
public static LocalisableString PleaseSelectABeatmap => new TranslatableString(getKey(@"please_select_a_beatmap"), @"Please select a beatmap!");
|
||||
|
||||
/// <summary>
|
||||
/// "Leaderboards are not available for this ruleset!"
|
||||
/// </summary>
|
||||
public static LocalisableString LeaderboardsAreNotAvailableForThisRuleset => new TranslatableString(getKey(@"leaderboards_are_not_available_for_this_ruleset"), @"Leaderboards are not available for this ruleset!");
|
||||
|
||||
/// <summary>
|
||||
/// "Leaderboards are not available for this beatmap!"
|
||||
/// </summary>
|
||||
public static LocalisableString LeaderboardsAreNotAvailableForThisBeatmap => new TranslatableString(getKey(@"leaderboards_are_not_available_for_this_beatmap"), @"Leaderboards are not available for this beatmap!");
|
||||
|
||||
/// <summary>
|
||||
/// "No records yet!"
|
||||
/// </summary>
|
||||
public static LocalisableString NoRecordsYet => new TranslatableString(getKey(@"no_records_yet"), @"No records yet!");
|
||||
|
||||
/// <summary>
|
||||
/// "Please sign in to view online leaderboards!"
|
||||
/// </summary>
|
||||
public static LocalisableString PleaseSignInToViewOnlineLeaderboards => new TranslatableString(getKey(@"please_sign_in_to_view_online_leaderboards"), @"Please sign in to view online leaderboards!");
|
||||
|
||||
/// <summary>
|
||||
/// "Please invest in an osu!supporter tag to view this leaderboard!"
|
||||
/// </summary>
|
||||
public static LocalisableString PleaseInvestInAnOsuSupporterTagToViewThisLeaderboard => new TranslatableString(getKey(@"please_invest_in_an_osu_supporter_tag_to_view_this_leaderboard"), @"Please invest in an osu!supporter tag to view this leaderboard!");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
@ -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 osu.Game.Extensions;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
|
||||
namespace osu.Game.Online.API.Requests
|
||||
@ -8,14 +10,14 @@ namespace osu.Game.Online.API.Requests
|
||||
public class GetWikiRequest : APIRequest<APIWikiPage>
|
||||
{
|
||||
private readonly string path;
|
||||
private readonly string locale;
|
||||
private readonly Language language;
|
||||
|
||||
public GetWikiRequest(string path, string locale = "en")
|
||||
public GetWikiRequest(string path, Language language = Language.en)
|
||||
{
|
||||
this.path = path;
|
||||
this.locale = locale;
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
protected override string Target => $"wiki/{locale}/{path}";
|
||||
protected override string Target => $"wiki/{language.ToCultureCode()}/{path}";
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ using osu.Game.Online.API;
|
||||
using osu.Game.Online.Placeholders;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Localisation;
|
||||
|
||||
namespace osu.Game.Online.Leaderboards
|
||||
{
|
||||
@ -311,25 +312,28 @@ namespace osu.Game.Online.Leaderboards
|
||||
switch (state)
|
||||
{
|
||||
case LeaderboardState.NetworkFailure:
|
||||
return new ClickablePlaceholder(@"Couldn't fetch scores!", FontAwesome.Solid.Sync)
|
||||
return new ClickablePlaceholder(LeaderboardStrings.CouldntFetchScores, FontAwesome.Solid.Sync)
|
||||
{
|
||||
Action = RefetchScores
|
||||
};
|
||||
|
||||
case LeaderboardState.NoneSelected:
|
||||
return new MessagePlaceholder(@"Please select a beatmap!");
|
||||
return new MessagePlaceholder(LeaderboardStrings.PleaseSelectABeatmap);
|
||||
|
||||
case LeaderboardState.Unavailable:
|
||||
return new MessagePlaceholder(@"Leaderboards are not available for this beatmap!");
|
||||
case LeaderboardState.RulesetUnavailable:
|
||||
return new MessagePlaceholder(LeaderboardStrings.LeaderboardsAreNotAvailableForThisRuleset);
|
||||
|
||||
case LeaderboardState.BeatmapUnavailable:
|
||||
return new MessagePlaceholder(LeaderboardStrings.LeaderboardsAreNotAvailableForThisBeatmap);
|
||||
|
||||
case LeaderboardState.NoScores:
|
||||
return new MessagePlaceholder(@"No records yet!");
|
||||
return new MessagePlaceholder(LeaderboardStrings.NoRecordsYet);
|
||||
|
||||
case LeaderboardState.NotLoggedIn:
|
||||
return new LoginPlaceholder(@"Please sign in to view online leaderboards!");
|
||||
return new LoginPlaceholder(LeaderboardStrings.PleaseSignInToViewOnlineLeaderboards);
|
||||
|
||||
case LeaderboardState.NotSupporter:
|
||||
return new MessagePlaceholder(@"Please invest in an osu!supporter tag to view this leaderboard!");
|
||||
return new MessagePlaceholder(LeaderboardStrings.PleaseInvestInAnOsuSupporterTagToViewThisLeaderboard);
|
||||
|
||||
case LeaderboardState.Retrieving:
|
||||
return null;
|
||||
|
@ -8,7 +8,8 @@ namespace osu.Game.Online.Leaderboards
|
||||
Success,
|
||||
Retrieving,
|
||||
NetworkFailure,
|
||||
Unavailable,
|
||||
BeatmapUnavailable,
|
||||
RulesetUnavailable,
|
||||
NoneSelected,
|
||||
NoScores,
|
||||
NotLoggedIn,
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Online.Placeholders
|
||||
@ -12,7 +13,7 @@ namespace osu.Game.Online.Placeholders
|
||||
[Resolved(CanBeNull = true)]
|
||||
private LoginOverlay login { get; set; }
|
||||
|
||||
public LoginPlaceholder(string actionMessage)
|
||||
public LoginPlaceholder(LocalisableString actionMessage)
|
||||
: base(actionMessage, FontAwesome.Solid.UserLock)
|
||||
{
|
||||
Action = () => login?.Show();
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
@ -63,11 +64,17 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
};
|
||||
}
|
||||
|
||||
private CancellationTokenSource cancellationTokenSource;
|
||||
|
||||
private void updateDisplay(APIUser user)
|
||||
{
|
||||
var badges = user.Badges;
|
||||
cancellationTokenSource?.Cancel();
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
badgeFlowContainer.Clear();
|
||||
|
||||
var badges = user.Badges;
|
||||
|
||||
if (badges?.Length > 0)
|
||||
{
|
||||
Show();
|
||||
@ -79,7 +86,7 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
{
|
||||
// load in stable order regardless of async load order.
|
||||
badgeFlowContainer.Insert(displayIndex, asyncBadge);
|
||||
});
|
||||
}, cancellationTokenSource.Token);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -87,5 +94,11 @@ namespace osu.Game.Overlays.Profile.Header
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
cancellationTokenSource?.Cancel();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,9 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
public class SettingsToolboxGroup : Container, IExpandable
|
||||
{
|
||||
public const int CONTAINER_WIDTH = 270;
|
||||
|
||||
private const float transition_duration = 250;
|
||||
private const int container_width = 270;
|
||||
private const int border_thickness = 2;
|
||||
private const int header_height = 30;
|
||||
private const int corner_radius = 5;
|
||||
@ -49,7 +50,7 @@ namespace osu.Game.Overlays
|
||||
public SettingsToolboxGroup(string title)
|
||||
{
|
||||
AutoSizeAxes = Axes.Y;
|
||||
Width = container_width;
|
||||
Width = CONTAINER_WIDTH;
|
||||
Masking = true;
|
||||
CornerRadius = corner_radius;
|
||||
BorderColour = Color4.Black;
|
||||
@ -201,7 +202,5 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e) => true;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
@ -100,7 +101,12 @@ namespace osu.Game.Overlays
|
||||
cancellationToken?.Cancel();
|
||||
request?.Cancel();
|
||||
|
||||
request = new GetWikiRequest(e.NewValue);
|
||||
string[] values = e.NewValue.Split('/', 2);
|
||||
|
||||
if (values.Length > 1 && LanguageExtensions.TryParseCultureCode(values[0], out var language))
|
||||
request = new GetWikiRequest(values[1], language);
|
||||
else
|
||||
request = new GetWikiRequest(e.NewValue);
|
||||
|
||||
Loading.Show();
|
||||
|
||||
|
@ -5,8 +5,19 @@ using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface for <see cref="Mod"/>s that are updated every frame by a <see cref="Playfield"/>.
|
||||
/// </summary>
|
||||
public interface IUpdatableByPlayfield : IApplicableMod
|
||||
{
|
||||
/// <summary>
|
||||
/// Update this <see cref="Mod"/>.
|
||||
/// </summary>
|
||||
/// <param name="playfield">The main <see cref="Playfield"/></param>
|
||||
/// <remarks>
|
||||
/// This method is called once per frame during gameplay by the main <see cref="Playfield"/> only.
|
||||
/// To access nested <see cref="Playfield"/>s, use <see cref="Playfield.NestedPlayfields"/>.
|
||||
/// </remarks>
|
||||
void Update(Playfield playfield);
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,11 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
private readonly List<Playfield> nestedPlayfields = new List<Playfield>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="Playfield"/> is nested in another <see cref="Playfield"/>.
|
||||
/// </summary>
|
||||
public bool IsNested { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether judgements should be displayed by this and and all nested <see cref="Playfield"/>s.
|
||||
/// </summary>
|
||||
@ -206,6 +211,8 @@ namespace osu.Game.Rulesets.UI
|
||||
/// <param name="otherPlayfield">The <see cref="Playfield"/> to add.</param>
|
||||
protected void AddNested(Playfield otherPlayfield)
|
||||
{
|
||||
otherPlayfield.IsNested = true;
|
||||
|
||||
otherPlayfield.DisplayJudgements.BindTo(DisplayJudgements);
|
||||
|
||||
otherPlayfield.NewResult += (d, r) => NewResult?.Invoke(d, r);
|
||||
@ -229,7 +236,7 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (mods != null)
|
||||
if (!IsNested && mods != null)
|
||||
{
|
||||
foreach (var mod in mods)
|
||||
{
|
||||
|
@ -7,9 +7,9 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.IO.Legacy;
|
||||
using osu.Game.Replays.Legacy;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
@ -48,7 +48,7 @@ namespace osu.Game.Scoring.Legacy
|
||||
if (beatmap == null && !score.Replay.Frames.All(f => f is LegacyReplayFrame))
|
||||
throw new ArgumentException(@"Beatmap must be provided if frames are not already legacy frames.", nameof(beatmap));
|
||||
|
||||
if (score.ScoreInfo.Ruleset.OnlineID < 0 || score.ScoreInfo.Ruleset.OnlineID > ILegacyRuleset.MAX_LEGACY_RULESET_ID)
|
||||
if (!score.ScoreInfo.Ruleset.IsLegacyRuleset())
|
||||
throw new ArgumentException(@"Only scores in the osu, taiko, catch, or mania rulesets can be encoded to the legacy score format.", nameof(score));
|
||||
}
|
||||
|
||||
|
@ -143,6 +143,8 @@ namespace osu.Game.Screens.Play
|
||||
muteWarningShownOnce = sessionStatics.GetBindable<bool>(Static.MutedAudioNotificationShownOnce);
|
||||
batteryWarningShownOnce = sessionStatics.GetBindable<bool>(Static.LowBatteryNotificationShownOnce);
|
||||
|
||||
const float padding = 25;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
(content = new LogoTrackingContainer
|
||||
@ -158,20 +160,27 @@ namespace osu.Game.Screens.Play
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
PlayerSettings = new FillFlowContainer<PlayerSettingsGroup>
|
||||
new OsuScrollContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 20),
|
||||
Margin = new MarginPadding(25),
|
||||
Children = new PlayerSettingsGroup[]
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = SettingsToolboxGroup.CONTAINER_WIDTH + padding * 2,
|
||||
Padding = new MarginPadding { Vertical = padding },
|
||||
Masking = false,
|
||||
Child = PlayerSettings = new FillFlowContainer<PlayerSettingsGroup>
|
||||
{
|
||||
VisualSettings = new VisualSettings(),
|
||||
AudioSettings = new AudioSettings(),
|
||||
new InputSettings()
|
||||
}
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 20),
|
||||
Padding = new MarginPadding { Horizontal = padding },
|
||||
Children = new PlayerSettingsGroup[]
|
||||
{
|
||||
VisualSettings = new VisualSettings(),
|
||||
AudioSettings = new AudioSettings(),
|
||||
new InputSettings()
|
||||
}
|
||||
},
|
||||
},
|
||||
idleTracker = new IdleTracker(750),
|
||||
}),
|
||||
|
@ -53,6 +53,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
private double lastPlayAverage;
|
||||
private double lastPlayBeatmapOffset;
|
||||
|
||||
private SettingsButton? useAverageButton;
|
||||
|
||||
@ -156,7 +157,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
}
|
||||
|
||||
if (useAverageButton != null)
|
||||
useAverageButton.Enabled.Value = !Precision.AlmostEquals(lastPlayAverage, -Current.Value, Current.Precision / 2);
|
||||
useAverageButton.Enabled.Value = !Precision.AlmostEquals(Current.Value, lastPlayBeatmapOffset - lastPlayAverage, Current.Precision / 2);
|
||||
|
||||
realmWriteTask = realm.WriteAsync(r =>
|
||||
{
|
||||
@ -213,6 +214,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
}
|
||||
|
||||
lastPlayAverage = average;
|
||||
lastPlayBeatmapOffset = Current.Value;
|
||||
|
||||
referenceScoreContainer.AddRange(new Drawable[]
|
||||
{
|
||||
@ -225,7 +227,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
useAverageButton = new SettingsButton
|
||||
{
|
||||
Text = BeatmapOffsetControlStrings.CalibrateUsingLastPlay,
|
||||
Action = () => Current.Value = -lastPlayAverage
|
||||
Action = () => Current.Value = lastPlayBeatmapOffset - lastPlayAverage
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -4,10 +4,10 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
@ -32,7 +32,7 @@ namespace osu.Game.Screens.Play
|
||||
if (beatmapId <= 0)
|
||||
return null;
|
||||
|
||||
if (rulesetId < 0 || rulesetId > ILegacyRuleset.MAX_LEGACY_RULESET_ID)
|
||||
if (!Ruleset.Value.IsLegacyRuleset())
|
||||
return null;
|
||||
|
||||
return new CreateSoloScoreRequest(beatmapId, rulesetId, Game.VersionHash);
|
||||
|
@ -64,10 +64,22 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
// Prevent div-by-0 by enforcing a minimum bin size
|
||||
binSize = Math.Max(1, binSize);
|
||||
|
||||
bool roundUp = true;
|
||||
|
||||
foreach (var e in hitEvents)
|
||||
{
|
||||
int binOffset = (int)Math.Round(e.TimeOffset / binSize, MidpointRounding.AwayFromZero);
|
||||
bins[timing_distribution_centre_bin_index + binOffset]++;
|
||||
double binOffset = e.TimeOffset / binSize;
|
||||
|
||||
// .NET's round midpoint handling doesn't provide a behaviour that works amazingly for display
|
||||
// purposes here. We want midpoint rounding to roughly distribute evenly to each adjacent bucket
|
||||
// so the easiest way is to cycle between downwards and upwards rounding as we process events.
|
||||
if (Math.Abs(binOffset - (int)binOffset) == 0.5)
|
||||
{
|
||||
binOffset = (int)binOffset + Math.Sign(binOffset) * (roundUp ? 1 : 0);
|
||||
roundUp = !roundUp;
|
||||
}
|
||||
|
||||
bins[timing_distribution_centre_bin_index + (int)Math.Round(binOffset, MidpointRounding.AwayFromZero)]++;
|
||||
}
|
||||
|
||||
int maxCount = bins.Max();
|
||||
@ -160,8 +172,6 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Padding = new MarginPadding { Horizontal = 1 };
|
||||
|
||||
InternalChild = new Circle
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.Leaderboards;
|
||||
@ -98,6 +99,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
protected override APIRequest FetchScores(CancellationToken cancellationToken)
|
||||
{
|
||||
var fetchBeatmapInfo = BeatmapInfo;
|
||||
var fetchRuleset = ruleset.Value ?? fetchBeatmapInfo.Ruleset;
|
||||
|
||||
if (fetchBeatmapInfo == null)
|
||||
{
|
||||
@ -117,9 +119,15 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!fetchRuleset.IsLegacyRuleset())
|
||||
{
|
||||
SetErrorState(LeaderboardState.RulesetUnavailable);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (fetchBeatmapInfo.OnlineID <= 0 || fetchBeatmapInfo.Status <= BeatmapOnlineStatus.Pending)
|
||||
{
|
||||
SetErrorState(LeaderboardState.Unavailable);
|
||||
SetErrorState(LeaderboardState.BeatmapUnavailable);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -137,7 +145,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
else if (filterMods)
|
||||
requestMods = mods.Value;
|
||||
|
||||
var req = new GetScoresRequest(fetchBeatmapInfo, ruleset.Value ?? fetchBeatmapInfo.Ruleset, Scope, requestMods);
|
||||
var req = new GetScoresRequest(fetchBeatmapInfo, fetchRuleset, Scope, requestMods);
|
||||
|
||||
req.Success += r =>
|
||||
{
|
||||
|
@ -23,6 +23,8 @@ namespace osu.Game.Skinning.Editor
|
||||
{
|
||||
public class SkinComponentToolbox : ScrollingToolboxGroup
|
||||
{
|
||||
public const float WIDTH = 200;
|
||||
|
||||
public Action<Type> RequestPlacement;
|
||||
|
||||
private const float component_display_scale = 0.8f;
|
||||
@ -41,7 +43,7 @@ namespace osu.Game.Skinning.Editor
|
||||
: base("Components", height)
|
||||
{
|
||||
RelativeSizeAxes = Axes.None;
|
||||
Width = 200;
|
||||
Width = WIDTH;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -5,6 +5,7 @@ using JetBrains.Annotations;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Containers;
|
||||
@ -100,30 +101,14 @@ namespace osu.Game.Skinning.Editor
|
||||
{
|
||||
if (visibility.NewValue == Visibility.Visible)
|
||||
{
|
||||
updateMasking();
|
||||
target.AllowScaling = false;
|
||||
target.RelativePositionAxes = Axes.Both;
|
||||
|
||||
target.ScaleTo(VISIBLE_TARGET_SCALE, SkinEditor.TRANSITION_DURATION, Easing.OutQuint);
|
||||
target.MoveToX(0.095f, SkinEditor.TRANSITION_DURATION, Easing.OutQuint);
|
||||
target.SetCustomRect(new RectangleF(0.18f, 0.1f, VISIBLE_TARGET_SCALE, VISIBLE_TARGET_SCALE), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
target.AllowScaling = true;
|
||||
|
||||
target.ScaleTo(1, SkinEditor.TRANSITION_DURATION, Easing.OutQuint).OnComplete(_ => updateMasking());
|
||||
target.MoveToX(0f, SkinEditor.TRANSITION_DURATION, Easing.OutQuint);
|
||||
target.SetCustomRect(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMasking()
|
||||
{
|
||||
if (skinEditor == null)
|
||||
return;
|
||||
|
||||
target.Masking = skinEditor.State.Value == Visibility.Visible;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
|
@ -37,7 +37,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="10.9.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2022.223.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.304.0" />
|
||||
<PackageReference Include="Sentry" Version="3.14.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
|
@ -62,7 +62,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2022.223.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.211.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.304.0" />
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net6.0) -->
|
||||
<PropertyGroup>
|
||||
|
Loading…
Reference in New Issue
Block a user