Merge remote-tracking branch 'ppy-osu/master' into locmain

# Conflicts:
#	osu.Game.Rulesets.Mania/ManiaRuleset.cs
#	osu.Game/osu.Game.csproj
This commit is contained in:
LA
2026-02-11 21:10:48 +08:00
21 changed files with 390 additions and 94 deletions

View File

@@ -185,15 +185,20 @@ namespace osu.Game.Rulesets.Catch
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetCatch };
protected override IEnumerable<HitResult> GetValidHitResults()
public override IEnumerable<HitResult> GetValidHitResults()
{
return new[]
{
HitResult.Great,
HitResult.Miss,
HitResult.LargeTickHit,
HitResult.LargeTickMiss,
HitResult.SmallTickHit,
HitResult.SmallTickMiss,
HitResult.LargeBonus,
HitResult.IgnoreHit,
HitResult.IgnoreMiss,
};
}

View File

@@ -14,7 +14,6 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.LAsEzExtensions.Background;
using osu.Game.LAsEzExtensions.Statistics;
using osu.Game.Localisation;
using osu.Game.Overlays.Settings;
@@ -436,7 +435,7 @@ namespace osu.Game.Rulesets.Mania
return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast<int>().OrderDescending().First(v => variant >= v);
}
protected override IEnumerable<HitResult> GetValidHitResults()
public override IEnumerable<HitResult> GetValidHitResults()
{
return new[]
{
@@ -445,13 +444,11 @@ namespace osu.Game.Rulesets.Mania
HitResult.Good,
HitResult.Ok,
HitResult.Meh,
HitResult.IgnoreHit,
HitResult.IgnoreMiss,
HitResult.ComboBreak,
HitResult.Miss,
HitResult.Poor,
// HitResult.SmallBonus is used for awarding perfect bonus score but is not included here as
// it would be a bit redundant to show this to the user.
HitResult.IgnoreHit,
HitResult.ComboBreak,
HitResult.IgnoreMiss,
};
}

View File

