mirror of
https://github.com/ppy/osu.git
synced 2024-11-13 18:47:27 +08:00
Merge branch 'master' into menu-star-fountains
This commit is contained in:
commit
f3a95d4c13
@ -8,13 +8,9 @@
|
||||
<!-- NullabilityInfoContextSupport is disabled by default for Android -->
|
||||
<NullabilityInfoContextSupport>true</NullabilityInfoContextSupport>
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
<AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.710.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.716.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="mailto" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
@ -54,9 +54,6 @@ namespace osu.Desktop
|
||||
|
||||
client.OnReady += onReady;
|
||||
|
||||
// safety measure for now, until we performance test / improve backoff for failed connections.
|
||||
client.OnConnectionFailed += (_, _) => client.Deinitialize();
|
||||
|
||||
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network);
|
||||
|
||||
config.BindWith(OsuSetting.DiscordRichPresence, privacyMode);
|
||||
|
@ -26,7 +26,7 @@
|
||||
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
<PackageReference Include="System.IO.Packaging" Version="7.0.0" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.1.4.20" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Resources">
|
||||
<EmbeddedResource Include="lazer.ico" />
|
||||
|
@ -26,6 +26,8 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking.Statistics;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch
|
||||
@ -218,5 +220,17 @@ namespace osu.Game.Rulesets.Catch
|
||||
public override HitObjectComposer CreateHitObjectComposer() => new CatchHitObjectComposer(this);
|
||||
|
||||
public override IBeatmapVerifier CreateBeatmapVerifier() => new CatchBeatmapVerifier();
|
||||
|
||||
public override StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -412,7 +412,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 250
|
||||
}, true),
|
||||
new StatisticItem(string.Empty, () => new SimpleStatisticTable(3, new SimpleStatisticItem[]
|
||||
new StatisticItem("Statistics", () => new SimpleStatisticTable(2, new SimpleStatisticItem[]
|
||||
{
|
||||
new AverageHitError(score.HitEvents),
|
||||
new UnstableRate(score.HitEvents)
|
||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
}
|
||||
|
||||
protected override IEnumerable<HitObject> EnumerateHitObjects(IBeatmap beatmap)
|
||||
=> base.EnumerateHitObjects(beatmap).OrderBy(ho => (ManiaHitObject)ho, JudgementOrderComparer.DEFAULT);
|
||||
=> base.EnumerateHitObjects(beatmap).OrderBy(ho => ho, JudgementOrderComparer.DEFAULT);
|
||||
|
||||
protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion)
|
||||
{
|
||||
@ -34,11 +34,11 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
protected override double GetComboScoreChange(JudgementResult result)
|
||||
=> Judgement.ToNumericResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base));
|
||||
|
||||
private class JudgementOrderComparer : IComparer<ManiaHitObject>
|
||||
private class JudgementOrderComparer : IComparer<HitObject>
|
||||
{
|
||||
public static readonly JudgementOrderComparer DEFAULT = new JudgementOrderComparer();
|
||||
|
||||
public int Compare(ManiaHitObject? x, ManiaHitObject? y)
|
||||
public int Compare(HitObject? x, HitObject? y)
|
||||
{
|
||||
if (ReferenceEquals(x, y)) return 0;
|
||||
if (ReferenceEquals(x, null)) return -1;
|
||||
@ -52,7 +52,9 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
if (x is Note && y is not Note) return -1;
|
||||
if (x is not Note && y is Note) return 1;
|
||||
|
||||
return x.Column.CompareTo(y.Column);
|
||||
return x is ManiaHitObject maniaX && y is ManiaHitObject maniaY
|
||||
? maniaX.Column.CompareTo(maniaY.Column)
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -319,7 +319,7 @@ namespace osu.Game.Rulesets.Osu
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 250
|
||||
}, true),
|
||||
new StatisticItem(string.Empty, () => new SimpleStatisticTable(3, new SimpleStatisticItem[]
|
||||
new StatisticItem("Statistics", () => new SimpleStatisticTable(2, new SimpleStatisticItem[]
|
||||
{
|
||||
new AverageHitError(timedHitEvents),
|
||||
new UnstableRate(timedHitEvents)
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Scoring;
|
||||
@ -120,18 +121,22 @@ namespace osu.Game.Rulesets.Osu.Statistics
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "Overshoot",
|
||||
Font = OsuFont.GetFont(size: 12),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Padding = new MarginPadding(3),
|
||||
Origin = Anchor.BottomLeft,
|
||||
Padding = new MarginPadding(2),
|
||||
Rotation = -rotation,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Y = -(inner_portion + line_extension) / 2,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = "Undershoot",
|
||||
Font = OsuFont.GetFont(size: 12),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Padding = new MarginPadding(3),
|
||||
Origin = Anchor.TopRight,
|
||||
Rotation = -rotation,
|
||||
Padding = new MarginPadding(2),
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Y = (inner_portion + line_extension) / 2,
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@ -10,6 +11,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@ -36,11 +38,12 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements
|
||||
() => Is.EqualTo(expectedResult));
|
||||
}
|
||||
|
||||
protected void PerformTest(List<ReplayFrame> frames, Beatmap<TaikoHitObject>? beatmap = null)
|
||||
protected void PerformTest(List<ReplayFrame> frames, Beatmap<TaikoHitObject>? beatmap = null, Mod[]? mods = null)
|
||||
{
|
||||
AddStep("load player", () =>
|
||||
{
|
||||
Beatmap.Value = CreateWorkingBeatmap(beatmap);
|
||||
SelectedMods.Value = mods ?? Array.Empty<Mod>();
|
||||
|
||||
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
|
||||
|
||||
|
@ -75,6 +75,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements
|
||||
AssertResult<DrumRoll>(0, HitResult.IgnoreHit);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHitNoneStrongDrumRoll()
|
||||
{
|
||||
PerformTest(new List<ReplayFrame>
|
||||
{
|
||||
new TaikoReplayFrame(0),
|
||||
}, CreateBeatmap(createDrumRoll(true)));
|
||||
|
||||
AssertJudgementCount(12);
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
AssertResult<DrumRollTick>(i, HitResult.IgnoreMiss);
|
||||
AssertResult<DrumRollTick.StrongNestedHit>(i, HitResult.IgnoreMiss);
|
||||
}
|
||||
|
||||
AssertResult<DrumRoll>(0, HitResult.IgnoreHit);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHitAllStrongDrumRollWithOneKey()
|
||||
{
|
||||
|
@ -4,10 +4,14 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Replays;
|
||||
using osu.Game.Rulesets.Taiko.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests.Judgements
|
||||
{
|
||||
@ -157,5 +161,58 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements
|
||||
AssertJudgementCount(1);
|
||||
AssertResult<Hit>(0, HitResult.Ok);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStrongHitOneKeyWithHidden()
|
||||
{
|
||||
const double hit_time = 1000;
|
||||
|
||||
var beatmap = CreateBeatmap(new Hit
|
||||
{
|
||||
Type = HitType.Centre,
|
||||
StartTime = hit_time,
|
||||
IsStrong = true
|
||||
});
|
||||
|
||||
var hitWindows = new TaikoHitWindows();
|
||||
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
|
||||
|
||||
PerformTest(new List<ReplayFrame>
|
||||
{
|
||||
new TaikoReplayFrame(0),
|
||||
new TaikoReplayFrame(hit_time + hitWindows.WindowFor(HitResult.Ok) - 1, TaikoAction.LeftCentre),
|
||||
}, beatmap, new Mod[] { new TaikoModHidden() });
|
||||
|
||||
AssertJudgementCount(2);
|
||||
AssertResult<Hit>(0, HitResult.Ok);
|
||||
AssertResult<Hit.StrongNestedHit>(0, HitResult.IgnoreMiss);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStrongHitTwoKeysWithHidden()
|
||||
{
|
||||
const double hit_time = 1000;
|
||||
|
||||
var beatmap = CreateBeatmap(new Hit
|
||||
{
|
||||
Type = HitType.Centre,
|
||||
StartTime = hit_time,
|
||||
IsStrong = true
|
||||
});
|
||||
|
||||
var hitWindows = new TaikoHitWindows();
|
||||
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
|
||||
|
||||
PerformTest(new List<ReplayFrame>
|
||||
{
|
||||
new TaikoReplayFrame(0),
|
||||
new TaikoReplayFrame(hit_time + hitWindows.WindowFor(HitResult.Ok) - 1, TaikoAction.LeftCentre),
|
||||
new TaikoReplayFrame(hit_time + hitWindows.WindowFor(HitResult.Ok) + DrawableHit.StrongNestedHit.SECOND_HIT_WINDOW - 2, TaikoAction.LeftCentre, TaikoAction.RightCentre),
|
||||
}, beatmap, new Mod[] { new TaikoModHidden() });
|
||||
|
||||
AssertJudgementCount(2);
|
||||
AssertResult<Hit>(0, HitResult.Ok);
|
||||
AssertResult<Hit.StrongNestedHit>(0, HitResult.LargeBonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -114,5 +114,75 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements
|
||||
|
||||
AddAssert("all tick offsets are 0", () => JudgementResults.Where(r => r.HitObject is SwellTick).All(r => r.TimeOffset == 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure input is correctly sent to subsequent hits if a swell is fully completed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestHitSwellThenHitHit()
|
||||
{
|
||||
const double swell_time = 1000;
|
||||
const double hit_time = 1150;
|
||||
|
||||
Swell swell = new Swell
|
||||
{
|
||||
StartTime = swell_time,
|
||||
Duration = 100,
|
||||
RequiredHits = 1
|
||||
};
|
||||
|
||||
Hit hit = new Hit
|
||||
{
|
||||
StartTime = hit_time
|
||||
};
|
||||
|
||||
List<ReplayFrame> frames = new List<ReplayFrame>
|
||||
{
|
||||
new TaikoReplayFrame(0),
|
||||
new TaikoReplayFrame(swell_time, TaikoAction.LeftRim),
|
||||
new TaikoReplayFrame(hit_time, TaikoAction.RightCentre),
|
||||
};
|
||||
|
||||
PerformTest(frames, CreateBeatmap(swell, hit));
|
||||
|
||||
AssertJudgementCount(3);
|
||||
|
||||
AssertResult<SwellTick>(0, HitResult.IgnoreHit);
|
||||
AssertResult<Swell>(0, HitResult.LargeBonus);
|
||||
AssertResult<Hit>(0, HitResult.Great);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMissSwellThenHitHit()
|
||||
{
|
||||
const double swell_time = 1000;
|
||||
const double hit_time = 1150;
|
||||
|
||||
Swell swell = new Swell
|
||||
{
|
||||
StartTime = swell_time,
|
||||
Duration = 100,
|
||||
RequiredHits = 1
|
||||
};
|
||||
|
||||
Hit hit = new Hit
|
||||
{
|
||||
StartTime = hit_time
|
||||
};
|
||||
|
||||
List<ReplayFrame> frames = new List<ReplayFrame>
|
||||
{
|
||||
new TaikoReplayFrame(0),
|
||||
new TaikoReplayFrame(hit_time, TaikoAction.RightCentre),
|
||||
};
|
||||
|
||||
PerformTest(frames, CreateBeatmap(swell, hit));
|
||||
|
||||
AssertJudgementCount(3);
|
||||
|
||||
AssertResult<SwellTick>(0, HitResult.IgnoreMiss);
|
||||
AssertResult<Swell>(0, HitResult.IgnoreMiss);
|
||||
AssertResult<Hit>(0, HitResult.Great);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,8 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
hitObject.LifetimeEnd = state == ArmedState.Idle || !hitObject.AllJudged
|
||||
? hitObject.HitObject.GetEndTime() + hitObject.HitObject.HitWindows.WindowFor(HitResult.Miss)
|
||||
: hitObject.HitStateUpdateTime;
|
||||
// extend the lifetime end of the object in order to allow its nested strong hit (if any) to be judged.
|
||||
hitObject.LifetimeEnd += DrawableHit.StrongNestedHit.SECOND_HIT_WINDOW;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -195,14 +195,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
}
|
||||
|
||||
public override void OnKilled()
|
||||
{
|
||||
base.OnKilled();
|
||||
|
||||
if (Time.Current > ParentHitObject.HitObject.GetEndTime() && !Judged)
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
}
|
||||
}
|
||||
|
@ -108,14 +108,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||
}
|
||||
|
||||
public override void OnKilled()
|
||||
{
|
||||
base.OnKilled();
|
||||
|
||||
if (Time.Current > ParentHitObject.HitObject.GetEndTime() && !Judged)
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
@ -16,5 +17,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
: base(nestedHit)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnKilled()
|
||||
{
|
||||
base.OnKilled();
|
||||
|
||||
// usually, the strong nested hit isn't judged itself, it is judged by its parent object.
|
||||
// however, in rare cases (see: drum rolls, hits with hidden active),
|
||||
// it can happen that the hit window of the nested strong hit extends past the lifetime of the parent object.
|
||||
// this is a safety to prevent such cases from causing the nested hit to never be judged and as such prevent gameplay from completing.
|
||||
if (!Judged && Time.Current > ParentHitObject?.HitObject.GetEndTime())
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -276,6 +276,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
if (Time.Current < HitObject.StartTime)
|
||||
return false;
|
||||
|
||||
if (AllJudged)
|
||||
return false;
|
||||
|
||||
bool isCentre = e.Action == TaikoAction.LeftCentre || e.Action == TaikoAction.RightCentre;
|
||||
|
||||
// Ensure alternating centre and rim hits
|
||||
|
@ -256,7 +256,7 @@ namespace osu.Game.Rulesets.Taiko
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 250
|
||||
}, true),
|
||||
new StatisticItem(string.Empty, () => new SimpleStatisticTable(3, new SimpleStatisticItem[]
|
||||
new StatisticItem("Statistics", () => new SimpleStatisticTable(2, new SimpleStatisticItem[]
|
||||
{
|
||||
new AverageHitError(timedHitEvents),
|
||||
new UnstableRate(timedHitEvents)
|
||||
|
@ -478,8 +478,8 @@ namespace osu.Game.Tests.Chat
|
||||
Content = "This is a [http://www.simple-test.com simple test] with some [traps] and [[wiki links]]. Don't forget to visit https://osu.ppy.sh (now!)[http://google.com]\uD83D\uDE12"
|
||||
});
|
||||
|
||||
Assert.AreEqual("This is a simple test with some [traps] and wiki links. Don't forget to visit https://osu.ppy.sh now!\0\0\0", result.DisplayContent);
|
||||
Assert.AreEqual(5, result.Links.Count);
|
||||
Assert.AreEqual("This is a simple test with some [traps] and wiki links. Don't forget to visit https://osu.ppy.sh now![emoji]", result.DisplayContent);
|
||||
Assert.AreEqual(4, result.Links.Count);
|
||||
|
||||
Link f = result.Links.Find(l => l.Url == "https://dev.ppy.sh/wiki/wiki links");
|
||||
Assert.That(f, Is.Not.Null);
|
||||
@ -500,27 +500,22 @@ namespace osu.Game.Tests.Chat
|
||||
Assert.That(f, Is.Not.Null);
|
||||
Assert.AreEqual(78, f.Index);
|
||||
Assert.AreEqual(18, f.Length);
|
||||
|
||||
f = result.Links.Find(l => l.Url == "\uD83D\uDE12");
|
||||
Assert.That(f, Is.Not.Null);
|
||||
Assert.AreEqual(101, f.Index);
|
||||
Assert.AreEqual(3, f.Length);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmoji()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "Hello world\uD83D\uDE12<--This is an emoji,There are more:\uD83D\uDE10\uD83D\uDE00,\uD83D\uDE20" });
|
||||
Assert.AreEqual("Hello world\0\0\0<--This is an emoji,There are more:\0\0\0\0\0\0,\0\0\0", result.DisplayContent);
|
||||
Assert.AreEqual(result.Links.Count, 4);
|
||||
Assert.AreEqual(result.Links[0].Index, 11);
|
||||
Assert.AreEqual(result.Links[1].Index, 49);
|
||||
Assert.AreEqual(result.Links[2].Index, 52);
|
||||
Assert.AreEqual(result.Links[3].Index, 56);
|
||||
Assert.AreEqual(result.Links[0].Url, "\uD83D\uDE12");
|
||||
Assert.AreEqual(result.Links[1].Url, "\uD83D\uDE10");
|
||||
Assert.AreEqual(result.Links[2].Url, "\uD83D\uDE00");
|
||||
Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20");
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "Hello world\uD83D\uDE12<--This is an emoji,There are more emojis among us:\uD83D\uDE10\uD83D\uDE00,\uD83D\uDE20" });
|
||||
Assert.AreEqual("Hello world[emoji]<--This is an emoji,There are more emojis among us:[emoji][emoji],[emoji]", result.DisplayContent);
|
||||
Assert.AreEqual(result.Links.Count, 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmojiWithSuccessiveParens()
|
||||
{
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = "\uD83D\uDE10(let's hope this doesn't accidentally turn into a link)" });
|
||||
Assert.AreEqual("[emoji](let's hope this doesn't accidentally turn into a link)", result.DisplayContent);
|
||||
Assert.AreEqual(result.Links.Count, 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -722,6 +722,30 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestForceExitWithOperationInProgress()
|
||||
{
|
||||
AddStep("set hold delay to 0", () => Game.LocalConfig.SetValue(OsuSetting.UIHoldActivationDelay, 0.0));
|
||||
AddUntilStep("wait for dialog overlay", () => Game.ChildrenOfType<DialogOverlay>().SingleOrDefault() != null);
|
||||
|
||||
AddStep("start ongoing operation", () =>
|
||||
{
|
||||
Game.Notifications.Post(new ProgressNotification
|
||||
{
|
||||
Text = "Something is still running",
|
||||
Progress = 0.5f,
|
||||
State = ProgressNotificationState.Active,
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("attempt exit", () =>
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
Game.ScreenStack.CurrentScreen.Exit();
|
||||
});
|
||||
AddUntilStep("stopped at exit confirm", () => Game.ChildrenOfType<DialogOverlay>().Single().CurrentDialog is ConfirmExitDialog);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitGameFromSongSelect()
|
||||
{
|
||||
|
@ -148,7 +148,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
advanceToSongSelect();
|
||||
openSkinEditor();
|
||||
AddStep("select no fail and spun out", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModSpunOut() });
|
||||
AddStep("select relax and spun out", () => Game.SelectedMods.Value = new Mod[] { new OsuModRelax(), new OsuModSpunOut() });
|
||||
|
||||
switchToGameplayScene();
|
||||
|
||||
|
@ -89,6 +89,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
addMessageWithChecks($"Join my {OsuGameBase.OSU_PROTOCOL}chan/#english.", 1, expectedActions: LinkAction.OpenChannel);
|
||||
addMessageWithChecks("Join my #english or #japanese channels.", 2, expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel });
|
||||
addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", 1, expectedActions: LinkAction.OpenChannel);
|
||||
addMessageWithChecks("Hello world\uD83D\uDE12(<--This is an emoji). There are more:\uD83D\uDE10\uD83D\uDE00,\uD83D\uDE20");
|
||||
|
||||
void addMessageWithChecks(string text, int linkAmount = 0, bool isAction = false, bool isImportant = false, params LinkAction[] expectedActions)
|
||||
{
|
||||
|
@ -69,6 +69,35 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
}));
|
||||
}
|
||||
|
||||
private int onlineScoreID = 1;
|
||||
|
||||
[TestCase(1, ScoreRank.X)]
|
||||
[TestCase(0.9999, ScoreRank.S)]
|
||||
[TestCase(0.975, ScoreRank.S)]
|
||||
[TestCase(0.925, ScoreRank.A)]
|
||||
[TestCase(0.85, ScoreRank.B)]
|
||||
[TestCase(0.75, ScoreRank.C)]
|
||||
[TestCase(0.5, ScoreRank.D)]
|
||||
[TestCase(0.2, ScoreRank.D)]
|
||||
public void TestResultsWithPlayer(double accuracy, ScoreRank rank)
|
||||
{
|
||||
TestResultsScreen screen = null;
|
||||
|
||||
loadResultsScreen(() =>
|
||||
{
|
||||
var score = TestResources.CreateTestScoreInfo();
|
||||
|
||||
score.OnlineID = onlineScoreID++;
|
||||
score.HitEvents = TestSceneStatisticsPanel.CreatePositionDistributedHitEvents();
|
||||
score.Accuracy = accuracy;
|
||||
score.Rank = rank;
|
||||
|
||||
return screen = createResultsScreen(score);
|
||||
});
|
||||
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||
AddAssert("retry overlay present", () => screen.RetryOverlay != null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestResultsWithoutPlayer()
|
||||
{
|
||||
@ -82,34 +111,14 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
|
||||
stack.Push(screen = createResultsScreen());
|
||||
var score = TestResources.CreateTestScoreInfo();
|
||||
|
||||
stack.Push(screen = createResultsScreen(score));
|
||||
});
|
||||
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||
AddAssert("retry overlay not present", () => screen.RetryOverlay == null);
|
||||
}
|
||||
|
||||
[TestCase(0.2, ScoreRank.D)]
|
||||
[TestCase(0.5, ScoreRank.D)]
|
||||
[TestCase(0.75, ScoreRank.C)]
|
||||
[TestCase(0.85, ScoreRank.B)]
|
||||
[TestCase(0.925, ScoreRank.A)]
|
||||
[TestCase(0.975, ScoreRank.S)]
|
||||
[TestCase(0.9999, ScoreRank.S)]
|
||||
[TestCase(1, ScoreRank.X)]
|
||||
public void TestResultsWithPlayer(double accuracy, ScoreRank rank)
|
||||
{
|
||||
TestResultsScreen screen = null;
|
||||
|
||||
var score = TestResources.CreateTestScoreInfo();
|
||||
|
||||
score.Accuracy = accuracy;
|
||||
score.Rank = rank;
|
||||
|
||||
loadResultsScreen(() => screen = createResultsScreen(score));
|
||||
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||
AddAssert("retry overlay present", () => screen.RetryOverlay != null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestResultsForUnranked()
|
||||
{
|
||||
@ -328,13 +337,14 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
}
|
||||
}
|
||||
|
||||
private partial class TestResultsScreen : ResultsScreen
|
||||
private partial class TestResultsScreen : SoloResultsScreen
|
||||
{
|
||||
public HotkeyRetryOverlay RetryOverlay;
|
||||
|
||||
public TestResultsScreen(ScoreInfo score)
|
||||
: base(score, true)
|
||||
{
|
||||
ShowUserStatistics = true;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online.Solo;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -23,6 +24,7 @@ using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Ranking
|
||||
@ -30,19 +32,20 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
public partial class TestSceneStatisticsPanel : OsuTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestScoreWithTimeStatistics()
|
||||
public void TestScoreWithPositionStatistics()
|
||||
{
|
||||
var score = TestResources.CreateTestScoreInfo();
|
||||
score.HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents();
|
||||
score.OnlineID = 1234;
|
||||
score.HitEvents = CreatePositionDistributedHitEvents();
|
||||
|
||||
loadPanel(score);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScoreWithPositionStatistics()
|
||||
public void TestScoreWithTimeStatistics()
|
||||
{
|
||||
var score = TestResources.CreateTestScoreInfo();
|
||||
score.HitEvents = createPositionDistributedHitEvents();
|
||||
score.HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents();
|
||||
|
||||
loadPanel(score);
|
||||
}
|
||||
@ -79,28 +82,67 @@ namespace osu.Game.Tests.Visual.Ranking
|
||||
|
||||
private void loadPanel(ScoreInfo score) => AddStep("load panel", () =>
|
||||
{
|
||||
Child = new StatisticsPanel
|
||||
Child = new SoloStatisticsPanel(score)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
State = { Value = Visibility.Visible },
|
||||
Score = { Value = score }
|
||||
Score = { Value = score },
|
||||
StatisticsUpdate =
|
||||
{
|
||||
Value = new SoloStatisticsUpdate(score, new UserStatistics
|
||||
{
|
||||
Level = new UserStatistics.LevelInfo
|
||||
{
|
||||
Current = 5,
|
||||
Progress = 20,
|
||||
},
|
||||
GlobalRank = 38000,
|
||||
CountryRank = 12006,
|
||||
PP = 2134,
|
||||
RankedScore = 21123849,
|
||||
Accuracy = 0.985,
|
||||
PlayCount = 13375,
|
||||
PlayTime = 354490,
|
||||
TotalScore = 128749597,
|
||||
TotalHits = 0,
|
||||
MaxCombo = 1233,
|
||||
}, new UserStatistics
|
||||
{
|
||||
Level = new UserStatistics.LevelInfo
|
||||
{
|
||||
Current = 5,
|
||||
Progress = 30,
|
||||
},
|
||||
GlobalRank = 36000,
|
||||
CountryRank = 12000,
|
||||
PP = (decimal)2134.5,
|
||||
RankedScore = 23897015,
|
||||
Accuracy = 0.984,
|
||||
PlayCount = 13376,
|
||||
PlayTime = 35789,
|
||||
TotalScore = 132218497,
|
||||
TotalHits = 0,
|
||||
MaxCombo = 1233,
|
||||
})
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
private static List<HitEvent> createPositionDistributedHitEvents()
|
||||
public static List<HitEvent> CreatePositionDistributedHitEvents()
|
||||
{
|
||||
var hitEvents = new List<HitEvent>();
|
||||
var hitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents();
|
||||
|
||||
// Use constant seed for reproducibility
|
||||
var random = new Random(0);
|
||||
|
||||
for (int i = 0; i < 500; i++)
|
||||
for (int i = 0; i < hitEvents.Count; i++)
|
||||
{
|
||||
double angle = random.NextDouble() * 2 * Math.PI;
|
||||
double radius = random.NextDouble() * 0.5f * OsuHitObject.OBJECT_RADIUS;
|
||||
|
||||
var position = new Vector2((float)(radius * Math.Cos(angle)), (float)(radius * Math.Sin(angle)));
|
||||
|
||||
hitEvents.Add(new HitEvent(0, HitResult.Perfect, new HitCircle(), new HitCircle(), position));
|
||||
hitEvents[i] = hitEvents[i].With(position);
|
||||
}
|
||||
|
||||
return hitEvents;
|
||||
|
@ -195,16 +195,16 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
InputManager.ReleaseKey(Key.P);
|
||||
});
|
||||
|
||||
AddUntilStep("restore button shown", () => settingsKeyBindingRow.ChildrenOfType<RestoreDefaultValueButton<bool>>().First().Alpha > 0);
|
||||
AddUntilStep("restore button shown", () => settingsKeyBindingRow.ChildrenOfType<RevertToDefaultButton<bool>>().First().Alpha > 0);
|
||||
|
||||
AddStep("click reset button for bindings", () =>
|
||||
{
|
||||
var resetButton = settingsKeyBindingRow.ChildrenOfType<RestoreDefaultValueButton<bool>>().First();
|
||||
var resetButton = settingsKeyBindingRow.ChildrenOfType<RevertToDefaultButton<bool>>().First();
|
||||
|
||||
resetButton.TriggerClick();
|
||||
});
|
||||
|
||||
AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType<RestoreDefaultValueButton<bool>>().First().Alpha == 0);
|
||||
AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType<RevertToDefaultButton<bool>>().First().Alpha == 0);
|
||||
|
||||
AddAssert("binding cleared",
|
||||
() => settingsKeyBindingRow.ChildrenOfType<KeyBindingRow.KeyButton>().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.Defaults.ElementAt(0)));
|
||||
@ -225,7 +225,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
InputManager.ReleaseKey(Key.P);
|
||||
});
|
||||
|
||||
AddUntilStep("restore button shown", () => settingsKeyBindingRow.ChildrenOfType<RestoreDefaultValueButton<bool>>().First().Alpha > 0);
|
||||
AddUntilStep("restore button shown", () => settingsKeyBindingRow.ChildrenOfType<RevertToDefaultButton<bool>>().First().Alpha > 0);
|
||||
|
||||
AddStep("click reset button for bindings", () =>
|
||||
{
|
||||
@ -234,7 +234,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
resetButton.TriggerClick();
|
||||
});
|
||||
|
||||
AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType<RestoreDefaultValueButton<bool>>().First().Alpha == 0);
|
||||
AddUntilStep("restore button hidden", () => settingsKeyBindingRow.ChildrenOfType<RevertToDefaultButton<bool>>().First().Alpha == 0);
|
||||
|
||||
AddAssert("binding cleared",
|
||||
() => settingsKeyBindingRow.ChildrenOfType<KeyBindingRow.KeyButton>().ElementAt(0).KeyBinding.KeyCombination.Equals(settingsKeyBindingRow.Defaults.ElementAt(0)));
|
||||
|
@ -1,65 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Settings
|
||||
{
|
||||
public partial class TestSceneRestoreDefaultValueButton : OsuTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
private float scale = 1;
|
||||
|
||||
private readonly Bindable<float> current = new Bindable<float>
|
||||
{
|
||||
Default = default,
|
||||
Value = 1,
|
||||
};
|
||||
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
RestoreDefaultValueButton<float> restoreDefaultValueButton = null;
|
||||
|
||||
AddStep("create button", () => Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colours.GreySeaFoam
|
||||
},
|
||||
restoreDefaultValueButton = new RestoreDefaultValueButton<float>
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(scale),
|
||||
Current = current,
|
||||
}
|
||||
}
|
||||
});
|
||||
AddSliderStep("set scale", 1, 4, 1, scale =>
|
||||
{
|
||||
this.scale = scale;
|
||||
if (restoreDefaultValueButton != null)
|
||||
restoreDefaultValueButton.Scale = new Vector2(scale);
|
||||
});
|
||||
AddToggleStep("toggle default state", state => current.Value = state ? default : 1);
|
||||
AddToggleStep("toggle disabled state", state => current.Disabled = state);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Tests.Visual.UserInterface;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Settings
|
||||
{
|
||||
public partial class TestSceneRevertToDefaultButton : ThemeComparisonTestScene
|
||||
{
|
||||
private float scale = 1;
|
||||
|
||||
private readonly Bindable<float> current = new Bindable<float>
|
||||
{
|
||||
Default = default,
|
||||
Value = 1,
|
||||
};
|
||||
|
||||
protected override Drawable CreateContent() => new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Child = new RevertToDefaultButton<float>
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(scale),
|
||||
Current = current,
|
||||
}
|
||||
};
|
||||
|
||||
[Test]
|
||||
public void TestStates()
|
||||
{
|
||||
AddStep("create content", () => CreateThemedContent(OverlayColourScheme.Purple));
|
||||
AddSliderStep("set scale", 1, 4, 1, scale =>
|
||||
{
|
||||
this.scale = scale;
|
||||
foreach (var revertToDefaultButton in this.ChildrenOfType<RevertToDefaultButton<float>>())
|
||||
revertToDefaultButton.Parent!.Scale = new Vector2(scale);
|
||||
});
|
||||
AddToggleStep("toggle default state", state => current.Value = state ? default : 1);
|
||||
AddToggleStep("toggle disabled state", state => current.Disabled = state);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
public void TestRestoreDefaultValueButtonVisibility()
|
||||
{
|
||||
SettingsTextBox textBox = null;
|
||||
RestoreDefaultValueButton<string> restoreDefaultValueButton = null;
|
||||
RevertToDefaultButton<string> revertToDefaultButton = null;
|
||||
|
||||
AddStep("create settings item", () =>
|
||||
{
|
||||
@ -33,22 +33,22 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
};
|
||||
});
|
||||
AddUntilStep("wait for loaded", () => textBox.IsLoaded);
|
||||
AddStep("retrieve restore default button", () => restoreDefaultValueButton = textBox.ChildrenOfType<RestoreDefaultValueButton<string>>().Single());
|
||||
AddStep("retrieve restore default button", () => revertToDefaultButton = textBox.ChildrenOfType<RevertToDefaultButton<string>>().Single());
|
||||
|
||||
AddAssert("restore button hidden", () => restoreDefaultValueButton.Alpha == 0);
|
||||
AddAssert("restore button hidden", () => revertToDefaultButton.Alpha == 0);
|
||||
|
||||
AddStep("change value from default", () => textBox.Current.Value = "non-default");
|
||||
AddUntilStep("restore button shown", () => restoreDefaultValueButton.Alpha > 0);
|
||||
AddUntilStep("restore button shown", () => revertToDefaultButton.Alpha > 0);
|
||||
|
||||
AddStep("restore default", () => textBox.Current.SetDefault());
|
||||
AddUntilStep("restore button hidden", () => restoreDefaultValueButton.Alpha == 0);
|
||||
AddUntilStep("restore button hidden", () => revertToDefaultButton.Alpha == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSetAndClearLabelText()
|
||||
{
|
||||
SettingsTextBox textBox = null;
|
||||
RestoreDefaultValueButton<string> restoreDefaultValueButton = null;
|
||||
RevertToDefaultButton<string> revertToDefaultButton = null;
|
||||
OsuTextBox control = null;
|
||||
|
||||
AddStep("create settings item", () =>
|
||||
@ -61,25 +61,25 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
AddUntilStep("wait for loaded", () => textBox.IsLoaded);
|
||||
AddStep("retrieve components", () =>
|
||||
{
|
||||
restoreDefaultValueButton = textBox.ChildrenOfType<RestoreDefaultValueButton<string>>().Single();
|
||||
revertToDefaultButton = textBox.ChildrenOfType<RevertToDefaultButton<string>>().Single();
|
||||
control = textBox.ChildrenOfType<OsuTextBox>().Single();
|
||||
});
|
||||
|
||||
AddStep("set non-default value", () => restoreDefaultValueButton.Current.Value = "non-default");
|
||||
AddAssert("default value button centre aligned to control size", () => Precision.AlmostEquals(restoreDefaultValueButton.Parent.DrawHeight, control.DrawHeight, 1));
|
||||
AddStep("set non-default value", () => revertToDefaultButton.Current.Value = "non-default");
|
||||
AddAssert("default value button centre aligned to control size", () => Precision.AlmostEquals(revertToDefaultButton.Parent.DrawHeight, control.DrawHeight, 1));
|
||||
|
||||
AddStep("set label", () => textBox.LabelText = "label text");
|
||||
AddAssert("default value button centre aligned to label size", () =>
|
||||
{
|
||||
var label = textBox.ChildrenOfType<OsuSpriteText>().Single(spriteText => spriteText.Text == "label text");
|
||||
return Precision.AlmostEquals(restoreDefaultValueButton.Parent.DrawHeight, label.DrawHeight, 1);
|
||||
return Precision.AlmostEquals(revertToDefaultButton.Parent.DrawHeight, label.DrawHeight, 1);
|
||||
});
|
||||
|
||||
AddStep("clear label", () => textBox.LabelText = default);
|
||||
AddAssert("default value button centre aligned to control size", () => Precision.AlmostEquals(restoreDefaultValueButton.Parent.DrawHeight, control.DrawHeight, 1));
|
||||
AddAssert("default value button centre aligned to control size", () => Precision.AlmostEquals(revertToDefaultButton.Parent.DrawHeight, control.DrawHeight, 1));
|
||||
|
||||
AddStep("set warning text", () => textBox.SetNoticeText("This is some very important warning text! Hopefully it doesn't break the alignment of the default value indicator...", true));
|
||||
AddAssert("default value button centre aligned to control size", () => Precision.AlmostEquals(restoreDefaultValueButton.Parent.DrawHeight, control.DrawHeight, 1));
|
||||
AddAssert("default value button centre aligned to control size", () => Precision.AlmostEquals(revertToDefaultButton.Parent.DrawHeight, control.DrawHeight, 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -92,7 +92,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
{
|
||||
BindableFloat current = null;
|
||||
SettingsSlider<float> sliderBar = null;
|
||||
RestoreDefaultValueButton<float> restoreDefaultValueButton = null;
|
||||
RevertToDefaultButton<float> revertToDefaultButton = null;
|
||||
|
||||
AddStep("create settings item", () =>
|
||||
{
|
||||
@ -107,15 +107,15 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
};
|
||||
});
|
||||
AddUntilStep("wait for loaded", () => sliderBar.IsLoaded);
|
||||
AddStep("retrieve restore default button", () => restoreDefaultValueButton = sliderBar.ChildrenOfType<RestoreDefaultValueButton<float>>().Single());
|
||||
AddStep("retrieve restore default button", () => revertToDefaultButton = sliderBar.ChildrenOfType<RevertToDefaultButton<float>>().Single());
|
||||
|
||||
AddAssert("restore button hidden", () => restoreDefaultValueButton.Alpha == 0);
|
||||
AddAssert("restore button hidden", () => revertToDefaultButton.Alpha == 0);
|
||||
|
||||
AddStep("change value to next closest", () => sliderBar.Current.Value += current.Precision * 0.6f);
|
||||
AddUntilStep("restore button shown", () => restoreDefaultValueButton.Alpha > 0);
|
||||
AddUntilStep("restore button shown", () => revertToDefaultButton.Alpha > 0);
|
||||
|
||||
AddStep("restore default", () => sliderBar.Current.SetDefault());
|
||||
AddUntilStep("restore button hidden", () => restoreDefaultValueButton.Alpha == 0);
|
||||
AddUntilStep("restore button hidden", () => revertToDefaultButton.Alpha == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -9,8 +9,8 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
@ -304,11 +304,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddAssert("preset is not changed", () => panel.Preset.Value.Name == presetName);
|
||||
AddUntilStep("popover is unchanged", () => this.ChildrenOfType<OsuPopover>().FirstOrDefault() == popover);
|
||||
AddStep("edit preset name", () => popover.ChildrenOfType<LabelledTextBox>().First().Current.Value = "something new");
|
||||
AddStep("attempt preset edit", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(popover.ChildrenOfType<ShearedButton>().ElementAt(1));
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddStep("commit changes to textbox", () => InputManager.Key(Key.Enter));
|
||||
AddStep("attempt preset edit via select binding", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("popover closed", () => !this.ChildrenOfType<OsuPopover>().Any());
|
||||
AddAssert("preset is changed", () => panel.Preset.Value.Name != presetName);
|
||||
}
|
||||
|
@ -542,7 +542,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
AddStep("clear search", () => modSelectOverlay.SearchTerm = string.Empty);
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
AddAssert("mod select hidden", () => modSelectOverlay.State.Value == Visibility.Hidden);
|
||||
AddAssert("mod select still visible", () => modSelectOverlay.State.Value == Visibility.Visible);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -793,7 +793,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
AddStep("open customisation area", () => modSelectOverlay.CustomisationButton!.TriggerClick());
|
||||
AddStep("reset half time speed to default", () => modSelectOverlay.ChildrenOfType<ModSettingsArea>().Single()
|
||||
.ChildrenOfType<RestoreDefaultValueButton<double>>().Single().TriggerClick());
|
||||
.ChildrenOfType<RevertToDefaultButton<double>>().Single().TriggerClick());
|
||||
AddUntilStep("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType<DifficultyMultiplierDisplay>().Single().Current.Value, () => Is.EqualTo(0.7));
|
||||
}
|
||||
|
||||
|
@ -431,8 +431,9 @@ namespace osu.Game.Beatmaps
|
||||
beatmapInfo.Status = BeatmapOnlineStatus.LocallyModified;
|
||||
beatmapInfo.ResetOnlineInfo();
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
Realm.Write(r =>
|
||||
{
|
||||
using var stream = new MemoryStream();
|
||||
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
||||
new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw);
|
||||
|
||||
@ -458,23 +459,20 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
updateHashAndMarkDirty(setInfo);
|
||||
|
||||
Realm.Write(r =>
|
||||
{
|
||||
var liveBeatmapSet = r.Find<BeatmapSetInfo>(setInfo.ID)!;
|
||||
var liveBeatmapSet = r.Find<BeatmapSetInfo>(setInfo.ID)!;
|
||||
|
||||
setInfo.CopyChangesToRealm(liveBeatmapSet);
|
||||
setInfo.CopyChangesToRealm(liveBeatmapSet);
|
||||
|
||||
if (transferCollections)
|
||||
beatmapInfo.TransferCollectionReferences(r, oldMd5Hash);
|
||||
if (transferCollections)
|
||||
beatmapInfo.TransferCollectionReferences(r, oldMd5Hash);
|
||||
|
||||
liveBeatmapSet.Beatmaps.Single(b => b.ID == beatmapInfo.ID)
|
||||
.UpdateLocalScores(r);
|
||||
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);
|
||||
});
|
||||
}
|
||||
// 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);
|
||||
});
|
||||
|
||||
Debug.Assert(beatmapInfo.BeatmapSet != null);
|
||||
|
||||
|
@ -26,6 +26,9 @@ namespace osu.Game.Database
|
||||
if (score.IsLegacyScore)
|
||||
return false;
|
||||
|
||||
if (score.TotalScoreVersion > 30000002)
|
||||
return false;
|
||||
|
||||
// Recalculate the old-style standardised score to see if this was an old lazer score.
|
||||
bool oldScoreMatchesExpectations = GetOldStandardised(score) == score.TotalScore;
|
||||
// Some older scores don't have correct statistics populated, so let's give them benefit of doubt.
|
||||
|
@ -43,6 +43,9 @@ namespace osu.Game.Graphics
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private Clipboard clipboard { get; set; }
|
||||
|
||||
private Storage storage;
|
||||
|
||||
[Resolved]
|
||||
@ -116,7 +119,7 @@ namespace osu.Game.Graphics
|
||||
|
||||
using (var image = await host.TakeScreenshotAsync().ConfigureAwait(false))
|
||||
{
|
||||
host.GetClipboard()?.SetImage(image);
|
||||
clipboard.SetImage(image);
|
||||
|
||||
(string filename, var stream) = getWritableStream();
|
||||
|
||||
|
@ -27,6 +27,9 @@ namespace osu.Game.Graphics.UserInterface
|
||||
[Resolved]
|
||||
private GameHost host { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private Clipboard clipboard { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private OnScreenDisplay? onScreenDisplay { get; set; }
|
||||
|
||||
@ -92,8 +95,11 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
private void copyUrl()
|
||||
{
|
||||
host.GetClipboard()?.SetText(Link);
|
||||
onScreenDisplay?.Display(new CopyUrlToast());
|
||||
if (Link != null)
|
||||
{
|
||||
clipboard.SetText(Link);
|
||||
onScreenDisplay?.Display(new CopyUrlToast());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
return base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat)
|
||||
return false;
|
||||
|
@ -18,6 +18,9 @@ namespace osu.Game.Online.Chat
|
||||
[Resolved]
|
||||
private GameHost host { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private Clipboard clipboard { get; set; } = null!;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IDialogOverlay? dialogOverlay { get; set; }
|
||||
|
||||
@ -32,7 +35,7 @@ namespace osu.Game.Online.Chat
|
||||
public void OpenUrlExternally(string url, bool bypassWarning = false)
|
||||
{
|
||||
if (!bypassWarning && externalLinkWarning.Value && dialogOverlay != null)
|
||||
dialogOverlay.Push(new ExternalLinkDialog(url, () => host.OpenUrlExternally(url), () => host.GetClipboard()?.SetText(url)));
|
||||
dialogOverlay.Push(new ExternalLinkDialog(url, () => host.OpenUrlExternally(url), () => clipboard.SetText(url)));
|
||||
else
|
||||
host.OpenUrlExternally(url);
|
||||
}
|
||||
|
@ -279,11 +279,8 @@ namespace osu.Game.Online.Chat
|
||||
// handle channels
|
||||
handleMatches(channel_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}chan/{{0}}", result, startIndex, LinkAction.OpenChannel);
|
||||
|
||||
string empty = "";
|
||||
while (space-- > 0)
|
||||
empty += "\0";
|
||||
|
||||
handleMatches(emoji_regex, empty, "{0}", result, startIndex);
|
||||
// see: https://github.com/ppy/osu/pull/24190
|
||||
result.Text = Regex.Replace(result.Text, emoji_regex.ToString(), "[emoji]");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ namespace osu.Game.Overlays.Comments
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private GameHost host { get; set; } = null!;
|
||||
private Clipboard clipboard { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private OnScreenDisplay? onScreenDisplay { get; set; }
|
||||
@ -444,7 +444,7 @@ namespace osu.Game.Overlays.Comments
|
||||
|
||||
private void copyUrl()
|
||||
{
|
||||
host.GetClipboard()?.SetText($@"{api.APIEndpointUrl}/comments/{Comment.Id}");
|
||||
clipboard.SetText($@"{api.APIEndpointUrl}/comments/{Comment.Id}");
|
||||
onScreenDisplay?.Display(new CopyUrlToast());
|
||||
}
|
||||
|
||||
|
@ -229,7 +229,7 @@ namespace osu.Game.Overlays.Dialog
|
||||
{
|
||||
// Buttons are regularly added in BDL or LoadComplete, so let's schedule to ensure
|
||||
// they are ready to be pressed.
|
||||
Schedule(() => Buttons.OfType<T>().First().TriggerClick());
|
||||
Scheduler.AddOnce(() => Buttons.OfType<T>().FirstOrDefault()?.TriggerClick());
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
|
@ -8,10 +8,12 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@ -95,6 +97,18 @@ namespace osu.Game.Overlays.Mods
|
||||
}, true);
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Select:
|
||||
createButton.TriggerClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnPressed(e);
|
||||
}
|
||||
|
||||
private void createPreset()
|
||||
{
|
||||
realm.Write(r => r.Add(new ModPreset
|
||||
|
@ -8,11 +8,13 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osuTK;
|
||||
@ -130,6 +132,25 @@ namespace osu.Game.Overlays.Mods
|
||||
}, true);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(nameTextBox));
|
||||
}
|
||||
|
||||
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.Select:
|
||||
saveButton.TriggerClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnPressed(e);
|
||||
}
|
||||
|
||||
private void useCurrentMods()
|
||||
{
|
||||
saveableMods = selectedMods.Value.ToHashSet();
|
||||
@ -150,13 +171,6 @@ namespace osu.Game.Overlays.Mods
|
||||
return !saveableMods.SetEquals(selectedMods.Value);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(nameTextBox));
|
||||
}
|
||||
|
||||
private void save()
|
||||
{
|
||||
preset.PerformWrite(s =>
|
||||
|
@ -48,7 +48,8 @@ namespace osu.Game.Overlays.Mods
|
||||
/// Contrary to <see cref="OsuGameBase.AvailableMods"/> and <see cref="globalAvailableMods"/>, the <see cref="Mod"/> instances
|
||||
/// inside the <see cref="ModState"/> objects are owned solely by this <see cref="ModSelectOverlay"/> instance.
|
||||
/// </remarks>
|
||||
public Bindable<Dictionary<ModType, IReadOnlyList<ModState>>> AvailableMods { get; } = new Bindable<Dictionary<ModType, IReadOnlyList<ModState>>>(new Dictionary<ModType, IReadOnlyList<ModState>>());
|
||||
public Bindable<Dictionary<ModType, IReadOnlyList<ModState>>> AvailableMods { get; } =
|
||||
new Bindable<Dictionary<ModType, IReadOnlyList<ModState>>>(new Dictionary<ModType, IReadOnlyList<ModState>>());
|
||||
|
||||
private Func<Mod, bool> isValidMod = _ => true;
|
||||
|
||||
@ -636,12 +637,9 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
case GlobalAction.Select:
|
||||
{
|
||||
// Pressing select should select first filtered mod or completely hide the overlay in one shot if search term is empty.
|
||||
// Pressing select should select first filtered mod if a search is in progress.
|
||||
if (string.IsNullOrEmpty(SearchTerm))
|
||||
{
|
||||
hideOverlay(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
ModState? firstMod = columnFlow.Columns.OfType<ModColumn>().FirstOrDefault(m => m.IsPresent)?.AvailableMods.FirstOrDefault(x => x.Visible);
|
||||
|
||||
|
@ -1,28 +1,25 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osuTK;
|
||||
using osu.Game.Localisation;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays
|
||||
{
|
||||
public partial class RestoreDefaultValueButton<T> : OsuClickableContainer, IHasCurrentValue<T>
|
||||
public partial class RevertToDefaultButton<T> : OsuClickableContainer, IHasCurrentValue<T>
|
||||
{
|
||||
public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks;
|
||||
|
||||
@ -31,11 +28,20 @@ namespace osu.Game.Overlays
|
||||
|
||||
// this is intentionally not using BindableWithCurrent, as it can use the wrong IsDefault implementation when passed a BindableNumber.
|
||||
// using GetBoundCopy() ensures that the received bindable is of the exact same type as the source bindable and uses the proper IsDefault implementation.
|
||||
private Bindable<T> current;
|
||||
private Bindable<T>? current;
|
||||
|
||||
private SpriteIcon icon = null!;
|
||||
private Circle circle = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider? colourProvider { get; set; }
|
||||
|
||||
public Bindable<T> Current
|
||||
{
|
||||
get => current;
|
||||
get => current.AsNonNull();
|
||||
set
|
||||
{
|
||||
current?.UnbindAll();
|
||||
@ -50,43 +56,36 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
private const float size = 4;
|
||||
|
||||
private CircularContainer circle = null!;
|
||||
private Box background = null!;
|
||||
|
||||
public RestoreDefaultValueButton()
|
||||
public RevertToDefaultButton()
|
||||
: base(HoverSampleSet.Button)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colour)
|
||||
private void load()
|
||||
{
|
||||
// size intentionally much larger than actual drawn content, so that the button is easier to click.
|
||||
Size = new Vector2(3 * size);
|
||||
Size = new Vector2(14);
|
||||
|
||||
Add(circle = new CircularContainer
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(size),
|
||||
Masking = true,
|
||||
Child = background = new Box
|
||||
circle = new Circle
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colour.Lime1
|
||||
},
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.Solid.Undo,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(8),
|
||||
}
|
||||
});
|
||||
|
||||
Alpha = 0f;
|
||||
|
||||
Action += () =>
|
||||
{
|
||||
if (!current.Disabled)
|
||||
if (current?.Disabled == false)
|
||||
current.SetDefault();
|
||||
};
|
||||
}
|
||||
@ -120,28 +119,25 @@ namespace osu.Game.Overlays
|
||||
if (current == null)
|
||||
return;
|
||||
|
||||
Enabled.Value = !Current.Disabled;
|
||||
Enabled.Value = !current.Disabled;
|
||||
|
||||
if (!Current.Disabled)
|
||||
this.FadeTo(current.Disabled ? 0.2f : (current.IsDefault ? 0 : 1), fade_duration, Easing.OutQuint);
|
||||
|
||||
if (IsHovered && Enabled.Value)
|
||||
{
|
||||
this.FadeTo(Current.IsDefault ? 0 : 1, fade_duration, Easing.OutQuint);
|
||||
background.FadeColour(IsHovered ? colours.Lime0 : colours.Lime1, fade_duration, Easing.OutQuint);
|
||||
circle.TweenEdgeEffectTo(new EdgeEffectParameters
|
||||
{
|
||||
Colour = (IsHovered ? colours.Lime1 : colours.Lime3).Opacity(0.4f),
|
||||
Radius = IsHovered ? 8 : 4,
|
||||
Type = EdgeEffectType.Glow
|
||||
}, fade_duration, Easing.OutQuint);
|
||||
icon.RotateTo(-40, 500, Easing.OutQuint);
|
||||
|
||||
icon.FadeColour(colourProvider?.Light1 ?? colours.YellowLight, 300, Easing.OutQuint);
|
||||
circle.FadeColour(colourProvider?.Background2 ?? colours.Gray6, 300, Easing.OutQuint);
|
||||
this.ScaleTo(1.2f, 300, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
background.FadeColour(colours.Lime3, fade_duration, Easing.OutQuint);
|
||||
circle.TweenEdgeEffectTo(new EdgeEffectParameters
|
||||
{
|
||||
Colour = colours.Lime3.Opacity(0.1f),
|
||||
Radius = 2,
|
||||
Type = EdgeEffectType.Glow
|
||||
}, fade_duration, Easing.OutQuint);
|
||||
icon.RotateTo(0, 100, Easing.OutQuint);
|
||||
|
||||
icon.FadeColour(colourProvider?.Colour0 ?? colours.Yellow, 100, Easing.OutQuint);
|
||||
circle.FadeColour(colourProvider?.Background3 ?? colours.Gray3, 100, Easing.OutQuint);
|
||||
this.ScaleTo(1f, 100, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
@ -103,7 +103,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = SettingsPanel.CONTENT_MARGINS,
|
||||
Child = new RestoreDefaultValueButton<bool>
|
||||
Child = new RevertToDefaultButton<bool>
|
||||
{
|
||||
Current = isDefault,
|
||||
Action = RestoreDefaults,
|
||||
|
@ -217,7 +217,7 @@ namespace osu.Game.Overlays.Settings
|
||||
// intentionally done before LoadComplete to avoid overhead.
|
||||
if (ShowsDefaultIndicator)
|
||||
{
|
||||
defaultValueIndicatorContainer.Add(new RestoreDefaultValueButton<T>
|
||||
defaultValueIndicatorContainer.Add(new RevertToDefaultButton<T>
|
||||
{
|
||||
Current = controlWithCurrent.Current,
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace osu.Game.Rulesets.Difficulty
|
||||
{
|
||||
/// <summary>
|
||||
@ -19,5 +17,16 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
/// Performance of a perfect play for comparison.
|
||||
/// </summary>
|
||||
public PerformanceAttributes PerfectPerformance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new performance breakdown.
|
||||
/// </summary>
|
||||
/// <param name="performance">Actual gameplay performance.</param>
|
||||
/// <param name="perfectPerformance">Performance of a perfect play for comparison.</param>
|
||||
public PerformanceBreakdown(PerformanceAttributes performance, PerformanceAttributes perfectPerformance)
|
||||
{
|
||||
Performance = performance;
|
||||
PerfectPerformance = perfectPerformance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Difficulty
|
||||
getPerfectPerformance(score, cancellationToken)
|
||||
).ConfigureAwait(false);
|
||||
|
||||
return new PerformanceBreakdown { Performance = performanceArray[0], PerfectPerformance = performanceArray[1] };
|
||||
return new PerformanceBreakdown(performanceArray[0] ?? new PerformanceAttributes(), performanceArray[1] ?? new PerformanceAttributes());
|
||||
}
|
||||
|
||||
[ItemCanBeNull]
|
||||
|
@ -11,7 +11,7 @@ using osu.Game.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public abstract class ModAutoplay : Mod, IApplicableFailOverride, ICreateReplayData
|
||||
public abstract class ModAutoplay : Mod, ICreateReplayData
|
||||
{
|
||||
public override string Name => "Autoplay";
|
||||
public override string Acronym => "AT";
|
||||
@ -20,15 +20,11 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override LocalisableString Description => "Watch a perfect automated play through the song.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public bool PerformFail() => false;
|
||||
|
||||
public bool RestartOnFail => false;
|
||||
|
||||
public override bool UserPlayable => false;
|
||||
public override bool ValidForMultiplayer => false;
|
||||
public override bool ValidForMultiplayerAsFreeMod => false;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModCinema), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAdaptiveSpeed) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModCinema), typeof(ModRelax), typeof(ModAdaptiveSpeed) };
|
||||
|
||||
public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
|
||||
|
||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public abstract class ModFailCondition : Mod, IApplicableToHealthProcessor, IApplicableFailOverride
|
||||
{
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax) };
|
||||
|
||||
[SettingSource("Restart on fail", "Automatically restarts when failed.")]
|
||||
public BindableBool Restart { get; } = new BindableBool();
|
||||
|
@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Mods
|
||||
public override ModType Type => ModType.DifficultyReduction;
|
||||
public override LocalisableString Description => "You can't fail, no matter what.";
|
||||
public override double ScoreMultiplier => 0.5;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModAutoplay) };
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition) };
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,13 @@ namespace osu.Game.Scoring.Legacy
|
||||
|
||||
scoreInfo.IsLegacyScore = version < LegacyScoreEncoder.FIRST_LAZER_VERSION;
|
||||
|
||||
// TotalScoreVersion gets initialised to LATEST_VERSION.
|
||||
// In the case where the incoming score has either an osu!stable or old lazer version, we need
|
||||
// to mark it with the correct version increment to trigger reprocessing to new standardised scoring.
|
||||
//
|
||||
// See StandardisedScoreMigrationTools.ShouldMigrateToNewStandardised().
|
||||
scoreInfo.TotalScoreVersion = version < 30000002 ? 30000001 : LegacyScoreEncoder.LATEST_VERSION;
|
||||
|
||||
string beatmapHash = sr.ReadString();
|
||||
|
||||
workingBeatmap = GetBeatmap(beatmapHash);
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Screens.Edit.Compose
|
||||
public partial class ComposeScreen : EditorScreenWithTimeline, IGameplaySettings
|
||||
{
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
private Clipboard hostClipboard { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private EditorClock clock { get; set; }
|
||||
@ -116,7 +116,7 @@ namespace osu.Game.Screens.Edit.Compose
|
||||
// regardless of whether anything was even selected at all.
|
||||
// UX-wise this is generally strange and unexpected, but make it work anyways to preserve muscle memory.
|
||||
// note that this means that `getTimestamp()` must handle no-selection case, too.
|
||||
host.GetClipboard()?.SetText(getTimestamp());
|
||||
hostClipboard.SetText(getTimestamp());
|
||||
|
||||
if (CanCopy.Value)
|
||||
clipboard.Value = new ClipboardContent(EditorBeatmap).Serialize();
|
||||
|
@ -23,6 +23,7 @@ using osu.Framework.Screens;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
@ -76,6 +77,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
protected OnlinePlayScreen ParentScreen { get; private set; }
|
||||
|
||||
@ -284,6 +288,8 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
[Resolved(canBeNull: true)]
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
protected virtual bool IsConnected => api.State.Value == APIState.Online;
|
||||
|
||||
public override bool OnBackButton()
|
||||
{
|
||||
if (Room.RoomID.Value == null)
|
||||
@ -356,7 +362,12 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
if (ExitConfirmed)
|
||||
return true;
|
||||
|
||||
if (dialogOverlay == null || Room.RoomID.Value != null || Room.Playlist.Count == 0)
|
||||
if (!IsConnected)
|
||||
return true;
|
||||
|
||||
bool hasUnsavedChanges = Room.RoomID.Value == null && Room.Playlist.Count > 0;
|
||||
|
||||
if (dialogOverlay == null || !hasUnsavedChanges)
|
||||
return true;
|
||||
|
||||
// if the dialog is already displayed, block exiting until the user explicitly makes a decision.
|
||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
// To work around this, temporarily remove the room and trigger an immediate listing poll.
|
||||
if (e.Last is MultiplayerMatchSubScreen match)
|
||||
{
|
||||
RoomManager.RemoveRoom(match.Room);
|
||||
RoomManager?.RemoveRoom(match.Room);
|
||||
ListingPollingComponent.PollImmediately();
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
handleRoomLost();
|
||||
}
|
||||
|
||||
protected override bool IsConnected => base.IsConnected && client.IsConnected.Value;
|
||||
|
||||
protected override Drawable CreateMainContent() => new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -250,13 +252,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
public override bool OnExiting(ScreenExitEvent e)
|
||||
{
|
||||
// the room may not be left immediately after a disconnection due to async flow,
|
||||
// so checking the IsConnected status is also required.
|
||||
if (client.Room == null || !client.IsConnected.Value)
|
||||
{
|
||||
// room has not been created yet; exit immediately.
|
||||
// room has not been created yet or we're offline; exit immediately.
|
||||
if (client.Room == null || !IsConnected)
|
||||
return base.OnExiting(e);
|
||||
}
|
||||
|
||||
if (!exitConfirmed && dialogOverlay != null)
|
||||
{
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
@ -15,8 +13,8 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
|
||||
public virtual string ShortTitle => Title;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
protected IRoomManager RoomManager { get; private set; }
|
||||
[Resolved]
|
||||
protected IRoomManager? RoomManager { get; private set; }
|
||||
|
||||
protected OnlinePlaySubScreen()
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Input.Bindings;
|
||||
@ -30,7 +31,7 @@ namespace osu.Game.Screens.Play
|
||||
// Disallow replays from failing. (see https://github.com/ppy/osu/issues/6108)
|
||||
protected override bool CheckModsAllowFailure()
|
||||
{
|
||||
if (!replayIsFailedScore)
|
||||
if (!replayIsFailedScore && !GameplayState.Mods.OfType<ModAutoplay>().Any())
|
||||
return false;
|
||||
|
||||
return base.CheckModsAllowFailure();
|
||||
|
@ -5,9 +5,11 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.EnumExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Layout;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -113,94 +115,92 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
}
|
||||
}
|
||||
|
||||
if (barDrawables != null)
|
||||
{
|
||||
for (int i = 0; i < barDrawables.Length; i++)
|
||||
{
|
||||
barDrawables[i].UpdateOffset(bins[i].Sum(b => b.Value));
|
||||
}
|
||||
}
|
||||
if (barDrawables == null)
|
||||
createBarDrawables();
|
||||
else
|
||||
{
|
||||
int maxCount = bins.Max(b => b.Values.Sum());
|
||||
barDrawables = bins.Select((bin, i) => new Bar(bins[i], maxCount, i == timing_distribution_centre_bin_index)).ToArray();
|
||||
for (int i = 0; i < barDrawables.Length; i++)
|
||||
barDrawables[i].UpdateOffset(bins[i].Sum(b => b.Value));
|
||||
}
|
||||
}
|
||||
|
||||
Container axisFlow;
|
||||
private void createBarDrawables()
|
||||
{
|
||||
int maxCount = bins.Max(b => b.Values.Sum());
|
||||
barDrawables = bins.Select((_, i) => new Bar(bins[i], maxCount, i == timing_distribution_centre_bin_index)).ToArray();
|
||||
|
||||
const float axis_font_size = 12;
|
||||
Container axisFlow;
|
||||
|
||||
InternalChild = new GridContainer
|
||||
Padding = new MarginPadding { Horizontal = 5 };
|
||||
|
||||
InternalChild = new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[]
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.8f,
|
||||
Content = new[]
|
||||
new Drawable[]
|
||||
{
|
||||
new Drawable[]
|
||||
new GridContainer
|
||||
{
|
||||
new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[] { barDrawables }
|
||||
}
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
axisFlow = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = axis_font_size,
|
||||
}
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Content = new[] { barDrawables }
|
||||
}
|
||||
},
|
||||
RowDimensions = new[]
|
||||
new Drawable[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
}
|
||||
};
|
||||
axisFlow = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = StatisticItem.FONT_SIZE,
|
||||
}
|
||||
},
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
}
|
||||
};
|
||||
|
||||
// Our axis will contain one centre element + 5 points on each side, each with a value depending on the number of bins * bin size.
|
||||
double maxValue = timing_distribution_bins * binSize;
|
||||
double axisValueStep = maxValue / axis_points;
|
||||
// Our axis will contain one centre element + 5 points on each side, each with a value depending on the number of bins * bin size.
|
||||
double maxValue = timing_distribution_bins * binSize;
|
||||
double axisValueStep = maxValue / axis_points;
|
||||
|
||||
axisFlow.Add(new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "0",
|
||||
Font = OsuFont.GetFont(size: StatisticItem.FONT_SIZE, weight: FontWeight.SemiBold)
|
||||
});
|
||||
|
||||
for (int i = 1; i <= axis_points; i++)
|
||||
{
|
||||
double axisValue = i * axisValueStep;
|
||||
float position = (float)(axisValue / maxValue);
|
||||
float alpha = 1f - position * 0.8f;
|
||||
|
||||
axisFlow.Add(new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = "0",
|
||||
Font = OsuFont.GetFont(size: axis_font_size, weight: FontWeight.SemiBold)
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = -position / 2,
|
||||
Alpha = alpha,
|
||||
Text = axisValue.ToString("-0"),
|
||||
Font = OsuFont.GetFont(size: StatisticItem.FONT_SIZE, weight: FontWeight.SemiBold)
|
||||
});
|
||||
|
||||
for (int i = 1; i <= axis_points; i++)
|
||||
axisFlow.Add(new OsuSpriteText
|
||||
{
|
||||
double axisValue = i * axisValueStep;
|
||||
float position = (float)(axisValue / maxValue);
|
||||
float alpha = 1f - position * 0.8f;
|
||||
|
||||
axisFlow.Add(new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = -position / 2,
|
||||
Alpha = alpha,
|
||||
Text = axisValue.ToString("-0"),
|
||||
Font = OsuFont.GetFont(size: axis_font_size, weight: FontWeight.SemiBold)
|
||||
});
|
||||
|
||||
axisFlow.Add(new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = position / 2,
|
||||
Alpha = alpha,
|
||||
Text = axisValue.ToString("+0"),
|
||||
Font = OsuFont.GetFont(size: axis_font_size, weight: FontWeight.SemiBold)
|
||||
});
|
||||
}
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativePositionAxes = Axes.X,
|
||||
X = position / 2,
|
||||
Alpha = alpha,
|
||||
Text = axisValue.ToString("+0"),
|
||||
Font = OsuFont.GetFont(size: StatisticItem.FONT_SIZE, weight: FontWeight.SemiBold)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,13 +211,16 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
private readonly bool isCentre;
|
||||
private readonly float totalValue;
|
||||
|
||||
private float basalHeight;
|
||||
private const float minimum_height = 0.02f;
|
||||
|
||||
private float offsetAdjustment;
|
||||
|
||||
private Circle[] boxOriginals = null!;
|
||||
|
||||
private Circle? boxAdjustment;
|
||||
|
||||
private float? lastDrawHeight;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
@ -256,15 +259,17 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
else
|
||||
{
|
||||
// A bin with no value draws a grey dot instead.
|
||||
Circle dot = new Circle
|
||||
InternalChildren = boxOriginals = new[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Colour = isCentre ? Color4.White : Color4.Gray,
|
||||
Height = 0,
|
||||
new Circle
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Colour = isCentre ? Color4.White : Color4.Gray,
|
||||
Height = 0,
|
||||
}
|
||||
};
|
||||
InternalChildren = boxOriginals = new[] { dot };
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,31 +277,18 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (!values.Any())
|
||||
return;
|
||||
|
||||
updateBasalHeight();
|
||||
|
||||
foreach (var boxOriginal in boxOriginals)
|
||||
{
|
||||
boxOriginal.Y = 0;
|
||||
boxOriginal.Height = basalHeight;
|
||||
}
|
||||
|
||||
float offsetValue = 0;
|
||||
|
||||
for (int i = 0; i < values.Count; i++)
|
||||
{
|
||||
boxOriginals[i].MoveToY(offsetForValue(offsetValue) * BoundingBox.Height, duration, Easing.OutQuint);
|
||||
boxOriginals[i].ResizeHeightTo(heightForValue(values[i].Value), duration, Easing.OutQuint);
|
||||
offsetValue -= values[i].Value;
|
||||
}
|
||||
Scheduler.AddOnce(updateMetrics, true);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
|
||||
{
|
||||
base.Update();
|
||||
updateBasalHeight();
|
||||
if (invalidation.HasFlagFast(Invalidation.DrawSize))
|
||||
{
|
||||
if (lastDrawHeight != null && lastDrawHeight != DrawHeight)
|
||||
Scheduler.AddOnce(updateMetrics, false);
|
||||
}
|
||||
|
||||
return base.OnInvalidate(invalidation, source);
|
||||
}
|
||||
|
||||
public void UpdateOffset(float adjustment)
|
||||
@ -321,45 +313,32 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
}
|
||||
|
||||
offsetAdjustment = adjustment;
|
||||
drawAdjustmentBar();
|
||||
|
||||
Scheduler.AddOnce(updateMetrics, true);
|
||||
}
|
||||
|
||||
private void updateBasalHeight()
|
||||
{
|
||||
float newBasalHeight = DrawHeight > DrawWidth ? DrawWidth / DrawHeight : 1;
|
||||
|
||||
if (newBasalHeight == basalHeight)
|
||||
return;
|
||||
|
||||
basalHeight = newBasalHeight;
|
||||
foreach (var dot in boxOriginals)
|
||||
dot.Height = basalHeight;
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
private float offsetForValue(float value) => (1 - basalHeight) * value / maxValue;
|
||||
|
||||
private float heightForValue(float value) => MathF.Max(basalHeight + offsetForValue(value), 0);
|
||||
|
||||
private void draw()
|
||||
{
|
||||
resizeBars();
|
||||
|
||||
if (boxAdjustment != null)
|
||||
drawAdjustmentBar();
|
||||
}
|
||||
|
||||
private void resizeBars()
|
||||
private void updateMetrics(bool animate = true)
|
||||
{
|
||||
float offsetValue = 0;
|
||||
|
||||
for (int i = 0; i < values.Count; i++)
|
||||
for (int i = 0; i < boxOriginals.Length; i++)
|
||||
{
|
||||
boxOriginals[i].Y = offsetForValue(offsetValue) * DrawHeight;
|
||||
boxOriginals[i].Height = heightForValue(values[i].Value);
|
||||
offsetValue -= values[i].Value;
|
||||
int value = i < values.Count ? values[i].Value : 0;
|
||||
|
||||
var box = boxOriginals[i];
|
||||
|
||||
box.MoveToY(offsetForValue(offsetValue) * BoundingBox.Height, duration, Easing.OutQuint);
|
||||
box.ResizeHeightTo(heightForValue(value), duration, Easing.OutQuint);
|
||||
offsetValue -= value;
|
||||
}
|
||||
|
||||
if (boxAdjustment != null)
|
||||
drawAdjustmentBar();
|
||||
|
||||
if (!animate)
|
||||
FinishTransforms(true);
|
||||
|
||||
lastDrawHeight = DrawHeight;
|
||||
}
|
||||
|
||||
private void drawAdjustmentBar()
|
||||
@ -369,6 +348,10 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
boxAdjustment.ResizeHeightTo(heightForValue(offsetAdjustment), duration, Easing.OutQuint);
|
||||
boxAdjustment.FadeTo(!hasAdjustment ? 0 : 1, duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private float offsetForValue(float value) => (1 - minimum_height) * value / maxValue;
|
||||
|
||||
private float heightForValue(float value) => minimum_height + offsetForValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 18),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: StatisticItem.FONT_SIZE),
|
||||
Text = "Achieved PP",
|
||||
Colour = Color4Extensions.FromHex("#66FFCC")
|
||||
},
|
||||
@ -105,7 +105,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
{
|
||||
Origin = Anchor.CentreRight,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 18),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: StatisticItem.FONT_SIZE),
|
||||
Colour = Color4Extensions.FromHex("#66FFCC")
|
||||
}
|
||||
},
|
||||
@ -115,7 +115,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 18),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: StatisticItem.FONT_SIZE),
|
||||
Text = "Maximum",
|
||||
Colour = OsuColour.Gray(0.7f)
|
||||
},
|
||||
@ -123,7 +123,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: 18),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: StatisticItem.FONT_SIZE),
|
||||
Colour = OsuColour.Gray(0.7f)
|
||||
}
|
||||
}
|
||||
@ -208,7 +208,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
{
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Regular, size: StatisticItem.FONT_SIZE),
|
||||
Text = attribute.DisplayName,
|
||||
Colour = Colour4.White
|
||||
},
|
||||
@ -233,7 +233,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
{
|
||||
Origin = Anchor.CentreRight,
|
||||
Anchor = Anchor.CentreRight,
|
||||
Font = OsuFont.GetFont(weight: FontWeight.SemiBold),
|
||||
Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: StatisticItem.FONT_SIZE),
|
||||
Text = percentage.ToLocalisableString("0%"),
|
||||
Colour = Colour4.White
|
||||
}
|
||||
|
@ -44,13 +44,13 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
Text = Name,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.GetFont(size: 14)
|
||||
Font = OsuFont.GetFont(size: StatisticItem.FONT_SIZE)
|
||||
},
|
||||
value = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold)
|
||||
Font = OsuFont.GetFont(size: StatisticItem.FONT_SIZE, weight: FontWeight.Bold)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -98,12 +98,11 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
Direction = FillDirection.Vertical
|
||||
};
|
||||
|
||||
private partial class Spacer : CompositeDrawable
|
||||
public partial class Spacer : CompositeDrawable
|
||||
{
|
||||
public Spacer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Padding = new MarginPadding { Vertical = 4 };
|
||||
|
||||
InternalChild = new CircularContainer
|
||||
{
|
||||
|
@ -12,6 +12,11 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
/// </summary>
|
||||
public class StatisticItem
|
||||
{
|
||||
/// <summary>
|
||||
/// The recommended font size to use in statistic items to make sure they match others.
|
||||
/// </summary>
|
||||
public const float FONT_SIZE = 13;
|
||||
|
||||
/// <summary>
|
||||
/// The name of this item.
|
||||
/// </summary>
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
@ -15,42 +16,53 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
/// <summary>
|
||||
/// Wraps a <see cref="StatisticItem"/> to add a header and suitable layout for use in <see cref="ResultsScreen"/>.
|
||||
/// </summary>
|
||||
internal partial class StatisticContainer : CompositeDrawable
|
||||
internal partial class StatisticItemContainer : CompositeDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="StatisticContainer"/>.
|
||||
/// Creates a new <see cref="StatisticItemContainer"/>.
|
||||
/// </summary>
|
||||
/// <param name="item">The <see cref="StatisticItem"/> to display.</param>
|
||||
public StatisticContainer(StatisticItem item)
|
||||
public StatisticItemContainer(StatisticItem item)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
InternalChild = new GridContainer
|
||||
Padding = new MarginPadding(5);
|
||||
|
||||
InternalChild = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Content = new[]
|
||||
Masking = true,
|
||||
CornerRadius = 6,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new[]
|
||||
new Box
|
||||
{
|
||||
createHeader(item)
|
||||
Colour = ColourInfo.GradientVertical(
|
||||
OsuColour.Gray(0.25f),
|
||||
OsuColour.Gray(0.18f)
|
||||
),
|
||||
Alpha = 0.95f,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new Drawable[]
|
||||
new Container
|
||||
{
|
||||
new Container
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding(5),
|
||||
Children = new[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Top = 15 },
|
||||
Child = item.CreateContent()
|
||||
createHeader(item),
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding(10) { Top = 30 },
|
||||
Child = item.CreateContent()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -63,7 +75,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
return new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Height = 20,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5, 0),
|
||||
Children = new Drawable[]
|
||||
@ -81,7 +93,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Text = item.Name,
|
||||
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold),
|
||||
Font = OsuFont.GetFont(size: StatisticItem.FONT_SIZE, weight: FontWeight.SemiBold),
|
||||
}
|
||||
}
|
||||
};
|
@ -124,20 +124,23 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
}
|
||||
else
|
||||
{
|
||||
FillFlowContainer rows;
|
||||
FillFlowContainer flow;
|
||||
container = new OsuScrollContainer(Direction.Vertical)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Masking = false,
|
||||
ScrollbarOverlapsContent = false,
|
||||
Alpha = 0,
|
||||
Children = new[]
|
||||
{
|
||||
rows = new FillFlowContainer
|
||||
flow = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(30, 15)
|
||||
Spacing = new Vector2(30, 15),
|
||||
Direction = FillDirection.Full,
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -146,35 +149,22 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
|
||||
foreach (var item in statisticItems)
|
||||
{
|
||||
var columnContent = new List<Drawable>();
|
||||
|
||||
if (!hitEventsAvailable && item.RequiresHitEvents)
|
||||
{
|
||||
anyRequiredHitEvents = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
columnContent.Add(new StatisticContainer(item)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
|
||||
rows.Add(new GridContainer
|
||||
flow.Add(new StatisticItemContainer(item)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Content = new[] { columnContent.ToArray() },
|
||||
ColumnDimensions = new[] { new Dimension() },
|
||||
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }
|
||||
});
|
||||
}
|
||||
|
||||
if (anyRequiredHitEvents)
|
||||
{
|
||||
rows.Add(new FillFlowContainer
|
||||
flow.Add(new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
|
@ -7,7 +7,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Solo;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
{
|
||||
@ -18,7 +17,7 @@ namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
public Bindable<SoloStatisticsUpdate?> StatisticsUpdate { get; } = new Bindable<SoloStatisticsUpdate?>();
|
||||
|
||||
private LoadingLayer loadingLayer = null!;
|
||||
private FillFlowContainer content = null!;
|
||||
private GridContainer content = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
@ -33,21 +32,47 @@ namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
content = new FillFlowContainer
|
||||
content = new GridContainer
|
||||
{
|
||||
AlwaysPresent = true,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(10),
|
||||
Children = new Drawable[]
|
||||
ColumnDimensions = new[]
|
||||
{
|
||||
new GlobalRankChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new AccuracyChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new MaximumComboChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new RankedScoreChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new TotalScoreChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new PerformancePointsChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } }
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.Absolute, 30),
|
||||
new Dimension(),
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.Absolute, 10),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.Absolute, 10),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
new Drawable[]
|
||||
{
|
||||
new GlobalRankChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new SimpleStatisticTable.Spacer(),
|
||||
new PerformancePointsChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
},
|
||||
new Drawable[] { },
|
||||
new Drawable[]
|
||||
{
|
||||
new MaximumComboChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new SimpleStatisticTable.Spacer(),
|
||||
new AccuracyChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
},
|
||||
new Drawable[] { },
|
||||
new Drawable[]
|
||||
{
|
||||
new RankedScoreChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
new SimpleStatisticTable.Spacer(),
|
||||
new TotalScoreChangeRow { StatisticsUpdate = { BindTarget = StatisticsUpdate } },
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
@ -46,14 +47,15 @@ namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = Label,
|
||||
Font = OsuFont.Default.With(size: 18)
|
||||
Font = OsuFont.Default.With(size: StatisticItem.FONT_SIZE)
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
AutoSizeAxes = Axes.X,
|
||||
Height = StatisticItem.FONT_SIZE * 2,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
@ -65,17 +67,31 @@ namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
Spacing = new Vector2(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
changeIcon = new SpriteIcon
|
||||
new Container
|
||||
{
|
||||
Size = new Vector2(14),
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Size = new Vector2(18)
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Circle
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colours.Gray1
|
||||
},
|
||||
changeIcon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(10),
|
||||
},
|
||||
}
|
||||
},
|
||||
currentValueText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Font = OsuFont.Default.With(size: 18, weight: FontWeight.Bold)
|
||||
Font = OsuFont.Default.With(size: StatisticItem.FONT_SIZE, weight: FontWeight.Bold)
|
||||
},
|
||||
}
|
||||
},
|
||||
@ -83,7 +99,7 @@ namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Font = OsuFont.Default.With(weight: FontWeight.Bold)
|
||||
Font = OsuFont.Default.With(size: StatisticItem.FONT_SIZE, weight: FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,7 +139,7 @@ namespace osu.Game.Screens.Ranking.Statistics.User
|
||||
}
|
||||
else
|
||||
{
|
||||
comparisonColour = colours.Orange1;
|
||||
comparisonColour = colours.Gray4;
|
||||
icon = FontAwesome.Solid.Minus;
|
||||
}
|
||||
|
||||
|
@ -75,8 +75,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
if (changes?.HasCollectionChanges() == false)
|
||||
return;
|
||||
|
||||
ScoreInfo? topScore = sender.Detach().OrderByTotalScore().FirstOrDefault();
|
||||
|
||||
ScoreInfo? topScore = sender.MaxBy(info => (info.TotalScore, -info.Date.UtcDateTime.Ticks));
|
||||
updateable.Rank = topScore?.Rank;
|
||||
updateable.Alpha = topScore != null ? 1 : 0;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
@ -47,7 +47,7 @@ namespace osu.Game.Updater
|
||||
{
|
||||
Text = $"A newer release of osu! has been found ({version} → {latestTagName}).\n\n"
|
||||
+ "Check with your package manager / provider to bring osu! up-to-date!",
|
||||
Icon = FontAwesome.Solid.Upload,
|
||||
Icon = FontAwesome.Solid.Download,
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
@ -54,7 +54,7 @@ namespace osu.Game.Updater
|
||||
{
|
||||
Text = $"A newer release of osu! has been found ({version} → {latestTagName}).\n\n"
|
||||
+ "Click here to download the new version, which can be installed over the top of your existing installation",
|
||||
Icon = FontAwesome.Solid.Upload,
|
||||
Icon = FontAwesome.Solid.Download,
|
||||
Activated = () =>
|
||||
{
|
||||
host.OpenUrlExternally(getBestUrl(latest));
|
||||
|
@ -134,7 +134,7 @@ namespace osu.Game.Updater
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Icon = FontAwesome.Solid.Upload,
|
||||
Icon = FontAwesome.Solid.Download,
|
||||
Size = new Vector2(34),
|
||||
Colour = OsuColour.Gray(0.2f),
|
||||
Depth = float.MaxValue,
|
||||
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="11.1.2" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.710.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.716.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.719.0" />
|
||||
<PackageReference Include="Sentry" Version="3.28.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
|
@ -16,6 +16,6 @@
|
||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.710.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.716.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
Loading…
Reference in New Issue
Block a user