Compare commits
27 Commits
2025.825.0
...
2025.829.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 081355864e | |||
| 6435a835d1 | |||
| 628181a883 | |||
| 835329efd3 | |||
| 5399943118 | |||
| d07f82f6f4 | |||
|
|
2bea59e65f | ||
|
|
c0fd5637de | ||
|
|
5e7a99c97f | ||
|
|
8f628d16ae | ||
|
|
2ccb65aa65 | ||
|
|
4d851f2527 | ||
|
|
4bafbfb9e4 | ||
|
|
3f179e3903 | ||
|
|
196b28115e | ||
|
|
7660a9ba8e | ||
|
|
e908b80359 | ||
|
|
a2bf8e3988 | ||
| 5b186bb740 | |||
|
|
6e8246b539 | ||
|
|
3cca458c21 | ||
|
|
bc59270f3e | ||
|
|
c0c3690908 | ||
|
|
73624e4e25 | ||
|
|
f374af7ce7 | ||
|
|
7530ad1a7b | ||
|
|
a049f5065d |
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"dotnet.defaultSolution": "osu.Desktop.slnf"
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -198,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);
|
||||||
|
|
||||||
@@ -442,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,18 +53,18 @@ 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>
|
||||||
/// Public method to trigger a refresh of categories from the UI.
|
/// Public method to trigger a refresh of categories from the UI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RefreshCategories()
|
public void RefreshCategories(bool ignoreSuccess = false)
|
||||||
{
|
{
|
||||||
fetchCategories();
|
fetchCategories(ignoreSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchCategories()
|
private void fetchCategories(bool ignoreSuccess = false)
|
||||||
{
|
{
|
||||||
if (!shouldShowCustomBackgrounds) return;
|
if (!shouldShowCustomBackgrounds) return;
|
||||||
|
|
||||||
@@ -74,20 +74,28 @@ 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 +106,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);
|
||||||
|
|
||||||
|
|||||||
@@ -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,16 +5,13 @@
|
|||||||
|
|
||||||
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;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays.Settings;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
||||||
{
|
{
|
||||||
@@ -29,21 +26,19 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
|||||||
|
|
||||||
private SettingsEnumDropdown<BackgroundSource> backgroundSourceDropdown;
|
private SettingsEnumDropdown<BackgroundSource> backgroundSourceDropdown;
|
||||||
|
|
||||||
|
private Bindable<bool> useSeasonalBackgrounds;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config, IAPIProvider api)
|
private void load(OsuConfigManager config, IAPIProvider api)
|
||||||
{
|
{
|
||||||
user = api.LocalUser.GetBoundCopy();
|
user = api.LocalUser.GetBoundCopy();
|
||||||
|
useSeasonalBackgrounds = config.GetBindable<bool>(OsuSetting.UseSeasonalBackgroundsV2);
|
||||||
var backgroundModeBindable = config.GetBindable<SeasonalBackgroundMode>(OsuSetting.SeasonalBackgroundMode);
|
|
||||||
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>
|
||||||
@@ -58,28 +53,21 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
|||||||
Action = () => backgroundLoader.RefreshCategories()
|
Action = () => backgroundLoader.RefreshCategories()
|
||||||
};
|
};
|
||||||
|
|
||||||
backgroundLoader.AvailableCategories.BindValueChanged(categories => categoryDropdown.Items = categories.NewValue, true);
|
// TODO: the category dropdown disappear if no backgrounds (e.g. when first enabling the setting)
|
||||||
|
refreshButton.CanBeShown.BindTo(useSeasonalBackgrounds);
|
||||||
|
categoryDropdown.CanBeShown.BindTo(useSeasonalBackgrounds);
|
||||||
|
useSeasonalBackgrounds.BindValueChanged(
|
||||||
|
_ => backgroundLoader.RefreshCategories(true)
|
||||||
|
);
|
||||||
|
|
||||||
backgroundModeBindable.BindValueChanged(mode =>
|
backgroundLoader.AvailableCategories.BindValueChanged(categories => categoryDropdown.Items = categories.NewValue, true);
|
||||||
{
|
|
||||||
if (mode.NewValue == SeasonalBackgroundMode.Always)
|
|
||||||
{
|
|
||||||
categoryDropdown.Show();
|
|
||||||
refreshButton.Show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
categoryDropdown.Hide();
|
|
||||||
refreshButton.Hide();
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = UserInterfaceStrings.ShowMenuTips,
|
LabelText = UserInterfaceStrings.ShowMenuTips,
|
||||||
Current = config.GetBindable<bool>(OsuSetting.MenuTips)
|
Current = config.GetBindable<bool>(OsuSetting.MenuTips),
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ using osu.Game.Localisation;
|
|||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.Multiplayer;
|
using osu.Game.Online.Multiplayer;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Dialog;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Overlays.OSD;
|
using osu.Game.Overlays.OSD;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@@ -1312,6 +1313,8 @@ namespace osu.Game.Screens.Edit
|
|||||||
yield return upload;
|
yield return upload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
yield return new EditorMenuItem("Remove all online IDs", MenuItemType.Destructive, anonymizeBeatmap);
|
||||||
|
|
||||||
if (editorBeatmap.BeatmapInfo.OnlineID > 0)
|
if (editorBeatmap.BeatmapInfo.OnlineID > 0)
|
||||||
{
|
{
|
||||||
yield return new OsuMenuItemSpacer();
|
yield return new OsuMenuItemSpacer();
|
||||||
@@ -1396,6 +1399,14 @@ namespace osu.Game.Screens.Edit
|
|||||||
void startSubmission() => this.Push(new BeatmapSubmissionScreen());
|
void startSubmission() => this.Push(new BeatmapSubmissionScreen());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void anonymizeBeatmap()
|
||||||
|
{
|
||||||
|
dialogOverlay.Push(new ConfirmDialog(
|
||||||
|
"Really remove online IDs?",
|
||||||
|
() => playableBeatmap.BeatmapInfo.ResetOnlineInfo(true)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
private void exportBeatmap(bool legacy)
|
private void exportBeatmap(bool legacy)
|
||||||
{
|
{
|
||||||
if (HasUnsavedChanges)
|
if (HasUnsavedChanges)
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = ColourInfo.GradientVertical(
|
Colour = ColourInfo.GradientVertical(
|
||||||
Color4Extensions.FromHex(@"ff66ba"), // original osu! cookie pink
|
Color4Extensions.FromHex(@"ff66ba"), // original osu! cookie pink
|
||||||
Color4Extensions.Darken(Color4Extensions.FromHex(@"ff66ba"), 1.0f)
|
Color4Extensions.Darken(Color4Extensions.FromHex(@"ff66ba"), 0.5f)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
triangles = new TrianglesV2
|
triangles = new TrianglesV2
|
||||||
@@ -208,7 +208,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
SpawnRatio = 1.4f,
|
SpawnRatio = 1.4f,
|
||||||
Colour = ColourInfo.GradientVertical(
|
Colour = ColourInfo.GradientVertical(
|
||||||
Color4Extensions.FromHex(@"ff66ba"),
|
Color4Extensions.FromHex(@"ff66ba"),
|
||||||
Color4Extensions.Darken(Color4Extensions.FromHex(@"ff66ba"), 2.5f)
|
Color4Extensions.Darken(Color4Extensions.FromHex(@"ff66ba"), 1.0f)
|
||||||
),
|
),
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
@@ -260,19 +260,20 @@ namespace osu.Game.Screens.Menu
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateColour() {
|
public void UpdateColour()
|
||||||
|
{
|
||||||
if (triangles == null || colourBox == null)
|
if (triangles == null || colourBox == null)
|
||||||
return; // we're still loading
|
return; // we're still loading
|
||||||
|
|
||||||
|
colourBox.Colour = ColourInfo.GradientVertical(
|
||||||
|
logoColour.Value,
|
||||||
|
Color4Extensions.Darken(logoColour.Value, 0.5f)
|
||||||
|
);
|
||||||
|
|
||||||
triangles.Colour = ColourInfo.GradientVertical(
|
triangles.Colour = ColourInfo.GradientVertical(
|
||||||
logoColour.Value,
|
logoColour.Value,
|
||||||
Color4Extensions.Darken(logoColour.Value, 1.0f)
|
Color4Extensions.Darken(logoColour.Value, 1.0f)
|
||||||
);
|
);
|
||||||
|
|
||||||
colourBox.Colour = ColourInfo.GradientVertical(
|
|
||||||
logoColour.Value,
|
|
||||||
Color4Extensions.Darken(logoColour.Value, 2.5f)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Container LogoElements { get; private set; }
|
public Container LogoElements { get; private set; }
|
||||||
@@ -313,6 +314,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
ripple.Texture = textures.Get(@"Menu/logo");
|
ripple.Texture = textures.Get(@"Menu/logo");
|
||||||
|
|
||||||
logoColour = config.GetBindable<Colour4>(OsuSetting.MenuCookieColor);
|
logoColour = config.GetBindable<Colour4>(OsuSetting.MenuCookieColor);
|
||||||
|
logoColour.BindValueChanged(_ => UpdateColour());
|
||||||
|
UpdateColour();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int lastBeatIndex;
|
private int lastBeatIndex;
|
||||||
@@ -393,7 +396,6 @@ namespace osu.Game.Screens.Menu
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
UpdateColour();
|
|
||||||
|
|
||||||
const float scale_adjust_cutoff = 0.4f;
|
const float scale_adjust_cutoff = 0.4f;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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