@@ -286,19 +286,24 @@ namespace osu.Game.Rulesets.Osu
public override IRulesetConfigManager CreateConfig(SettingsStore? settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
protected override IEnumerable<HitResult> GetValidHitResults()
public override IEnumerable<HitResult> GetValidHitResults()
{
return new[]
{
HitResult.Great,
HitResult.Ok,
HitResult.Meh,
HitResult.Miss,
HitResult.LargeTickHit,
HitResult.LargeTickMiss,
HitResult.SmallTickHit,
HitResult.SmallTickMiss,
HitResult.SliderTailHit,
HitResult.SmallBonus,
HitResult.LargeBonus,
HitResult.IgnoreHit,
HitResult.IgnoreMiss,
};
}

View File

@@ -231,15 +231,18 @@ namespace osu.Game.Rulesets.Taiko
public override RulesetSettingsSubsection CreateSettings() => new TaikoSettingsSubsection(this);
protected override IEnumerable<HitResult> GetValidHitResults()
public override IEnumerable<HitResult> GetValidHitResults()
{
return new[]
{
HitResult.Great,
HitResult.Ok,
HitResult.Miss,
HitResult.SmallBonus,
HitResult.LargeBonus,
HitResult.IgnoreHit,
HitResult.IgnoreMiss,
};
}

View File

@@ -7,6 +7,7 @@ using System.Linq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
@@ -116,6 +117,69 @@ namespace osu.Game.Tests.Database
});
}
[Test]
public void TestFakedRulesetIdIsDetected()
{
RunTestWithRealm((realm, storage) =>
{
LoadTestRuleset.HasImplementations = true;
LoadTestRuleset.Version = Ruleset.CURRENT_RULESET_API_VERSION;
var ruleset = new LoadTestRuleset();
string rulesetShortName = ruleset.RulesetInfo.ShortName;
realm.Write(r => r.Add(new RulesetInfo(rulesetShortName, ruleset.RulesetInfo.Name, ruleset.RulesetInfo.InstantiationInfo, 0)
{
Available = true,
}));
Assert.That(realm.Run(r => r.Find<RulesetInfo>(rulesetShortName)!.Available), Is.True);
// Availability is updated on construction of a RealmRulesetStore
using var _ = new RealmRulesetStore(realm, storage);
Assert.That(realm.Run(r => r.Find<RulesetInfo>(rulesetShortName)!.Available), Is.False);
});
}
[Test]
public void TestMultipleRulesetWithSameOnlineIdsAreDetected()
{
RunTestWithRealm((realm, storage) =>
{
LoadTestRuleset.HasImplementations = true;
LoadTestRuleset.Version = Ruleset.CURRENT_RULESET_API_VERSION;
LoadTestRuleset.OnlineID = 2;
var first = new LoadTestRuleset();
var second = new CatchRuleset();
realm.Write(r => r.Add(new RulesetInfo(first.ShortName, first.RulesetInfo.Name, first.RulesetInfo.InstantiationInfo, first.RulesetInfo.OnlineID)
{
Available = true,
}));
realm.Write(r => r.Add(new RulesetInfo(second.ShortName, second.RulesetInfo.Name, second.RulesetInfo.InstantiationInfo, second.RulesetInfo.OnlineID)
{
Available = true,
}));
Assert.That(realm.Run(r => r.Find<RulesetInfo>(first.ShortName)!.Available), Is.True);
Assert.That(realm.Run(r => r.Find<RulesetInfo>(second.ShortName)!.Available), Is.True);
// Availability is updated on construction of a RealmRulesetStore
using var _ = new RealmRulesetStore(realm, storage);
Assert.That(realm.Run(r => r.Find<RulesetInfo>(first.ShortName)!.Available), Is.False);
Assert.That(realm.Run(r => r.Find<RulesetInfo>(second.ShortName)!.Available), Is.False);
realm.Write(r => r.Remove(r.Find<RulesetInfo>(first.ShortName)!));
using var __ = new RealmRulesetStore(realm, storage);
Assert.That(realm.Run(r => r.Find<RulesetInfo>(second.ShortName)!.Available), Is.True);
});
}
private class LoadTestRuleset : Ruleset
{
public override string RulesetAPIVersionSupported => Version;
@@ -124,6 +188,13 @@ namespace osu.Game.Tests.Database
public static string Version { get; set; } = CURRENT_RULESET_API_VERSION;
public static int OnlineID { get; set; } = -1;
public LoadTestRuleset()
{
RulesetInfo.OnlineID = OnlineID;
}
public override IEnumerable<Mod> GetModsFor(ModType type)
{
if (!HasImplementations)

View File

@@ -526,7 +526,7 @@ namespace osu.Game.Tests.Rulesets.Scoring
// ReSharper disable once MemberHidesStaticFromOuterClass
private class TestRuleset : Ruleset
{
protected override IEnumerable<HitResult> GetValidHitResults() => new[] { HitResult.Great };
public override IEnumerable<HitResult> GetValidHitResults() => new[] { HitResult.Great };
public override IEnumerable<Mod> GetModsFor(ModType type) => throw new NotImplementedException();

View File

@@ -0,0 +1,61 @@
// 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.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Graphics.Cursor;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Users.Drawables;
using osuTK;
namespace osu.Game.Tests.Visual.Online
{
public partial class TestSceneClickableTeamFlag : OsuManualInputManagerTestScene
{
[SetUpSteps]
public void SetUp()
{
AddStep("create flags", () =>
{
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Spacing = new Vector2(10f),
Children = new[]
{
new ClickableTeamFlag(
new APITeam
{
Id = 1,
Name = "Collective Wangs",
ShortName = "WANG",
}, showTooltipOnHover: false) { Width = 300, Height = 150 },
new ClickableTeamFlag(
new APITeam
{
Id = 2,
Name = "mom?",
ShortName = "MOM",
FlagUrl = "https://assets.ppy.sh/teams/flag/1/b46fb10dbfd8a35dc50e6c00296c0dc6172dffc3ed3d3a4b379277ba498399fe.png",
}, showTooltipOnHover: true) { Width = 300, Height = 150 },
},
};
});
}
[Test]
public void TestHover()
{
AddStep("hover flag with no tooltip", () => InputManager.MoveMouseTo(this.ChildrenOfType<ClickableTeamFlag>().ElementAt(0)));
AddWaitStep("wait", 3);
AddAssert("tooltip is not visible", () => this.ChildrenOfType<OsuTooltipContainer.OsuTooltip>().FirstOrDefault()?.State.Value, () => Is.EqualTo(Visibility.Hidden));
AddStep("hover flag with tooltip", () => InputManager.MoveMouseTo(this.ChildrenOfType<ClickableTeamFlag>().ElementAt(1)));
AddUntilStep("wait for tooltip to show", () => this.ChildrenOfType<OsuTooltipContainer.OsuTooltip>().FirstOrDefault()?.State.Value, () => Is.EqualTo(Visibility.Visible));
}
}
}

View File

@@ -0,0 +1,43 @@
// 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.Game.Online.API.Requests.Responses;
using osu.Game.Users.Drawables;
namespace osu.Game.Tests.Visual.Online
{
[TestFixture]
public partial class TestSceneUpdateableTeamFlag : OsuTestScene
{
[Test]
public void TestHideOnNull()
{
UpdateableTeamFlag flag = null!;
AddStep("create flag with team", () => Child = flag = new UpdateableTeamFlag(createTeam(), hideOnNull: true) { Width = 300, Height = 150 });
AddAssert("flag is present", () => flag.IsPresent, () => Is.True);
AddStep("set team to null", () => flag.Team = null);
AddAssert("flag is not present", () => flag.IsPresent, () => Is.False);
}
[Test]
public void DontHideOnNull()
{
UpdateableTeamFlag flag = null!;
AddStep("create flag with team", () => Child = flag = new UpdateableTeamFlag(createTeam(), hideOnNull: false) { Width = 300, Height = 150 });
AddAssert("flag is present", () => flag.IsPresent, () => Is.True);
AddStep("set team to null", () => flag.Team = null);
AddAssert("flag is present", () => flag.IsPresent, () => Is.True);
}
private static APITeam createTeam() => new APITeam
{
Id = 2,
Name = "mom?",
ShortName = "MOM",
FlagUrl = @"https://assets.ppy.sh/teams/flag/1/b46fb10dbfd8a35dc50e6c00296c0dc6172dffc3ed3d3a4b379277ba498399fe.png",
};
}
}

View File

@@ -51,7 +51,7 @@ namespace osu.Game.Database
var beatmap = new Beatmap();
HitResult maxRulesetJudgement = ruleset.GetHitResults().First().result;
HitResult maxRulesetJudgement = ruleset.GetHitResultsForDisplay().First().result;
// This is a list of all results, ordered from best to worst.
// We are constructing a "best possible" score from the statistics provided because it's the best we can do.

View File

@@ -18,6 +18,6 @@ namespace osu.Game.Online.API.Requests.Responses
public string ShortName { get; set; } = string.Empty;
[JsonProperty(@"flag_url")]
public string FlagUrl = string.Empty;
public string? FlagUrl = string.Empty;
}
}

