Compare commits
28 Commits
70f7f09a83
...
2025.827.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 628181a883 | |||
| 835329efd3 | |||
| 5399943118 | |||
| d07f82f6f4 | |||
|
|
2bea59e65f | ||
|
|
c0fd5637de | ||
|
|
5e7a99c97f | ||
|
|
8f628d16ae | ||
|
|
2ccb65aa65 | ||
|
|
4d851f2527 | ||
|
|
4bafbfb9e4 | ||
|
|
3f179e3903 | ||
|
|
196b28115e | ||
|
|
7660a9ba8e | ||
|
|
e908b80359 | ||
|
|
a2bf8e3988 | ||
| 5b186bb740 | |||
|
|
6e8246b539 | ||
| 6cb99c13c2 | |||
|
|
3cca458c21 | ||
|
|
bc59270f3e | ||
| 96008e06ab | |||
| 590b0a8028 | |||
|
|
c0c3690908 | ||
|
|
73624e4e25 | ||
|
|
f374af7ce7 | ||
|
|
7530ad1a7b | ||
|
|
a049f5065d |
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
|
|
||||||
public override BindableBool ComboBasedSize { get; } = new BindableBool(true);
|
public override BindableBool ComboBasedSize { get; } = new BindableBool(true);
|
||||||
|
|
||||||
public override float DefaultFlashlightSize => 325;
|
public override float DefaultFlashlightSize => 203.125f;
|
||||||
|
|
||||||
protected override Flashlight CreateFlashlight() => new CatchFlashlight(this, playfield);
|
protected override Flashlight CreateFlashlight() => new CatchFlashlight(this, playfield);
|
||||||
|
|
||||||
|
|||||||
@@ -32,26 +32,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestComboBasedSize([Values] bool comboBasedSize) => CreateModTest(new ModTestData { Mod = new OsuModFlashlight { ComboBasedSize = { Value = comboBasedSize } }, PassCondition = () => true });
|
public void TestComboBasedSize([Values] bool comboBasedSize) => CreateModTest(new ModTestData { Mod = new OsuModFlashlight { ComboBasedSize = { Value = comboBasedSize } }, PassCondition = () => true });
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestPlayfieldBasedSize()
|
|
||||||
{
|
|
||||||
OsuModFlashlight flashlight;
|
|
||||||
CreateModTest(new ModTestData
|
|
||||||
{
|
|
||||||
Mods = [flashlight = new OsuModFlashlight(), new OsuModBarrelRoll()],
|
|
||||||
PassCondition = () =>
|
|
||||||
{
|
|
||||||
var flashlightOverlay = Player.DrawableRuleset.Overlays
|
|
||||||
.ChildrenOfType<ModFlashlight<OsuHitObject>.Flashlight>()
|
|
||||||
.First();
|
|
||||||
|
|
||||||
// the combo check is here because the flashlight radius decreases for the first time at 100 combo
|
|
||||||
// and hardcoding it here eliminates the need to meddle in flashlight internals further by e.g. exposing `GetComboScaleFor()`
|
|
||||||
return flashlightOverlay.GetSize() < flashlight.DefaultFlashlightSize && Player.GameplayState.ScoreProcessor.Combo.Value < 100;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSliderDimsOnlyAfterStartTime()
|
public void TestSliderDimsOnlyAfterStartTime()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ namespace osu.Game.Rulesets.Osu.HUD
|
|||||||
if (PositionDisplayStyle.Value == PositionDisplay.Normalised && lastObjectPosition != null)
|
if (PositionDisplayStyle.Value == PositionDisplay.Normalised && lastObjectPosition != null)
|
||||||
{
|
{
|
||||||
hitPosition = AccuracyHeatmap.FindRelativeHitPosition(lastObjectPosition.Value, ((OsuHitObject)circleJudgement.HitObject).StackedEndPosition,
|
hitPosition = AccuracyHeatmap.FindRelativeHitPosition(lastObjectPosition.Value, ((OsuHitObject)circleJudgement.HitObject).StackedEndPosition,
|
||||||
circleJudgement.CursorPositionAtHit.Value, objectRadius, 45) * 0.5f;
|
circleJudgement.CursorPositionAtHit.Value, objectRadius, 45) * (inner_portion / 2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public override BindableBool ComboBasedSize { get; } = new BindableBool(true);
|
public override BindableBool ComboBasedSize { get; } = new BindableBool(true);
|
||||||
|
|
||||||
public override float DefaultFlashlightSize => 200;
|
public override float DefaultFlashlightSize => 125;
|
||||||
|
|
||||||
private OsuFlashlight flashlight = null!;
|
private OsuFlashlight flashlight = null!;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Mods;
|
using osu.Game.Rulesets.Taiko.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.UI;
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@@ -12,6 +15,34 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods
|
|||||||
{
|
{
|
||||||
public partial class TestSceneTaikoModFlashlight : TaikoModTestScene
|
public partial class TestSceneTaikoModFlashlight : TaikoModTestScene
|
||||||
{
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestAspectRatios([Values] bool withClassicMod)
|
||||||
|
{
|
||||||
|
if (withClassicMod)
|
||||||
|
CreateModTest(new ModTestData { Mods = new Mod[] { new TaikoModFlashlight(), new TaikoModClassic() }, PassCondition = () => true });
|
||||||
|
else
|
||||||
|
CreateModTest(new ModTestData { Mod = new TaikoModFlashlight(), PassCondition = () => true });
|
||||||
|
|
||||||
|
AddStep("clear dim", () => LocalConfig.SetValue(OsuSetting.DimLevel, 0.0));
|
||||||
|
|
||||||
|
AddStep("reset", () => Stack.FillMode = FillMode.Stretch);
|
||||||
|
AddStep("set to 16:9", () =>
|
||||||
|
{
|
||||||
|
Stack.FillAspectRatio = 16 / 9f;
|
||||||
|
Stack.FillMode = FillMode.Fit;
|
||||||
|
});
|
||||||
|
AddStep("set to 4:3", () =>
|
||||||
|
{
|
||||||
|
Stack.FillAspectRatio = 4 / 3f;
|
||||||
|
Stack.FillMode = FillMode.Fit;
|
||||||
|
});
|
||||||
|
AddSliderStep("aspect ratio", 0.01f, 5f, 1f, v =>
|
||||||
|
{
|
||||||
|
Stack.FillAspectRatio = v;
|
||||||
|
Stack.FillMode = FillMode.Fit;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase(1f)]
|
[TestCase(1f)]
|
||||||
[TestCase(0.5f)]
|
[TestCase(0.5f)]
|
||||||
[TestCase(1.25f)]
|
[TestCase(1.25f)]
|
||||||
|
|||||||
@@ -47,28 +47,15 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
this.taikoPlayfield = taikoPlayfield;
|
this.taikoPlayfield = taikoPlayfield;
|
||||||
|
|
||||||
FlashlightSize = adjustSizeForPlayfieldAspectRatio(GetSize());
|
FlashlightSize = new Vector2(0, GetSize());
|
||||||
FlashlightSmoothness = 1.4f;
|
FlashlightSmoothness = 1.4f;
|
||||||
|
|
||||||
AddLayout(flashlightProperties);
|
AddLayout(flashlightProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the aspect ratio-adjusted size of the flashlight.
|
|
||||||
/// This ensures that the size of the flashlight remains independent of taiko-specific aspect ratio adjustments.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="size">
|
|
||||||
/// The size of the flashlight.
|
|
||||||
/// The value provided here should always come from <see cref="ModFlashlight{T}.Flashlight.GetSize"/>.
|
|
||||||
/// </param>
|
|
||||||
private Vector2 adjustSizeForPlayfieldAspectRatio(float size)
|
|
||||||
{
|
|
||||||
return new Vector2(0, size * taikoPlayfield.Parent!.Scale.Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateFlashlightSize(float size)
|
protected override void UpdateFlashlightSize(float size)
|
||||||
{
|
{
|
||||||
this.TransformTo(nameof(FlashlightSize), adjustSizeForPlayfieldAspectRatio(size), FLASHLIGHT_FADE_DURATION);
|
this.TransformTo(nameof(FlashlightSize), new Vector2(0, size), FLASHLIGHT_FADE_DURATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string FragmentShader => "CircularFlashlight";
|
protected override string FragmentShader => "CircularFlashlight";
|
||||||
@@ -82,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
FlashlightPosition = ToLocalSpace(taikoPlayfield.HitTarget.ScreenSpaceDrawQuad.Centre);
|
FlashlightPosition = ToLocalSpace(taikoPlayfield.HitTarget.ScreenSpaceDrawQuad.Centre);
|
||||||
|
|
||||||
ClearTransforms(targetMember: nameof(FlashlightSize));
|
ClearTransforms(targetMember: nameof(FlashlightSize));
|
||||||
FlashlightSize = adjustSizeForPlayfieldAspectRatio(GetSize());
|
FlashlightSize = new Vector2(0, GetSize());
|
||||||
|
|
||||||
flashlightProperties.Validate();
|
flashlightProperties.Validate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
protected TestReplayPlayer Player = null!;
|
protected TestReplayPlayer Player = null!;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFailedBeatmapLoad()
|
||||||
|
{
|
||||||
|
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo, withHitObjects: false));
|
||||||
|
|
||||||
|
AddUntilStep("wait for exit", () => Player.IsCurrentScreen());
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestPauseViaSpace()
|
public void TestPauseViaSpace()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using osu.Framework.Configuration;
|
|||||||
using osu.Framework.Configuration.Tracking;
|
using osu.Framework.Configuration.Tracking;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Extensions.LocalisationExtensions;
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.Beatmaps.Drawables.Cards;
|
using osu.Game.Beatmaps.Drawables.Cards;
|
||||||
@@ -41,6 +42,8 @@ namespace osu.Game.Configuration
|
|||||||
SetDefault(OsuSetting.Ruleset, string.Empty);
|
SetDefault(OsuSetting.Ruleset, string.Empty);
|
||||||
SetDefault(OsuSetting.Skin, SkinInfo.ARGON_SKIN.ToString());
|
SetDefault(OsuSetting.Skin, SkinInfo.ARGON_SKIN.ToString());
|
||||||
|
|
||||||
|
SetDefault(OsuSetting.MenuCookieColor, Colour4.FromHex(@"ff66ba"));
|
||||||
|
|
||||||
SetDefault(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Local);
|
SetDefault(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Local);
|
||||||
SetDefault(OsuSetting.BeatmapLeaderboardSortMode, LeaderboardSortMode.Score);
|
SetDefault(OsuSetting.BeatmapLeaderboardSortMode, LeaderboardSortMode.Score);
|
||||||
SetDefault(OsuSetting.BeatmapDetailModsFilter, false);
|
SetDefault(OsuSetting.BeatmapDetailModsFilter, false);
|
||||||
@@ -65,6 +68,7 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
SetDefault(OsuSetting.ToolbarClockDisplayMode, ToolbarClockDisplayMode.Full);
|
SetDefault(OsuSetting.ToolbarClockDisplayMode, ToolbarClockDisplayMode.Full);
|
||||||
|
|
||||||
|
SetDefault(OsuSetting.ForceLegacySongSelect, false);
|
||||||
SetDefault(OsuSetting.SongSelectBackgroundBlur, false);
|
SetDefault(OsuSetting.SongSelectBackgroundBlur, false);
|
||||||
|
|
||||||
// Online settings
|
// Online settings
|
||||||
@@ -194,6 +198,7 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
SetDefault(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin);
|
SetDefault(OsuSetting.MenuBackgroundSource, BackgroundSource.Skin);
|
||||||
SetDefault(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Never);
|
SetDefault(OsuSetting.SeasonalBackgroundMode, SeasonalBackgroundMode.Never);
|
||||||
|
SetDefault(OsuSetting.UseSeasonalBackgroundsV2, true);
|
||||||
|
|
||||||
SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full);
|
SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full);
|
||||||
|
|
||||||
@@ -405,6 +410,7 @@ namespace osu.Game.Configuration
|
|||||||
ChatDisplayHeight,
|
ChatDisplayHeight,
|
||||||
BeatmapListingCardSize,
|
BeatmapListingCardSize,
|
||||||
ToolbarClockDisplayMode,
|
ToolbarClockDisplayMode,
|
||||||
|
ForceLegacySongSelect,
|
||||||
SongSelectBackgroundBlur,
|
SongSelectBackgroundBlur,
|
||||||
Version,
|
Version,
|
||||||
ShowFirstRunSetup,
|
ShowFirstRunSetup,
|
||||||
@@ -412,6 +418,7 @@ namespace osu.Game.Configuration
|
|||||||
Skin,
|
Skin,
|
||||||
ScreenshotFormat,
|
ScreenshotFormat,
|
||||||
ScreenshotCaptureMenuCursor,
|
ScreenshotCaptureMenuCursor,
|
||||||
|
MenuCookieColor,
|
||||||
BeatmapSkins,
|
BeatmapSkins,
|
||||||
BeatmapColours,
|
BeatmapColours,
|
||||||
BeatmapHitsounds,
|
BeatmapHitsounds,
|
||||||
@@ -436,6 +443,7 @@ namespace osu.Game.Configuration
|
|||||||
MenuBackgroundSource,
|
MenuBackgroundSource,
|
||||||
GameplayDisableWinKey,
|
GameplayDisableWinKey,
|
||||||
SeasonalBackgroundMode,
|
SeasonalBackgroundMode,
|
||||||
|
UseSeasonalBackgroundsV2, // TODO: add migrations
|
||||||
BackgroundCategory,
|
BackgroundCategory,
|
||||||
EditorWaveformOpacity,
|
EditorWaveformOpacity,
|
||||||
EditorShowHitMarkers,
|
EditorShowHitMarkers,
|
||||||
|
|||||||
@@ -727,7 +727,7 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Log(@$"Failed to update ranked/submitted dates for beatmap set {id}: {e}");
|
Logger.Log(@$"Failed to update user tags for beatmap {id}: {e}");
|
||||||
++failedCount;
|
++failedCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,19 +33,19 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private IAPIProvider api { get; set; }
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
private Bindable<SeasonalBackgroundMode> backgroundMode;
|
private Bindable<bool> useSeasonalBackgrounds;
|
||||||
private Bindable<string> selectedCategory;
|
private Bindable<string> selectedCategory;
|
||||||
private Bindable<APISeasonalBackgrounds> currentBackgrounds;
|
private Bindable<APISeasonalBackgrounds> currentBackgrounds;
|
||||||
|
|
||||||
private int currentBackgroundIndex;
|
private int currentBackgroundIndex;
|
||||||
|
|
||||||
private bool shouldShowCustomBackgrounds => backgroundMode.Value != SeasonalBackgroundMode.Never;
|
private bool shouldShowCustomBackgrounds => useSeasonalBackgrounds.Value;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config, SessionStatics sessionStatics)
|
private void load(OsuConfigManager config, SessionStatics sessionStatics)
|
||||||
{
|
{
|
||||||
backgroundMode = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode);
|
useSeasonalBackgrounds = config.GetBindable<bool>(OsuSetting.UseSeasonalBackgroundsV2);
|
||||||
backgroundMode.BindValueChanged(_ => BackgroundChanged?.Invoke());
|
useSeasonalBackgrounds.BindValueChanged(_ => BackgroundChanged?.Invoke());
|
||||||
|
|
||||||
selectedCategory = config.GetBindable<string>(OsuSetting.BackgroundCategory);
|
selectedCategory = config.GetBindable<string>(OsuSetting.BackgroundCategory);
|
||||||
selectedCategory.BindValueChanged(_ => fetchBackgroundsForSelectedCategory());
|
selectedCategory.BindValueChanged(_ => fetchBackgroundsForSelectedCategory());
|
||||||
@@ -53,7 +53,7 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
currentBackgrounds = sessionStatics.GetBindable<APISeasonalBackgrounds>(Static.SeasonalBackgrounds);
|
currentBackgrounds = sessionStatics.GetBindable<APISeasonalBackgrounds>(Static.SeasonalBackgrounds);
|
||||||
|
|
||||||
if (shouldShowCustomBackgrounds)
|
if (shouldShowCustomBackgrounds)
|
||||||
fetchCategories();
|
fetchCategories(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -64,7 +64,7 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
fetchCategories();
|
fetchCategories();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchCategories()
|
private void fetchCategories(bool ignoreSuccess = false)
|
||||||
{
|
{
|
||||||
if (!shouldShowCustomBackgrounds) return;
|
if (!shouldShowCustomBackgrounds) return;
|
||||||
|
|
||||||
@@ -74,20 +74,27 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
{
|
{
|
||||||
var serverCategories = response.Categories ?? Enumerable.Empty<string>();
|
var serverCategories = response.Categories ?? Enumerable.Empty<string>();
|
||||||
|
|
||||||
AvailableCategories.Value = new[] { "Default" }.Concat(serverCategories)
|
AvailableCategories.Value = serverCategories.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
|
|
||||||
|
if (!AvailableCategories.Value.Any()) {
|
||||||
|
selectedCategory.Value = "";
|
||||||
|
return; // we don't have any categories!!!
|
||||||
|
}
|
||||||
|
|
||||||
if (!AvailableCategories.Value.Contains(selectedCategory.Value))
|
if (!AvailableCategories.Value.Contains(selectedCategory.Value))
|
||||||
selectedCategory.Value = "Default";
|
selectedCategory.Value = AvailableCategories.Value.Contains("Default")
|
||||||
else
|
? "Default"
|
||||||
fetchBackgroundsForSelectedCategory();
|
: AvailableCategories.Value.ElementAt(0);
|
||||||
|
|
||||||
OnCategoriesRefreshed?.Invoke();
|
fetchBackgroundsForSelectedCategory();
|
||||||
|
|
||||||
|
if (!ignoreSuccess)
|
||||||
|
OnCategoriesRefreshed?.Invoke();
|
||||||
};
|
};
|
||||||
|
|
||||||
request.Failure += exception =>
|
request.Failure += exception =>
|
||||||
{
|
{
|
||||||
AvailableCategories.Value = new[] { "Íå óäàëîñü çàãðóçèòü..." };
|
AvailableCategories.Value = Array.Empty<string>();
|
||||||
OnLoadFailure?.Invoke(exception);
|
OnLoadFailure?.Invoke(exception);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -98,13 +105,6 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
{
|
{
|
||||||
if (!shouldShowCustomBackgrounds) return;
|
if (!shouldShowCustomBackgrounds) return;
|
||||||
|
|
||||||
if (AvailableCategories.Value.Count() == 1 && AvailableCategories.Value.First().Contains("Íå óäàëîñü"))
|
|
||||||
{
|
|
||||||
currentBackgrounds.Value = new APISeasonalBackgrounds { Backgrounds = new List<APISeasonalBackground>() };
|
|
||||||
BackgroundChanged?.Invoke();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string categoryToFetch = selectedCategory.Value == "Default" ? null : selectedCategory.Value;
|
string categoryToFetch = selectedCategory.Value == "Default" ? null : selectedCategory.Value;
|
||||||
var request = new GetSeasonalBackgroundsRequest(categoryToFetch);
|
var request = new GetSeasonalBackgroundsRequest(categoryToFetch);
|
||||||
|
|
||||||
|
|||||||
@@ -184,6 +184,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString SelectedMods => new TranslatableString(getKey(@"selected_mods"), @"Selected Mods");
|
public static LocalisableString SelectedMods => new TranslatableString(getKey(@"selected_mods"), @"Selected Mods");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Use legacy song select (SelectV1)"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ForceLegacySongSelect => new TranslatableString(getKey(@"force_select_v1"), @"Use legacy song select (SelectV1)");
|
||||||
|
|
||||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ namespace osu.Game
|
|||||||
private readonly ScreenshotManager screenshotManager = new ScreenshotManager();
|
private readonly ScreenshotManager screenshotManager = new ScreenshotManager();
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private readonly SeasonalBackgroundLoader backgroundLoader;
|
private SeasonalBackgroundLoader seasonalBackgroundLoader;
|
||||||
|
|
||||||
protected SentryLogger SentryLogger;
|
protected SentryLogger SentryLogger;
|
||||||
|
|
||||||
@@ -253,10 +253,6 @@ namespace osu.Game
|
|||||||
|
|
||||||
public OsuGame(string[] args = null)
|
public OsuGame(string[] args = null)
|
||||||
{
|
{
|
||||||
backgroundLoader = new SeasonalBackgroundLoader();
|
|
||||||
backgroundLoader.OnLoadFailure += handleBackgroundLoadFailure;
|
|
||||||
backgroundLoader.OnCategoriesRefreshed += handleCategoriesRefreshed;
|
|
||||||
|
|
||||||
this.args = args;
|
this.args = args;
|
||||||
|
|
||||||
Logger.NewEntry += forwardGeneralLogToNotifications;
|
Logger.NewEntry += forwardGeneralLogToNotifications;
|
||||||
@@ -271,6 +267,8 @@ namespace osu.Game
|
|||||||
tabletLogNotifyOnError = true;
|
tabletLogNotifyOnError = true;
|
||||||
}, true);
|
}, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
initializeSeasonalBackgrounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IOverlayManager
|
#region IOverlayManager
|
||||||
@@ -409,18 +407,6 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void handleCategoriesRefreshed()
|
|
||||||
{
|
|
||||||
Schedule(() =>
|
|
||||||
{
|
|
||||||
Notifications?.Post(new SimpleNotification
|
|
||||||
{
|
|
||||||
Text = ButtonSystemStrings.SeasonalBackgroundsRefreshed,
|
|
||||||
Icon = FontAwesome.Solid.CheckCircle,
|
|
||||||
Transient = true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
@@ -1252,7 +1238,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
loadComponentSingleFile(screenshotManager, Add);
|
loadComponentSingleFile(screenshotManager, Add);
|
||||||
|
|
||||||
loadComponentSingleFile(backgroundLoader, Add);
|
loadComponentSingleFile(seasonalBackgroundLoader, Add);
|
||||||
|
|
||||||
// dependency on notification overlay, dependent by settings overlay
|
// dependency on notification overlay, dependent by settings overlay
|
||||||
loadComponentSingleFile(CreateUpdateManager(), Add, true);
|
loadComponentSingleFile(CreateUpdateManager(), Add, true);
|
||||||
@@ -1355,18 +1341,6 @@ namespace osu.Game
|
|||||||
handleStartupImport();
|
handleStartupImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBackgroundLoadFailure(Exception exception)
|
|
||||||
{
|
|
||||||
Schedule(() =>
|
|
||||||
{
|
|
||||||
Notifications?.Post(new SimpleErrorNotification
|
|
||||||
{
|
|
||||||
Text = ButtonSystemStrings.SeasonalBackgroundsFail,
|
|
||||||
Icon = FontAwesome.Solid.ExclamationTriangle,
|
|
||||||
Transient = true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
private void handleBackButton()
|
private void handleBackButton()
|
||||||
{
|
{
|
||||||
// TODO: this is SUPER SUPER bad.
|
// TODO: this is SUPER SUPER bad.
|
||||||
@@ -1503,6 +1477,40 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initializeSeasonalBackgrounds()
|
||||||
|
{
|
||||||
|
seasonalBackgroundLoader = new SeasonalBackgroundLoader();
|
||||||
|
seasonalBackgroundLoader.OnCategoriesRefreshed += handleCategoriesRefreshed;
|
||||||
|
seasonalBackgroundLoader.OnLoadFailure += handleBackgroundLoadFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCategoriesRefreshed()
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
Notifications?.Post(new SimpleNotification
|
||||||
|
{
|
||||||
|
Text = ButtonSystemStrings.SeasonalBackgroundsRefreshed,
|
||||||
|
Icon = FontAwesome.Solid.CheckCircle,
|
||||||
|
Transient = true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleBackgroundLoadFailure(Exception exception)
|
||||||
|
{
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
Notifications?.Post(new SimpleErrorNotification
|
||||||
|
{
|
||||||
|
Text = ButtonSystemStrings.SeasonalBackgroundsFail,
|
||||||
|
Icon = FontAwesome.Solid.ExclamationTriangle,
|
||||||
|
Transient = true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private Task asyncLoadStream;
|
private Task asyncLoadStream;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
@@ -35,13 +37,11 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
|||||||
var backgroundModeBindable = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode);
|
var backgroundModeBindable = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode);
|
||||||
var enabledProxyBindable = new Bindable<bool>();
|
var enabledProxyBindable = new Bindable<bool>();
|
||||||
|
|
||||||
backgroundModeBindable.BindValueChanged(mode => enabledProxyBindable.Value = mode.NewValue == SeasonalBackgroundMode.Always, true);
|
|
||||||
enabledProxyBindable.BindValueChanged(enabled => backgroundModeBindable.Value = enabled.NewValue ? SeasonalBackgroundMode.Always : SeasonalBackgroundMode.Never);
|
|
||||||
|
|
||||||
var backgroundToggle = new SettingsCheckbox
|
var backgroundToggle = new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = UserInterfaceStrings.UseSeasonalBackgrounds,
|
LabelText = UserInterfaceStrings.UseSeasonalBackgrounds,
|
||||||
Current = enabledProxyBindable
|
Current = config.GetBindable<bool>(OsuSetting.UseSeasonalBackgroundsV2),
|
||||||
|
ClassicDefault = true
|
||||||
};
|
};
|
||||||
|
|
||||||
var categoryDropdown = new SettingsDropdown<string>
|
var categoryDropdown = new SettingsDropdown<string>
|
||||||
@@ -104,6 +104,12 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
|||||||
backgroundToggle,
|
backgroundToggle,
|
||||||
categoryDropdown,
|
categoryDropdown,
|
||||||
refreshButton,
|
refreshButton,
|
||||||
|
new SettingsColour
|
||||||
|
{
|
||||||
|
LabelText = @"osu! logo colour",
|
||||||
|
Current = config.GetBindable<Colour4>(OsuSetting.MenuCookieColor),
|
||||||
|
ClassicDefault = Colour4.FromHex(@"ff66ba"),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
|||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = UserInterfaceStrings.ForceLegacySongSelect,
|
||||||
|
Current = config.GetBindable<bool>(OsuSetting.ForceLegacySongSelect),
|
||||||
|
ClassicDefault = false
|
||||||
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = UserInterfaceStrings.ShowConvertedBeatmaps,
|
LabelText = UserInterfaceStrings.ShowConvertedBeatmaps,
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@@ -14,8 +13,8 @@ using osu.Framework.Graphics.Rendering.Vertices;
|
|||||||
using osu.Framework.Graphics.Shaders;
|
using osu.Framework.Graphics.Shaders;
|
||||||
using osu.Framework.Graphics.Shaders.Types;
|
using osu.Framework.Graphics.Shaders.Types;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Layout;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.OpenGL.Vertices;
|
using osu.Game.Graphics.OpenGL.Vertices;
|
||||||
@@ -85,7 +84,11 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
flashlight.Colour = Color4.Black;
|
flashlight.Colour = Color4.Black;
|
||||||
|
|
||||||
flashlight.Combo.BindTo(Combo);
|
flashlight.Combo.BindTo(Combo);
|
||||||
flashlight.GetPlayfieldScale = () => drawableRuleset.PlayfieldAdjustmentContainer.Scale;
|
|
||||||
|
var playfieldDrawInfoTracker = new PlayfieldDrawInfoTracker();
|
||||||
|
|
||||||
|
drawableRuleset.PlayfieldAdjustmentContainer.Add(playfieldDrawInfoTracker);
|
||||||
|
flashlight.PlayfieldDrawInfoTracker = playfieldDrawInfoTracker;
|
||||||
|
|
||||||
drawableRuleset.Overlays.Add(new Container
|
drawableRuleset.Overlays.Add(new Container
|
||||||
{
|
{
|
||||||
@@ -111,7 +114,9 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
|
|
||||||
public override bool RemoveCompletedTransforms => false;
|
public override bool RemoveCompletedTransforms => false;
|
||||||
|
|
||||||
internal Func<Vector2>? GetPlayfieldScale;
|
internal PlayfieldDrawInfoTracker PlayfieldDrawInfoTracker { get; set; } = null!;
|
||||||
|
|
||||||
|
private DrawInfo playfieldDrawInfo => PlayfieldDrawInfoTracker.DrawInfo;
|
||||||
|
|
||||||
private readonly float defaultFlashlightSize;
|
private readonly float defaultFlashlightSize;
|
||||||
private readonly float sizeMultiplier;
|
private readonly float sizeMultiplier;
|
||||||
@@ -146,6 +151,8 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
isBreakTime.BindTo(player.IsBreakTime);
|
isBreakTime.BindTo(player.IsBreakTime);
|
||||||
isBreakTime.BindValueChanged(_ => UpdateFlashlightSize(GetSize()), true);
|
isBreakTime.BindValueChanged(_ => UpdateFlashlightSize(GetSize()), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlayfieldDrawInfoTracker.OnDrawInfoInvalidate += () => Invalidate(Invalidation.DrawNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void UpdateFlashlightSize(float size);
|
protected abstract void UpdateFlashlightSize(float size);
|
||||||
@@ -156,15 +163,6 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
{
|
{
|
||||||
float size = defaultFlashlightSize * sizeMultiplier;
|
float size = defaultFlashlightSize * sizeMultiplier;
|
||||||
|
|
||||||
if (GetPlayfieldScale != null)
|
|
||||||
{
|
|
||||||
Vector2 playfieldScale = GetPlayfieldScale();
|
|
||||||
|
|
||||||
Debug.Assert(Precision.AlmostEquals(Math.Abs(playfieldScale.X), Math.Abs(playfieldScale.Y)),
|
|
||||||
@"Playfield has non-proportional scaling. Flashlight implementations should be revisited with regard to balance.");
|
|
||||||
size *= Math.Abs(playfieldScale.X);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBreakTime.Value)
|
if (isBreakTime.Value)
|
||||||
size *= 2.5f;
|
size *= 2.5f;
|
||||||
else if (comboBasedSize)
|
else if (comboBasedSize)
|
||||||
@@ -265,7 +263,11 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
shader = Source.shader;
|
shader = Source.shader;
|
||||||
screenSpaceDrawQuad = Source.ScreenSpaceDrawQuad;
|
screenSpaceDrawQuad = Source.ScreenSpaceDrawQuad;
|
||||||
flashlightPosition = Vector2Extensions.Transform(Source.FlashlightPosition, DrawInfo.Matrix);
|
flashlightPosition = Vector2Extensions.Transform(Source.FlashlightPosition, DrawInfo.Matrix);
|
||||||
flashlightSize = Source.FlashlightSize * DrawInfo.Matrix.ExtractScale().Xy;
|
|
||||||
|
// scale the flashlight based on the playfield to match gameplay components scale.
|
||||||
|
Vector2 drawInfoScale = Source.playfieldDrawInfo.Matrix.ExtractScale().Xy;
|
||||||
|
flashlightSize = Source.FlashlightSize * drawInfoScale;
|
||||||
|
|
||||||
flashlightDim = Source.FlashlightDim;
|
flashlightDim = Source.FlashlightDim;
|
||||||
flashlightSmoothness = Source.flashlightSmoothness;
|
flashlightSmoothness = Source.flashlightSmoothness;
|
||||||
}
|
}
|
||||||
@@ -321,5 +323,33 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The purpose of this component is to track any changes to <c>Playfield.Parent.DrawInfo</c>
|
||||||
|
/// (by being added to the content of <see cref="PlayfieldAdjustmentContainer"/>).
|
||||||
|
/// All in order for the flashlight to invalidate its draw node and read any changes in the playfield's scaling.
|
||||||
|
/// </summary>
|
||||||
|
internal partial class PlayfieldDrawInfoTracker : Component
|
||||||
|
{
|
||||||
|
private readonly LayoutValue drawInfoLayout = new LayoutValue(Invalidation.DrawInfo);
|
||||||
|
|
||||||
|
public Action? OnDrawInfoInvalidate;
|
||||||
|
|
||||||
|
public PlayfieldDrawInfoTracker()
|
||||||
|
{
|
||||||
|
AddLayout(drawInfoLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (!drawInfoLayout.IsValid)
|
||||||
|
{
|
||||||
|
OnDrawInfoInvalidate?.Invoke();
|
||||||
|
drawInfoLayout.Validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ using osu.Game.Screens.Edit;
|
|||||||
using osu.Game.Screens.OnlinePlay.DailyChallenge;
|
using osu.Game.Screens.OnlinePlay.DailyChallenge;
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||||
|
using osu.Game.Screens.Select;
|
||||||
using osu.Game.Screens.SelectV2;
|
using osu.Game.Screens.SelectV2;
|
||||||
using osu.Game.Seasonal;
|
using osu.Game.Seasonal;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@@ -118,12 +119,15 @@ namespace osu.Game.Screens.Menu
|
|||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
private IDisposable logoProxy;
|
private IDisposable logoProxy;
|
||||||
|
|
||||||
|
private Bindable<bool> forceSSV1;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics, AudioManager audio)
|
private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics, AudioManager audio)
|
||||||
{
|
{
|
||||||
holdDelay = config.GetBindable<double>(OsuSetting.UIHoldActivationDelay);
|
holdDelay = config.GetBindable<double>(OsuSetting.UIHoldActivationDelay);
|
||||||
loginDisplayed = statics.GetBindable<bool>(Static.LoginOverlayDisplayed);
|
loginDisplayed = statics.GetBindable<bool>(Static.LoginOverlayDisplayed);
|
||||||
showMobileDisclaimer = config.GetBindable<bool>(OsuSetting.ShowMobileDisclaimer);
|
showMobileDisclaimer = config.GetBindable<bool>(OsuSetting.ShowMobileDisclaimer);
|
||||||
|
forceSSV1 = config.GetBindable<bool>(OsuSetting.ForceLegacySongSelect);
|
||||||
|
|
||||||
if (host.CanExit)
|
if (host.CanExit)
|
||||||
{
|
{
|
||||||
@@ -479,7 +483,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadSongSelect() => this.Push(new SoloSongSelect());
|
private void loadSongSelect() => this.Push(forceSSV1.Value ? new PlaySongSelect() : new SoloSongSelect());
|
||||||
|
|
||||||
private partial class MobileDisclaimerDialog : PopupDialog
|
private partial class MobileDisclaimerDialog : PopupDialog
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
@@ -20,6 +21,7 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Framework.Input.StateChanges;
|
using osu.Framework.Input.StateChanges;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.Backgrounds;
|
using osu.Game.Graphics.Backgrounds;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
@@ -63,7 +65,10 @@ namespace osu.Game.Screens.Menu
|
|||||||
protected Sample SampleDownbeat;
|
protected Sample SampleDownbeat;
|
||||||
|
|
||||||
private readonly Container colourAndTriangles;
|
private readonly Container colourAndTriangles;
|
||||||
private readonly TrianglesV2 triangles;
|
private Box colourBox;
|
||||||
|
private TrianglesV2 triangles;
|
||||||
|
|
||||||
|
private Bindable<Colour4> logoColour;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return value decides whether the logo should play its own sample for the click action.
|
/// Return value decides whether the logo should play its own sample for the click action.
|
||||||
@@ -186,10 +191,13 @@ namespace osu.Game.Screens.Menu
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
colourBox = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex(@"ff66ab"), Color4Extensions.FromHex(@"cc5289")),
|
Colour = ColourInfo.GradientVertical(
|
||||||
|
Color4Extensions.FromHex(@"ff66ba"), // original osu! cookie pink
|
||||||
|
Color4Extensions.Darken(Color4Extensions.FromHex(@"ff66ba"), 0.5f)
|
||||||
|
),
|
||||||
},
|
},
|
||||||
triangles = new TrianglesV2
|
triangles = new TrianglesV2
|
||||||
{
|
{
|
||||||
@@ -198,7 +206,10 @@ namespace osu.Game.Screens.Menu
|
|||||||
Thickness = 0.009f,
|
Thickness = 0.009f,
|
||||||
ScaleAdjust = 3,
|
ScaleAdjust = 3,
|
||||||
SpawnRatio = 1.4f,
|
SpawnRatio = 1.4f,
|
||||||
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex(@"ff66ab"), Color4Extensions.FromHex(@"b6346f")),
|
Colour = ColourInfo.GradientVertical(
|
||||||
|
Color4Extensions.FromHex(@"ff66ba"),
|
||||||
|
Color4Extensions.Darken(Color4Extensions.FromHex(@"ff66ba"), 1.0f)
|
||||||
|
),
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -249,6 +260,21 @@ namespace osu.Game.Screens.Menu
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateColour() {
|
||||||
|
if (triangles == null || colourBox == null)
|
||||||
|
return; // we're still loading
|
||||||
|
|
||||||
|
colourBox.Colour = ColourInfo.GradientVertical(
|
||||||
|
logoColour.Value,
|
||||||
|
Color4Extensions.Darken(logoColour.Value, 0.5f)
|
||||||
|
);
|
||||||
|
|
||||||
|
triangles.Colour = ColourInfo.GradientVertical(
|
||||||
|
logoColour.Value,
|
||||||
|
Color4Extensions.Darken(logoColour.Value, 1.0f)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public Container LogoElements { get; private set; }
|
public Container LogoElements { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -276,7 +302,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(TextureStore textures, AudioManager audio)
|
private void load(TextureStore textures, AudioManager audio, OsuConfigManager config)
|
||||||
{
|
{
|
||||||
sampleClick = audio.Samples.Get(@"Menu/osu-logo-select");
|
sampleClick = audio.Samples.Get(@"Menu/osu-logo-select");
|
||||||
|
|
||||||
@@ -285,6 +311,10 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
logo.Texture = textures.Get(@"Menu/logo");
|
logo.Texture = textures.Get(@"Menu/logo");
|
||||||
ripple.Texture = textures.Get(@"Menu/logo");
|
ripple.Texture = textures.Get(@"Menu/logo");
|
||||||
|
|
||||||
|
logoColour = config.GetBindable<Colour4>(OsuSetting.MenuCookieColor);
|
||||||
|
logoColour.BindValueChanged(_ => UpdateColour());
|
||||||
|
UpdateColour();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int lastBeatIndex;
|
private int lastBeatIndex;
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -31,17 +29,19 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private readonly Func<IBeatmap, IReadOnlyList<Mod>, Score> createScore;
|
private readonly Func<IBeatmap, IReadOnlyList<Mod>, Score> createScore;
|
||||||
|
|
||||||
private PlaybackSettings playbackSettings;
|
|
||||||
|
|
||||||
[Cached(typeof(IGameplayLeaderboardProvider))]
|
[Cached(typeof(IGameplayLeaderboardProvider))]
|
||||||
private readonly SoloGameplayLeaderboardProvider leaderboardProvider = new SoloGameplayLeaderboardProvider();
|
private readonly SoloGameplayLeaderboardProvider leaderboardProvider = new SoloGameplayLeaderboardProvider();
|
||||||
|
|
||||||
protected override UserActivity InitialActivity => new UserActivity.WatchingReplay(Score.ScoreInfo);
|
protected override UserActivity? InitialActivity =>
|
||||||
|
// score may be null if LoadedBeatmapSuccessfully is false.
|
||||||
|
Score == null ? null : new UserActivity.WatchingReplay(Score.ScoreInfo);
|
||||||
|
|
||||||
private bool isAutoplayPlayback => GameplayState.Mods.OfType<ModAutoplay>().Any();
|
private bool isAutoplayPlayback => GameplayState.Mods.OfType<ModAutoplay>().Any();
|
||||||
|
|
||||||
private double? lastFrameTime;
|
private double? lastFrameTime;
|
||||||
private ReplayFailIndicator failIndicator;
|
|
||||||
|
private ReplayFailIndicator? failIndicator;
|
||||||
|
private PlaybackSettings? playbackSettings;
|
||||||
|
|
||||||
protected override bool CheckModsAllowFailure()
|
protected override bool CheckModsAllowFailure()
|
||||||
{
|
{
|
||||||
@@ -60,12 +60,12 @@ namespace osu.Game.Screens.Play
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReplayPlayer(Score score, PlayerConfiguration configuration = null)
|
public ReplayPlayer(Score score, PlayerConfiguration? configuration = null)
|
||||||
: this((_, _) => score, configuration)
|
: this((_, _) => score, configuration)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReplayPlayer(Func<IBeatmap, IReadOnlyList<Mod>, Score> createScore, PlayerConfiguration configuration = null)
|
public ReplayPlayer(Func<IBeatmap, IReadOnlyList<Mod>, Score> createScore, PlayerConfiguration? configuration = null)
|
||||||
: base(configuration)
|
: base(configuration)
|
||||||
{
|
{
|
||||||
this.createScore = createScore;
|
this.createScore = createScore;
|
||||||
@@ -133,6 +133,9 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
{
|
{
|
||||||
|
if (!LoadedBeatmapSuccessfully)
|
||||||
|
return false;
|
||||||
|
|
||||||
switch (e.Action)
|
switch (e.Action)
|
||||||
{
|
{
|
||||||
case GlobalAction.StepReplayBackward:
|
case GlobalAction.StepReplayBackward:
|
||||||
@@ -144,11 +147,11 @@ namespace osu.Game.Screens.Play
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.SeekReplayBackward:
|
case GlobalAction.SeekReplayBackward:
|
||||||
SeekInDirection(-5 * (float)playbackSettings.UserPlaybackRate.Value);
|
SeekInDirection(-5 * (float)playbackSettings!.UserPlaybackRate.Value);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.SeekReplayForward:
|
case GlobalAction.SeekReplayForward:
|
||||||
SeekInDirection(5 * (float)playbackSettings.UserPlaybackRate.Value);
|
SeekInDirection(5 * (float)playbackSettings!.UserPlaybackRate.Value);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case GlobalAction.TogglePauseReplay:
|
case GlobalAction.TogglePauseReplay:
|
||||||
@@ -192,7 +195,7 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
// base logic intentionally suppressed - we have our own custom fail interaction
|
// base logic intentionally suppressed - we have our own custom fail interaction
|
||||||
ScoreProcessor.FailScore(Score.ScoreInfo);
|
ScoreProcessor.FailScore(Score.ScoreInfo);
|
||||||
failIndicator.Display();
|
failIndicator!.Display();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnSuspending(ScreenTransitionEvent e)
|
public override void OnSuspending(ScreenTransitionEvent e)
|
||||||
@@ -204,18 +207,18 @@ namespace osu.Game.Screens.Play
|
|||||||
public override bool OnExiting(ScreenExitEvent e)
|
public override bool OnExiting(ScreenExitEvent e)
|
||||||
{
|
{
|
||||||
// safety against filters or samples from the indicator playing long after the screen is exited
|
// safety against filters or samples from the indicator playing long after the screen is exited
|
||||||
failIndicator.RemoveAndDisposeImmediately();
|
failIndicator?.RemoveAndDisposeImmediately();
|
||||||
return base.OnExiting(e);
|
return base.OnExiting(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopAllAudioEffects()
|
private void stopAllAudioEffects()
|
||||||
{
|
{
|
||||||
// safety against filters or samples from the indicator playing long after the screen is exited
|
// safety against filters or samples from the indicator playing long after the screen is exited
|
||||||
failIndicator.RemoveAndDisposeImmediately();
|
failIndicator?.RemoveAndDisposeImmediately();
|
||||||
|
|
||||||
if (GameplayClockContainer is MasterGameplayClockContainer master)
|
if (GameplayClockContainer is MasterGameplayClockContainer master)
|
||||||
{
|
{
|
||||||
playbackSettings.UserPlaybackRate.UnbindFrom(master.UserPlaybackRate);
|
playbackSettings?.UserPlaybackRate.UnbindFrom(master.UserPlaybackRate);
|
||||||
master.UserPlaybackRate.SetDefault();
|
master.UserPlaybackRate.SetDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,11 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
var scoresToShow = new List<GameplayLeaderboardScore>();
|
var scoresToShow = new List<GameplayLeaderboardScore>();
|
||||||
|
|
||||||
var scoresRequest = new IndexPlaylistScoresRequest(room.RoomID!.Value, playlistItem.ID);
|
var scoresRequest = new IndexPlaylistScoresRequest(room.RoomID!.Value, playlistItem.ID);
|
||||||
scoresRequest.Success += response =>
|
api.Perform(scoresRequest);
|
||||||
|
|
||||||
|
var response = scoresRequest.Response;
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
{
|
{
|
||||||
isPartial = response.Scores.Count < response.TotalScores;
|
isPartial = response.Scores.Count < response.TotalScores;
|
||||||
|
|
||||||
@@ -50,8 +54,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
|
|
||||||
if (response.UserScore != null && response.Scores.All(s => s.ID != response.UserScore.ID))
|
if (response.UserScore != null && response.Scores.All(s => s.ID != response.UserScore.ID))
|
||||||
scoresToShow.Add(new GameplayLeaderboardScore(response.UserScore, tracked: false, GameplayLeaderboardScore.ComboDisplayMode.Highest));
|
scoresToShow.Add(new GameplayLeaderboardScore(response.UserScore, tracked: false, GameplayLeaderboardScore.ComboDisplayMode.Highest));
|
||||||
};
|
}
|
||||||
api.Perform(scoresRequest);
|
|
||||||
|
|
||||||
if (gameplayState != null)
|
if (gameplayState != null)
|
||||||
{
|
{
|
||||||
@@ -62,8 +65,12 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
|
|
||||||
// touching the public bindable must happen on the update thread for general thread safety,
|
// touching the public bindable must happen on the update thread for general thread safety,
|
||||||
// since we may have external subscribers bound already
|
// since we may have external subscribers bound already
|
||||||
Schedule(() => scores.AddRange(scoresToShow));
|
Schedule(() =>
|
||||||
Scheduler.AddDelayed(sort, 1000, true);
|
{
|
||||||
|
scores.AddRange(scoresToShow);
|
||||||
|
sort();
|
||||||
|
Scheduler.AddDelayed(sort, 1000, true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// logic shared with SoloGameplayLeaderboardProvider
|
// logic shared with SoloGameplayLeaderboardProvider
|
||||||
|
|||||||
166
osu.Game/Screens/Select/PlaySongSelect.cs
Normal file
166
osu.Game/Screens/Select/PlaySongSelect.cs
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Localisation;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Notifications;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Screens.Ranking;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Select
|
||||||
|
{
|
||||||
|
public partial class PlaySongSelect : SongSelect
|
||||||
|
{
|
||||||
|
private OsuScreen? playerLoader;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private INotificationOverlay? notifications { get; set; }
|
||||||
|
|
||||||
|
public override bool AllowExternalScreenChange => true;
|
||||||
|
|
||||||
|
public override MenuItem[] CreateForwardNavigationMenuItemsForBeatmap(Func<BeatmapInfo> getBeatmap) => new MenuItem[]
|
||||||
|
{
|
||||||
|
new OsuMenuItem(ButtonSystemStrings.Play.ToSentence(), MenuItemType.Highlighted, () => FinaliseSelection(getBeatmap())),
|
||||||
|
new OsuMenuItem(ButtonSystemStrings.Edit.ToSentence(), MenuItemType.Standard, () => Edit(getBeatmap()))
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override UserActivity InitialActivity => new UserActivity.ChoosingBeatmap();
|
||||||
|
|
||||||
|
private PlayBeatmapDetailArea playBeatmapDetailArea = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
BeatmapOptions.AddButton(ButtonSystemStrings.Edit.ToSentence(), @"beatmap", FontAwesome.Solid.PencilAlt, colours.Yellow, () => Edit());
|
||||||
|
|
||||||
|
AddInternal(new SongSelectTouchInputDetector());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void PresentScore(ScoreInfo score) =>
|
||||||
|
FinaliseSelection(score.BeatmapInfo, score.Ruleset, () => this.Push(new SoloResultsScreen(score)));
|
||||||
|
|
||||||
|
protected override BeatmapDetailArea CreateBeatmapDetailArea()
|
||||||
|
{
|
||||||
|
playBeatmapDetailArea = new PlayBeatmapDetailArea
|
||||||
|
{
|
||||||
|
Leaderboard =
|
||||||
|
{
|
||||||
|
ScoreSelected = PresentScore
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return playBeatmapDetailArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
|
{
|
||||||
|
switch (e.Key)
|
||||||
|
{
|
||||||
|
case Key.Enter:
|
||||||
|
case Key.KeypadEnter:
|
||||||
|
// this is a special hard-coded case; we can't rely on OnPressed (of SongSelect) as GlobalActionContainer is
|
||||||
|
// matching with exact modifier consideration (so Ctrl+Enter would be ignored).
|
||||||
|
FinaliseSelection();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnKeyDown(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IReadOnlyList<Mod>? modsAtGameplayStart;
|
||||||
|
|
||||||
|
private ModAutoplay? getAutoplayMod() => Ruleset.Value.CreateInstance().GetAutoplayMod();
|
||||||
|
|
||||||
|
protected override bool OnStart()
|
||||||
|
{
|
||||||
|
if (playerLoader != null) return false;
|
||||||
|
|
||||||
|
modsAtGameplayStart = Mods.Value.Select(m => m.DeepClone()).ToArray();
|
||||||
|
|
||||||
|
// Ctrl+Enter should start map with autoplay enabled.
|
||||||
|
if (GetContainingInputManager()?.CurrentState?.Keyboard.ControlPressed == true)
|
||||||
|
{
|
||||||
|
var autoInstance = getAutoplayMod();
|
||||||
|
|
||||||
|
if (autoInstance == null)
|
||||||
|
{
|
||||||
|
notifications?.Post(new SimpleNotification
|
||||||
|
{
|
||||||
|
Text = NotificationsStrings.NoAutoplayMod
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mods = Mods.Value.Append(autoInstance).ToArray();
|
||||||
|
|
||||||
|
if (!ModUtils.CheckCompatibleSet(mods, out var invalid))
|
||||||
|
mods = mods.Except(invalid).Append(autoInstance).ToArray();
|
||||||
|
|
||||||
|
Mods.Value = mods;
|
||||||
|
}
|
||||||
|
|
||||||
|
SampleConfirm?.Play();
|
||||||
|
|
||||||
|
this.Push(playerLoader = new PlayerLoader(createPlayer));
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Player createPlayer()
|
||||||
|
{
|
||||||
|
Player player;
|
||||||
|
|
||||||
|
var replayGeneratingMod = Mods.Value.OfType<ICreateReplayData>().FirstOrDefault();
|
||||||
|
|
||||||
|
if (replayGeneratingMod != null)
|
||||||
|
{
|
||||||
|
player = new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
player = new SoloPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnResuming(ScreenTransitionEvent e)
|
||||||
|
{
|
||||||
|
base.OnResuming(e);
|
||||||
|
revertMods();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnExiting(ScreenExitEvent e)
|
||||||
|
{
|
||||||
|
if (base.OnExiting(e))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
revertMods();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void revertMods()
|
||||||
|
{
|
||||||
|
if (playerLoader == null) return;
|
||||||
|
|
||||||
|
Mods.Value = modsAtGameplayStart;
|
||||||
|
playerLoader = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,21 @@ namespace osu.Game.Screens.SelectV2
|
|||||||
get => working;
|
get => working;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value == working)
|
if (working == null && value == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// this guard papers over excessive refreshes of the background asset which occur if `working == value` type guards are used.
|
||||||
|
// the root cause of why `working == value` type guards fail here is that `SongSelect` will invalidate working beatmaps very often
|
||||||
|
// (via https://github.com/ppy/osu/blob/d3ae20dd882381e109c20ca00ee5237e4dd1750d/osu.Game/Screens/SelectV2/SongSelect.cs#L506-L507),
|
||||||
|
// due to a variety of causes, ranging from "someone typed a letter in the search box" (which triggers a refilter -> presentation of new items -> `ensureGlobalBeatmapValid()`),
|
||||||
|
// to "someone just went into the editor and replaced every single file in the set, including the background".
|
||||||
|
// the following guard approximates the most appropriate debounce criterion, which is the contents of the actual asset that is supposed to be displayed in the background,
|
||||||
|
// i.e. if the hash of the new background file matches the old, then we do not bother updating the working beatmap here.
|
||||||
|
//
|
||||||
|
// note that this is basically a reimplementation of the caching scheme in `WorkingBeatmapCache.getBackgroundFromStore()`,
|
||||||
|
// which cannot be used directly by retrieving the texture and checking texture reference equality,
|
||||||
|
// because missing the cache would incur a synchronous texture load on the update thread.
|
||||||
|
if (getBackgroundFileHash(working) == getBackgroundFileHash(value))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
working = value;
|
working = value;
|
||||||
@@ -52,6 +66,9 @@ namespace osu.Game.Screens.SelectV2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string? getBackgroundFileHash(WorkingBeatmap? working)
|
||||||
|
=> working?.BeatmapSetInfo.GetFile(working.Metadata.BackgroundFile)?.File.Hash;
|
||||||
|
|
||||||
public PanelSetBackground()
|
public PanelSetBackground()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|||||||
Reference in New Issue
Block a user