// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osuTK; using osuTK.Graphics; namespace osu.Game.Beatmaps.Drawables { public partial class DifficultyIcon : CompositeDrawable, IHasCustomTooltip, IHasCurrentValue { /// /// Size of this difficulty icon. /// public new Vector2 Size { get => iconContainer.Size; set => iconContainer.Size = value; } /// /// Which type of tooltip to show. Only works if a beatmap was provided at construction time. /// public DifficultyIconTooltipType TooltipType { get; set; } = DifficultyIconTooltipType.StarRating; private readonly IBeatmapInfo? beatmap; private readonly IRulesetInfo ruleset; private readonly Mod[]? mods; private Drawable background = null!; private readonly Container iconContainer; private readonly BindableWithCurrent difficulty = new BindableWithCurrent(); public virtual Bindable Current { get => difficulty.Current; set => difficulty.Current = value; } [Resolved] private IRulesetStore rulesets { get; set; } = null!; /// /// Creates a new . Will use provided beatmap's for initial value. /// /// The beatmap to be displayed in the tooltip, and to be used for the initial star rating value. /// An array of mods to account for in the calculations /// An optional ruleset to be used for the icon display, in place of the beatmap's ruleset. public DifficultyIcon(IBeatmapInfo beatmap, IRulesetInfo? ruleset = null, Mod[]? mods = null) : this(ruleset ?? beatmap.Ruleset) { this.beatmap = beatmap; this.mods = mods; Current.Value = new StarDifficulty(beatmap.StarRating, 0); } /// /// Creates a new without an associated beatmap. /// /// The ruleset to be used for the icon display. public DifficultyIcon(IRulesetInfo ruleset) { this.ruleset = ruleset; AutoSizeAxes = Axes.Both; InternalChild = iconContainer = new Container { Size = new Vector2(20f) }; } [BackgroundDependencyLoader] private void load(OsuColour colours) { iconContainer.Children = new Drawable[] { new CircularContainer { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, Masking = true, EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0.06f), Type = EdgeEffectType.Shadow, Radius = 3, }, Child = background = new Box { RelativeSizeAxes = Axes.Both, }, }, new ConstrainedIconContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) Icon = getRulesetIcon() }, }; Current.BindValueChanged(difficulty => background.Colour = colours.ForStarDifficulty(difficulty.NewValue.Stars), true); } private Drawable getRulesetIcon() { int? onlineID = ruleset.OnlineID; if (onlineID >= 0 && rulesets.GetRuleset(onlineID.Value)?.CreateInstance() is Ruleset rulesetInstance) return rulesetInstance.CreateIcon(); return new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }; } ITooltip IHasCustomTooltip. GetCustomTooltip() => new DifficultyIconTooltip(); DifficultyIconTooltipContent IHasCustomTooltip. TooltipContent => (TooltipType != DifficultyIconTooltipType.None && beatmap != null ? new DifficultyIconTooltipContent(beatmap, Current, ruleset, mods, TooltipType) : null)!; } public enum DifficultyIconTooltipType { /// /// No tooltip. /// None, /// /// Star rating only. /// StarRating, /// /// Star rating, OD, HP, CS, AR, length, BPM, and max combo. /// Extended, } }