View File

@@ -106,7 +106,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
var ruleset = scores.First().Ruleset.CreateInstance();
foreach (var resultGroup in ruleset.GetHitResults().GroupBy(r => r.displayName))
foreach (var resultGroup in ruleset.GetHitResultsForDisplay().GroupBy(r => r.displayName))
{
if (!resultGroup.Any(r => allScoreStatistics.Contains(r.result)))
continue;

View File

@@ -93,6 +93,12 @@ namespace osu.Game.Rulesets
$"Ruleset API version is too old (was {instance.RulesetAPIVersionSupported}, expected {Ruleset.CURRENT_RULESET_API_VERSION})");
}
if (r.OnlineID != instanceInfo.OnlineID)
throw new InvalidOperationException($@"Online ID mismatch for ruleset {r.ShortName}: database has {r.OnlineID}, constructed instance has {instanceInfo.OnlineID}");
if (r.OnlineID > 0 && rulesets.Any(otherRuleset => otherRuleset.ShortName != r.ShortName && otherRuleset.OnlineID == r.OnlineID))
throw new InvalidOperationException($@"Ruleset {r.ShortName} shares online ID {r.OnlineID} with another ruleset");
// If a ruleset isn't up-to-date with the API, it could cause a crash at an arbitrary point of execution.
// To eagerly handle cases of missing implementations, enumerate all types here and mark as non-available on throw.
resolvedType.Assembly.GetTypes();

View File

