Merge branch 'master' into footer-v2-button-animation
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 17 KiB |
BIN
assets/lazer.png
Before Width: | Height: | Size: 321 KiB After Width: | Height: | Size: 438 KiB |
@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.423.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.509.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -1,76 +0,0 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Android.Input;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays.Settings;
|
||||
|
||||
namespace osu.Android
|
||||
{
|
||||
public partial class AndroidJoystickSettings : SettingsSubsection
|
||||
{
|
||||
protected override LocalisableString Header => JoystickSettingsStrings.JoystickGamepad;
|
||||
|
||||
private readonly AndroidJoystickHandler joystickHandler;
|
||||
|
||||
private readonly Bindable<bool> enabled = new BindableBool(true);
|
||||
|
||||
private SettingsSlider<float> deadzoneSlider = null!;
|
||||
|
||||
private Bindable<float> handlerDeadzone = null!;
|
||||
|
||||
private Bindable<float> localDeadzone = null!;
|
||||
|
||||
public AndroidJoystickSettings(AndroidJoystickHandler joystickHandler)
|
||||
{
|
||||
this.joystickHandler = joystickHandler;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
// use local bindable to avoid changing enabled state of game host's bindable.
|
||||
handlerDeadzone = joystickHandler.DeadzoneThreshold.GetBoundCopy();
|
||||
localDeadzone = handlerDeadzone.GetUnboundCopy();
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = CommonStrings.Enabled,
|
||||
Current = enabled
|
||||
},
|
||||
deadzoneSlider = new SettingsSlider<float>
|
||||
{
|
||||
LabelText = JoystickSettingsStrings.DeadzoneThreshold,
|
||||
KeyboardStep = 0.01f,
|
||||
DisplayAsPercentage = true,
|
||||
Current = localDeadzone,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
enabled.BindTo(joystickHandler.Enabled);
|
||||
enabled.BindValueChanged(e => deadzoneSlider.Current.Disabled = !e.NewValue, true);
|
||||
|
||||
handlerDeadzone.BindValueChanged(val =>
|
||||
{
|
||||
bool disabled = localDeadzone.Disabled;
|
||||
|
||||
localDeadzone.Disabled = false;
|
||||
localDeadzone.Value = val.NewValue;
|
||||
localDeadzone.Disabled = disabled;
|
||||
}, true);
|
||||
|
||||
localDeadzone.BindValueChanged(val => handlerDeadzone.Value = val.NewValue);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
// 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 Android.OS;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Android.Input;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Overlays.Settings.Sections.Input;
|
||||
|
||||
namespace osu.Android
|
||||
{
|
||||
public partial class AndroidMouseSettings : SettingsSubsection
|
||||
{
|
||||
private readonly AndroidMouseHandler mouseHandler;
|
||||
|
||||
protected override LocalisableString Header => MouseSettingsStrings.Mouse;
|
||||
|
||||
private Bindable<double> handlerSensitivity = null!;
|
||||
|
||||
private Bindable<double> localSensitivity = null!;
|
||||
|
||||
private Bindable<bool> relativeMode = null!;
|
||||
|
||||
public AndroidMouseSettings(AndroidMouseHandler mouseHandler)
|
||||
{
|
||||
this.mouseHandler = mouseHandler;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager osuConfig)
|
||||
{
|
||||
// use local bindable to avoid changing enabled state of game host's bindable.
|
||||
handlerSensitivity = mouseHandler.Sensitivity.GetBoundCopy();
|
||||
localSensitivity = handlerSensitivity.GetUnboundCopy();
|
||||
|
||||
relativeMode = mouseHandler.UseRelativeMode.GetBoundCopy();
|
||||
|
||||
// High precision/pointer capture is only available on Android 8.0 and up
|
||||
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
|
||||
{
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = MouseSettingsStrings.HighPrecisionMouse,
|
||||
TooltipText = MouseSettingsStrings.HighPrecisionMouseTooltip,
|
||||
Current = relativeMode,
|
||||
Keywords = new[] { @"raw", @"input", @"relative", @"cursor", @"captured", @"pointer" },
|
||||
},
|
||||
new MouseSettings.SensitivitySetting
|
||||
{
|
||||
LabelText = MouseSettingsStrings.CursorSensitivity,
|
||||
Current = localSensitivity,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = MouseSettingsStrings.DisableMouseWheelVolumeAdjust,
|
||||
TooltipText = MouseSettingsStrings.DisableMouseWheelVolumeAdjustTooltip,
|
||||
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableWheel),
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
LabelText = MouseSettingsStrings.DisableClicksDuringGameplay,
|
||||
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
relativeMode.BindValueChanged(relative => localSensitivity.Disabled = !relative.NewValue, true);
|
||||
|
||||
handlerSensitivity.BindValueChanged(val =>
|
||||
{
|
||||
bool disabled = localSensitivity.Disabled;
|
||||
|
||||
localSensitivity.Disabled = false;
|
||||
localSensitivity.Value = val.NewValue;
|
||||
localSensitivity.Disabled = disabled;
|
||||
}, true);
|
||||
|
||||
localSensitivity.BindValueChanged(val => handlerSensitivity.Value = val.NewValue);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,13 +5,9 @@ using System;
|
||||
using Android.App;
|
||||
using Microsoft.Maui.Devices;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Android.Input;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Input.Handlers;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Overlays.Settings.Sections.Input;
|
||||
using osu.Game.Updater;
|
||||
using osu.Game.Utils;
|
||||
|
||||
@ -88,24 +84,6 @@ namespace osu.Android
|
||||
|
||||
protected override BatteryInfo CreateBatteryInfo() => new AndroidBatteryInfo();
|
||||
|
||||
public override SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler)
|
||||
{
|
||||
switch (handler)
|
||||
{
|
||||
case AndroidMouseHandler mh:
|
||||
return new AndroidMouseSettings(mh);
|
||||
|
||||
case AndroidJoystickHandler jh:
|
||||
return new AndroidJoystickSettings(jh);
|
||||
|
||||
case AndroidTouchHandler th:
|
||||
return new TouchSettings(th);
|
||||
|
||||
default:
|
||||
return base.CreateSettingsSubsectionFor(handler);
|
||||
}
|
||||
}
|
||||
|
||||
private class AndroidBatteryInfo : BatteryInfo
|
||||
{
|
||||
public override double? ChargeLevel => Battery.ChargeLevel;
|
||||
|
@ -1,24 +1,8 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:pathData="M73.92,44.43C74.82,44.43 75.43,45.1 75.43,46.02V54.54C75.43,55.46 74.82,56.13 73.92,56.13C73,56.13 72.41,55.46 72.41,54.54V46.02C72.41,45.1 73,44.43 73.92,44.43ZM73.92,61.55C72.82,61.55 71.95,60.68 71.95,59.58C71.95,58.51 72.82,57.64 73.92,57.64C75.02,57.64 75.89,58.51 75.89,59.58C75.89,60.68 75.02,61.55 73.92,61.55Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M68.41,48.55C69.33,48.55 69.92,49.22 69.92,50.11V55.77C69.92,59.94 67.35,61.55 64.22,61.55C61.08,61.55 58.5,59.94 58.5,55.77V50.11C58.5,49.22 59.09,48.55 60.01,48.55C60.91,48.55 61.52,49.22 61.52,50.11V55.56C61.52,57.84 62.48,58.74 64.22,58.74C65.94,58.74 66.9,57.84 66.9,55.56V50.11C66.9,49.22 67.51,48.55 68.41,48.55Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M49.94,52.01C49.94,52.85 50.81,53.16 52.47,53.54C54.78,54.1 56.98,54.69 56.98,57.53C56.98,60.3 54.93,61.55 51.99,61.55C49.56,61.55 47.79,60.71 46.97,59.73C46.33,58.97 46.41,58.3 47.02,57.71C47.79,56.97 48.46,57.28 48.89,57.66C49.58,58.3 50.43,58.97 52.07,58.97C53.29,58.97 54.06,58.56 54.06,57.74C54.06,56.92 53.24,56.64 51.09,56.05C48.97,55.46 47.08,54.9 47.08,52.36C47.08,49.52 49.38,48.35 51.94,48.35C53.4,48.35 55.06,48.73 56.08,49.83C56.52,50.27 56.85,50.88 56.08,51.72C55.32,52.52 54.75,52.29 54.22,51.88C53.73,51.52 52.88,50.91 51.6,50.91C50.73,50.91 49.94,51.19 49.94,52.01Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M38.79,61.55C34.9,61.55 32.11,58.74 32.11,54.95C32.11,51.14 34.9,48.35 38.79,48.35C42.68,48.35 45.47,51.14 45.47,54.95C45.47,58.74 42.68,61.55 38.79,61.55ZM38.79,58.74C41.04,58.74 42.45,57.1 42.45,54.95C42.45,52.8 41.04,51.14 38.79,51.14C36.54,51.14 35.13,52.8 35.13,54.95C35.13,57.1 36.54,58.74 38.79,58.74Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M86,54C86,71.67 71.67,86 54,86C36.33,86 22,71.67 22,54C22,36.33 36.33,22 54,22C71.67,22 86,36.33 86,54ZM25.2,54C25.2,69.91 38.09,82.8 54,82.8C69.91,82.8 82.8,69.91 82.8,54C82.8,38.09 69.91,25.2 54,25.2C38.09,25.2 25.2,38.09 25.2,54Z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M36.78,54.99C36.78,56.09 37.65,56.96 38.75,56.96C39.85,56.96 40.72,56.09 40.72,54.99C40.72,53.91 39.85,53.04 38.75,53.04C37.65,53.04 36.78,53.91 36.78,54.99Z"
|
||||
android:fillColor="#000000"/>
|
||||
<vector android:height="108dp" android:viewportHeight="434"
|
||||
android:viewportWidth="434" android:width="108dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#000000" android:pathData="M299.36,178.05C303.08,178.05 305.62,180.81 305.62,184.62V219.92C305.62,223.74 303.08,226.5 299.36,226.5C295.55,226.5 293.11,223.74 293.11,219.92V184.62C293.11,180.81 295.55,178.05 299.36,178.05ZM299.36,248.97C294.81,248.97 291.2,245.37 291.2,240.81C291.2,236.35 294.81,232.75 299.36,232.75C303.92,232.75 307.53,236.35 307.53,240.81C307.53,245.37 303.92,248.97 299.36,248.97Z"/>
|
||||
<path android:fillColor="#000000" android:pathData="M276.52,195.12C280.34,195.12 282.77,197.87 282.77,201.58V225.01C282.77,242.29 272.12,248.97 259.19,248.97C246.15,248.97 235.49,242.29 235.49,225.01V201.58C235.49,197.87 237.93,195.12 241.75,195.12C245.46,195.12 248,197.87 248,201.58V224.16C248,233.6 251.98,237.31 259.19,237.31C266.29,237.31 270.27,233.6 270.27,224.16V201.58C270.27,197.87 272.81,195.12 276.52,195.12Z"/>
|
||||
<path android:fillColor="#000000" android:pathData="M200.02,209.43C200.02,212.93 203.63,214.2 210.52,215.79C220.06,218.12 229.18,220.56 229.18,232.33C229.18,243.78 220.7,248.97 208.51,248.97C198.43,248.97 191.12,245.47 187.73,241.44C185.08,238.26 185.4,235.51 187.94,233.07C191.12,229.99 193.88,231.27 195.68,232.86C198.54,235.51 202.04,238.26 208.82,238.26C213.91,238.26 217.09,236.57 217.09,233.18C217.09,229.78 213.7,228.62 204.8,226.18C196,223.74 188.15,221.41 188.15,210.91C188.15,199.15 197.69,194.27 208.29,194.27C214.34,194.27 221.23,195.86 225.47,200.42C227.27,202.22 228.65,204.76 225.47,208.26C222.29,211.55 219.96,210.6 217.73,208.9C215.71,207.41 212.22,204.87 206.92,204.87C203.31,204.87 200.02,206.04 200.02,209.43Z"/>
|
||||
<path android:fillColor="#000000" android:pathData="M153.74,248.97C138.46,248.97 127.5,237.27 127.5,221.53C127.5,205.68 138.46,194.09 153.74,194.09C169.03,194.09 179.99,205.68 179.99,221.53C179.99,237.27 169.03,248.97 153.74,248.97ZM153.74,237.27C162.59,237.27 168.12,230.46 168.12,221.53C168.12,212.6 162.59,205.68 153.74,205.68C144.89,205.68 139.36,212.6 139.36,221.53C139.36,230.46 144.89,237.27 153.74,237.27Z"/>
|
||||
<path android:fillColor="#000000" android:pathData="M349,217.5C349,290.13 290.13,349 217.5,349C144.88,349 86,290.13 86,217.5C86,144.88 144.88,86 217.5,86C290.13,86 349,144.88 349,217.5ZM99.15,217.5C99.15,282.86 152.14,335.85 217.5,335.85C282.86,335.85 335.85,282.86 335.85,217.5C335.85,152.14 282.86,99.15 217.5,99.15C152.14,99.15 99.15,152.14 99.15,217.5Z"/>
|
||||
</vector>
|
||||
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 49 KiB |
@ -107,7 +107,13 @@ namespace osu.Desktop
|
||||
}
|
||||
}
|
||||
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { IPCPort = !tournamentClient ? OsuGame.IPC_PORT : null }))
|
||||
var hostOptions = new HostOptions
|
||||
{
|
||||
IPCPort = !tournamentClient ? OsuGame.IPC_PORT : null,
|
||||
FriendlyGameName = OsuGameBase.GAME_NAME,
|
||||
};
|
||||
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, hostOptions))
|
||||
{
|
||||
if (!host.IsPrimaryInstance)
|
||||
{
|
||||
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
}
|
||||
|
||||
// Generally all the control points are within the visible area all the time.
|
||||
public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => true;
|
||||
public override bool UpdateSubTreeMasking() => true;
|
||||
|
||||
/// <summary>
|
||||
/// Handles correction of invalid path types.
|
||||
|
@ -8,7 +8,6 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -37,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
|
||||
// For osu! gameplay, everything is always on screen.
|
||||
// Skipping masking calculations improves performance in intense beatmaps (ie. https://osu.ppy.sh/beatmapsets/150945#osu/372245)
|
||||
public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => false;
|
||||
public override bool UpdateSubTreeMasking() => false;
|
||||
|
||||
public SmokeContainer Smoke { get; }
|
||||
public FollowPointRenderer FollowPoints { get; }
|
||||
|
@ -7,7 +7,6 @@ using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@ -345,7 +344,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
public void Add(Drawable proxy) => AddInternal(proxy);
|
||||
|
||||
public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds)
|
||||
public override bool UpdateSubTreeMasking()
|
||||
{
|
||||
// DrawableHitObject disables masking.
|
||||
// Hitobject content is proxied and unproxied based on hit status and the IsMaskedAway value could get stuck because of this.
|
||||
|
@ -14,6 +14,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.IO.Legacy;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
@ -31,6 +32,7 @@ using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Scoring.Legacy;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
@ -224,6 +226,12 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
new OsuModDoubleTime { SpeedChange = { Value = 1.1 } }
|
||||
};
|
||||
scoreInfo.OnlineID = 123123;
|
||||
scoreInfo.User = new APIUser
|
||||
{
|
||||
Username = "spaceman_atlas",
|
||||
Id = 3035836,
|
||||
CountryCode = CountryCode.PL
|
||||
};
|
||||
scoreInfo.ClientVersion = "2023.1221.0";
|
||||
|
||||
var beatmap = new TestBeatmap(ruleset);
|
||||
@ -248,6 +256,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.MaximumStatistics, Is.EqualTo(scoreInfo.MaximumStatistics));
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.Mods, Is.EqualTo(scoreInfo.Mods));
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.ClientVersion, Is.EqualTo("2023.1221.0"));
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.RealmUser.OnlineID, Is.EqualTo(3035836));
|
||||
});
|
||||
}
|
||||
|
||||
@ -413,6 +422,80 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTotalScoreWithoutModsReadIfPresent()
|
||||
{
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
var scoreInfo = TestResources.CreateTestScoreInfo(ruleset);
|
||||
scoreInfo.Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime { SpeedChange = { Value = 1.1 } }
|
||||
};
|
||||
scoreInfo.OnlineID = 123123;
|
||||
scoreInfo.ClientVersion = "2023.1221.0";
|
||||
scoreInfo.TotalScoreWithoutMods = 1_000_000;
|
||||
scoreInfo.TotalScore = 1_020_000;
|
||||
|
||||
var beatmap = new TestBeatmap(ruleset);
|
||||
var score = new Score
|
||||
{
|
||||
ScoreInfo = scoreInfo,
|
||||
Replay = new Replay
|
||||
{
|
||||
Frames = new List<ReplayFrame>
|
||||
{
|
||||
new OsuReplayFrame(2000, OsuPlayfield.BASE_SIZE / 2, OsuAction.LeftButton)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var decodedAfterEncode = encodeThenDecode(LegacyBeatmapDecoder.LATEST_VERSION, score, beatmap);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.TotalScoreWithoutMods, Is.EqualTo(1_000_000));
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.TotalScore, Is.EqualTo(1_020_000));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTotalScoreWithoutModsBackwardsPopulatedIfMissing()
|
||||
{
|
||||
var ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
var scoreInfo = TestResources.CreateTestScoreInfo(ruleset);
|
||||
scoreInfo.Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime { SpeedChange = { Value = 1.1 } }
|
||||
};
|
||||
scoreInfo.OnlineID = 123123;
|
||||
scoreInfo.ClientVersion = "2023.1221.0";
|
||||
scoreInfo.TotalScoreWithoutMods = 0;
|
||||
scoreInfo.TotalScore = 1_020_000;
|
||||
|
||||
var beatmap = new TestBeatmap(ruleset);
|
||||
var score = new Score
|
||||
{
|
||||
ScoreInfo = scoreInfo,
|
||||
Replay = new Replay
|
||||
{
|
||||
Frames = new List<ReplayFrame>
|
||||
{
|
||||
new OsuReplayFrame(2000, OsuPlayfield.BASE_SIZE / 2, OsuAction.LeftButton)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var decodedAfterEncode = encodeThenDecode(LegacyBeatmapDecoder.LATEST_VERSION, score, beatmap);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.TotalScoreWithoutMods, Is.EqualTo(1_000_000));
|
||||
Assert.That(decodedAfterEncode.ScoreInfo.TotalScore, Is.EqualTo(1_020_000));
|
||||
});
|
||||
}
|
||||
|
||||
private static Score encodeThenDecode(int beatmapVersion, Score score, TestBeatmap beatmap)
|
||||
{
|
||||
var encodeStream = new MemoryStream();
|
||||
|
@ -287,7 +287,7 @@ namespace osu.Game.Tests.Scores.IO
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserLookedUpForOnlineScore()
|
||||
public void TestUserLookedUpByUsernameForOnlineScoreIfUserIDMissing()
|
||||
{
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
|
||||
{
|
||||
@ -301,6 +301,9 @@ namespace osu.Game.Tests.Scores.IO
|
||||
switch (req)
|
||||
{
|
||||
case GetUserRequest userRequest:
|
||||
if (userRequest.Lookup != "Test user")
|
||||
return false;
|
||||
|
||||
userRequest.TriggerSuccess(new APIUser
|
||||
{
|
||||
Username = "Test user",
|
||||
@ -350,7 +353,7 @@ namespace osu.Game.Tests.Scores.IO
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserLookedUpForLegacyOnlineScore()
|
||||
public void TestUserLookedUpByUsernameForLegacyOnlineScore()
|
||||
{
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
|
||||
{
|
||||
@ -364,6 +367,9 @@ namespace osu.Game.Tests.Scores.IO
|
||||
switch (req)
|
||||
{
|
||||
case GetUserRequest userRequest:
|
||||
if (userRequest.Lookup != "Test user")
|
||||
return false;
|
||||
|
||||
userRequest.TriggerSuccess(new APIUser
|
||||
{
|
||||
Username = "Test user",
|
||||
@ -413,7 +419,7 @@ namespace osu.Game.Tests.Scores.IO
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserNotLookedUpForOfflineScore()
|
||||
public void TestUserNotLookedUpForOfflineScoreIfUserIDMissing()
|
||||
{
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
|
||||
{
|
||||
@ -427,6 +433,9 @@ namespace osu.Game.Tests.Scores.IO
|
||||
switch (req)
|
||||
{
|
||||
case GetUserRequest userRequest:
|
||||
if (userRequest.Lookup != "Test user")
|
||||
return false;
|
||||
|
||||
userRequest.TriggerSuccess(new APIUser
|
||||
{
|
||||
Username = "Test user",
|
||||
@ -476,6 +485,73 @@ namespace osu.Game.Tests.Scores.IO
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserLookedUpByOnlineIDIfPresent([Values] bool isOnlineScore)
|
||||
{
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
|
||||
{
|
||||
try
|
||||
{
|
||||
var osu = LoadOsuIntoHost(host, true);
|
||||
|
||||
var api = (DummyAPIAccess)osu.API;
|
||||
api.HandleRequest = req =>
|
||||
{
|
||||
switch (req)
|
||||
{
|
||||
case GetUserRequest userRequest:
|
||||
if (userRequest.Lookup != "5555")
|
||||
return false;
|
||||
|
||||
userRequest.TriggerSuccess(new APIUser
|
||||
{
|
||||
Username = "Some other guy",
|
||||
CountryCode = CountryCode.DE,
|
||||
Id = 5555
|
||||
});
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var beatmap = BeatmapImportHelper.LoadOszIntoOsu(osu, TestResources.GetQuickTestBeatmapForImport()).GetResultSafely();
|
||||
|
||||
var toImport = new ScoreInfo
|
||||
{
|
||||
Rank = ScoreRank.B,
|
||||
TotalScore = 987654,
|
||||
Accuracy = 0.8,
|
||||
MaxCombo = 500,
|
||||
Combo = 250,
|
||||
User = new APIUser { Id = 5555 },
|
||||
Date = DateTimeOffset.Now,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
BeatmapInfo = beatmap.Beatmaps.First()
|
||||
};
|
||||
if (isOnlineScore)
|
||||
toImport.OnlineID = 12345;
|
||||
|
||||
var imported = LoadScoreIntoOsu(osu, toImport);
|
||||
|
||||
Assert.AreEqual(toImport.Rank, imported.Rank);
|
||||
Assert.AreEqual(toImport.TotalScore, imported.TotalScore);
|
||||
Assert.AreEqual(toImport.Accuracy, imported.Accuracy);
|
||||
Assert.AreEqual(toImport.MaxCombo, imported.MaxCombo);
|
||||
Assert.AreEqual(toImport.Date, imported.Date);
|
||||
Assert.AreEqual(toImport.OnlineID, imported.OnlineID);
|
||||
Assert.AreEqual("Some other guy", imported.RealmUser.Username);
|
||||
Assert.AreEqual(5555, imported.RealmUser.OnlineID);
|
||||
Assert.AreEqual(CountryCode.DE, imported.RealmUser.CountryCode);
|
||||
}
|
||||
finally
|
||||
{
|
||||
host.Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ScoreInfo LoadScoreIntoOsu(OsuGameBase osu, ScoreInfo score, ArchiveReader archive = null)
|
||||
{
|
||||
// clone to avoid attaching the input score to realm.
|
||||
|
@ -7,7 +7,7 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -97,15 +97,12 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
public void TestUnrankedBadge()
|
||||
{
|
||||
AddStep(@"Add unranked mod", () => changeMods(new[] { new OsuModDeflate() }));
|
||||
AddUntilStep("Unranked badge shown", () => footerButtonMods.UnrankedBadge.Alpha == 1);
|
||||
AddUntilStep("Unranked badge shown", () => footerButtonMods.ChildrenOfType<FooterButtonModsV2.UnrankedBadge>().Single().Alpha == 1);
|
||||
AddStep(@"Clear selected mod", () => changeMods(Array.Empty<Mod>()));
|
||||
AddUntilStep("Unranked badge not shown", () => footerButtonMods.UnrankedBadge.Alpha == 0);
|
||||
AddUntilStep("Unranked badge not shown", () => footerButtonMods.ChildrenOfType<FooterButtonModsV2.UnrankedBadge>().Single().Alpha == 0);
|
||||
}
|
||||
|
||||
private void changeMods(IReadOnlyList<Mod> mods)
|
||||
{
|
||||
footerButtonMods.Current.Value = mods;
|
||||
}
|
||||
private void changeMods(IReadOnlyList<Mod> mods) => footerButtonMods.Current.Value = mods;
|
||||
|
||||
private bool assertModsMultiplier(IEnumerable<Mod> mods)
|
||||
{
|
||||
@ -117,7 +114,6 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
private partial class TestFooterButtonModsV2 : FooterButtonModsV2
|
||||
{
|
||||
public new Container UnrankedBadge => base.UnrankedBadge;
|
||||
public new OsuSpriteText MultiplierText => base.MultiplierText;
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Tournament.Screens.Ladder
|
||||
|
||||
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
|
||||
|
||||
public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => false;
|
||||
public override bool UpdateSubTreeMasking() => false;
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
|
@ -92,8 +92,9 @@ namespace osu.Game.Database
|
||||
/// 38 2023-12-10 Add EndTimeObjectCount and TotalObjectCount to BeatmapInfo.
|
||||
/// 39 2023-12-19 Migrate any EndTimeObjectCount and TotalObjectCount values of 0 to -1 to better identify non-calculated values.
|
||||
/// 40 2023-12-21 Add ScoreInfo.Version to keep track of which build scores were set on.
|
||||
/// 41 2024-04-17 Add ScoreInfo.TotalScoreWithoutMods for future mod multiplier rebalances.
|
||||
/// </summary>
|
||||
private const int schema_version = 40;
|
||||
private const int schema_version = 41;
|
||||
|
||||
/// <summary>
|
||||
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
|
||||
@ -1130,6 +1131,12 @@ namespace osu.Game.Database
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 41:
|
||||
foreach (var score in migration.NewRealm.All<ScoreInfo>())
|
||||
LegacyScoreDecoder.PopulateTotalScoreWithoutMods(score);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Logger.Log($"Migration completed in {stopwatch.ElapsedMilliseconds}ms");
|
||||
|
@ -16,6 +16,7 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Scoring.Legacy;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Scoring.Legacy;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
@ -248,6 +249,7 @@ namespace osu.Game.Database
|
||||
score.Accuracy = computeAccuracy(score, scoreProcessor);
|
||||
score.Rank = computeRank(score, scoreProcessor);
|
||||
score.TotalScore = convertFromLegacyTotalScore(score, ruleset, beatmap);
|
||||
LegacyScoreDecoder.PopulateTotalScoreWithoutMods(score);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
44
osu.Game/Localisation/DeleteConfirmationContentStrings.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// 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 DeleteConfirmationContentStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.DeleteConfirmationContent";
|
||||
|
||||
/// <summary>
|
||||
/// "Are you sure you want to delete all beatmaps?"
|
||||
/// </summary>
|
||||
public static LocalisableString Beatmaps => new TranslatableString(getKey(@"beatmaps"), @"Are you sure you want to delete all beatmaps?");
|
||||
|
||||
/// <summary>
|
||||
/// "Are you sure you want to delete all beatmaps videos? This cannot be undone!"
|
||||
/// </summary>
|
||||
public static LocalisableString BeatmapVideos => new TranslatableString(getKey(@"beatmap_videos"), @"Are you sure you want to delete all beatmaps videos? This cannot be undone!");
|
||||
|
||||
/// <summary>
|
||||
/// "Are you sure you want to delete all skins? This cannot be undone!"
|
||||
/// </summary>
|
||||
public static LocalisableString Skins => new TranslatableString(getKey(@"skins"), @"Are you sure you want to delete all skins? This cannot be undone!");
|
||||
|
||||
/// <summary>
|
||||
/// "Are you sure you want to delete all collections? This cannot be undone!"
|
||||
/// </summary>
|
||||
public static LocalisableString Collections => new TranslatableString(getKey(@"collections"), @"Are you sure you want to delete all collections? This cannot be undone!");
|
||||
|
||||
/// <summary>
|
||||
/// "Are you sure you want to delete all scores? This cannot be undone!"
|
||||
/// </summary>
|
||||
public static LocalisableString Scores => new TranslatableString(getKey(@"collections"), @"Are you sure you want to delete all scores? This cannot be undone!");
|
||||
|
||||
/// <summary>
|
||||
/// "Are you sure you want to delete all mod presets?"
|
||||
/// </summary>
|
||||
public static LocalisableString ModPresets => new TranslatableString(getKey(@"mod_presets"), @"Are you sure you want to delete all mod presets?");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
@ -10,9 +10,9 @@ namespace osu.Game.Localisation
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.DeleteConfirmationDialog";
|
||||
|
||||
/// <summary>
|
||||
/// "Confirm deletion of"
|
||||
/// "Caution"
|
||||
/// </summary>
|
||||
public static LocalisableString HeaderText => new TranslatableString(getKey(@"header_text"), @"Confirm deletion of");
|
||||
public static LocalisableString HeaderText => new TranslatableString(getKey(@"header_text"), @"Caution");
|
||||
|
||||
/// <summary>
|
||||
/// "Yes. Go for it."
|
||||
|
@ -1,19 +0,0 @@
|
||||
// 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 FooterButtonModsV2Strings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.FooterButtonModsV2";
|
||||
|
||||
/// <summary>
|
||||
/// "{0} mods"
|
||||
/// </summary>
|
||||
public static LocalisableString Mods(int count) => new TranslatableString(getKey(@"mods"), @"{0} mods", count);
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
@ -14,10 +14,16 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString ModSelectTitle => new TranslatableString(getKey(@"mod_select_title"), @"Mod Select");
|
||||
|
||||
/// <summary>
|
||||
/// "{0} mods"
|
||||
/// </summary>
|
||||
public static LocalisableString Mods(int count) => new TranslatableString(getKey(@"mods"), @"{0} mods", count);
|
||||
|
||||
/// <summary>
|
||||
/// "Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun."
|
||||
/// </summary>
|
||||
public static LocalisableString ModSelectDescription => new TranslatableString(getKey(@"mod_select_description"), @"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.");
|
||||
public static LocalisableString ModSelectDescription => new TranslatableString(getKey(@"mod_select_description"),
|
||||
@"Mods provide different ways to enjoy gameplay. Some have an effect on the score you can achieve during ranked play. Others are just for fun.");
|
||||
|
||||
/// <summary>
|
||||
/// "Mod Customisation"
|
||||
|
@ -33,6 +33,9 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
[JsonProperty("total_score")]
|
||||
public long TotalScore { get; set; }
|
||||
|
||||
[JsonProperty("total_score_without_mods")]
|
||||
public long TotalScoreWithoutMods { get; set; }
|
||||
|
||||
[JsonProperty("accuracy")]
|
||||
public double Accuracy { get; set; }
|
||||
|
||||
@ -206,6 +209,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
Ruleset = new RulesetInfo { OnlineID = RulesetID },
|
||||
Passed = Passed,
|
||||
TotalScore = TotalScore,
|
||||
TotalScoreWithoutMods = TotalScoreWithoutMods,
|
||||
LegacyTotalScore = LegacyTotalScore,
|
||||
Accuracy = Accuracy,
|
||||
MaxCombo = MaxCombo,
|
||||
@ -239,6 +243,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
Rank = score.Rank,
|
||||
TotalScore = score.TotalScore,
|
||||
TotalScoreWithoutMods = score.TotalScoreWithoutMods,
|
||||
Accuracy = score.Accuracy,
|
||||
PP = score.PP,
|
||||
MaxCombo = score.MaxCombo,
|
||||
|
@ -75,6 +75,12 @@ namespace osu.Game
|
||||
{
|
||||
public static readonly string[] VIDEO_EXTENSIONS = { ".mp4", ".mov", ".avi", ".flv", ".mpg", ".wmv", ".m4v" };
|
||||
|
||||
#if DEBUG
|
||||
public const string GAME_NAME = "osu! (development)";
|
||||
#else
|
||||
public const string GAME_NAME = "osu!";
|
||||
#endif
|
||||
|
||||
public const string OSU_PROTOCOL = "osu://";
|
||||
|
||||
public const string CLIENT_STREAM_NAME = @"lazer";
|
||||
@ -241,11 +247,7 @@ namespace osu.Game
|
||||
|
||||
public OsuGameBase()
|
||||
{
|
||||
Name = @"osu!";
|
||||
|
||||
#if DEBUG
|
||||
Name += " (development)";
|
||||
#endif
|
||||
Name = GAME_NAME;
|
||||
|
||||
allowableExceptions = UnhandledExceptionsBeforeCrash;
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ namespace osu.Game.Overlays.Dialog
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
TextAnchor = Anchor.TopCentre,
|
||||
Padding = new MarginPadding { Horizontal = 5 },
|
||||
Padding = new MarginPadding { Horizontal = 15 },
|
||||
},
|
||||
body = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 18))
|
||||
{
|
||||
@ -219,7 +219,7 @@ namespace osu.Game.Overlays.Dialog
|
||||
TextAnchor = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Horizontal = 5 },
|
||||
Padding = new MarginPadding { Horizontal = 15 },
|
||||
},
|
||||
buttonsContainer = new FillFlowContainer<PopupDialogButton>
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
deleteBeatmapsButton.Enabled.Value = false;
|
||||
Task.Run(() => beatmaps.Delete()).ContinueWith(_ => Schedule(() => deleteBeatmapsButton.Enabled.Value = true));
|
||||
}));
|
||||
}, DeleteConfirmationContentStrings.Beatmaps));
|
||||
}
|
||||
});
|
||||
|
||||
@ -40,11 +40,11 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
Text = MaintenanceSettingsStrings.DeleteAllBeatmapVideos,
|
||||
Action = () =>
|
||||
{
|
||||
dialogOverlay?.Push(new MassVideoDeleteConfirmationDialog(() =>
|
||||
dialogOverlay?.Push(new MassDeleteConfirmationDialog(() =>
|
||||
{
|
||||
deleteBeatmapVideosButton.Enabled.Value = false;
|
||||
Task.Run(beatmaps.DeleteAllVideos).ContinueWith(_ => Schedule(() => deleteBeatmapVideosButton.Enabled.Value = true));
|
||||
}));
|
||||
}, DeleteConfirmationContentStrings.BeatmapVideos));
|
||||
}
|
||||
});
|
||||
AddRange(new Drawable[]
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
Text = MaintenanceSettingsStrings.DeleteAllCollections,
|
||||
Action = () =>
|
||||
{
|
||||
dialogOverlay?.Push(new MassDeleteConfirmationDialog(deleteAllCollections));
|
||||
dialogOverlay?.Push(new MassDeleteConfirmationDialog(deleteAllCollections, DeleteConfirmationContentStrings.Collections));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2,15 +2,16 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
public partial class MassDeleteConfirmationDialog : DangerousActionDialog
|
||||
{
|
||||
public MassDeleteConfirmationDialog(Action deleteAction)
|
||||
public MassDeleteConfirmationDialog(Action deleteAction, LocalisableString deleteContent)
|
||||
{
|
||||
BodyText = "Everything?";
|
||||
BodyText = deleteContent;
|
||||
DangerousAction = deleteAction;
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
// 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;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
public partial class MassVideoDeleteConfirmationDialog : MassDeleteConfirmationDialog
|
||||
{
|
||||
public MassVideoDeleteConfirmationDialog(Action deleteAction)
|
||||
: base(deleteAction)
|
||||
{
|
||||
BodyText = "All beatmap videos? This cannot be undone!";
|
||||
}
|
||||
}
|
||||
}
|
@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
deleteAllButton.Enabled.Value = false;
|
||||
Task.Run(deleteAllModPresets).ContinueWith(t => Schedule(onAllModPresetsDeleted, t));
|
||||
}));
|
||||
}, DeleteConfirmationContentStrings.ModPresets));
|
||||
}
|
||||
},
|
||||
undeleteButton = new SettingsButton
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
deleteScoresButton.Enabled.Value = false;
|
||||
Task.Run(() => scores.Delete()).ContinueWith(_ => Schedule(() => deleteScoresButton.Enabled.Value = true));
|
||||
}));
|
||||
}, DeleteConfirmationContentStrings.Scores));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||
{
|
||||
deleteSkinsButton.Enabled.Value = false;
|
||||
Task.Run(() => skins.Delete()).ContinueWith(_ => Schedule(() => deleteSkinsButton.Enabled.Value = true));
|
||||
}));
|
||||
}, DeleteConfirmationContentStrings.Skins));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ using osu.Framework.Extensions.ListExtensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Utils;
|
||||
@ -632,7 +631,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
#endregion
|
||||
|
||||
public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => false;
|
||||
public override bool UpdateSubTreeMasking() => false;
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
|
@ -56,6 +56,14 @@ namespace osu.Game.Rulesets.Scoring
|
||||
/// </summary>
|
||||
public readonly BindableLong TotalScore = new BindableLong { MinValue = 0 };
|
||||
|
||||
/// <summary>
|
||||
/// The total number of points awarded for the score without including mod multipliers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The purpose of this property is to enable future lossless rebalances of mod multipliers.
|
||||
/// </remarks>
|
||||
public readonly BindableLong TotalScoreWithoutMods = new BindableLong { MinValue = 0 };
|
||||
|
||||
/// <summary>
|
||||
/// The current accuracy.
|
||||
/// </summary>
|
||||
@ -363,7 +371,8 @@ namespace osu.Game.Rulesets.Scoring
|
||||
double comboProgress = maximumComboPortion > 0 ? currentComboPortion / maximumComboPortion : 1;
|
||||
double accuracyProcess = maximumAccuracyJudgementCount > 0 ? (double)currentAccuracyJudgementCount / maximumAccuracyJudgementCount : 1;
|
||||
|
||||
TotalScore.Value = (long)Math.Round(ComputeTotalScore(comboProgress, accuracyProcess, currentBonusPortion) * scoreMultiplier);
|
||||
TotalScoreWithoutMods.Value = (long)Math.Round(ComputeTotalScore(comboProgress, accuracyProcess, currentBonusPortion));
|
||||
TotalScore.Value = (long)Math.Round(TotalScoreWithoutMods.Value * scoreMultiplier);
|
||||
}
|
||||
|
||||
private void updateRank()
|
||||
@ -446,6 +455,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
score.MaximumStatistics[result] = MaximumResultCounts.GetValueOrDefault(result);
|
||||
|
||||
// Populate total score after everything else.
|
||||
score.TotalScoreWithoutMods = TotalScoreWithoutMods.Value;
|
||||
score.TotalScore = TotalScore.Value;
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ namespace osu.Game.Rulesets.UI
|
||||
break;
|
||||
|
||||
base.UpdateSubTree();
|
||||
UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat);
|
||||
UpdateSubTreeMasking();
|
||||
} while (state == PlaybackState.RequiresCatchUp && stopwatch.ElapsedMilliseconds < max_catchup_milliseconds);
|
||||
|
||||
return true;
|
||||
|
@ -43,6 +43,12 @@ namespace osu.Game.Scoring.Legacy
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public ScoreRank? Rank;
|
||||
|
||||
[JsonProperty("user_id")]
|
||||
public int UserID = -1;
|
||||
|
||||
[JsonProperty("total_score_without_mods")]
|
||||
public long? TotalScoreWithoutMods { get; set; }
|
||||
|
||||
public static LegacyReplaySoloScoreInfo FromScore(ScoreInfo score) => new LegacyReplaySoloScoreInfo
|
||||
{
|
||||
OnlineID = score.OnlineID,
|
||||
@ -51,6 +57,8 @@ namespace osu.Game.Scoring.Legacy
|
||||
MaximumStatistics = score.MaximumStatistics.Where(kvp => kvp.Value != 0).ToDictionary(),
|
||||
ClientVersion = score.ClientVersion,
|
||||
Rank = score.Rank,
|
||||
UserID = score.User.OnlineID,
|
||||
TotalScoreWithoutMods = score.TotalScoreWithoutMods > 0 ? score.TotalScoreWithoutMods : null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -131,6 +131,13 @@ namespace osu.Game.Scoring.Legacy
|
||||
score.ScoreInfo.Mods = readScore.Mods.Select(m => m.ToMod(currentRuleset)).ToArray();
|
||||
score.ScoreInfo.ClientVersion = readScore.ClientVersion;
|
||||
decodedRank = readScore.Rank;
|
||||
if (readScore.UserID > 1)
|
||||
score.ScoreInfo.RealmUser.OnlineID = readScore.UserID;
|
||||
|
||||
if (readScore.TotalScoreWithoutMods is long totalScoreWithoutMods)
|
||||
score.ScoreInfo.TotalScoreWithoutMods = totalScoreWithoutMods;
|
||||
else
|
||||
PopulateTotalScoreWithoutMods(score.ScoreInfo);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -242,6 +249,16 @@ namespace osu.Game.Scoring.Legacy
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
public static void PopulateTotalScoreWithoutMods(ScoreInfo score)
|
||||
{
|
||||
double modMultiplier = 1;
|
||||
|
||||
foreach (var mod in score.Mods)
|
||||
modMultiplier *= mod.ScoreMultiplier;
|
||||
|
||||
score.TotalScoreWithoutMods = (long)Math.Round(score.TotalScore / modMultiplier);
|
||||
}
|
||||
|
||||
private void readLegacyReplay(Replay replay, StreamReader reader)
|
||||
{
|
||||
float lastTime = beatmapOffset;
|
||||
|
@ -103,6 +103,14 @@ namespace osu.Game.Scoring
|
||||
}
|
||||
|
||||
// Very naive local caching to improve performance of large score imports (where the username is usually the same for most or all scores).
|
||||
|
||||
// TODO: `UserLookupCache` cannot currently be used here because of async foibles.
|
||||
// It only supports lookups by user ID (username would require web changes), and even then the ID lookups cannot be used.
|
||||
// That is because that component provides an async interface, and async functions cannot be consumed safely here due to the rigid structure of `RealmArchiveModelImporter`.
|
||||
// The importer has two paths, one async and one sync; the async path runs the sync path in a task.
|
||||
// This means that sometimes `PostImport()` is called from a sync context, and sometimes from an async one, whilst itself being a sync method.
|
||||
// That in turn makes `.GetResultSafely()` not callable inside `PostImport()`, as it will throw when called from an async context,
|
||||
private readonly Dictionary<int, APIUser> idLookupCache = new Dictionary<int, APIUser>();
|
||||
private readonly Dictionary<string, APIUser> usernameLookupCache = new Dictionary<string, APIUser>();
|
||||
|
||||
protected override void PostImport(ScoreInfo model, Realm realm, ImportParameters parameters)
|
||||
@ -127,24 +135,34 @@ namespace osu.Game.Scoring
|
||||
if (model.RealmUser.OnlineID == APIUser.SYSTEM_USER_ID)
|
||||
return;
|
||||
|
||||
if (model.OnlineID < 0 && model.LegacyOnlineID <= 0)
|
||||
return;
|
||||
|
||||
string username = model.RealmUser.Username;
|
||||
|
||||
if (usernameLookupCache.TryGetValue(username, out var existing))
|
||||
if (model.RealmUser.OnlineID > 1)
|
||||
{
|
||||
model.User = existing;
|
||||
model.User = lookupUserById(model.RealmUser.OnlineID) ?? model.User;
|
||||
return;
|
||||
}
|
||||
|
||||
var userRequest = new GetUserRequest(username);
|
||||
if (model.OnlineID < 0 && model.LegacyOnlineID <= 0)
|
||||
return;
|
||||
|
||||
model.User = lookupUserByName(model.RealmUser.Username) ?? model.User;
|
||||
}
|
||||
|
||||
private APIUser? lookupUserById(int id)
|
||||
{
|
||||
if (idLookupCache.TryGetValue(id, out var existing))
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
|
||||
var userRequest = new GetUserRequest(id);
|
||||
|
||||
api.Perform(userRequest);
|
||||
|
||||
if (userRequest.Response is APIUser user)
|
||||
{
|
||||
usernameLookupCache.TryAdd(username, new APIUser
|
||||
APIUser cachedUser;
|
||||
|
||||
idLookupCache.TryAdd(id, cachedUser = new APIUser
|
||||
{
|
||||
// Because this is a permanent cache, let's only store the pieces we're interested in,
|
||||
// rather than the full API response. If we start to store more than these three fields
|
||||
@ -154,8 +172,41 @@ namespace osu.Game.Scoring
|
||||
CountryCode = user.CountryCode,
|
||||
});
|
||||
|
||||
model.User = user;
|
||||
return cachedUser;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private APIUser? lookupUserByName(string username)
|
||||
{
|
||||
if (usernameLookupCache.TryGetValue(username, out var existing))
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
|
||||
var userRequest = new GetUserRequest(username);
|
||||
|
||||
api.Perform(userRequest);
|
||||
|
||||
if (userRequest.Response is APIUser user)
|
||||
{
|
||||
APIUser cachedUser;
|
||||
|
||||
usernameLookupCache.TryAdd(username, cachedUser = new APIUser
|
||||
{
|
||||
// Because this is a permanent cache, let's only store the pieces we're interested in,
|
||||
// rather than the full API response. If we start to store more than these three fields
|
||||
// in realm, this should be undone.
|
||||
Id = user.Id,
|
||||
Username = user.Username,
|
||||
CountryCode = user.CountryCode,
|
||||
});
|
||||
|
||||
return cachedUser;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,8 +65,19 @@ namespace osu.Game.Scoring
|
||||
|
||||
public bool DeletePending { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The total number of points awarded for the score.
|
||||
/// </summary>
|
||||
public long TotalScore { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The total number of points awarded for the score without including mod multipliers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The purpose of this property is to enable future lossless rebalances of mod multipliers.
|
||||
/// </remarks>
|
||||
public long TotalScoreWithoutMods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The version of processing applied to calculate total score as stored in the database.
|
||||
/// If this does not match <see cref="LegacyScoreEncoder.LATEST_VERSION"/>,
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
@ -20,7 +19,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
public partial class TimelineTickDisplay : TimelinePart<PointVisualisation>
|
||||
{
|
||||
// With current implementation every tick in the sub-tree should be visible, no need to check whether they are masked away.
|
||||
public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) => false;
|
||||
public override bool UpdateSubTreeMasking() => false;
|
||||
|
||||
[Resolved]
|
||||
private EditorBeatmap beatmap { get; set; } = null!;
|
||||
|
@ -32,6 +32,11 @@ namespace osu.Game.Screens.Select.FooterV2
|
||||
{
|
||||
// todo: see https://github.com/ppy/osu-framework/issues/3271
|
||||
private const float torus_scale_factor = 1.2f;
|
||||
private const float bar_shear_width = 7f;
|
||||
private const float bar_height = 37f;
|
||||
private const float mod_display_portion = 0.65f;
|
||||
|
||||
private static readonly Vector2 bar_shear = new Vector2(bar_shear_width / bar_height, 0);
|
||||
|
||||
private readonly BindableWithCurrent<IReadOnlyList<Mod>> current = new BindableWithCurrent<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
|
||||
@ -43,7 +48,7 @@ namespace osu.Game.Screens.Select.FooterV2
|
||||
|
||||
private Container modDisplayBar = null!;
|
||||
|
||||
protected Container UnrankedBadge { get; private set; } = null!;
|
||||
private Drawable unrankedBadge = null!;
|
||||
|
||||
private ModDisplay modDisplay = null!;
|
||||
private OsuSpriteText modCountText = null!;
|
||||
@ -59,57 +64,19 @@ namespace osu.Game.Screens.Select.FooterV2
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
const float bar_shear_width = 7f;
|
||||
const float bar_height = 37f;
|
||||
const float mod_display_portion = 0.65f;
|
||||
|
||||
var barShear = new Vector2(bar_shear_width / bar_height, 0);
|
||||
|
||||
Text = "Mods";
|
||||
Icon = FontAwesome.Solid.ExchangeAlt;
|
||||
AccentColour = colours.Lime1;
|
||||
|
||||
AddRange(new[]
|
||||
{
|
||||
UnrankedBadge = new ContainerWithTooltip
|
||||
{
|
||||
Position = new Vector2(BUTTON_WIDTH + 5f, -5f),
|
||||
Depth = float.MaxValue,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Shear = barShear,
|
||||
CornerRadius = CORNER_RADIUS,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = bar_height,
|
||||
Masking = true,
|
||||
BorderColour = Color4.White,
|
||||
BorderThickness = 2f,
|
||||
TooltipText = ModSelectOverlayStrings.UnrankedExplanation,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colours.Orange2,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Shear = -barShear,
|
||||
Text = ModSelectOverlayStrings.Unranked.ToUpper(),
|
||||
Margin = new MarginPadding { Horizontal = 15 },
|
||||
UseFullGlyphHeight = false,
|
||||
Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold),
|
||||
Colour = Color4.Black,
|
||||
}
|
||||
}
|
||||
},
|
||||
unrankedBadge = new UnrankedBadge(),
|
||||
modDisplayBar = new Container
|
||||
{
|
||||
Y = -5f,
|
||||
Depth = float.MaxValue,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Shear = barShear,
|
||||
Shear = bar_shear,
|
||||
CornerRadius = CORNER_RADIUS,
|
||||
Size = new Vector2(BUTTON_WIDTH, bar_height),
|
||||
Masking = true,
|
||||
@ -139,7 +106,7 @@ namespace osu.Game.Screens.Select.FooterV2
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Shear = -barShear,
|
||||
Shear = -bar_shear,
|
||||
UseFullGlyphHeight = false,
|
||||
Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold)
|
||||
}
|
||||
@ -161,7 +128,7 @@ namespace osu.Game.Screens.Select.FooterV2
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Shear = -barShear,
|
||||
Shear = -bar_shear,
|
||||
Scale = new Vector2(0.6f),
|
||||
Current = { BindTarget = Current },
|
||||
ExpansionMode = ExpansionMode.AlwaysContracted,
|
||||
@ -170,7 +137,7 @@ namespace osu.Game.Screens.Select.FooterV2
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Shear = -barShear,
|
||||
Shear = -bar_shear,
|
||||
Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold),
|
||||
Mods = { BindTarget = Current },
|
||||
}
|
||||
@ -215,8 +182,8 @@ namespace osu.Game.Screens.Select.FooterV2
|
||||
modDisplay.FadeOut(duration, easing);
|
||||
modCountText.FadeOut(duration, easing);
|
||||
|
||||
UnrankedBadge.MoveToY(20, duration, easing);
|
||||
UnrankedBadge.FadeOut(duration, easing);
|
||||
unrankedBadge.MoveToY(20, duration, easing);
|
||||
unrankedBadge.FadeOut(duration, easing);
|
||||
|
||||
// add delay to let unranked indicator hide first before resizing the button back to its original width.
|
||||
this.Delay(duration).ResizeWidthTo(BUTTON_WIDTH, duration, easing);
|
||||
@ -233,21 +200,21 @@ namespace osu.Game.Screens.Select.FooterV2
|
||||
|
||||
if (Current.Value.Any(m => !m.Ranked))
|
||||
{
|
||||
UnrankedBadge.MoveToX(BUTTON_WIDTH + 5, duration, easing);
|
||||
UnrankedBadge.FadeIn(duration, easing);
|
||||
unrankedBadge.MoveToX(0, duration, easing);
|
||||
unrankedBadge.FadeIn(duration, easing);
|
||||
|
||||
this.ResizeWidthTo(BUTTON_WIDTH + UnrankedBadge.DrawWidth + 10, duration, easing);
|
||||
this.ResizeWidthTo(BUTTON_WIDTH + 5 + unrankedBadge.DrawWidth, duration, easing);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnrankedBadge.MoveToX(BUTTON_WIDTH + 5 - UnrankedBadge.DrawWidth, duration, easing);
|
||||
UnrankedBadge.FadeOut(duration, easing);
|
||||
unrankedBadge.MoveToX(-unrankedBadge.DrawWidth, duration, easing);
|
||||
unrankedBadge.FadeOut(duration, easing);
|
||||
|
||||
this.ResizeWidthTo(BUTTON_WIDTH, duration, easing);
|
||||
}
|
||||
|
||||
modDisplayBar.MoveToY(-5, duration, Easing.OutQuint);
|
||||
UnrankedBadge.MoveToY(-5, duration, easing);
|
||||
unrankedBadge.MoveToY(-5, duration, easing);
|
||||
modDisplayBar.FadeIn(duration, easing);
|
||||
}
|
||||
|
||||
@ -266,13 +233,16 @@ namespace osu.Game.Screens.Select.FooterV2
|
||||
{
|
||||
public readonly Bindable<IReadOnlyList<Mod>> Mods = new Bindable<IReadOnlyList<Mod>>();
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Mods.BindValueChanged(v => Text = FooterButtonModsV2Strings.Mods(v.NewValue.Count).ToUpper(), true);
|
||||
Mods.BindValueChanged(v => Text = ModSelectOverlayStrings.Mods(v.NewValue.Count).ToUpper(), true);
|
||||
}
|
||||
|
||||
public ITooltip<IReadOnlyList<Mod>> GetCustomTooltip() => new ModTooltip();
|
||||
public ITooltip<IReadOnlyList<Mod>> GetCustomTooltip() => new ModTooltip(colourProvider);
|
||||
|
||||
public IReadOnlyList<Mod>? TooltipContent => Mods.Value;
|
||||
|
||||
@ -280,8 +250,16 @@ namespace osu.Game.Screens.Select.FooterV2
|
||||
{
|
||||
private ModDisplay extendedModDisplay = null!;
|
||||
|
||||
[Cached]
|
||||
private OverlayColourProvider colourProvider;
|
||||
|
||||
public ModTooltip(OverlayColourProvider colourProvider)
|
||||
{
|
||||
this.colourProvider = colourProvider;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
private void load()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
CornerRadius = CORNER_RADIUS;
|
||||
@ -315,9 +293,49 @@ namespace osu.Game.Screens.Select.FooterV2
|
||||
}
|
||||
}
|
||||
|
||||
private partial class ContainerWithTooltip : Container, IHasTooltip
|
||||
internal partial class UnrankedBadge : CompositeDrawable, IHasTooltip
|
||||
{
|
||||
public LocalisableString TooltipText { get; set; }
|
||||
public LocalisableString TooltipText { get; }
|
||||
|
||||
public UnrankedBadge()
|
||||
{
|
||||
Margin = new MarginPadding { Left = BUTTON_WIDTH + 5f };
|
||||
Y = -5f;
|
||||
Depth = float.MaxValue;
|
||||
Origin = Anchor.BottomLeft;
|
||||
Shear = bar_shear;
|
||||
CornerRadius = CORNER_RADIUS;
|
||||
AutoSizeAxes = Axes.X;
|
||||
Height = bar_height;
|
||||
Masking = true;
|
||||
BorderColour = Color4.White;
|
||||
BorderThickness = 2f;
|
||||
TooltipText = ModSelectOverlayStrings.UnrankedExplanation;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colours.Orange2,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Shear = -bar_shear,
|
||||
Text = ModSelectOverlayStrings.Unranked.ToUpper(),
|
||||
Margin = new MarginPadding { Horizontal = 15 },
|
||||
UseFullGlyphHeight = false,
|
||||
Font = OsuFont.Torus.With(size: 14 * torus_scale_factor, weight: FontWeight.Bold),
|
||||
Colour = Color4.Black,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,8 +35,8 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="11.5.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2024.423.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.410.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2024.509.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.510.0" />
|
||||
<PackageReference Include="Sentry" Version="4.3.0" />
|
||||
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
||||
<PackageReference Include="SharpCompress" Version="0.36.0" />
|
||||
|
@ -23,6 +23,6 @@
|
||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2024.423.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2024.509.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
Before Width: | Height: | Size: 443 KiB After Width: | Height: | Size: 386 KiB |