mirror of
https://github.com/ppy/osu.git
synced 2024-11-13 16:13:34 +08:00
Merge branch 'master' into move-local-input-control
This commit is contained in:
commit
d5a48a4b9f
@ -9,6 +9,9 @@ indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[g_*.cs]
|
||||
generated_code = true
|
||||
|
||||
[*.cs]
|
||||
end_of_line = crlf
|
||||
insert_final_newline = true
|
||||
|
@ -11,7 +11,7 @@
|
||||
<AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.625.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.707.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" />
|
||||
|
@ -187,7 +187,7 @@ namespace osu.Desktop
|
||||
return edit.BeatmapInfo.ToString() ?? string.Empty;
|
||||
|
||||
case UserActivity.WatchingReplay watching:
|
||||
return watching.BeatmapInfo.ToString();
|
||||
return watching.BeatmapInfo?.ToString() ?? string.Empty;
|
||||
|
||||
case UserActivity.InLobby lobby:
|
||||
return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;
|
||||
|
@ -147,14 +147,12 @@ namespace osu.Desktop
|
||||
{
|
||||
base.SetHost(host);
|
||||
|
||||
var desktopWindow = (SDL2DesktopWindow)host.Window;
|
||||
|
||||
var iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico");
|
||||
if (iconStream != null)
|
||||
desktopWindow.SetIconFromStream(iconStream);
|
||||
host.Window.SetIconFromStream(iconStream);
|
||||
|
||||
desktopWindow.CursorState |= CursorState.Hidden;
|
||||
desktopWindow.Title = Name;
|
||||
host.Window.CursorState |= CursorState.Hidden;
|
||||
host.Window.Title = Name;
|
||||
}
|
||||
|
||||
protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo();
|
||||
|
@ -4,6 +4,7 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@ -59,7 +60,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
|
||||
lastDisplayedCombo = combo;
|
||||
|
||||
if (Time.Elapsed < 0)
|
||||
if ((Clock as IGameplayClock)?.IsRewinding == true)
|
||||
{
|
||||
// needs more work to make rewind somehow look good.
|
||||
// basically we want the previous increment to play... or turning off RemoveCompletedTransforms (not feasible from a performance angle).
|
||||
|
@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
@ -96,7 +97,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
comboDisplay.X = Catcher.X;
|
||||
|
||||
if (Time.Elapsed <= 0)
|
||||
if ((Clock as IGameplayClock)?.IsRewinding == true)
|
||||
{
|
||||
// This is probably a wrong value, but currently the true value is not recorded.
|
||||
// Setting `true` will prevent generation of false-positive after-images (with more false-negatives).
|
||||
|
@ -16,6 +16,7 @@ using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
@ -298,7 +299,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
return false;
|
||||
|
||||
// do not run any of this logic when rewinding, as it inverts order of presses/releases.
|
||||
if (Time.Elapsed < 0)
|
||||
if ((Clock as IGameplayClock)?.IsRewinding == true)
|
||||
return false;
|
||||
|
||||
if (CheckHittable?.Invoke(this, Time.Current) == false)
|
||||
@ -337,7 +338,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
return;
|
||||
|
||||
// do not run any of this logic when rewinding, as it inverts order of presses/releases.
|
||||
if (Time.Elapsed < 0)
|
||||
if ((Clock as IGameplayClock)?.IsRewinding == true)
|
||||
return;
|
||||
|
||||
Tail.UpdateResult();
|
||||
|
@ -14,6 +14,7 @@ using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
@ -179,16 +180,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
private Vector2? lastPosition;
|
||||
|
||||
private bool rewinding;
|
||||
|
||||
public void UpdateProgress(double completionProgress)
|
||||
{
|
||||
Position = drawableSlider.HitObject.CurvePositionAt(completionProgress);
|
||||
|
||||
var diff = lastPosition.HasValue ? lastPosition.Value - Position : Position - drawableSlider.HitObject.CurvePositionAt(completionProgress + 0.01f);
|
||||
|
||||
if (Clock.ElapsedFrameTime != 0)
|
||||
rewinding = Clock.ElapsedFrameTime < 0;
|
||||
bool rewinding = (Clock as IGameplayClock)?.IsRewinding == true;
|
||||
|
||||
// Ensure the value is substantially high enough to allow for Atan2 to get a valid angle.
|
||||
if (diff.LengthFast < 0.01f)
|
||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
effectiveMissCount = Math.Max(1.0, 1000.0 / totalSuccessfulHits) * countMiss;
|
||||
|
||||
// TODO: The detection of rulesets is temporary until the leftover old skills have been reworked.
|
||||
bool isConvert = score.BeatmapInfo.Ruleset.OnlineID != 1;
|
||||
bool isConvert = score.BeatmapInfo!.Ruleset.OnlineID != 1;
|
||||
|
||||
double multiplier = 1.13;
|
||||
|
||||
|
@ -417,6 +417,60 @@ namespace osu.Game.Tests.Database
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestImport_Modify_Revert()
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
var importer = new BeatmapImporter(storage, realm);
|
||||
using var store = new RealmRulesetStore(realm, storage);
|
||||
|
||||
var imported = await LoadOszIntoStore(importer, realm.Realm);
|
||||
|
||||
await createScoreForBeatmap(realm.Realm, imported.Beatmaps.First());
|
||||
|
||||
var score = realm.Run(r => r.All<ScoreInfo>().Single());
|
||||
|
||||
string originalHash = imported.Beatmaps.First().Hash;
|
||||
const string modified_hash = "new_hash";
|
||||
|
||||
Assert.That(imported.Beatmaps.First().Scores.Single(), Is.EqualTo(score));
|
||||
|
||||
Assert.That(score.BeatmapHash, Is.EqualTo(originalHash));
|
||||
Assert.That(score.BeatmapInfo, Is.EqualTo(imported.Beatmaps.First()));
|
||||
|
||||
// imitate making local changes via editor
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
realm.Write(r =>
|
||||
{
|
||||
BeatmapInfo beatmap = imported.Beatmaps.First();
|
||||
beatmap.Hash = modified_hash;
|
||||
beatmap.ResetOnlineInfo();
|
||||
beatmap.UpdateLocalScores(r);
|
||||
});
|
||||
|
||||
Assert.That(!imported.Beatmaps.First().Scores.Any());
|
||||
|
||||
Assert.That(score.BeatmapInfo, Is.Null);
|
||||
Assert.That(score.BeatmapHash, Is.EqualTo(originalHash));
|
||||
|
||||
// imitate reverting the local changes made above
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
realm.Write(r =>
|
||||
{
|
||||
BeatmapInfo beatmap = imported.Beatmaps.First();
|
||||
beatmap.Hash = originalHash;
|
||||
beatmap.ResetOnlineInfo();
|
||||
beatmap.UpdateLocalScores(r);
|
||||
});
|
||||
|
||||
Assert.That(imported.Beatmaps.First().Scores.Single(), Is.EqualTo(score));
|
||||
|
||||
Assert.That(score.BeatmapHash, Is.EqualTo(originalHash));
|
||||
Assert.That(score.BeatmapInfo, Is.EqualTo(imported.Beatmaps.First()));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestImport_ThenModifyMapWithScore_ThenImport()
|
||||
{
|
||||
@ -431,19 +485,19 @@ namespace osu.Game.Tests.Database
|
||||
|
||||
await createScoreForBeatmap(realm.Realm, imported.Beatmaps.First());
|
||||
|
||||
Assert.That(imported.Beatmaps.First().Scores.Any());
|
||||
|
||||
// imitate making local changes via editor
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
realm.Write(_ =>
|
||||
realm.Write(r =>
|
||||
{
|
||||
BeatmapInfo beatmap = imported.Beatmaps.First();
|
||||
beatmap.Hash = "new_hash";
|
||||
beatmap.ResetOnlineInfo();
|
||||
beatmap.UpdateLocalScores(r);
|
||||
});
|
||||
|
||||
// for now, making changes to a beatmap doesn't remove the backlink from the score to the beatmap.
|
||||
// the logic of ensuring that scores match the beatmap is upheld via comparing the hash in usages (see: https://github.com/ppy/osu/pull/22539).
|
||||
// TODO: revisit when fixing https://github.com/ppy/osu/issues/24069.
|
||||
Assert.That(imported.Beatmaps.First().Scores.Any());
|
||||
Assert.That(!imported.Beatmaps.First().Scores.Any());
|
||||
|
||||
var importedSecondTime = await importer.Import(new ImportTask(temp));
|
||||
|
||||
@ -461,6 +515,7 @@ namespace osu.Game.Tests.Database
|
||||
Assert.That(importedFirstTimeBeatmap.Hash != importedSecondTimeBeatmap.Hash);
|
||||
Assert.That(!importedFirstTimeBeatmap.Scores.Any());
|
||||
Assert.That(importedSecondTimeBeatmap.Scores.Count() == 1);
|
||||
Assert.That(importedSecondTimeBeatmap.Scores.Single().BeatmapInfo, Is.EqualTo(importedSecondTimeBeatmap));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -383,14 +383,14 @@ namespace osu.Game.Tests.Database
|
||||
|
||||
beatmapInfo.Hash = new_beatmap_hash;
|
||||
beatmapInfo.ResetOnlineInfo();
|
||||
beatmapInfo.UpdateLocalScores(s.Realm!);
|
||||
});
|
||||
|
||||
realm.Run(r => r.Refresh());
|
||||
|
||||
// for now, making changes to a beatmap doesn't remove the backlink from the score to the beatmap.
|
||||
// the logic of ensuring that scores match the beatmap is upheld via comparing the hash in usages (https://github.com/ppy/osu/pull/22539).
|
||||
// TODO: revisit when fixing https://github.com/ppy/osu/issues/24069.
|
||||
// making changes to a beatmap doesn't remove the score from realm, but should disassociate the beatmap.
|
||||
checkCount<ScoreInfo>(realm, 1);
|
||||
Assert.That(realm.Run(r => r.All<ScoreInfo>().First().BeatmapInfo), Is.Null);
|
||||
|
||||
// reimport the original beatmap before local modifications
|
||||
var importAfterUpdate = await importer.ImportAsUpdate(new ProgressNotification(), new ImportTask(pathOnlineCopy), importBeforeUpdate.Value);
|
||||
|
@ -87,10 +87,10 @@ namespace osu.Game.Tests.Models
|
||||
var mock = new Mock<IScoreInfo>();
|
||||
|
||||
mock.Setup(m => m.User).Returns(new APIUser { Username = "user" }); // TODO: temporary.
|
||||
mock.Setup(m => m.Beatmap.Metadata.Artist).Returns("artist");
|
||||
mock.Setup(m => m.Beatmap.Metadata.Title).Returns("title");
|
||||
mock.Setup(m => m.Beatmap.Metadata.Author.Username).Returns("author");
|
||||
mock.Setup(m => m.Beatmap.DifficultyName).Returns("difficulty");
|
||||
mock.Setup(m => m.Beatmap!.Metadata.Artist).Returns("artist");
|
||||
mock.Setup(m => m.Beatmap!.Metadata.Title).Returns("title");
|
||||
mock.Setup(m => m.Beatmap!.Metadata.Author.Username).Returns("author");
|
||||
mock.Setup(m => m.Beatmap!.DifficultyName).Returns("difficulty");
|
||||
|
||||
Assert.That(mock.Object.GetDisplayString(), Is.EqualTo("user playing artist - title (author) [difficulty]"));
|
||||
}
|
||||
|
@ -125,6 +125,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public IEnumerable<double> NonGameplayAdjustments => throw new NotImplementedException();
|
||||
public IBindable<bool> IsPaused => throw new NotImplementedException();
|
||||
public bool IsRewinding => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("Add trigger source", () => Player.GameplayClockContainer.Add(sampleTriggerSource = new TestGameplaySampleTriggerSource(Player.DrawableRuleset.Playfield.HitObjectContainer)));
|
||||
AddStep("Add trigger source", () => Player.DrawableRuleset.FrameStableComponents.Add(sampleTriggerSource = new TestGameplaySampleTriggerSource(Player.DrawableRuleset.Playfield.HitObjectContainer)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -153,6 +153,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
waitForAliveObjectIndex(2);
|
||||
checkValidObjectIndex(2);
|
||||
|
||||
// test rewinding
|
||||
seekBeforeIndex(1);
|
||||
waitForAliveObjectIndex(1);
|
||||
checkValidObjectIndex(1);
|
||||
|
||||
seekBeforeIndex(1, 400);
|
||||
checkValidObjectIndex(0);
|
||||
|
||||
seekBeforeIndex(3);
|
||||
waitForAliveObjectIndex(3);
|
||||
checkValidObjectIndex(3);
|
||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
AddStep("show excess mods score", () =>
|
||||
{
|
||||
var score = TestResources.CreateTestScoreInfo();
|
||||
score.Mods = score.BeatmapInfo.Ruleset.CreateInstance().CreateAllMods().ToArray();
|
||||
score.Mods = score.BeatmapInfo!.Ruleset.CreateInstance().CreateAllMods().ToArray();
|
||||
showPanel(CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo)), score);
|
||||
});
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
var author = new RealmUser { Username = "mapper_name" };
|
||||
|
||||
var score = TestResources.CreateTestScoreInfo(createTestBeatmap(author));
|
||||
score.Mods = score.BeatmapInfo.Ruleset.CreateInstance().CreateAllMods().ToArray();
|
||||
score.Mods = score.BeatmapInfo!.Ruleset.CreateInstance().CreateAllMods().ToArray();
|
||||
|
||||
showPanel(score);
|
||||
});
|
||||
|
@ -405,7 +405,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
public UnrankedSoloResultsScreen(ScoreInfo score)
|
||||
: base(score, true)
|
||||
{
|
||||
Score.BeatmapInfo.OnlineID = 0;
|
||||
Score.BeatmapInfo!.OnlineID = 0;
|
||||
Score.BeatmapInfo.Status = BeatmapOnlineStatus.Pending;
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,32 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
notificationOverlay.UnreadCount.ValueChanged += count => { displayedCount.Text = $"unread count: {count.NewValue}"; };
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestBasicFlow()
|
||||
{
|
||||
setState(Visibility.Visible);
|
||||
AddStep(@"simple #1", sendHelloNotification);
|
||||
AddStep(@"simple #2", sendAmazingNotification);
|
||||
AddStep(@"progress #1", sendUploadProgress);
|
||||
AddStep(@"progress #2", sendDownloadProgress);
|
||||
|
||||
checkProgressingCount(2);
|
||||
|
||||
setState(Visibility.Hidden);
|
||||
|
||||
AddRepeatStep(@"add many simple", sendManyNotifications, 3);
|
||||
|
||||
waitForCompletion();
|
||||
|
||||
AddStep(@"progress #3", sendUploadProgress);
|
||||
|
||||
checkProgressingCount(1);
|
||||
|
||||
checkDisplayedCount(33);
|
||||
|
||||
waitForCompletion();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestForwardWithFlingRight()
|
||||
{
|
||||
@ -411,32 +437,6 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddUntilStep("wait for update applied", () => applyUpdate);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasicFlow()
|
||||
{
|
||||
setState(Visibility.Visible);
|
||||
AddStep(@"simple #1", sendHelloNotification);
|
||||
AddStep(@"simple #2", sendAmazingNotification);
|
||||
AddStep(@"progress #1", sendUploadProgress);
|
||||
AddStep(@"progress #2", sendDownloadProgress);
|
||||
|
||||
checkProgressingCount(2);
|
||||
|
||||
setState(Visibility.Hidden);
|
||||
|
||||
AddRepeatStep(@"add many simple", sendManyNotifications, 3);
|
||||
|
||||
waitForCompletion();
|
||||
|
||||
AddStep(@"progress #3", sendUploadProgress);
|
||||
|
||||
checkProgressingCount(1);
|
||||
|
||||
checkDisplayedCount(33);
|
||||
|
||||
waitForCompletion();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestImportantWhileClosed()
|
||||
{
|
||||
|
@ -163,8 +163,12 @@ namespace osu.Game
|
||||
{
|
||||
foreach (var score in r.All<ScoreInfo>())
|
||||
{
|
||||
if (score.Statistics.Sum(kvp => kvp.Value) > 0 && score.MaximumStatistics.Sum(kvp => kvp.Value) == 0)
|
||||
if (score.BeatmapInfo != null
|
||||
&& score.Statistics.Sum(kvp => kvp.Value) > 0
|
||||
&& score.MaximumStatistics.Sum(kvp => kvp.Value) == 0)
|
||||
{
|
||||
scoreIds.Add(score.ID);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -204,7 +208,9 @@ namespace osu.Game
|
||||
{
|
||||
Logger.Log("Querying for scores that need total score conversion...");
|
||||
|
||||
HashSet<Guid> scoreIds = realmAccess.Run(r => new HashSet<Guid>(r.All<ScoreInfo>().Where(s => s.TotalScoreVersion == 30000002).AsEnumerable().Select(s => s.ID)));
|
||||
HashSet<Guid> scoreIds = realmAccess.Run(r => new HashSet<Guid>(r.All<ScoreInfo>()
|
||||
.Where(s => s.BeatmapInfo != null && s.TotalScoreVersion == 30000002)
|
||||
.AsEnumerable().Select(s => s.ID)));
|
||||
|
||||
Logger.Log($"Found {scoreIds.Count} scores which require total score conversion.");
|
||||
|
||||
|
@ -20,7 +20,6 @@ using osu.Game.IO;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Scoring;
|
||||
using Realms;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
@ -210,8 +209,7 @@ namespace osu.Game.Beatmaps
|
||||
// Let's reattach any matching scores that exist in the database, based on hash.
|
||||
foreach (BeatmapInfo beatmap in model.Beatmaps)
|
||||
{
|
||||
foreach (var score in realm.All<ScoreInfo>().Where(score => score.BeatmapHash == beatmap.Hash))
|
||||
score.BeatmapInfo = beatmap;
|
||||
beatmap.UpdateLocalScores(realm);
|
||||
}
|
||||
|
||||
ProcessBeatmap?.Invoke(model, parameters.Batch ? MetadataLookupScope.LocalCacheFirst : MetadataLookupScope.OnlineFirst);
|
||||
|
@ -234,6 +234,22 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Local scores are retained separate from a beatmap's lifetime, matched via <see cref="ScoreInfo.BeatmapHash"/>.
|
||||
/// Therefore we need to detach / reattach scores when a beatmap is edited or imported.
|
||||
/// </summary>
|
||||
/// <param name="realm">A realm instance in an active write transaction.</param>
|
||||
public void UpdateLocalScores(Realm realm)
|
||||
{
|
||||
// first disassociate any scores which are already attached and no longer valid.
|
||||
foreach (var score in Scores)
|
||||
score.BeatmapInfo = null;
|
||||
|
||||
// then attach any scores which match the new hash.
|
||||
foreach (var score in realm.All<ScoreInfo>().Where(s => s.BeatmapHash == Hash))
|
||||
score.BeatmapInfo = this;
|
||||
}
|
||||
|
||||
IBeatmapMetadataInfo IBeatmapInfo.Metadata => Metadata;
|
||||
IBeatmapSetInfo? IBeatmapInfo.BeatmapSet => BeatmapSet;
|
||||
IRulesetInfo IBeatmapInfo.Ruleset => Ruleset;
|
||||
|
@ -467,6 +467,9 @@ namespace osu.Game.Beatmaps
|
||||
if (transferCollections)
|
||||
beatmapInfo.TransferCollectionReferences(r, oldMd5Hash);
|
||||
|
||||
liveBeatmapSet.Beatmaps.Single(b => b.ID == beatmapInfo.ID)
|
||||
.UpdateLocalScores(r);
|
||||
|
||||
// do not look up metadata.
|
||||
// this is a locally-modified set now, so looking up metadata is busy work at best and harmful at worst.
|
||||
ProcessBeatmap?.Invoke(liveBeatmapSet, MetadataLookupScope.None);
|
||||
|
@ -64,6 +64,8 @@ namespace osu.Game.Beatmaps
|
||||
[Resolved]
|
||||
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
|
||||
|
||||
public bool IsRewinding { get; private set; }
|
||||
|
||||
public bool IsCoupled
|
||||
{
|
||||
get => decoupledClock.IsCoupled;
|
||||
@ -133,6 +135,9 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
else
|
||||
finalClockSource.ProcessFrame();
|
||||
|
||||
if (Clock.ElapsedFrameTime != 0)
|
||||
IsRewinding = Clock.ElapsedFrameTime < 0;
|
||||
}
|
||||
|
||||
public double TotalAppliedOffset
|
||||
|
@ -897,7 +897,7 @@ namespace osu.Game.Database
|
||||
var scores = migration.NewRealm.All<ScoreInfo>();
|
||||
|
||||
foreach (var score in scores)
|
||||
score.BeatmapHash = score.BeatmapInfo.Hash;
|
||||
score.BeatmapHash = score.BeatmapInfo?.Hash ?? string.Empty;
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -29,11 +29,6 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString ClearAll => new TranslatableString(getKey(@"clear_all"), @"Clear All");
|
||||
|
||||
/// <summary>
|
||||
/// "Cancel All"
|
||||
/// </summary>
|
||||
public static LocalisableString CancelAll => new TranslatableString(getKey(@"cancel_all"), @"Cancel All");
|
||||
|
||||
/// <summary>
|
||||
/// "Your battery level is low! Charge your device to prevent interruptions during gameplay."
|
||||
/// </summary>
|
||||
|
@ -171,6 +171,8 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
public abstract partial class HighlightMessageNotification : SimpleNotification
|
||||
{
|
||||
public override string PopInSampleName => "UI/notification-mention";
|
||||
|
||||
protected HighlightMessageNotification(Message message, Channel channel)
|
||||
{
|
||||
this.message = message;
|
||||
|
@ -185,7 +185,7 @@ namespace osu.Game.Online.Spectator
|
||||
IsPlaying = true;
|
||||
|
||||
// transfer state at point of beginning play
|
||||
currentState.BeatmapID = score.ScoreInfo.BeatmapInfo.OnlineID;
|
||||
currentState.BeatmapID = score.ScoreInfo.BeatmapInfo!.OnlineID;
|
||||
currentState.RulesetID = score.ScoreInfo.RulesetID;
|
||||
currentState.Mods = score.ScoreInfo.Mods.Select(m => new APIMod(m)).ToArray();
|
||||
currentState.State = SpectatedUserState.Playing;
|
||||
|
@ -289,9 +289,9 @@ namespace osu.Game
|
||||
{
|
||||
base.SetHost(host);
|
||||
|
||||
if (host.Window is SDL2Window sdlWindow)
|
||||
if (host.Window != null)
|
||||
{
|
||||
sdlWindow.DragDrop += path =>
|
||||
host.Window.DragDrop += path =>
|
||||
{
|
||||
// on macOS/iOS, URL associations are handled via SDL_DROPFILE events.
|
||||
if (path.StartsWith(OSU_PROTOCOL, StringComparison.Ordinal))
|
||||
|
@ -163,7 +163,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
},
|
||||
username,
|
||||
#pragma warning disable 618
|
||||
new StatisticText(score.MaxCombo, score.BeatmapInfo.MaxCombo, @"0\x"),
|
||||
new StatisticText(score.MaxCombo, score.BeatmapInfo!.MaxCombo, @"0\x"),
|
||||
#pragma warning restore 618
|
||||
};
|
||||
|
||||
|
@ -123,7 +123,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||
accuracyColumn.Text = value.DisplayAccuracy;
|
||||
maxComboColumn.Text = value.MaxCombo.ToLocalisableString(@"0\x");
|
||||
|
||||
ppColumn.Alpha = value.BeatmapInfo.Status.GrantsPerformancePoints() ? 1 : 0;
|
||||
ppColumn.Alpha = value.BeatmapInfo!.Status.GrantsPerformancePoints() ? 1 : 0;
|
||||
|
||||
if (value.PP is double pp)
|
||||
ppColumn.Text = pp.ToLocalisableString(@"N0");
|
||||
|
@ -42,8 +42,8 @@ namespace osu.Game.Overlays
|
||||
IEnumerable<Notification> AllNotifications { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All ongoing operations (ie. any <see cref="ProgressNotification"/> not in a completed state).
|
||||
/// All ongoing operations (ie. any <see cref="ProgressNotification"/> not in a completed or cancelled state).
|
||||
/// </summary>
|
||||
public IEnumerable<ProgressNotification> OngoingOperations => AllNotifications.OfType<ProgressNotification>().Where(p => p.State != ProgressNotificationState.Completed && p.State != ProgressNotificationState.Cancelled);
|
||||
public IEnumerable<ProgressNotification> OngoingOperations => AllNotifications.OfType<ProgressNotification>().Where(p => p.Ongoing);
|
||||
}
|
||||
}
|
||||
|
@ -108,8 +108,8 @@ namespace osu.Game.Overlays
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Children = new[]
|
||||
{
|
||||
new NotificationSection(AccountsStrings.NotificationsTitle, new[] { typeof(SimpleNotification) }, NotificationsStrings.ClearAll),
|
||||
new NotificationSection(NotificationsStrings.RunningTasks, new[] { typeof(ProgressNotification) }, NotificationsStrings.CancelAll),
|
||||
new NotificationSection(AccountsStrings.NotificationsTitle, new[] { typeof(SimpleNotification) }),
|
||||
new NotificationSection(NotificationsStrings.RunningTasks, new[] { typeof(ProgressNotification) }),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -169,7 +169,7 @@ namespace osu.Game.Overlays
|
||||
|
||||
Logger.Log($"⚠️ {notification.Text}");
|
||||
|
||||
notification.Closed += notificationClosed;
|
||||
notification.Closed += () => notificationClosed(notification);
|
||||
|
||||
if (notification is IHasCompletionTarget hasCompletionTarget)
|
||||
hasCompletionTarget.CompletionTarget = Post;
|
||||
@ -229,17 +229,20 @@ namespace osu.Game.Overlays
|
||||
mainContent.FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.In);
|
||||
}
|
||||
|
||||
private void notificationClosed() => Schedule(() =>
|
||||
private void notificationClosed(Notification notification) => Schedule(() =>
|
||||
{
|
||||
updateCounts();
|
||||
|
||||
// this debounce is currently shared between popin/popout sounds, which means one could potentially not play when the user is expecting it.
|
||||
// popout is constant across all notification types, and should therefore be handled using playback concurrency instead, but seems broken at the moment.
|
||||
playDebouncedSample("UI/overlay-pop-out");
|
||||
playDebouncedSample(notification.PopOutSampleName);
|
||||
});
|
||||
|
||||
private void playDebouncedSample(string sampleName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sampleName))
|
||||
return;
|
||||
|
||||
if (lastSamplePlayback == null || Time.Current - lastSamplePlayback > OsuGameBase.SAMPLE_DEBOUNCE_TIME)
|
||||
{
|
||||
audio.Samples.Get(sampleName)?.Play();
|
||||
|
@ -50,7 +50,8 @@ namespace osu.Game.Overlays.Notifications
|
||||
/// </summary>
|
||||
public virtual bool DisplayOnTop => true;
|
||||
|
||||
public virtual string PopInSampleName => "UI/notification-pop-in";
|
||||
public virtual string PopInSampleName => "UI/notification-default";
|
||||
public virtual string PopOutSampleName => "UI/overlay-pop-out";
|
||||
|
||||
protected NotificationLight Light;
|
||||
|
||||
|
@ -13,6 +13,7 @@ using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Localisation;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Notifications
|
||||
@ -38,15 +39,12 @@ namespace osu.Game.Overlays.Notifications
|
||||
|
||||
public IEnumerable<Type> AcceptedNotificationTypes { get; }
|
||||
|
||||
private readonly LocalisableString clearButtonText;
|
||||
|
||||
private readonly LocalisableString titleText;
|
||||
|
||||
public NotificationSection(LocalisableString title, IEnumerable<Type> acceptedNotificationTypes, LocalisableString clearButtonText)
|
||||
public NotificationSection(LocalisableString title, IEnumerable<Type> acceptedNotificationTypes)
|
||||
{
|
||||
AcceptedNotificationTypes = acceptedNotificationTypes.ToArray();
|
||||
|
||||
this.clearButtonText = clearButtonText.ToUpper();
|
||||
titleText = title;
|
||||
}
|
||||
|
||||
@ -75,7 +73,7 @@ namespace osu.Game.Overlays.Notifications
|
||||
{
|
||||
new ClearAllButton
|
||||
{
|
||||
Text = clearButtonText,
|
||||
Text = NotificationsStrings.ClearAll.ToUpper(),
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Action = clearAll
|
||||
@ -115,10 +113,11 @@ namespace osu.Game.Overlays.Notifications
|
||||
});
|
||||
}
|
||||
|
||||
private void clearAll()
|
||||
private void clearAll() => notifications.Children.ForEach(c =>
|
||||
{
|
||||
notifications.Children.ForEach(c => c.Close(true));
|
||||
}
|
||||
if (c is not ProgressNotification p || !p.Ongoing)
|
||||
c.Close(true);
|
||||
});
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
|
@ -10,6 +10,8 @@ namespace osu.Game.Overlays.Notifications
|
||||
{
|
||||
public partial class ProgressCompletionNotification : SimpleNotification
|
||||
{
|
||||
public override string PopInSampleName => "UI/notification-done";
|
||||
|
||||
public ProgressCompletionNotification()
|
||||
{
|
||||
Icon = FontAwesome.Solid.Check;
|
||||
|
@ -4,6 +4,8 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
@ -25,8 +27,15 @@ namespace osu.Game.Overlays.Notifications
|
||||
|
||||
public Func<bool>? CancelRequested { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the operation represented by the <see cref="ProgressNotification"/> is still ongoing.
|
||||
/// </summary>
|
||||
public bool Ongoing => State != ProgressNotificationState.Completed && State != ProgressNotificationState.Cancelled;
|
||||
|
||||
protected override bool AllowFlingDismiss => false;
|
||||
|
||||
public override string PopOutSampleName => State is ProgressNotificationState.Cancelled ? base.PopOutSampleName : "";
|
||||
|
||||
/// <summary>
|
||||
/// The function to post completion notifications back to.
|
||||
/// </summary>
|
||||
@ -122,6 +131,7 @@ namespace osu.Game.Overlays.Notifications
|
||||
cancellationTokenSource.Cancel();
|
||||
|
||||
IconContent.FadeColour(ColourInfo.GradientVertical(Color4.Gray, Color4.Gray.Lighten(0.5f)), colour_fade_duration);
|
||||
cancelSample?.Play();
|
||||
loadingSpinner.Hide();
|
||||
|
||||
var icon = new SpriteIcon
|
||||
@ -190,6 +200,8 @@ namespace osu.Game.Overlays.Notifications
|
||||
|
||||
private LoadingSpinner loadingSpinner = null!;
|
||||
|
||||
private Sample? cancelSample;
|
||||
|
||||
private readonly TextFlowContainer textDrawable;
|
||||
|
||||
public ProgressNotification()
|
||||
@ -217,7 +229,7 @@ namespace osu.Game.Overlays.Notifications
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load(OsuColour colours, AudioManager audioManager)
|
||||
{
|
||||
colourQueued = colours.YellowDark;
|
||||
colourActive = colours.Blue;
|
||||
@ -236,6 +248,8 @@ namespace osu.Game.Overlays.Notifications
|
||||
Size = new Vector2(loading_spinner_size),
|
||||
}
|
||||
});
|
||||
|
||||
cancelSample = audioManager.Samples.Get(@"UI/notification-cancel");
|
||||
}
|
||||
|
||||
public override void Close(bool runFlingAnimation)
|
||||
|
@ -7,7 +7,7 @@ namespace osu.Game.Overlays.Notifications
|
||||
{
|
||||
public partial class SimpleErrorNotification : SimpleNotification
|
||||
{
|
||||
public override string PopInSampleName => "UI/error-notification-pop-in";
|
||||
public override string PopInSampleName => "UI/notification-error";
|
||||
|
||||
public SimpleErrorNotification()
|
||||
{
|
||||
|
@ -24,6 +24,7 @@ using osu.Game.Rulesets.Objects.Pooling;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -688,7 +689,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
protected bool UpdateResult(bool userTriggered)
|
||||
{
|
||||
// It's possible for input to get into a bad state when rewinding gameplay, so results should not be processed
|
||||
if (Time.Elapsed < 0)
|
||||
if ((Clock as IGameplayClock)?.IsRewinding == true)
|
||||
return false;
|
||||
|
||||
if (Judged)
|
||||
|
@ -171,6 +171,9 @@ namespace osu.Game.Rulesets.UI
|
||||
// The manual clock time has changed in the above code. The framed clock now needs to be updated
|
||||
// to ensure that the its time is valid for our children before input is processed
|
||||
framedClock.ProcessFrame();
|
||||
|
||||
if (framedClock.ElapsedFrameTime != 0)
|
||||
IsRewinding = framedClock.ElapsedFrameTime < 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -247,6 +250,8 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
public IBindable<bool> IsPaused { get; } = new BindableBool();
|
||||
|
||||
public bool IsRewinding { get; private set; }
|
||||
|
||||
public double CurrentTime => framedClock.CurrentTime;
|
||||
|
||||
public double Rate => framedClock.Rate;
|
||||
|
@ -69,6 +69,14 @@ namespace osu.Game.Rulesets.UI
|
||||
hitSound.Play();
|
||||
});
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (gameplayClock?.IsRewinding == true)
|
||||
mostValidObject = null;
|
||||
}
|
||||
|
||||
protected HitObject? GetMostValidObject()
|
||||
{
|
||||
if (mostValidObject == null || isAlreadyHit(mostValidObject))
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Scoring
|
||||
|
||||
double? PP { get; }
|
||||
|
||||
IBeatmapInfo Beatmap { get; }
|
||||
IBeatmapInfo? Beatmap { get; }
|
||||
|
||||
IRulesetInfo Ruleset { get; }
|
||||
|
||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Scoring.Legacy
|
||||
{
|
||||
sw.Write((byte)(score.ScoreInfo.Ruleset.OnlineID));
|
||||
sw.Write(LATEST_VERSION);
|
||||
sw.Write(score.ScoreInfo.BeatmapInfo.MD5Hash);
|
||||
sw.Write(score.ScoreInfo.BeatmapInfo!.MD5Hash);
|
||||
sw.Write(score.ScoreInfo.User.Username);
|
||||
sw.Write(FormattableString.Invariant($"lazer-{score.ScoreInfo.User.Username}-{score.ScoreInfo.Date}").ComputeMD5Hash());
|
||||
sw.Write((ushort)(score.ScoreInfo.GetCount300() ?? 0));
|
||||
|
@ -64,6 +64,8 @@ namespace osu.Game.Scoring
|
||||
|
||||
protected override void Populate(ScoreInfo model, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Debug.Assert(model.BeatmapInfo != null);
|
||||
|
||||
// Ensure the beatmap is not detached.
|
||||
if (!model.BeatmapInfo.IsManaged)
|
||||
model.BeatmapInfo = realm.Find<BeatmapInfo>(model.BeatmapInfo.ID)!;
|
||||
@ -101,10 +103,12 @@ namespace osu.Game.Scoring
|
||||
/// <param name="score">The score to populate the statistics of.</param>
|
||||
public void PopulateMaximumStatistics(ScoreInfo score)
|
||||
{
|
||||
Debug.Assert(score.BeatmapInfo != null);
|
||||
|
||||
if (score.MaximumStatistics.Select(kvp => kvp.Value).Sum() > 0)
|
||||
return;
|
||||
|
||||
var beatmap = score.BeatmapInfo.Detach();
|
||||
var beatmap = score.BeatmapInfo!.Detach();
|
||||
var ruleset = score.Ruleset.Detach();
|
||||
var rulesetInstance = ruleset.CreateInstance();
|
||||
|
||||
|
@ -35,9 +35,16 @@ namespace osu.Game.Scoring
|
||||
/// The <see cref="BeatmapInfo"/> this score was made against.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When setting this, make sure to also set <see cref="BeatmapHash"/> to allow relational consistency when a beatmap is potentially changed.
|
||||
/// <para>
|
||||
/// This property may be <see langword="null"/> if the score was set on a beatmap (or a version of the beatmap) that is not available locally
|
||||
/// e.g. due to online updates, or local modifications to the beatmap.
|
||||
/// The property will only link to a <see cref="BeatmapInfo"/> if its <see cref="Beatmaps.BeatmapInfo.Hash"/> matches <see cref="BeatmapHash"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Due to the above, whenever setting this, make sure to also set <see cref="BeatmapHash"/> to allow relational consistency when a beatmap is potentially changed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public BeatmapInfo BeatmapInfo { get; set; } = null!;
|
||||
public BeatmapInfo? BeatmapInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="osu.Game.Beatmaps.BeatmapInfo.Hash"/> at the point in time when the score was set.
|
||||
@ -150,14 +157,12 @@ namespace osu.Game.Scoring
|
||||
public int RankInt { get; set; }
|
||||
|
||||
IRulesetInfo IScoreInfo.Ruleset => Ruleset;
|
||||
IBeatmapInfo IScoreInfo.Beatmap => BeatmapInfo;
|
||||
IBeatmapInfo? IScoreInfo.Beatmap => BeatmapInfo;
|
||||
IUser IScoreInfo.User => User;
|
||||
IEnumerable<INamedFileUsage> IHasNamedFiles.Files => Files;
|
||||
|
||||
#region Properties required to make things work with existing usages
|
||||
|
||||
public Guid BeatmapInfoID => BeatmapInfo.ID;
|
||||
|
||||
public int UserID => RealmUser.OnlineID;
|
||||
|
||||
public int RulesetID => Ruleset.OnlineID;
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Scoring
|
||||
/// <summary>
|
||||
/// A user-presentable display title representing this score.
|
||||
/// </summary>
|
||||
public static string GetDisplayTitle(this IScoreInfo scoreInfo) => $"{scoreInfo.User.Username} playing {scoreInfo.Beatmap.GetDisplayTitle()}";
|
||||
public static string GetDisplayTitle(this IScoreInfo scoreInfo) => $"{scoreInfo.User.Username} playing {scoreInfo.Beatmap?.GetDisplayTitle() ?? "unknown"}";
|
||||
|
||||
/// <summary>
|
||||
/// Orders an array of <see cref="ScoreInfo"/>s by total score.
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Scoring
|
||||
{
|
||||
var score = lookup.ScoreInfo;
|
||||
|
||||
var attributes = await difficultyCache.GetDifficultyAsync(score.BeatmapInfo, score.Ruleset, score.Mods, token).ConfigureAwait(false);
|
||||
var attributes = await difficultyCache.GetDifficultyAsync(score.BeatmapInfo!, score.Ruleset, score.Mods, token).ConfigureAwait(false);
|
||||
|
||||
// Performance calculation requires the beatmap and ruleset to be locally available. If not, return a default value.
|
||||
if (attributes?.Attributes == null)
|
||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Screens.Play
|
||||
if (gameplayClock.CurrentTime < firstBreakTime)
|
||||
firstBreakTime = null;
|
||||
|
||||
if (gameplayClock.ElapsedFrameTime < 0)
|
||||
if (gameplayClock.IsRewinding)
|
||||
return;
|
||||
|
||||
if (combo.NewValue == 0 && (combo.OldValue > 20 || (alwaysPlayFirst.Value && firstBreakTime == null)))
|
||||
|
@ -19,11 +19,10 @@ namespace osu.Game.Screens.Play
|
||||
[Cached(typeof(IGameplayClock))]
|
||||
public partial class GameplayClockContainer : Container, IAdjustableClock, IGameplayClock
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether gameplay is paused.
|
||||
/// </summary>
|
||||
public IBindable<bool> IsPaused => isPaused;
|
||||
|
||||
public bool IsRewinding => GameplayClock.IsRewinding;
|
||||
|
||||
/// <summary>
|
||||
/// The source clock. Should generally not be used for any timekeeping purposes.
|
||||
/// </summary>
|
||||
|
@ -23,6 +23,14 @@ namespace osu.Game.Screens.Play
|
||||
/// </summary>
|
||||
IAdjustableAudioComponent AdjustmentsFromMods { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether gameplay is paused.
|
||||
/// </summary>
|
||||
IBindable<bool> IsPaused { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the clock is currently rewinding.
|
||||
/// </summary>
|
||||
bool IsRewinding { get; }
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,6 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -114,8 +112,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private Ruleset ruleset;
|
||||
|
||||
private Sample sampleRestart;
|
||||
|
||||
public BreakOverlay BreakOverlay;
|
||||
|
||||
/// <summary>
|
||||
@ -195,7 +191,7 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(AudioManager audio, OsuConfigManager config, OsuGameBase game, CancellationToken cancellationToken)
|
||||
private void load(OsuConfigManager config, OsuGameBase game, CancellationToken cancellationToken)
|
||||
{
|
||||
var gameplayMods = Mods.Value.Select(m => m.DeepClone()).ToArray();
|
||||
|
||||
@ -213,8 +209,6 @@ namespace osu.Game.Screens.Play
|
||||
if (playableBeatmap == null)
|
||||
return;
|
||||
|
||||
sampleRestart = audio.Samples.Get(@"Gameplay/restart");
|
||||
|
||||
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
|
||||
|
||||
if (game != null)
|
||||
@ -295,14 +289,17 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
if (Configuration.AllowRestart)
|
||||
{
|
||||
rulesetSkinProvider.Add(new HotkeyRetryOverlay
|
||||
rulesetSkinProvider.AddRange(new Drawable[]
|
||||
{
|
||||
Action = () =>
|
||||
new HotkeyRetryOverlay
|
||||
{
|
||||
if (!this.IsCurrentScreen()) return;
|
||||
Action = () =>
|
||||
{
|
||||
if (!this.IsCurrentScreen()) return;
|
||||
|
||||
fadeOut(true);
|
||||
Restart(true);
|
||||
fadeOut(true);
|
||||
Restart(true);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -673,7 +670,6 @@ namespace osu.Game.Screens.Play
|
||||
// stopping here is to ensure music doesn't become audible after exiting back to PlayerLoader.
|
||||
musicController.Stop();
|
||||
|
||||
sampleRestart?.Play();
|
||||
RestartRequested?.Invoke(quickRestart);
|
||||
|
||||
PerformExit(false);
|
||||
|
@ -15,6 +15,7 @@ using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Audio.Effects;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
@ -25,6 +26,7 @@ using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Play.PlayerSettings;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
@ -76,6 +78,8 @@ namespace osu.Game.Screens.Play
|
||||
private AudioFilter lowPassFilter = null!;
|
||||
private AudioFilter highPassFilter = null!;
|
||||
|
||||
private SkinnableSound sampleRestart = null!;
|
||||
|
||||
[Cached]
|
||||
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||
|
||||
@ -199,7 +203,8 @@ namespace osu.Game.Screens.Play
|
||||
},
|
||||
idleTracker = new IdleTracker(750),
|
||||
lowPassFilter = new AudioFilter(audio.TrackMixer),
|
||||
highPassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass)
|
||||
highPassFilter = new AudioFilter(audio.TrackMixer, BQFType.HighPass),
|
||||
sampleRestart = new SkinnableSound(new SampleInfo(@"Gameplay/restart", @"pause-retry-click"))
|
||||
};
|
||||
|
||||
if (Beatmap.Value.BeatmapInfo.EpilepsyWarning)
|
||||
@ -265,6 +270,8 @@ namespace osu.Game.Screens.Play
|
||||
playerConsumed = false;
|
||||
cancelLoad();
|
||||
|
||||
sampleRestart.Play();
|
||||
|
||||
contentIn();
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
IBeatmapInfo beatmap = score.ScoreInfo.BeatmapInfo;
|
||||
|
||||
Debug.Assert(beatmap.OnlineID > 0);
|
||||
Debug.Assert(beatmap!.OnlineID > 0);
|
||||
|
||||
return new SubmitSoloScoreRequest(score.ScoreInfo, token, beatmap.OnlineID);
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Screens.Ranking.Expanded
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapDifficultyCache beatmapDifficultyCache)
|
||||
{
|
||||
var beatmap = score.BeatmapInfo;
|
||||
var beatmap = score.BeatmapInfo!;
|
||||
var metadata = beatmap.BeatmapSet?.Metadata ?? beatmap.Metadata;
|
||||
string creator = metadata.Author.Username;
|
||||
|
||||
|
@ -305,7 +305,7 @@ namespace osu.Game.Screens.Ranking
|
||||
float origLocation = detachedPanelContainer.ToLocalSpace(screenSpacePos).X;
|
||||
expandedPanel.MoveToX(origLocation)
|
||||
.Then()
|
||||
.MoveToX(StatisticsPanel.SIDE_PADDING, 150, Easing.OutQuint);
|
||||
.MoveToX(StatisticsPanel.SIDE_PADDING, 400, Easing.OutElasticQuarter);
|
||||
|
||||
// Hide contracted panels.
|
||||
foreach (var contracted in ScorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted))
|
||||
@ -313,7 +313,7 @@ namespace osu.Game.Screens.Ranking
|
||||
ScorePanelList.HandleInput = false;
|
||||
|
||||
// Dim background.
|
||||
ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.1f), 150));
|
||||
ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.4f), 400, Easing.OutQuint));
|
||||
|
||||
detachedPanel = expandedPanel;
|
||||
}
|
||||
@ -329,7 +329,7 @@ namespace osu.Game.Screens.Ranking
|
||||
float origLocation = detachedPanel.Parent.ToLocalSpace(screenSpacePos).X;
|
||||
detachedPanel.MoveToX(origLocation)
|
||||
.Then()
|
||||
.MoveToX(0, 150, Easing.OutQuint);
|
||||
.MoveToX(0, 250, Easing.OutElasticQuarter);
|
||||
|
||||
// Show contracted panels.
|
||||
foreach (var contracted in ScorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted))
|
||||
@ -337,7 +337,7 @@ namespace osu.Game.Screens.Ranking
|
||||
ScorePanelList.HandleInput = true;
|
||||
|
||||
// Un-dim background.
|
||||
ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.5f), 150));
|
||||
ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.5f), 250, Easing.OutQuint));
|
||||
|
||||
detachedPanel = null;
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Screens.Ranking
|
||||
|
||||
protected override APIRequest? FetchScores(Action<IEnumerable<ScoreInfo>>? scoresCallback)
|
||||
{
|
||||
if (Score.BeatmapInfo.OnlineID <= 0 || Score.BeatmapInfo.Status <= BeatmapOnlineStatus.Pending)
|
||||
if (Score.BeatmapInfo!.OnlineID <= 0 || Score.BeatmapInfo.Status <= BeatmapOnlineStatus.Pending)
|
||||
return null;
|
||||
|
||||
getScoreRequest = new GetScoresRequest(Score.BeatmapInfo, Score.Ruleset);
|
||||
|
@ -223,7 +223,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
this.FadeIn(150, Easing.OutQuint);
|
||||
this.FadeIn(350, Easing.OutQuint);
|
||||
|
||||
popInSample?.Play();
|
||||
wasOpened = true;
|
||||
@ -231,7 +231,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
this.FadeOut(150, Easing.OutQuint);
|
||||
this.FadeOut(250, Easing.OutQuint);
|
||||
|
||||
if (wasOpened)
|
||||
popOutSample?.Play();
|
||||
|
@ -4,9 +4,7 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
using osu.Game.Scoring;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
@ -20,11 +18,8 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager beatmapManager, ScoreManager scoreManager)
|
||||
private void load(ScoreManager scoreManager)
|
||||
{
|
||||
BeatmapInfo? beatmapInfo = beatmapManager.QueryBeatmap(b => b.ID == score.BeatmapInfoID);
|
||||
Debug.Assert(beatmapInfo != null);
|
||||
|
||||
BodyText = $"{score.User} ({score.DisplayAccuracy}, {score.Rank})";
|
||||
|
||||
Icon = FontAwesome.Regular.TrashAlt;
|
||||
|
@ -111,7 +111,7 @@ namespace osu.Game.Users
|
||||
|
||||
protected string Username => score.User.Username;
|
||||
|
||||
public BeatmapInfo BeatmapInfo => score.BeatmapInfo;
|
||||
public BeatmapInfo? BeatmapInfo => score.BeatmapInfo;
|
||||
|
||||
public WatchingReplay(ScoreInfo score)
|
||||
{
|
||||
|
@ -36,8 +36,8 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="11.1.2" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.625.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.625.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.707.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.707.0" />
|
||||
<PackageReference Include="Sentry" Version="3.28.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
|
@ -16,6 +16,6 @@
|
||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.625.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.707.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -5,6 +5,7 @@
|
||||
<s:Boolean x:Key="/Default/CodeInspection/ExcludedFiles/FileMasksToSkip/=_002A_002Ewav/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=2A66DD92_002DADB1_002D4994_002D89E2_002DC94E04ACDA0D_002Fd_003AMigrations/@EntryIndexedValue">ExplicitlyExcluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=D9A367C9_002D4C1A_002D489F_002D9B05_002DA0CEA2B53B58/@EntryIndexedValue">ExplicitlyExcluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/GeneratedCode/GeneratedFileMasks/=g_005F_002A_002Ecs/@EntryIndexedValue">g_*.cs</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">SOLUTION</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeAccessorOwnerBody/@EntryIndexedValue">WARNING</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeDefaultValueWhenTypeEvident/@EntryIndexedValue">WARNING</s:String>
|
||||
|
Loading…
Reference in New Issue
Block a user