@@ -13,6 +13,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
using osu.Framework.IO.Stores;
using osu.Framework.Localisation;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Configuration;
@@ -343,13 +344,17 @@ namespace osu.Game.Rulesets
public virtual StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => Array.Empty<StatisticItem>();
/// <summary>
/// Get all valid <see cref="HitResult"/>s for this ruleset.
/// Generally used for results display purposes, where it can't be determined if zero-count means the user has not achieved any or the type is not used by this ruleset.
/// Get all <see cref="HitResult"/>s for this ruleset which are important enough to displayed to the end user.
/// Used for results display purposes, where it can't be determined if zero-count means the user has not achieved any or the type is not used by this ruleset.
/// </summary>
/// <remarks>
/// <see cref="HitResult.Miss"/> is implicitly included. Special types like <see cref="HitResult.IgnoreHit"/> are not returned by this method.
/// Values are returned as ordered by <see cref="OrderAttribute"/>.
/// </remarks>
/// <returns>
/// All valid <see cref="HitResult"/>s along with a display-friendly name.
/// All relevant <see cref="HitResult"/>s along with a display-friendly name.
/// </returns>
public IEnumerable<(HitResult result, LocalisableString displayName)> GetHitResults()
public IEnumerable<(HitResult result, LocalisableString displayName)> GetHitResultsForDisplay()
{
var validResults = GetValidHitResults();
@@ -362,6 +367,7 @@ namespace osu.Game.Rulesets
case HitResult.None:
case HitResult.IgnoreHit:
case HitResult.IgnoreMiss:
case HitResult.ComboBreak:
// display is handled as a completion count with corresponding "hit" type.
case HitResult.LargeTickMiss:
case HitResult.SmallTickMiss:
@@ -375,12 +381,10 @@ namespace osu.Game.Rulesets
/// <summary>
/// Get all valid <see cref="HitResult"/>s for this ruleset.
/// Generally used for results display purposes, where it can't be determined if zero-count means the user has not achieved any or the type is not used by this ruleset.
/// Used for strict validation purposes. The ruleset should return ALL applicable <see cref="HitResult"/> types here
/// (except <see cref="HitResult.None"/> and obsolete types).
/// </summary>
/// <remarks>
/// <see cref="HitResult.Miss"/> is implicitly included. Special types like <see cref="HitResult.IgnoreHit"/> are ignored even when specified.
/// </remarks>
protected virtual IEnumerable<HitResult> GetValidHitResults() => EnumExtensions.GetValuesInOrder<HitResult>();
public virtual IEnumerable<HitResult> GetValidHitResults() => EnumExtensions.GetValuesInOrder<HitResult>();
/// <summary>
/// Get a display friendly name for the specified result type.

View File

@@ -211,7 +211,7 @@ namespace osu.Game.Scoring.Legacy
var scoreProcessor = rulesetInstance.CreateScoreProcessor();
// Populate the maximum statistics.
HitResult maxBasicResult = rulesetInstance.GetHitResults()
HitResult maxBasicResult = rulesetInstance.GetHitResultsForDisplay()
.Select(h => h.result)
.Where(h => h.IsBasic()).MaxBy(scoreProcessor.GetBaseScoreForResult);

View File

@@ -402,7 +402,7 @@ namespace osu.Game.Scoring
public IEnumerable<HitResultDisplayStatistic> GetStatisticsForDisplay()
{
foreach (var r in Ruleset.CreateInstance().GetHitResults())
foreach (var r in Ruleset.CreateInstance().GetHitResultsForDisplay())
{
int value = Statistics.GetValueOrDefault(r.result);

View File

@@ -245,7 +245,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
userFlag.CountryCode = user?.CountryCode ?? default;
teamFlagContainer.Child = new UpdateableTeamFlag(user?.Team)
{
Size = new Vector2(40, 20)
Size = new Vector2(40, 20),
};
username.Text = user?.Username ?? string.Empty;

View File

@@ -32,7 +32,7 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter
{
// Due to weirdness in judgements, some results have the same name and should be aggregated for display purposes.
// There's only one case of this right now ("slider end").
foreach (var group in ruleset.Value.CreateInstance().GetHitResults().GroupBy(r => r.displayName))
foreach (var group in ruleset.Value.CreateInstance().GetHitResultsForDisplay().GroupBy(r => r.displayName))
{
var judgementCount = new JudgementCount
{

View File

@@ -0,0 +1,52 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Users.Drawables
{
public partial class ClickableTeamFlag : OsuClickableContainer
{
private readonly APITeam? team;
[Resolved]
private OsuGame? game { get; set; }
[Resolved]
private IAPIProvider api { get; set; } = null!;
/// <summary>
/// A clickable flag component for the specified team, with UI sounds and a tooltip.
/// </summary>
/// <param name="team">The team. A null value will show a placeholder background.</param>
/// <param name="showTooltipOnHover">If set to true, the team's name is displayed in the tooltip.</param>
public ClickableTeamFlag(APITeam? team, bool showTooltipOnHover = true)
{
this.team = team;
if (team == null)
return;
Action = openProfile;
if (showTooltipOnHover)
TooltipText = team.Name;
}
[BackgroundDependencyLoader]
private void load()
{
LoadComponentAsync(new DrawableTeamFlag(team) { RelativeSizeAxes = Axes.Both }, Add);
}
private void openProfile()
{
if (team != null)
game?.OpenUrlExternally($@"{api.Endpoints.WebsiteUrl}/teams/{team.Id}");
}
}
}

View File

@@ -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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Users.Drawables
{
[LongRunningLoad]
public partial class DrawableTeamFlag : CompositeDrawable
{
private readonly APITeam? team;
private readonly Sprite sprite;
/// <summary>
/// A simple, non-interactable flag sprite for the specified user.
/// </summary>
/// <param name="team">The team. A null value will show a placeholder background.</param>
public DrawableTeamFlag(APITeam? team)
{
this.team = team;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Colour4.FromHex("333"),
},
sprite = new Sprite
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fit,
}
};
}
[BackgroundDependencyLoader]
private void load(LargeTextureStore textures)
{
if (team != null)
sprite.Texture = textures.Get(team.FlagUrl);
}
}
}

View File

@@ -1,17 +1,9 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Framework.Graphics.Effects;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Users.Drawables
@@ -31,10 +23,52 @@ namespace osu.Game.Users.Drawables
}
}
public new bool Masking
{
get => base.Masking;
set => base.Masking = value;
}
private bool useDefaultRadius = true;
public new float CornerRadius
{
get => base.CornerRadius;
set
{
useDefaultRadius = false;
base.CornerRadius = value;
}
}
public new EdgeEffectParameters EdgeEffect
{
get => base.EdgeEffect;
set => base.EdgeEffect = value;
}
protected override double LoadDelay => 200;
public UpdateableTeamFlag(APITeam? team = null)
private readonly bool isInteractive;
private readonly bool hideOnNull;
private readonly bool showTooltipOnHover;
/// <summary>
/// Construct a new UpdateableTeamFlag.
/// </summary>
/// <param name="team">The initial team to display.</param>
/// <param name="isInteractive">If set to true, hover/click sounds will play and clicking the flag will open the team's profile.</param>
/// <param name="showTooltipOnHover">
/// If set to true, the team's name is displayed in the tooltip.
/// Only has an effect if <see cref="isInteractive"/> is true.
/// </param>
/// <param name="hideOnNull">Whether to hide the flag when the provided team is null.</param>
public UpdateableTeamFlag(APITeam? team = null, bool isInteractive = true, bool hideOnNull = true, bool showTooltipOnHover = true)
{
this.isInteractive = isInteractive;
this.hideOnNull = hideOnNull;
this.showTooltipOnHover = showTooltipOnHover;
Team = team;
Masking = true;
@@ -42,69 +76,33 @@ namespace osu.Game.Users.Drawables
protected override Drawable? CreateDrawable(APITeam? team)
{
if (team == null)
if (team == null && hideOnNull)
return Empty();
return new TeamFlag(team) { RelativeSizeAxes = Axes.Both };
}
if (isInteractive)
{
return new ClickableTeamFlag(team, showTooltipOnHover)
{
RelativeSizeAxes = Axes.Both,
};
}
// Generally we just want team flags to disappear if the user doesn't have one.
// This also handles fill flow cases and avoids spacing being added for non-displaying flags.
public override bool IsPresent => base.IsPresent && Team != null;
return new DrawableTeamFlag(team)
{
RelativeSizeAxes = Axes.Both,
};
}
protected override void Update()
{
base.Update();
CornerRadius = DrawHeight / 8;
if (useDefaultRadius)
base.CornerRadius = DrawHeight / 8;
}
[LongRunningLoad]
public partial class TeamFlag : CompositeDrawable, IHasTooltip
{
private readonly APITeam team;
public LocalisableString TooltipText { get; }
[Resolved]
private OsuGame? game { get; set; }
[Resolved]
private IAPIProvider api { get; set; } = null!;
public TeamFlag(APITeam team)
{
this.team = team;
TooltipText = team.Name;
}
[BackgroundDependencyLoader]
private void load(LargeTextureStore textures)
{
InternalChildren = new Drawable[]
{
new HoverClickSounds(),
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Colour4.FromHex("333"),
},
new Sprite
{
RelativeSizeAxes = Axes.Both,
Texture = textures.Get(team.FlagUrl),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fit,
}
};
}
protected override bool OnClick(ClickEvent e)
{
game?.OpenUrlExternally($"{api.Endpoints.WebsiteUrl}/teams/{team.Id}");
return true;
}
}
// Generally we just want team flags to disappear if the user doesn't have one.
// This also handles fill flow cases and avoids spacing being added for non-displaying flags.
public override bool IsPresent => base.IsPresent && (Team != null || !hideOnNull);
}
}

View File

@@ -35,8 +35,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Realm" Version="20.1.0" />
<!-- <PackageReference Include="ppy.osu.Framework" Version="2026.209.0" />-->
<!-- <PackageReference Include="ppy.osu.Game.Resources" Version="2026.123.0" />-->
<PackageReference Include="Sentry" Version="5.1.1" />
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
<PackageReference Include="SharpCompress" Version="0.39.0" />