From b011edd85d7aefaa00b6d8fe83ca7d8def901149 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 5 Aug 2018 13:12:31 +0200 Subject: [PATCH 01/54] added Overlays container to RulesetContainer this is required for mods such as flashlight which always want to draw objects above hitcircles. If just adding children to a RulesetContainer the hit circles would always be above the added children (no matter the Depth). --- osu.Game/Rulesets/UI/RulesetContainer.cs | 12 ++++++++++++ osu.Game/Screens/Play/Player.cs | 1 + 2 files changed, 13 insertions(+) diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index ee34e2df04..27dbc70164 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -69,6 +69,12 @@ namespace osu.Game.Rulesets.UI /// public Playfield Playfield => playfield.Value; + private readonly Container overlays; + /// + /// Place to put drawables above hit objects but below UI. + /// + public Container Overlays => overlays; + /// /// The cursor provided by this . May be null if no cursor is provided. /// @@ -88,6 +94,12 @@ namespace osu.Game.Rulesets.UI { Ruleset = ruleset; playfield = new Lazy(CreatePlayfield); + overlays = new Container + { + RelativeSizeAxes = Axes.Both, + Width = 1, + Height = 1 + }; IsPaused.ValueChanged += paused => { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2e23bb16f0..31c3e06705 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -178,6 +178,7 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, Child = RulesetContainer }, + RulesetContainer.Overlays, new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) { Anchor = Anchor.Centre, From b33954d9db69ec37ec6f70ec4c8dca60420df957 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 5 Aug 2018 14:20:56 +0200 Subject: [PATCH 02/54] Implement blinds mod --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 29 ++++++ .../Objects/Drawables/DrawableOsuBlinds.cs | 96 +++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- osu.Game/Rulesets/Mods/ModBlinds.cs | 24 +++++ 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs create mode 100644 osu.Game/Rulesets/Mods/ModBlinds.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs new file mode 100644 index 0000000000..cefaf02a17 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModBlinds : ModBlinds + { + public override double ScoreMultiplier => 1.12; + private DrawableOsuBlinds flashlight; + + public override void ApplyToRulesetContainer(RulesetContainer rulesetContainer) + { + rulesetContainer.Overlays.Add(flashlight = new DrawableOsuBlinds(restrictTo: rulesetContainer.Playfield)); + } + + public override void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + { + scoreProcessor.Health.ValueChanged += val => { + flashlight.Value = (float)val; + }; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs new file mode 100644 index 0000000000..ced5947ba6 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs @@ -0,0 +1,96 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using OpenTK.Graphics; +using osu.Framework.Allocation; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + /// + /// Element for the Blinds mod drawing 2 black boxes covering the whole screen which resize inside a restricted area with some leniency. + /// + public class DrawableOsuBlinds : Container + { + private Box box1, box2; + private float target = 1; + private readonly float easing = 1; + private readonly Container restrictTo; + + /// + /// + /// Percentage of playfield to extend blinds over. Basically moves the origin points where the blinds start. + /// + /// + /// -1 would mean the blinds always cover the whole screen no matter health. + /// 0 would mean the blinds will only ever be on the edge of the playfield on 0% health. + /// 1 would mean the blinds are fully outside the playfield on 50% health. + /// Infinity would mean the blinds are always outside the playfield except on 100% health. + /// + /// + private const float leniency = 0.2f; + + public DrawableOsuBlinds(Container restrictTo) + { + this.restrictTo = restrictTo; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + Width = 1; + Height = 1; + + Add(box1 = new Box + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + Colour = Color4.Black, + RelativeSizeAxes = Axes.Y, + Width = 0, + Height = 1 + }); + Add(box2 = new Box + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Colour = Color4.Black, + RelativeSizeAxes = Axes.Y, + Width = 0, + Height = 1 + }); + } + + protected override void Update() + { + float start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X; + float end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X; + float rawWidth = end - start; + start -= rawWidth * leniency * 0.5f; + end += rawWidth * leniency * 0.5f; + float width = (end - start) * 0.5f * easing; + // different values in case the playfield ever moves from center to somewhere else. + box1.Width = start + width; + box2.Width = DrawWidth - end + width; + } + + /// + /// Health between 0 and 1 for the blinds to base the width on. Will get animated for 200ms using out-quintic easing. + /// + public float Value + { + set + { + target = value; + this.TransformTo(nameof(easing), target, 200, Easing.OutQuint); + } + get + { + return target; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index fa6e9a018a..294078b0f3 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu new MultiMod(new OsuModSuddenDeath(), new OsuModPerfect()), new MultiMod(new OsuModDoubleTime(), new OsuModNightcore()), new OsuModHidden(), - new OsuModFlashlight(), + new MultiMod(new OsuModFlashlight(), new OsuModBlinds()), }; case ModType.Conversion: return new Mod[] diff --git a/osu.Game/Rulesets/Mods/ModBlinds.cs b/osu.Game/Rulesets/Mods/ModBlinds.cs new file mode 100644 index 0000000000..1494b314c2 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModBlinds.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModBlinds : Mod, IApplicableToRulesetContainer, IApplicableToScoreProcessor + where T : HitObject + { + public override string Name => "Blinds"; + public override string ShortenedName => "BL"; + public override FontAwesome Icon => FontAwesome.fa_osu_mod_flashlight; + public override ModType Type => ModType.DifficultyIncrease; + public override string Description => "Play with blinds on your screen."; + public override bool Ranked => false; + + public abstract void ApplyToRulesetContainer(RulesetContainer rulesetContainer); + public abstract void ApplyToScoreProcessor(ScoreProcessor scoreProcessor); + } +} From 5f3c0549c99b17b9441b09df4f545b7ed3f029cf Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sat, 15 Sep 2018 23:44:22 +0200 Subject: [PATCH 03/54] Sprites in blinds mod & gameplay improvements There are now skinnable actual blinds (shoji screen panels) The black overlay is still behind them to avoid cheating with skins The blinds don't open linearly anymore, they are health squared now When easy mod is on, there is always a little gap open --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 4 +- .../Objects/Drawables/DrawableOsuBlinds.cs | 74 ++++++++++++++++++- .../Drawables/Pieces/ModBlindsPanelSprite.cs | 26 +++++++ osu.Game/Rulesets/UI/RulesetContainer.cs | 2 + 4 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index cefaf02a17..a4441a2bdc 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -6,6 +6,7 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using System.Linq; namespace osu.Game.Rulesets.Osu.Mods { @@ -16,7 +17,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToRulesetContainer(RulesetContainer rulesetContainer) { - rulesetContainer.Overlays.Add(flashlight = new DrawableOsuBlinds(restrictTo: rulesetContainer.Playfield)); + bool hasEasy = rulesetContainer.ActiveMods.Count(mod => mod is ModEasy) > 0; + rulesetContainer.Overlays.Add(flashlight = new DrawableOsuBlinds(restrictTo: rulesetContainer.Playfield, hasEasy: hasEasy)); } public override void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs index ced5947ba6..69fc4a1d57 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs @@ -6,6 +6,10 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using OpenTK.Graphics; using osu.Framework.Allocation; +using osu.Game.Skinning; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -14,10 +18,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// public class DrawableOsuBlinds : Container { + /// + /// Black background boxes behind blind panel textures. + /// private Box box1, box2; + private Sprite panelLeft, panelRight; + private Sprite bgPanelLeft, bgPanelRight; + private ISkinSource skin; + private float target = 1; private readonly float easing = 1; + private readonly Container restrictTo; + private readonly bool hasEasy; /// /// @@ -30,15 +43,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// Infinity would mean the blinds are always outside the playfield except on 100% health. /// /// - private const float leniency = 0.2f; + private const float leniency = 0.1f; - public DrawableOsuBlinds(Container restrictTo) + /// + /// Multiplier for adding a gap when the Easy mod is also currently applied. + /// + private const float easy_position_multiplier = 0.95f; + + public DrawableOsuBlinds(Container restrictTo, bool hasEasy) { this.restrictTo = restrictTo; + this.hasEasy = hasEasy; } [BackgroundDependencyLoader] - private void load() + private void load(ISkinSource skin, TextureStore textures) { RelativeSizeAxes = Axes.Both; Width = 1; @@ -62,6 +81,36 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Width = 0, Height = 1 }); + + Add(bgPanelLeft = new ModBlindsPanelSprite { + Origin = Anchor.TopRight, + Colour = Color4.Gray + }); + Add(bgPanelRight = new ModBlindsPanelSprite { + Origin = Anchor.TopLeft, + Colour = Color4.Gray + }); + + Add(panelLeft = new ModBlindsPanelSprite { + Origin = Anchor.TopRight + }); + Add(panelRight = new ModBlindsPanelSprite { + Origin = Anchor.TopLeft + }); + + this.skin = skin; + skin.SourceChanged += skinChanged; + PanelTexture = textures.Get("Play/osu/blinds-panel"); + } + + private void skinChanged() + { + PanelTexture = skin.GetTexture("Play/osu/blinds-panel"); + } + + private static float applyAdjustmentCurve(float value) + { + return value * value; } protected override void Update() @@ -71,10 +120,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables float rawWidth = end - start; start -= rawWidth * leniency * 0.5f; end += rawWidth * leniency * 0.5f; - float width = (end - start) * 0.5f * easing; + + float width = (end - start) * 0.5f * applyAdjustmentCurve((hasEasy ? easy_position_multiplier : 1) * easing); // different values in case the playfield ever moves from center to somewhere else. box1.Width = start + width; box2.Width = DrawWidth - end + width; + + panelLeft.X = start + width; + panelRight.X = end - width; + bgPanelLeft.X = start; + bgPanelRight.X = end; } /// @@ -92,5 +147,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables return target; } } + + public Texture PanelTexture + { + set + { + panelLeft.Texture = value; + panelRight.Texture = value; + bgPanelLeft.Texture = value; + bgPanelRight.Texture = value; + } + } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs new file mode 100644 index 0000000000..459ea920fa --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class ModBlindsPanelSprite : Sprite + { + public ModBlindsPanelSprite() + { + RelativeSizeAxes = Axes.None; + Anchor = Anchor.TopLeft; + } + + protected override void Update() + { + Height = Parent?.DrawHeight ?? 0; + if (Height == 0 || Texture is null) + Width = 0; + else + Width = Texture.Width / (float)Texture.Height * Height; + } + } +} diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 02ec17e969..7b146f5b84 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -328,6 +328,8 @@ namespace osu.Game.Rulesets.UI Playfield.Size = GetAspectAdjustedSize() * PlayfieldArea; } + public IEnumerable ActiveMods { get => Mods; } + /// /// Computes the size of the in relative coordinate space after aspect adjustments. /// From 91b25870ef382039b7479ebdf3b4d4532c504701 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 16 Sep 2018 12:48:36 +0200 Subject: [PATCH 04/54] Make fruit catcher enter and leave what's behind the blinds --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 17 +- .../Objects/Drawables/DrawableOsuBlinds.cs | 164 ++++++++++++++++-- 2 files changed, 164 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index a4441a2bdc..45c22b7153 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -6,7 +6,6 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using System.Linq; namespace osu.Game.Rulesets.Osu.Mods { @@ -17,8 +16,16 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToRulesetContainer(RulesetContainer rulesetContainer) { - bool hasEasy = rulesetContainer.ActiveMods.Count(mod => mod is ModEasy) > 0; - rulesetContainer.Overlays.Add(flashlight = new DrawableOsuBlinds(restrictTo: rulesetContainer.Playfield, hasEasy: hasEasy)); + bool hasEasy = false; + bool hasHardrock = false; + foreach (var mod in rulesetContainer.ActiveMods) + { + if (mod is ModEasy) + hasEasy = true; + if (mod is ModHardRock) + hasHardrock = true; + } + rulesetContainer.Overlays.Add(flashlight = new DrawableOsuBlinds(restrictTo: rulesetContainer.Playfield, hasEasy: hasEasy, hasHardrock: hasHardrock)); } public override void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) @@ -26,6 +33,10 @@ namespace osu.Game.Rulesets.Osu.Mods scoreProcessor.Health.ValueChanged += val => { flashlight.Value = (float)val; }; + scoreProcessor.Combo.ValueChanged += val => { + if (val > 0 && val % 30 == 0) + flashlight.TriggerNPC(); + }; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs index 69fc4a1d57..7c539e0e94 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs @@ -10,6 +10,7 @@ using osu.Game.Skinning; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using System; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -24,13 +25,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Box box1, box2; private Sprite panelLeft, panelRight; private Sprite bgPanelLeft, bgPanelRight; + + private Drawable bgRandomNpc; + private Drawable randomNpc; + private const float npc_movement_start = 1.5f; + private float npcPosition = npc_movement_start; + private bool animatingNPC; + private Random random; + private ISkinSource skin; + private float targetClamp = 1; private float target = 1; private readonly float easing = 1; + private const float black_depth = 10; + private const float bg_panel_depth = 8; + private const float fg_panel_depth = 4; + private const float npc_depth = 6; + private readonly Container restrictTo; - private readonly bool hasEasy; + private readonly bool modEasy, modHardrock; /// /// @@ -50,10 +65,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// private const float easy_position_multiplier = 0.95f; - public DrawableOsuBlinds(Container restrictTo, bool hasEasy) + public DrawableOsuBlinds(Container restrictTo, bool hasEasy, bool hasHardrock) { this.restrictTo = restrictTo; - this.hasEasy = hasEasy; + + modEasy = hasEasy; + modHardrock = hasHardrock; } [BackgroundDependencyLoader] @@ -70,7 +87,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Colour = Color4.Black, RelativeSizeAxes = Axes.Y, Width = 0, - Height = 1 + Height = 1, + Depth = black_depth }); Add(box2 = new Box { @@ -79,23 +97,56 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Colour = Color4.Black, RelativeSizeAxes = Axes.Y, Width = 0, - Height = 1 + Height = 1, + Depth = black_depth }); Add(bgPanelLeft = new ModBlindsPanelSprite { Origin = Anchor.TopRight, - Colour = Color4.Gray + Colour = Color4.Gray, + Depth = bg_panel_depth + 1 }); - Add(bgPanelRight = new ModBlindsPanelSprite { - Origin = Anchor.TopLeft, - Colour = Color4.Gray + Add(panelLeft = new ModBlindsPanelSprite { + Origin = Anchor.TopRight, + Depth = bg_panel_depth }); - Add(panelLeft = new ModBlindsPanelSprite { - Origin = Anchor.TopRight + Add(bgPanelRight = new ModBlindsPanelSprite { + Origin = Anchor.TopLeft, + Colour = Color4.Gray, + Depth = fg_panel_depth + 1 }); Add(panelRight = new ModBlindsPanelSprite { - Origin = Anchor.TopLeft + Origin = Anchor.TopLeft, + Depth = fg_panel_depth + }); + + + random = new Random(); + Add(bgRandomNpc = new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = Color4.Black, + Width = 512 * 0.4f, + Height = 512 * 0.95f, + RelativePositionAxes = Axes.Y, + X = -512, + Y = 0, + Depth = black_depth + }); + Add(new SkinnableDrawable("Play/Catch/fruit-catcher-idle", name => randomNpc = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = textures.Get(name), + Width = 512, + Height = 512, + RelativePositionAxes = Axes.Y, + X = -512, + Y = 0 + }) { + Depth = npc_depth }); this.skin = skin; @@ -108,9 +159,36 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables PanelTexture = skin.GetTexture("Play/osu/blinds-panel"); } + private float applyGap(float value) + { + float ret; + if (modEasy) + { + float multiplier = 0.95f; + ret = value * multiplier; + } + else if (modHardrock) + { + float multiplier = 1.1f; + ret = value * multiplier; + } + else + { + ret = value; + } + + if (ret > targetClamp) + return targetClamp; + else if (ret < 0) + return 0; + else + return ret; + } + private static float applyAdjustmentCurve(float value) { - return value * value; + // lagrange polinominal for (0,0) (0.5,0.35) (1,1) should make a good curve + return 0.6f * value * value + 0.4f * value; } protected override void Update() @@ -121,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables start -= rawWidth * leniency * 0.5f; end += rawWidth * leniency * 0.5f; - float width = (end - start) * 0.5f * applyAdjustmentCurve((hasEasy ? easy_position_multiplier : 1) * easing); + float width = (end - start) * 0.5f * applyAdjustmentCurve(applyGap(easing)); // different values in case the playfield ever moves from center to somewhere else. box1.Width = start + width; box2.Width = DrawWidth - end + width; @@ -130,6 +208,64 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables panelRight.X = end - width; bgPanelLeft.X = start; bgPanelRight.X = end; + + float adjustedNpcPosition = npcPosition * rawWidth; + if (randomNpc != null) + randomNpc.X = adjustedNpcPosition; + bgRandomNpc.X = adjustedNpcPosition; + } + + public void TriggerNPC() + { + if (animatingNPC) + return; + + bool left = (random.Next() & 1) != 0; + bool exit = (random.Next() & 1) != 0; + float start, end; + + if (left) + { + start = -npc_movement_start; + end = npc_movement_start; + + randomNpc.Scale = new OpenTK.Vector2(1, 1); + } + else + { + start = npc_movement_start; + end = -npc_movement_start; + + randomNpc.Scale = new OpenTK.Vector2(-1, 1); + } + + // depths for exit from the left and entry from the right + if (left == exit) + { + ChangeChildDepth(bgPanelLeft, fg_panel_depth + 1); + ChangeChildDepth(panelLeft, fg_panel_depth); + + ChangeChildDepth(bgPanelRight, bg_panel_depth + 1); + ChangeChildDepth(panelRight, bg_panel_depth); + } + else // depths for entry from the left or exit from the right + { + ChangeChildDepth(bgPanelLeft, bg_panel_depth + 1); + ChangeChildDepth(panelLeft, bg_panel_depth); + + ChangeChildDepth(bgPanelRight, fg_panel_depth + 1); + ChangeChildDepth(panelRight, fg_panel_depth); + } + + animatingNPC = true; + npcPosition = start; + this.TransformTo(nameof(npcPosition), end, 3000, Easing.OutSine).Finally(_ => animatingNPC = false); + + targetClamp = 1; + this.Delay(600).TransformTo(nameof(targetClamp), 0.6f, 300).Delay(500).TransformTo(nameof(targetClamp), 1f, 300); + + randomNpc?.FadeIn(250).Delay(2000).FadeOut(500); + bgRandomNpc.FadeIn(250).Delay(2000).FadeOut(500); } /// From 633e8fafee41ada907889bd43f725ad08f95c25b Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 16 Sep 2018 12:57:54 +0200 Subject: [PATCH 05/54] Style & const fix --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 2 +- .../Objects/Drawables/DrawableOsuBlinds.cs | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 45c22b7153..9639dd5dd8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Mods }; scoreProcessor.Combo.ValueChanged += val => { if (val > 0 && val % 30 == 0) - flashlight.TriggerNPC(); + flashlight.TriggerNpc(); }; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs index 7c539e0e94..8c2a569e92 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Drawable randomNpc; private const float npc_movement_start = 1.5f; private float npcPosition = npc_movement_start; - private bool animatingNPC; + private bool animatingNpc; private Random random; private ISkinSource skin; @@ -164,12 +164,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables float ret; if (modEasy) { - float multiplier = 0.95f; + const float multiplier = 0.95f; ret = value * multiplier; } else if (modHardrock) { - float multiplier = 1.1f; + const float multiplier = 1.1f; ret = value * multiplier; } else @@ -215,9 +215,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables bgRandomNpc.X = adjustedNpcPosition; } - public void TriggerNPC() + public void TriggerNpc() { - if (animatingNPC) + if (animatingNpc) return; bool left = (random.Next() & 1) != 0; @@ -257,9 +257,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ChangeChildDepth(panelRight, fg_panel_depth); } - animatingNPC = true; + animatingNpc = true; npcPosition = start; - this.TransformTo(nameof(npcPosition), end, 3000, Easing.OutSine).Finally(_ => animatingNPC = false); + this.TransformTo(nameof(npcPosition), end, 3000, Easing.OutSine).Finally(_ => animatingNpc = false); targetClamp = 1; this.Delay(600).TransformTo(nameof(targetClamp), 0.6f, 300).Delay(500).TransformTo(nameof(targetClamp), 1f, 300); From c9ea5ce817fac5d860fefef7d2cf1fd7be459463 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 16 Sep 2018 16:51:18 +0200 Subject: [PATCH 06/54] Made blinds open during breaks and start --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 2 +- .../Objects/Drawables/DrawableOsuBlinds.cs | 89 ++++++++++++------- .../Drawables/Pieces/ModBlindsPanelSprite.cs | 2 +- osu.Game/Rulesets/Mods/ModBlinds.cs | 2 +- 4 files changed, 62 insertions(+), 33 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 9639dd5dd8..4baea5d0c0 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (mod is ModHardRock) hasHardrock = true; } - rulesetContainer.Overlays.Add(flashlight = new DrawableOsuBlinds(restrictTo: rulesetContainer.Playfield, hasEasy: hasEasy, hasHardrock: hasHardrock)); + rulesetContainer.Overlays.Add(flashlight = new DrawableOsuBlinds(rulesetContainer.Playfield, hasEasy, hasHardrock, rulesetContainer.Beatmap)); } public override void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs index 8c2a569e92..ae099cd589 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using System; +using osu.Game.Beatmaps; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -22,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// /// Black background boxes behind blind panel textures. /// - private Box box1, box2; + private Box blackBoxLeft, blackBoxRight; private Sprite panelLeft, panelRight; private Sprite bgPanelLeft, bgPanelRight; @@ -30,12 +31,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Drawable randomNpc; private const float npc_movement_start = 1.5f; private float npcPosition = npc_movement_start; - private bool animatingNpc; + private bool animatingBlinds; + private Beatmap beatmap; private Random random; private ISkinSource skin; private float targetClamp = 1; + private float targetBreakMultiplier = 0; private float target = 1; private readonly float easing = 1; @@ -60,14 +63,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// private const float leniency = 0.1f; - /// - /// Multiplier for adding a gap when the Easy mod is also currently applied. - /// - private const float easy_position_multiplier = 0.95f; - - public DrawableOsuBlinds(Container restrictTo, bool hasEasy, bool hasHardrock) + public DrawableOsuBlinds(Container restrictTo, bool hasEasy, bool hasHardrock, Beatmap beatmap) { this.restrictTo = restrictTo; + this.beatmap = beatmap; modEasy = hasEasy; modHardrock = hasHardrock; @@ -80,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Width = 1; Height = 1; - Add(box1 = new Box + Add(blackBoxLeft = new Box { Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, @@ -90,7 +89,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Height = 1, Depth = black_depth }); - Add(box2 = new Box + Add(blackBoxRight = new Box { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -161,28 +160,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private float applyGap(float value) { - float ret; + const float easy_multiplier = 0.95f; + const float hardrock_multiplier = 1.1f; + + float multiplier = 1; if (modEasy) { - const float multiplier = 0.95f; - ret = value * multiplier; + multiplier = easy_multiplier; + // TODO: include OD/CS } else if (modHardrock) { - const float multiplier = 1.1f; - ret = value * multiplier; - } - else - { - ret = value; + multiplier = hardrock_multiplier; + // TODO: include OD/CS } - if (ret > targetClamp) - return targetClamp; - else if (ret < 0) - return 0; - else - return ret; + return OpenTK.MathHelper.Clamp(value * multiplier, 0, targetClamp) * targetBreakMultiplier; } private static float applyAdjustmentCurve(float value) @@ -201,8 +194,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables float width = (end - start) * 0.5f * applyAdjustmentCurve(applyGap(easing)); // different values in case the playfield ever moves from center to somewhere else. - box1.Width = start + width; - box2.Width = DrawWidth - end + width; + blackBoxLeft.Width = start + width; + blackBoxRight.Width = DrawWidth - end + width; panelLeft.X = start + width; panelRight.X = end - width; @@ -215,9 +208,45 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables bgRandomNpc.X = adjustedNpcPosition; } + protected override void LoadComplete() + { + const float break_open_early = 500; + + base.LoadComplete(); + + var firstObj = beatmap.HitObjects[0]; + var startDelay = firstObj.StartTime - firstObj.TimePreempt - firstObj.TimeFadeIn; + + using (BeginAbsoluteSequence(startDelay, true)) + LeaveBreak(); + + foreach (var breakInfo in beatmap.Breaks) + { + if (breakInfo.HasEffect) + { + using (BeginAbsoluteSequence(breakInfo.StartTime - break_open_early, true)) + { + EnterBreak(); + using (BeginDelayedSequence(breakInfo.Duration + break_open_early, true)) + LeaveBreak(); + } + } + } + } + + public void EnterBreak() + { + this.TransformTo(nameof(targetBreakMultiplier), 0f, 1000, Easing.OutSine); + } + + public void LeaveBreak() + { + this.TransformTo(nameof(targetBreakMultiplier), 1f, 2500, Easing.OutBounce); + } + public void TriggerNpc() { - if (animatingNpc) + if (animatingBlinds) return; bool left = (random.Next() & 1) != 0; @@ -257,9 +286,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables ChangeChildDepth(panelRight, fg_panel_depth); } - animatingNpc = true; + animatingBlinds = true; npcPosition = start; - this.TransformTo(nameof(npcPosition), end, 3000, Easing.OutSine).Finally(_ => animatingNpc = false); + this.TransformTo(nameof(npcPosition), end, 3000, Easing.OutSine).Finally(_ => animatingBlinds = false); targetClamp = 1; this.Delay(600).TransformTo(nameof(targetClamp), 0.6f, 300).Delay(500).TransformTo(nameof(targetClamp), 1f, 300); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs index 459ea920fa..c6e2db1842 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces protected override void Update() { Height = Parent?.DrawHeight ?? 0; - if (Height == 0 || Texture is null) + if (Height == 0 || Texture == null) Width = 0; else Width = Texture.Width / (float)Texture.Height * Height; diff --git a/osu.Game/Rulesets/Mods/ModBlinds.cs b/osu.Game/Rulesets/Mods/ModBlinds.cs index 1494b314c2..bf68300cd6 100644 --- a/osu.Game/Rulesets/Mods/ModBlinds.cs +++ b/osu.Game/Rulesets/Mods/ModBlinds.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods { public override string Name => "Blinds"; public override string ShortenedName => "BL"; - public override FontAwesome Icon => FontAwesome.fa_osu_mod_flashlight; + public override FontAwesome Icon => FontAwesome.fa_adjust; public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Play with blinds on your screen."; public override bool Ranked => false; From 2de3b33780ac0871188be90a29a703475f7576de Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 16 Sep 2018 16:57:43 +0200 Subject: [PATCH 07/54] Readonly fixes --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs index ae099cd589..4fe5b4e4fb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs @@ -32,13 +32,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private const float npc_movement_start = 1.5f; private float npcPosition = npc_movement_start; private bool animatingBlinds; - private Beatmap beatmap; + + private readonly Beatmap beatmap; private Random random; private ISkinSource skin; private float targetClamp = 1; - private float targetBreakMultiplier = 0; + private readonly float targetBreakMultiplier = 0; private float target = 1; private readonly float easing = 1; From 8a01fc1bffa8c323da188aaa50a19c446ce2f61b Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Mon, 17 Sep 2018 20:31:50 +0200 Subject: [PATCH 08/54] Make random in blinds mod the same every replay --- .../Objects/Drawables/DrawableOsuBlinds.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs index 4fe5b4e4fb..15d394dbb4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs @@ -121,8 +121,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Depth = fg_panel_depth }); - - random = new Random(); + // seed with unique seed per map so NPC always comes from the same sides for a same map for reproducible replays. + random = new Random(beatmap.Metadata.ToString().GetHashCode()); Add(bgRandomNpc = new Box { Anchor = Anchor.Centre, @@ -133,7 +133,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RelativePositionAxes = Axes.Y, X = -512, Y = 0, - Depth = black_depth + Depth = black_depth, + Alpha = 0 }); Add(new SkinnableDrawable("Play/Catch/fruit-catcher-idle", name => randomNpc = new Sprite { @@ -144,7 +145,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Height = 512, RelativePositionAxes = Axes.Y, X = -512, - Y = 0 + Y = 0, + Alpha = 0 }) { Depth = npc_depth }); From 72e82b660d91eeae22a9d0d5acc8d25f274a8cf2 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 28 Oct 2018 01:14:16 +0200 Subject: [PATCH 09/54] Adjust blinds animations based on player feedback --- .../Objects/Drawables/DrawableOsuBlinds.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs index 1ba18dacbc..2bbd31fc46 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs @@ -214,13 +214,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void LoadComplete() { const float break_open_early = 500; + const float break_close_late = 250; base.LoadComplete(); var firstObj = beatmap.HitObjects[0]; - var startDelay = firstObj.StartTime - firstObj.TimePreempt - firstObj.TimeFadeIn; + var startDelay = firstObj.StartTime - firstObj.TimePreempt; - using (BeginAbsoluteSequence(startDelay, true)) + using (BeginAbsoluteSequence(startDelay + break_close_late, true)) LeaveBreak(); foreach (var breakInfo in beatmap.Breaks) @@ -230,7 +231,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables using (BeginAbsoluteSequence(breakInfo.StartTime - break_open_early, true)) { EnterBreak(); - using (BeginDelayedSequence(breakInfo.Duration + break_open_early, true)) + using (BeginDelayedSequence(breakInfo.Duration + break_open_early + break_close_late, true)) LeaveBreak(); } } From 46b98702e1ce0149e6c0e70e1b8325559f644488 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Thu, 6 Dec 2018 12:48:11 +0100 Subject: [PATCH 10/54] make target animation call more obvious --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 2 +- .../Objects/Drawables/DrawableOsuBlinds.cs | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 9f33653bc7..cfdc63a57e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { scoreProcessor.Health.ValueChanged += val => { - flashlight.Value = (float)val; + flashlight.AnimateTarget((float)val); }; scoreProcessor.Combo.ValueChanged += val => { if (val > 0 && val % 30 == 0) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs index 7e72c5c7bb..eb725e09db 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs @@ -305,17 +305,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// /// Health between 0 and 1 for the blinds to base the width on. Will get animated for 200ms using out-quintic easing. /// - public float Value + public void AnimateTarget(float value) { - set - { - target = value; - this.TransformTo(nameof(easing), target, 200, Easing.OutQuint); - } - get - { - return target; - } + target = value; + this.TransformTo(nameof(easing), target, 200, Easing.OutQuint); + } + + public float Target + { + get => target; } public Texture PanelTexture From a14de5bd1b9a3b38102cf5e8a060184ddbedc1af Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Thu, 6 Dec 2018 12:52:39 +0100 Subject: [PATCH 11/54] Remove blinds random NPC --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 4 - .../Objects/Drawables/DrawableOsuBlinds.cs | 92 ------------------- 2 files changed, 96 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index cfdc63a57e..a9379c9133 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -33,10 +33,6 @@ namespace osu.Game.Rulesets.Osu.Mods scoreProcessor.Health.ValueChanged += val => { flashlight.AnimateTarget((float)val); }; - scoreProcessor.Combo.ValueChanged += val => { - if (val > 0 && val % 30 == 0) - flashlight.TriggerNpc(); - }; } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs index eb725e09db..df7006da1d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs @@ -12,7 +12,6 @@ using osu.Game.Skinning; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osuTK; using osuTK.Graphics; -using System; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -28,14 +27,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Sprite panelLeft, panelRight; private Sprite bgPanelLeft, bgPanelRight; - private Drawable bgRandomNpc; - private Drawable randomNpc; private const float npc_movement_start = 1.5f; private float npcPosition = npc_movement_start; private bool animatingBlinds; private readonly Beatmap beatmap; - private Random random; private ISkinSource skin; @@ -122,36 +118,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Depth = fg_panel_depth }); - // seed with unique seed per map so NPC always comes from the same sides for a same map for reproducible replays. - random = new Random(beatmap.Metadata.ToString().GetHashCode()); - Add(bgRandomNpc = new Box - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Colour = Color4.Black, - Width = 512 * 0.4f, - Height = 512 * 0.95f, - RelativePositionAxes = Axes.Y, - X = -512, - Y = 0, - Depth = black_depth, - Alpha = 0 - }); - Add(new SkinnableDrawable("Play/Catch/fruit-catcher-idle", name => randomNpc = new Sprite - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Texture = textures.Get(name), - Width = 512, - Height = 512, - RelativePositionAxes = Axes.Y, - X = -512, - Y = 0, - Alpha = 0 - }) { - Depth = npc_depth - }); - this.skin = skin; skin.SourceChanged += skinChanged; PanelTexture = textures.Get("Play/osu/blinds-panel"); @@ -205,11 +171,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables panelRight.X = end - width; bgPanelLeft.X = start; bgPanelRight.X = end; - - float adjustedNpcPosition = npcPosition * rawWidth; - if (randomNpc != null) - randomNpc.X = adjustedNpcPosition; - bgRandomNpc.X = adjustedNpcPosition; } protected override void LoadComplete() @@ -249,59 +210,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables this.TransformTo(nameof(targetBreakMultiplier), 1f, 2500, Easing.OutBounce); } - public void TriggerNpc() - { - if (animatingBlinds) - return; - - bool left = (random.Next() & 1) != 0; - bool exit = (random.Next() & 1) != 0; - float start, end; - - if (left) - { - start = -npc_movement_start; - end = npc_movement_start; - - randomNpc.Scale = new Vector2(1, 1); - } - else - { - start = npc_movement_start; - end = -npc_movement_start; - - randomNpc.Scale = new Vector2(-1, 1); - } - - // depths for exit from the left and entry from the right - if (left == exit) - { - ChangeChildDepth(bgPanelLeft, fg_panel_depth + 1); - ChangeChildDepth(panelLeft, fg_panel_depth); - - ChangeChildDepth(bgPanelRight, bg_panel_depth + 1); - ChangeChildDepth(panelRight, bg_panel_depth); - } - else // depths for entry from the left or exit from the right - { - ChangeChildDepth(bgPanelLeft, bg_panel_depth + 1); - ChangeChildDepth(panelLeft, bg_panel_depth); - - ChangeChildDepth(bgPanelRight, fg_panel_depth + 1); - ChangeChildDepth(panelRight, fg_panel_depth); - } - - animatingBlinds = true; - npcPosition = start; - this.TransformTo(nameof(npcPosition), end, 3000, Easing.OutSine).Finally(_ => animatingBlinds = false); - - targetClamp = 1; - this.Delay(600).TransformTo(nameof(targetClamp), 0.6f, 300).Delay(500).TransformTo(nameof(targetClamp), 1f, 300); - - randomNpc?.FadeIn(250).Delay(2000).FadeOut(500); - bgRandomNpc.FadeIn(250).Delay(2000).FadeOut(500); - } - /// /// Health between 0 and 1 for the blinds to base the width on. Will get animated for 200ms using out-quintic easing. /// From 6eff7d9acc18cd8768de3d0baa85fd185dc28df1 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Thu, 6 Dec 2018 14:34:10 +0100 Subject: [PATCH 12/54] Style fixes --- .../Objects/Drawables/DrawableOsuBlinds.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs index df7006da1d..d06f1250d8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs @@ -27,10 +27,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Sprite panelLeft, panelRight; private Sprite bgPanelLeft, bgPanelRight; - private const float npc_movement_start = 1.5f; - private float npcPosition = npc_movement_start; - private bool animatingBlinds; - private readonly Beatmap beatmap; private ISkinSource skin; @@ -43,7 +39,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private const float black_depth = 10; private const float bg_panel_depth = 8; private const float fg_panel_depth = 4; - private const float npc_depth = 6; private readonly CompositeDrawable restrictTo; private readonly bool modEasy, modHardrock; @@ -210,6 +205,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables this.TransformTo(nameof(targetBreakMultiplier), 1f, 2500, Easing.OutBounce); } + /// + /// Value between 0 and 1 setting a maximum "closedness" for the blinds. + /// Useful for animating how far the blinds can be opened while keeping them at the original position if they are wider open than this. + /// + public float TargetClamp + { + get => targetClamp; + set => targetClamp = value; + } + /// /// Health between 0 and 1 for the blinds to base the width on. Will get animated for 200ms using out-quintic easing. /// From d379d0276115d66b422ee9bc249824ccb8d60fd8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Dec 2018 20:12:56 +0900 Subject: [PATCH 13/54] Remove unnecessary base class --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 31 +++++++++++----------- osu.Game/Rulesets/Mods/ModBlinds.cs | 24 ----------------- 2 files changed, 16 insertions(+), 39 deletions(-) delete mode 100644 osu.Game/Rulesets/Mods/ModBlinds.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index a9379c9133..f216ae5814 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Linq; +using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; @@ -9,30 +11,29 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModBlinds : ModBlinds + public class OsuModBlinds : Mod, IApplicableToRulesetContainer, IApplicableToScoreProcessor { + public override string Name => "Blinds"; + public override string Acronym => "BL"; + public override FontAwesome Icon => FontAwesome.fa_adjust; + public override ModType Type => ModType.DifficultyIncrease; + public override string Description => "Play with blinds on your screen."; + public override bool Ranked => false; + public override double ScoreMultiplier => 1.12; private DrawableOsuBlinds flashlight; - public override void ApplyToRulesetContainer(RulesetContainer rulesetContainer) + public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) { - bool hasEasy = false; - bool hasHardrock = false; - foreach (var mod in rulesetContainer.ActiveMods) - { - if (mod is ModEasy) - hasEasy = true; - if (mod is ModHardRock) - hasHardrock = true; - } + bool hasEasy = rulesetContainer.ActiveMods.Any(m => m is ModEasy); + bool hasHardrock = rulesetContainer.ActiveMods.Any(m => m is ModHardRock); + rulesetContainer.Overlays.Add(flashlight = new DrawableOsuBlinds(rulesetContainer.Playfield.HitObjectContainer, hasEasy, hasHardrock, rulesetContainer.Beatmap)); } - public override void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { - scoreProcessor.Health.ValueChanged += val => { - flashlight.AnimateTarget((float)val); - }; + scoreProcessor.Health.ValueChanged += val => { flashlight.AnimateTarget((float)val); }; } } } diff --git a/osu.Game/Rulesets/Mods/ModBlinds.cs b/osu.Game/Rulesets/Mods/ModBlinds.cs deleted file mode 100644 index e4a17551ec..0000000000 --- a/osu.Game/Rulesets/Mods/ModBlinds.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Graphics; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModBlinds : Mod, IApplicableToRulesetContainer, IApplicableToScoreProcessor - where T : HitObject - { - public override string Name => "Blinds"; - public override string Acronym => "BL"; - public override FontAwesome Icon => FontAwesome.fa_adjust; - public override ModType Type => ModType.DifficultyIncrease; - public override string Description => "Play with blinds on your screen."; - public override bool Ranked => false; - - public abstract void ApplyToRulesetContainer(RulesetContainer rulesetContainer); - public abstract void ApplyToScoreProcessor(ScoreProcessor scoreProcessor); - } -} From 7d9cdf6f81c41f4fa4b96111bebb2e70a9391fac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Dec 2018 20:13:03 +0900 Subject: [PATCH 14/54] Remove unnecessary private field --- osu.Game/Rulesets/UI/RulesetContainer.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 72e32290e2..67bcb7581f 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -68,11 +68,10 @@ namespace osu.Game.Rulesets.UI /// public Playfield Playfield => playfield.Value; - private readonly Container overlays; /// /// Place to put drawables above hit objects but below UI. /// - public Container Overlays => overlays; + public readonly Container Overlays; /// /// The cursor provided by this . May be null if no cursor is provided. @@ -93,7 +92,7 @@ namespace osu.Game.Rulesets.UI { Ruleset = ruleset; playfield = new Lazy(CreatePlayfield); - overlays = new Container + Overlays = new Container { RelativeSizeAxes = Axes.Both, Width = 1, From 4f34d42b3372aea76d29a3a09e771fac1b6f2af7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Dec 2018 21:11:35 +0900 Subject: [PATCH 15/54] Major code refactors --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 222 +++++++++++++++- .../Objects/Drawables/DrawableOsuBlinds.cs | 243 ------------------ .../Drawables/Pieces/ModBlindsPanelSprite.cs | 26 -- 3 files changed, 217 insertions(+), 274 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs delete mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index f216ae5814..4078dce643 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -2,38 +2,250 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; +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.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; +using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModBlinds : Mod, IApplicableToRulesetContainer, IApplicableToScoreProcessor { public override string Name => "Blinds"; + public override string Description => "Play with blinds on your screen."; public override string Acronym => "BL"; + public override FontAwesome Icon => FontAwesome.fa_adjust; public override ModType Type => ModType.DifficultyIncrease; - public override string Description => "Play with blinds on your screen."; + public override bool Ranked => false; public override double ScoreMultiplier => 1.12; - private DrawableOsuBlinds flashlight; + private DrawableOsuBlinds blinds; public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) { bool hasEasy = rulesetContainer.ActiveMods.Any(m => m is ModEasy); bool hasHardrock = rulesetContainer.ActiveMods.Any(m => m is ModHardRock); - rulesetContainer.Overlays.Add(flashlight = new DrawableOsuBlinds(rulesetContainer.Playfield.HitObjectContainer, hasEasy, hasHardrock, rulesetContainer.Beatmap)); + rulesetContainer.Overlays.Add(blinds = new DrawableOsuBlinds(rulesetContainer.Playfield.HitObjectContainer, hasEasy, hasHardrock, rulesetContainer.Beatmap)); } public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { - scoreProcessor.Health.ValueChanged += val => { flashlight.AnimateTarget((float)val); }; + scoreProcessor.Health.ValueChanged += val => { blinds.AnimateClosedness((float)val); }; + } + + /// + /// Element for the Blinds mod drawing 2 black boxes covering the whole screen which resize inside a restricted area with some leniency. + /// + public class DrawableOsuBlinds : Container + { + /// + /// Black background boxes behind blind panel textures. + /// + private Box blackBoxLeft, blackBoxRight; + + private Drawable panelLeft, panelRight, bgPanelLeft, bgPanelRight; + + private readonly Beatmap beatmap; + + /// + /// Value between 0 and 1 setting a maximum "closedness" for the blinds. + /// Useful for animating how far the blinds can be opened while keeping them at the original position if they are wider open than this. + /// + private const float target_clamp = 1; + + private readonly float targetBreakMultiplier = 0; + private readonly float easing = 1; + + private const float black_depth = 10; + private const float bg_panel_depth = 8; + private const float fg_panel_depth = 4; + + private readonly CompositeDrawable restrictTo; + private readonly bool modEasy, modHardrock; + + /// + /// + /// Percentage of playfield to extend blinds over. Basically moves the origin points where the blinds start. + /// + /// + /// -1 would mean the blinds always cover the whole screen no matter health. + /// 0 would mean the blinds will only ever be on the edge of the playfield on 0% health. + /// 1 would mean the blinds are fully outside the playfield on 50% health. + /// Infinity would mean the blinds are always outside the playfield except on 100% health. + /// + /// + private const float leniency = 0.1f; + + public DrawableOsuBlinds(CompositeDrawable restrictTo, bool hasEasy, bool hasHardrock, Beatmap beatmap) + { + this.restrictTo = restrictTo; + this.beatmap = beatmap; + + modEasy = hasEasy; + modHardrock = hasHardrock; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + Children = new[] + { + blackBoxLeft = new Box + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + Colour = Color4.Black, + RelativeSizeAxes = Axes.Y, + Width = 0, + Height = 1, + Depth = black_depth + }, + blackBoxRight = new Box + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Colour = Color4.Black, + RelativeSizeAxes = Axes.Y, + Width = 0, + Height = 1, + Depth = black_depth + }, + bgPanelLeft = new ModBlindsPanel + { + Origin = Anchor.TopRight, + Colour = Color4.Gray, + Depth = bg_panel_depth + 1 + }, + panelLeft = new ModBlindsPanel + { + Origin = Anchor.TopRight, + Depth = bg_panel_depth + }, + bgPanelRight = new ModBlindsPanel + { + Origin = Anchor.TopLeft, + Colour = Color4.Gray, + Depth = fg_panel_depth + 1 + }, + panelRight = new ModBlindsPanel + { + Origin = Anchor.TopLeft, + Depth = fg_panel_depth + }, + }; + } + + private float applyGap(float value) + { + const float easy_multiplier = 0.95f; + const float hardrock_multiplier = 1.1f; + + float multiplier = 1; + if (modEasy) + { + multiplier = easy_multiplier; + // TODO: include OD/CS + } + else if (modHardrock) + { + multiplier = hardrock_multiplier; + // TODO: include OD/CS + } + + return MathHelper.Clamp(value * multiplier, 0, target_clamp) * targetBreakMultiplier; + } + + private static float applyAdjustmentCurve(float value) + { + // lagrange polinominal for (0,0) (0.5,0.35) (1,1) should make a good curve + return 0.6f * value * value + 0.4f * value; + } + + protected override void Update() + { + float start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X; + float end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X; + + float rawWidth = end - start; + + start -= rawWidth * leniency * 0.5f; + end += rawWidth * leniency * 0.5f; + + float width = (end - start) * 0.5f * applyAdjustmentCurve(applyGap(easing)); + + // different values in case the playfield ever moves from center to somewhere else. + blackBoxLeft.Width = start + width; + blackBoxRight.Width = DrawWidth - end + width; + + panelLeft.X = start + width; + panelRight.X = end - width; + bgPanelLeft.X = start; + bgPanelRight.X = end; + } + + protected override void LoadComplete() + { + const float break_open_early = 500; + const float break_close_late = 250; + + base.LoadComplete(); + + var firstObj = beatmap.HitObjects[0]; + var startDelay = firstObj.StartTime - firstObj.TimePreempt; + + using (BeginAbsoluteSequence(startDelay + break_close_late, true)) + leaveBreak(); + + foreach (var breakInfo in beatmap.Breaks) + { + if (breakInfo.HasEffect) + { + using (BeginAbsoluteSequence(breakInfo.StartTime - break_open_early, true)) + { + enterBreak(); + using (BeginDelayedSequence(breakInfo.Duration + break_open_early + break_close_late, true)) + leaveBreak(); + } + } + } + } + + private void enterBreak() => this.TransformTo(nameof(targetBreakMultiplier), 0f, 1000, Easing.OutSine); + + private void leaveBreak() => this.TransformTo(nameof(targetBreakMultiplier), 1f, 2500, Easing.OutBounce); + + /// + /// 0 is open, 1 is closed. + /// + public void AnimateClosedness(float value) => this.TransformTo(nameof(easing), value, 200, Easing.OutQuint); + + public class ModBlindsPanel : CompositeDrawable + { + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + InternalChild = new SkinnableDrawable("Play/osu/blinds-panel", s => new Sprite { Texture = textures.Get("Play/osu/blinds-panel") }) + { + RelativeSizeAxes = Axes.Both, + }; + } + } } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs deleted file mode 100644 index d06f1250d8..0000000000 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuBlinds.cs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -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.Beatmaps; -using osu.Game.Skinning; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - /// - /// Element for the Blinds mod drawing 2 black boxes covering the whole screen which resize inside a restricted area with some leniency. - /// - public class DrawableOsuBlinds : Container - { - /// - /// Black background boxes behind blind panel textures. - /// - private Box blackBoxLeft, blackBoxRight; - private Sprite panelLeft, panelRight; - private Sprite bgPanelLeft, bgPanelRight; - - private readonly Beatmap beatmap; - - private ISkinSource skin; - - private float targetClamp = 1; - private readonly float targetBreakMultiplier = 0; - private float target = 1; - private readonly float easing = 1; - - private const float black_depth = 10; - private const float bg_panel_depth = 8; - private const float fg_panel_depth = 4; - - private readonly CompositeDrawable restrictTo; - private readonly bool modEasy, modHardrock; - - /// - /// - /// Percentage of playfield to extend blinds over. Basically moves the origin points where the blinds start. - /// - /// - /// -1 would mean the blinds always cover the whole screen no matter health. - /// 0 would mean the blinds will only ever be on the edge of the playfield on 0% health. - /// 1 would mean the blinds are fully outside the playfield on 50% health. - /// Infinity would mean the blinds are always outside the playfield except on 100% health. - /// - /// - private const float leniency = 0.1f; - - public DrawableOsuBlinds(CompositeDrawable restrictTo, bool hasEasy, bool hasHardrock, Beatmap beatmap) - { - this.restrictTo = restrictTo; - this.beatmap = beatmap; - - modEasy = hasEasy; - modHardrock = hasHardrock; - } - - [BackgroundDependencyLoader] - private void load(ISkinSource skin, TextureStore textures) - { - RelativeSizeAxes = Axes.Both; - Width = 1; - Height = 1; - - Add(blackBoxLeft = new Box - { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - Colour = Color4.Black, - RelativeSizeAxes = Axes.Y, - Width = 0, - Height = 1, - Depth = black_depth - }); - Add(blackBoxRight = new Box - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Colour = Color4.Black, - RelativeSizeAxes = Axes.Y, - Width = 0, - Height = 1, - Depth = black_depth - }); - - Add(bgPanelLeft = new ModBlindsPanelSprite { - Origin = Anchor.TopRight, - Colour = Color4.Gray, - Depth = bg_panel_depth + 1 - }); - Add(panelLeft = new ModBlindsPanelSprite { - Origin = Anchor.TopRight, - Depth = bg_panel_depth - }); - - Add(bgPanelRight = new ModBlindsPanelSprite { - Origin = Anchor.TopLeft, - Colour = Color4.Gray, - Depth = fg_panel_depth + 1 - }); - Add(panelRight = new ModBlindsPanelSprite { - Origin = Anchor.TopLeft, - Depth = fg_panel_depth - }); - - this.skin = skin; - skin.SourceChanged += skinChanged; - PanelTexture = textures.Get("Play/osu/blinds-panel"); - } - - private void skinChanged() - { - PanelTexture = skin.GetTexture("Play/osu/blinds-panel"); - } - - private float applyGap(float value) - { - const float easy_multiplier = 0.95f; - const float hardrock_multiplier = 1.1f; - - float multiplier = 1; - if (modEasy) - { - multiplier = easy_multiplier; - // TODO: include OD/CS - } - else if (modHardrock) - { - multiplier = hardrock_multiplier; - // TODO: include OD/CS - } - - return MathHelper.Clamp(value * multiplier, 0, targetClamp) * targetBreakMultiplier; - } - - private static float applyAdjustmentCurve(float value) - { - // lagrange polinominal for (0,0) (0.5,0.35) (1,1) should make a good curve - return 0.6f * value * value + 0.4f * value; - } - - protected override void Update() - { - float start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X; - float end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X; - float rawWidth = end - start; - start -= rawWidth * leniency * 0.5f; - end += rawWidth * leniency * 0.5f; - - float width = (end - start) * 0.5f * applyAdjustmentCurve(applyGap(easing)); - // different values in case the playfield ever moves from center to somewhere else. - blackBoxLeft.Width = start + width; - blackBoxRight.Width = DrawWidth - end + width; - - panelLeft.X = start + width; - panelRight.X = end - width; - bgPanelLeft.X = start; - bgPanelRight.X = end; - } - - protected override void LoadComplete() - { - const float break_open_early = 500; - const float break_close_late = 250; - - base.LoadComplete(); - - var firstObj = beatmap.HitObjects[0]; - var startDelay = firstObj.StartTime - firstObj.TimePreempt; - - using (BeginAbsoluteSequence(startDelay + break_close_late, true)) - LeaveBreak(); - - foreach (var breakInfo in beatmap.Breaks) - { - if (breakInfo.HasEffect) - { - using (BeginAbsoluteSequence(breakInfo.StartTime - break_open_early, true)) - { - EnterBreak(); - using (BeginDelayedSequence(breakInfo.Duration + break_open_early + break_close_late, true)) - LeaveBreak(); - } - } - } - } - - public void EnterBreak() - { - this.TransformTo(nameof(targetBreakMultiplier), 0f, 1000, Easing.OutSine); - } - - public void LeaveBreak() - { - this.TransformTo(nameof(targetBreakMultiplier), 1f, 2500, Easing.OutBounce); - } - - /// - /// Value between 0 and 1 setting a maximum "closedness" for the blinds. - /// Useful for animating how far the blinds can be opened while keeping them at the original position if they are wider open than this. - /// - public float TargetClamp - { - get => targetClamp; - set => targetClamp = value; - } - - /// - /// Health between 0 and 1 for the blinds to base the width on. Will get animated for 200ms using out-quintic easing. - /// - public void AnimateTarget(float value) - { - target = value; - this.TransformTo(nameof(easing), target, 200, Easing.OutQuint); - } - - public float Target - { - get => target; - } - - public Texture PanelTexture - { - set - { - panelLeft.Texture = value; - panelRight.Texture = value; - bgPanelLeft.Texture = value; - bgPanelRight.Texture = value; - } - } - } -} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs deleted file mode 100644 index c6e2db1842..0000000000 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ModBlindsPanelSprite.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces -{ - public class ModBlindsPanelSprite : Sprite - { - public ModBlindsPanelSprite() - { - RelativeSizeAxes = Axes.None; - Anchor = Anchor.TopLeft; - } - - protected override void Update() - { - Height = Parent?.DrawHeight ?? 0; - if (Height == 0 || Texture == null) - Width = 0; - else - Width = Texture.Width / (float)Texture.Height * Height; - } - } -} From 3892454eccfd936dcb8de103b2d21167a881ce0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 15 Dec 2018 16:34:48 +0900 Subject: [PATCH 16/54] Improve the way text search works at song select --- .../Screens/Select/Carousel/CarouselBeatmap.cs | 6 +++--- osu.Game/Screens/Select/FilterCriteria.cs | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 272332f1ce..0f548bb667 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -26,10 +26,10 @@ namespace osu.Game.Screens.Select.Carousel bool match = criteria.Ruleset == null || Beatmap.RulesetID == criteria.Ruleset.ID || Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps; - if (!string.IsNullOrEmpty(criteria.SearchText)) + foreach (var criteriaTerm in criteria.SearchTerms) match &= - Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteria.SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0) || - Beatmap.Version.IndexOf(criteria.SearchText, StringComparison.InvariantCultureIgnoreCase) >= 0; + Beatmap.Metadata.SearchableTerms.Any(term => term.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0) || + Beatmap.Version.IndexOf(criteriaTerm, StringComparison.InvariantCultureIgnoreCase) >= 0; Filtered.Value = !match; } diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index bea806f00f..71cfedfdbf 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Linq; using osu.Game.Rulesets; using osu.Game.Screens.Select.Filter; @@ -10,8 +12,22 @@ namespace osu.Game.Screens.Select { public GroupMode Group; public SortMode Sort; - public string SearchText; + + public string[] SearchTerms = Array.Empty(); + public RulesetInfo Ruleset; public bool AllowConvertedBeatmaps; + + private string searchText; + + public string SearchText + { + get { return searchText; } + set + { + searchText = value; + SearchTerms = searchText.Split(',', ' ', '!').Where(s => !string.IsNullOrEmpty(s)).ToArray(); + } + } } } From eec5afa3828ac70ea639b39030f431d156e25eb4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 15 Dec 2018 16:37:37 +0900 Subject: [PATCH 17/54] Change inspection and add redundant parenthesis to appease codefactor --- osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs | 2 +- osu.sln.DotSettings | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 0f548bb667..49779f5e11 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Select.Carousel { base.Filter(criteria); - bool match = criteria.Ruleset == null || Beatmap.RulesetID == criteria.Ruleset.ID || Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps; + bool match = criteria.Ruleset == null || Beatmap.RulesetID == criteria.Ruleset.ID || (Beatmap.RulesetID == 0 && criteria.Ruleset.ID > 0 && criteria.AllowConvertedBeatmaps); foreach (var criteriaTerm in criteria.SearchTerms) match &= diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index d6882282e6..112efb37f0 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -8,7 +8,8 @@ SOLUTION HINT WARNING - WARNING + + True WARNING WARNING HINT From 00998d54432abb578b711a441cb1badb4ede0ba0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 17 Dec 2018 14:29:11 +0900 Subject: [PATCH 18/54] Fix web requests not getting correctly handled on first connection --- osu.Game/Online/API/APIAccess.cs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index a1376b8b94..57dd4f1568 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -141,7 +141,7 @@ namespace osu.Game.Online.API State = APIState.Online; }; - if (!handleRequest(userReq)) + if (!handleRequest(userReq, out _)) { Thread.Sleep(500); continue; @@ -170,11 +170,15 @@ namespace osu.Game.Online.API lock (queue) { if (queue.Count == 0) break; - req = queue.Dequeue(); + req = queue.Peek(); } // TODO: handle failures better - handleRequest(req); + handleRequest(req, out var removeFromQueue); + + if (removeFromQueue) + lock (queue) + queue.Dequeue(); } Thread.Sleep(50); @@ -193,9 +197,11 @@ namespace osu.Game.Online.API /// Handle a single API request. /// /// The request. - /// true if we should remove this request from the queue. - private bool handleRequest(APIRequest req) + /// true if the request succeeded. + private bool handleRequest(APIRequest req, out bool removeFromQueue) { + removeFromQueue = true; + try { Logger.Log($@"Performing request {req}", LoggingTarget.Network); @@ -216,12 +222,12 @@ namespace osu.Game.Online.API } catch (WebException we) { - var removeFromQueue = handleWebException(we); + removeFromQueue = handleWebException(we); if (removeFromQueue) req.Fail(we); - return removeFromQueue; + return false; } catch (Exception e) { @@ -229,7 +235,7 @@ namespace osu.Game.Online.API log.Add(@"API level timeout exception was hit"); req.Fail(e); - return true; + return false; } } From 6088612a266b0fd407d015849262236d0ff548ed Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 18 Dec 2018 20:19:40 +0900 Subject: [PATCH 19/54] Remove all retry logic and simplify overall handling of API requests --- osu.Game/Online/API/APIAccess.cs | 38 +++++++------------------------ osu.Game/Online/API/APIRequest.cs | 35 ++++++++++------------------ 2 files changed, 20 insertions(+), 53 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 57dd4f1568..7786bb55ac 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -102,7 +102,7 @@ namespace osu.Game.Online.API if (queue.Count == 0) { log.Add(@"Queueing a ping request"); - Queue(new ListChannelsRequest { Timeout = 5000 }); + Queue(new GetUserRequest()); } break; @@ -141,7 +141,7 @@ namespace osu.Game.Online.API State = APIState.Online; }; - if (!handleRequest(userReq, out _)) + if (!handleRequest(userReq)) { Thread.Sleep(500); continue; @@ -170,15 +170,10 @@ namespace osu.Game.Online.API lock (queue) { if (queue.Count == 0) break; - req = queue.Peek(); + req = queue.Dequeue(); } - // TODO: handle failures better - handleRequest(req, out var removeFromQueue); - - if (removeFromQueue) - lock (queue) - queue.Dequeue(); + handleRequest(req); } Thread.Sleep(50); @@ -195,46 +190,29 @@ namespace osu.Game.Online.API /// /// Handle a single API request. + /// Ensures all exceptions are caught and dealt with correctly. /// /// The request. /// true if the request succeeded. - private bool handleRequest(APIRequest req, out bool removeFromQueue) + private bool handleRequest(APIRequest req) { - removeFromQueue = true; - try { - Logger.Log($@"Performing request {req}", LoggingTarget.Network); - req.Failure += ex => - { - if (ex is WebException we) - handleWebException(we); - }; - req.Perform(this); //we could still be in initialisation, at which point we don't want to say we're Online yet. - if (IsLoggedIn) - State = APIState.Online; + if (IsLoggedIn) State = APIState.Online; failureCount = 0; return true; } catch (WebException we) { - removeFromQueue = handleWebException(we); - - if (removeFromQueue) - req.Fail(we); - + handleWebException(we); return false; } catch (Exception e) { - if (e is TimeoutException) - log.Add(@"API level timeout exception was hit"); - - req.Fail(e); return false; } } diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index adbedb2aac..41f774e83c 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.IO.Network; +using osu.Framework.Logging; namespace osu.Game.Online.API { @@ -35,23 +36,12 @@ namespace osu.Game.Online.API /// public abstract class APIRequest { - /// - /// The maximum amount of time before this request will fail. - /// - public int Timeout = WebRequest.DEFAULT_TIMEOUT; - protected virtual string Target => string.Empty; protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri); protected virtual string Uri => $@"{API.Endpoint}/api/v2/{Target}"; - private double remainingTime => Math.Max(0, Timeout - (DateTimeOffset.UtcNow - (startTime ?? DateTimeOffset.MinValue)).TotalMilliseconds); - - public bool ExceededTimeout => remainingTime == 0; - - private DateTimeOffset? startTime; - protected APIAccess API; protected WebRequest WebRequest; @@ -75,27 +65,24 @@ namespace osu.Game.Online.API { API = api; - if (checkAndProcessFailure()) + if (checkAndScheduleFailure()) return; - if (startTime == null) - startTime = DateTimeOffset.UtcNow; - - if (remainingTime <= 0) - throw new TimeoutException(@"API request timeout hit"); - WebRequest = CreateWebRequest(); WebRequest.Failed += Fail; WebRequest.AllowRetryOnTimeout = false; WebRequest.AddHeader("Authorization", $"Bearer {api.AccessToken}"); - if (checkAndProcessFailure()) + if (checkAndScheduleFailure()) return; if (!WebRequest.Aborted) //could have been aborted by a Cancel() call + { + Logger.Log($@"Performing request {this}", LoggingTarget.Network); WebRequest.Perform(); + } - if (checkAndProcessFailure()) + if (checkAndScheduleFailure()) return; api.Schedule(delegate { Success?.Invoke(); }); @@ -105,19 +92,21 @@ namespace osu.Game.Online.API public void Fail(Exception e) { - cancelled = true; + if (cancelled) return; + cancelled = true; WebRequest?.Abort(); + Logger.Log($@"Failing request {this} ({e})", LoggingTarget.Network); pendingFailure = () => Failure?.Invoke(e); - checkAndProcessFailure(); + checkAndScheduleFailure(); } /// /// Checked for cancellation or error. Also queues up the Failed event if we can. /// /// Whether we are in a failed or cancelled state. - private bool checkAndProcessFailure() + private bool checkAndScheduleFailure() { if (API == null || pendingFailure == null) return cancelled; From 4a1af67893d7959ebe77353ec5cf63afa8d0407f Mon Sep 17 00:00:00 2001 From: Roman Kapustin Date: Tue, 18 Dec 2018 22:49:53 +0300 Subject: [PATCH 20/54] Do not delete file on import failure --- osu.Game/Database/ArchiveModelManager.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 50767608af..77e92421e9 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -149,8 +149,10 @@ namespace osu.Game.Database try { notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}"; + + TModel import; using (ArchiveReader reader = getReaderFrom(path)) - imported.Add(Import(reader)); + imported.Add(import = Import(reader)); notification.Progress = (float)current / paths.Length; @@ -160,7 +162,7 @@ namespace osu.Game.Database // TODO: Add a check to prevent files from storage to be deleted. try { - if (File.Exists(path)) + if (import != null && File.Exists(path)) File.Delete(path); } catch (Exception e) From bacc07f5ec3ed6e873a6872bd1f2b406d6e4d80f Mon Sep 17 00:00:00 2001 From: Styphix Date: Tue, 18 Dec 2018 19:44:31 -0500 Subject: [PATCH 21/54] Changed `OsuFocusedOverlayContainer` to `WaveOverlayContainer` from `ChannelSelectionOverlay` Not sure what colour i shouldve gone for but im certain that this should be fine --- .../Overlays/Chat/Selection/ChannelSelectionOverlay.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs index 3afac211f1..0cc0076903 100644 --- a/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/Selection/ChannelSelectionOverlay.cs @@ -20,7 +20,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Chat.Selection { - public class ChannelSelectionOverlay : OsuFocusedOverlayContainer + public class ChannelSelectionOverlay : WaveOverlayContainer { public static readonly float WIDTH_PADDING = 170; @@ -39,6 +39,11 @@ namespace osu.Game.Overlays.Chat.Selection { RelativeSizeAxes = Axes.X; + Waves.FirstWaveColour = OsuColour.FromHex("353535"); + Waves.SecondWaveColour = OsuColour.FromHex("434343"); + Waves.ThirdWaveColour = OsuColour.FromHex("515151"); + Waves.FourthWaveColour = OsuColour.FromHex("595959"); + Children = new Drawable[] { new Container From 9d8170efa0b69bcbbbccd172130013d248a43428 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 19 Dec 2018 14:32:43 +0900 Subject: [PATCH 22/54] Only go into failing state if previously online --- osu.Game/Online/API/APIAccess.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 7786bb55ac..cfbcf0326a 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -268,8 +268,12 @@ namespace osu.Game.Online.API //we might try again at an api level. return false; - State = APIState.Failing; - flushQueue(); + if (State == APIState.Online) + { + State = APIState.Failing; + flushQueue(); + } + return true; } From 8d458b0b6e6d5d57047834bfcb06c5576efe7566 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Dec 2018 19:45:50 +0900 Subject: [PATCH 23/54] Update framework version --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 305c9035b5..8ae644c06a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - + From 07f8b8e334d398de787475881cf34b698c7ba879 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Dec 2018 15:47:26 +0900 Subject: [PATCH 24/54] Update resources --- osu-resources | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-resources b/osu-resources index 694cb03f19..9880089b4e 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 694cb03f19c93106ed0f2593f3e506e835fb652a +Subproject commit 9880089b4e8fcd78d68f30c8a40d43bf8dccca86 From bb850da2002a9322437fbd209bd139f0e0435bd2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Dec 2018 15:48:45 +0900 Subject: [PATCH 25/54] Remove skinnability for now Not sure if we want this going forward. --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 4078dce643..ec2839b4ad 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -14,7 +14,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -235,15 +234,12 @@ namespace osu.Game.Rulesets.Osu.Mods /// public void AnimateClosedness(float value) => this.TransformTo(nameof(easing), value, 200, Easing.OutQuint); - public class ModBlindsPanel : CompositeDrawable + public class ModBlindsPanel : Sprite { [BackgroundDependencyLoader] private void load(TextureStore textures) { - InternalChild = new SkinnableDrawable("Play/osu/blinds-panel", s => new Sprite { Texture = textures.Get("Play/osu/blinds-panel") }) - { - RelativeSizeAxes = Axes.Both, - }; + Texture = textures.Get("Play/osu/blinds-panel"); } } } From 3a13899ce1d48881cc60035d31a8a9fba20e0b1e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Dec 2018 16:50:38 +0900 Subject: [PATCH 26/54] Add stand-alone chat component --- .../Visual/TestCaseMatchChatDisplay.cs | 83 +++++++++ osu.Game/Online/Chat/StandAloneChatDisplay.cs | 175 ++++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 osu.Game.Tests/Visual/TestCaseMatchChatDisplay.cs create mode 100644 osu.Game/Online/Chat/StandAloneChatDisplay.cs diff --git a/osu.Game.Tests/Visual/TestCaseMatchChatDisplay.cs b/osu.Game.Tests/Visual/TestCaseMatchChatDisplay.cs new file mode 100644 index 0000000000..c959eeb3d4 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseMatchChatDisplay.cs @@ -0,0 +1,83 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Game.Online.Chat; +using osu.Game.Users; +using osuTK; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseStandAloneChatDisplay : OsuTestCase + { + private readonly Channel testChannel = new Channel(); + + private readonly User admin = new User + { + Username = "HappyStick", + Id = 2, + Colour = "f2ca34" + }; + + private readonly User redUser = new User + { + Username = "BanchoBot", + Id = 3, + }; + + private readonly User blueUser = new User + { + Username = "Zallius", + Id = 4, + }; + + public TestCaseStandAloneChatDisplay() + { + StandAloneChatDisplay chatDisplay; + + Add(chatDisplay = new StandAloneChatDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(400, 80) + }); + + chatDisplay.Channel.Value = testChannel; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + AddStep("message from admin", () => testChannel.AddLocalEcho(new LocalEchoMessage + { + Sender = admin, + Content = "I am a wang!" + })); + + AddStep("message from team red", () => testChannel.AddLocalEcho(new LocalEchoMessage + { + Sender = redUser, + Content = "I am team red." + })); + + AddStep("message from team red", () => testChannel.AddLocalEcho(new LocalEchoMessage + { + Sender = redUser, + Content = "I plan to win!" + })); + + AddStep("message from team blue", () => testChannel.AddLocalEcho(new LocalEchoMessage + { + Sender = blueUser, + Content = "Not on my watch. Prepare to eat saaaaaaaaaand. Lots and lots of saaaaaaand." + })); + + AddStep("message from admin", () => testChannel.AddLocalEcho(new LocalEchoMessage + { + Sender = admin, + Content = "Okay okay, calm down guys. Let's do this!" + })); + } + } +} diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs new file mode 100644 index 0000000000..30a730c5a1 --- /dev/null +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -0,0 +1,175 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Online.Chat +{ + /// + /// Display a chat channel in an insolated region. + /// + public class StandAloneChatDisplay : CompositeDrawable + { + public readonly Bindable Channel = new Bindable(); + private readonly FillFlowContainer messagesFlow; + private Channel lastChannel; + + public StandAloneChatDisplay() + { + CornerRadius = 10; + Masking = true; + + InternalChildren = new Drawable[] + { + new Box + { + Colour = Color4.Black, + Alpha = 0.8f, + RelativeSizeAxes = Axes.Both + }, + messagesFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + LayoutEasing = Easing.Out, + LayoutDuration = 500, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Direction = FillDirection.Vertical + } + }; + + Channel.BindValueChanged(channelChanged); + } + + public void Contract() + { + this.FadeIn(300); + this.MoveToY(0, 500, Easing.OutQuint); + } + + public void Expand() + { + this.FadeOut(200); + this.MoveToY(100, 500, Easing.In); + } + + protected virtual Drawable CreateMessage(Message message) + { + return new StandAloneMessage(message); + } + + private void channelChanged(Channel channel) + { + if (lastChannel != null) + lastChannel.NewMessagesArrived -= newMessages; + + lastChannel = channel; + messagesFlow.Clear(); + + if (channel == null) return; + + channel.NewMessagesArrived += newMessages; + } + + private void newMessages(IEnumerable messages) + { + var excessChildren = messagesFlow.Children.Count - 10; + if (excessChildren > 0) + foreach (var c in messagesFlow.Children.Take(excessChildren)) + c.Expire(); + + foreach (var message in messages) + { + var formatted = MessageFormatter.FormatMessage(message); + var drawable = CreateMessage(formatted); + drawable.Y = messagesFlow.Height; + messagesFlow.Add(drawable); + } + } + + protected class StandAloneMessage : CompositeDrawable + { + protected readonly Message Message; + protected OsuSpriteText SenderText; + protected Circle ColourBox; + + public StandAloneMessage(Message message) + { + Message = message; + } + + [BackgroundDependencyLoader] + private void load() + { + Margin = new MarginPadding(3); + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + InternalChildren = new Drawable[] + { + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + Width = 0.2f, + Children = new Drawable[] + { + SenderText = new OsuSpriteText + { + Font = @"Exo2.0-Bold", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Text = Message.Sender.ToString() + } + } + }, + new Container + { + Size = new Vector2(8, OsuSpriteText.FONT_SIZE), + Margin = new MarginPadding { Horizontal = 3 }, + Children = new Drawable[] + { + ColourBox = new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(8) + } + } + }, + new OsuTextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.5f, + Text = Message.DisplayContent + } + } + } + }; + + if (!string.IsNullOrEmpty(Message.Sender.Colour)) + SenderText.Colour = ColourBox.Colour = OsuColour.FromHex(Message.Sender.Colour); + } + } + } +} From 65447d6f4afcab996dabc0acf74856acbfa0c6f3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Dec 2018 17:01:08 +0900 Subject: [PATCH 27/54] Add optional parameters to target messages at a specific channel --- osu.Game/Online/Chat/ChannelManager.cs | 44 +++++++++++++++----------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index a63af0f7a3..4241b47cd3 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -96,12 +96,14 @@ namespace osu.Game.Online.Chat /// /// The message text that is going to be posted /// Is true if the message is an action, e.g.: user is currently eating - public void PostMessage(string text, bool isAction = false) + /// An optional target channel. If null, will be used. + public void PostMessage(string text, bool isAction = false, Channel target = null) { - if (CurrentChannel.Value == null) - return; + if (target == null) + target = CurrentChannel.Value; - var currentChannel = CurrentChannel.Value; + if (target == null) + return; void dequeueAndRun() { @@ -113,7 +115,7 @@ namespace osu.Game.Online.Chat { if (!api.IsLoggedIn) { - currentChannel.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); + target.AddNewMessages(new ErrorMessage("Please sign in to participate in chat!")); return; } @@ -121,29 +123,29 @@ namespace osu.Game.Online.Chat { Sender = api.LocalUser.Value, Timestamp = DateTimeOffset.Now, - ChannelId = CurrentChannel.Value.Id, + ChannelId = target.Id, IsAction = isAction, Content = text }; - currentChannel.AddLocalEcho(message); + target.AddLocalEcho(message); // if this is a PM and the first message, we need to do a special request to create the PM channel - if (currentChannel.Type == ChannelType.PM && !currentChannel.Joined) + if (target.Type == ChannelType.PM && !target.Joined) { - var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(currentChannel.Users.First(), message); + var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(target.Users.First(), message); createNewPrivateMessageRequest.Success += createRes => { - currentChannel.Id = createRes.ChannelID; - currentChannel.ReplaceMessage(message, createRes.Message); + target.Id = createRes.ChannelID; + target.ReplaceMessage(message, createRes.Message); dequeueAndRun(); }; createNewPrivateMessageRequest.Failure += exception => { Logger.Error(exception, "Posting message failed."); - currentChannel.ReplaceMessage(message, null); + target.ReplaceMessage(message, null); dequeueAndRun(); }; @@ -155,14 +157,14 @@ namespace osu.Game.Online.Chat req.Success += m => { - currentChannel.ReplaceMessage(message, m); + target.ReplaceMessage(message, m); dequeueAndRun(); }; req.Failure += exception => { Logger.Error(exception, "Posting message failed."); - currentChannel.ReplaceMessage(message, null); + target.ReplaceMessage(message, null); dequeueAndRun(); }; @@ -178,9 +180,13 @@ namespace osu.Game.Online.Chat /// Posts a command locally. Commands like /help will result in a help message written in the current channel. /// /// the text containing the command identifier and command parameters. - public void PostCommand(string text) + /// An optional target channel. If null, will be used. + public void PostCommand(string text, Channel target = null) { - if (CurrentChannel.Value == null) + if (target == null) + target = CurrentChannel.Value; + + if (target == null) return; var parameters = text.Split(new[] { ' ' }, 2); @@ -192,7 +198,7 @@ namespace osu.Game.Online.Chat case "me": if (string.IsNullOrWhiteSpace(content)) { - CurrentChannel.Value.AddNewMessages(new ErrorMessage("Usage: /me [action]")); + target.AddNewMessages(new ErrorMessage("Usage: /me [action]")); break; } @@ -200,11 +206,11 @@ namespace osu.Game.Online.Chat break; case "help": - CurrentChannel.Value.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); + target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]")); break; default: - CurrentChannel.Value.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help")); + target.AddNewMessages(new ErrorMessage($@"""/{command}"" is not supported! For a list of supported commands see /help")); break; } } From bc8b0485d8cf58772d76aae94060baaded4a9430 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Dec 2018 18:21:37 +0900 Subject: [PATCH 28/54] Add textbox/posting support --- ...ay.cs => TestCaseStandAloneChatDisplay.cs} | 27 ++++++++-- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 54 ++++++++++++++++++- 2 files changed, 76 insertions(+), 5 deletions(-) rename osu.Game.Tests/Visual/{TestCaseMatchChatDisplay.cs => TestCaseStandAloneChatDisplay.cs} (73%) diff --git a/osu.Game.Tests/Visual/TestCaseMatchChatDisplay.cs b/osu.Game.Tests/Visual/TestCaseStandAloneChatDisplay.cs similarity index 73% rename from osu.Game.Tests/Visual/TestCaseMatchChatDisplay.cs rename to osu.Game.Tests/Visual/TestCaseStandAloneChatDisplay.cs index c959eeb3d4..16ce720ab1 100644 --- a/osu.Game.Tests/Visual/TestCaseMatchChatDisplay.cs +++ b/osu.Game.Tests/Visual/TestCaseStandAloneChatDisplay.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Online.Chat; using osu.Game.Users; @@ -31,24 +32,42 @@ namespace osu.Game.Tests.Visual Id = 4, }; + [Cached] + private ChannelManager channelManager = new ChannelManager(); + + private readonly StandAloneChatDisplay chatDisplay; + private readonly StandAloneChatDisplay chatDisplay2; + public TestCaseStandAloneChatDisplay() { - StandAloneChatDisplay chatDisplay; + Add(channelManager); Add(chatDisplay = new StandAloneChatDisplay { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding(20), Size = new Vector2(400, 80) }); - chatDisplay.Channel.Value = testChannel; + Add(chatDisplay2 = new StandAloneChatDisplay(true) + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Margin = new MarginPadding(20), + Size = new Vector2(400, 150) + }); } protected override void LoadComplete() { base.LoadComplete(); + channelManager.CurrentChannel.Value = testChannel; + + chatDisplay.Channel.Value = testChannel; + chatDisplay2.Channel.Value = testChannel; + AddStep("message from admin", () => testChannel.AddLocalEcho(new LocalEchoMessage { Sender = admin, diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 30a730c5a1..cb4bf9fdf8 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -8,9 +8,11 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osuTK; using osuTK.Graphics; @@ -22,10 +24,20 @@ namespace osu.Game.Online.Chat public class StandAloneChatDisplay : CompositeDrawable { public readonly Bindable Channel = new Bindable(); + private readonly FillFlowContainer messagesFlow; + private Channel lastChannel; - public StandAloneChatDisplay() + private readonly FocusedTextBox textbox; + + protected ChannelManager ChannelManager; + + /// + /// Construct a new instance. + /// + /// Whether a textbox for posting new messages should be displayed. + public StandAloneChatDisplay(bool postingTextbox = false) { CornerRadius = 10; Masking = true; @@ -50,9 +62,49 @@ namespace osu.Game.Online.Chat } }; + const float textbox_height = 30; + + if (postingTextbox) + { + messagesFlow.Y -= textbox_height; + AddInternal(textbox = new FocusedTextBox + { + RelativeSizeAxes = Axes.X, + Height = textbox_height, + PlaceholderText = "type your message", + OnCommit = postMessage, + ReleaseFocusOnCommit = false, + HoldFocus = true, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + }); + } + Channel.BindValueChanged(channelChanged); } + [BackgroundDependencyLoader(true)] + private void load(ChannelManager manager) + { + if (ChannelManager == null) + ChannelManager = manager; + } + + private void postMessage(TextBox sender, bool newtext) + { + var text = textbox.Text.Trim(); + + if (string.IsNullOrWhiteSpace(text)) + return; + + if (text[0] == '/') + ChannelManager?.PostCommand(text.Substring(1)); + else + ChannelManager?.PostMessage(text); + + textbox.Text = string.Empty; + } + public void Contract() { this.FadeIn(300); From d3368df94dd3338cbe611304185a773b89bf7df6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Dec 2018 19:35:32 +0900 Subject: [PATCH 29/54] Simplify changes to RulesetContainer --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 4 +-- osu.Game/Rulesets/UI/RulesetContainer.cs | 35 ++++++++++------------ osu.Game/Screens/Play/Player.cs | 1 - 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index ec2839b4ad..f6c2c1215d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -35,8 +35,8 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) { - bool hasEasy = rulesetContainer.ActiveMods.Any(m => m is ModEasy); - bool hasHardrock = rulesetContainer.ActiveMods.Any(m => m is ModHardRock); + bool hasEasy = rulesetContainer.Mods.Any(m => m is ModEasy); + bool hasHardrock = rulesetContainer.Mods.Any(m => m is ModHardRock); rulesetContainer.Overlays.Add(blinds = new DrawableOsuBlinds(rulesetContainer.Playfield.HitObjectContainer, hasEasy, hasHardrock, rulesetContainer.Beatmap)); } diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 67bcb7581f..22d4eee5e4 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.UI /// /// Place to put drawables above hit objects but below UI. /// - public readonly Container Overlays; + public Container Overlays { get; protected set; } /// /// The cursor provided by this . May be null if no cursor is provided. @@ -92,12 +92,6 @@ namespace osu.Game.Rulesets.UI { Ruleset = ruleset; playfield = new Lazy(CreatePlayfield); - Overlays = new Container - { - RelativeSizeAxes = Axes.Both, - Width = 1, - Height = 1 - }; IsPaused.ValueChanged += paused => { @@ -215,7 +209,7 @@ namespace osu.Game.Rulesets.UI /// /// The mods which are to be applied. /// - protected IEnumerable Mods; + public IEnumerable Mods { get; protected set; } /// /// The this was created with. @@ -226,7 +220,6 @@ namespace osu.Game.Rulesets.UI protected override Container Content => content; private Container content; - private IEnumerable mods; /// /// Whether to assume the beatmap passed into this is for the current ruleset. @@ -256,17 +249,24 @@ namespace osu.Game.Rulesets.UI [BackgroundDependencyLoader] private void load(OsuConfigManager config) { - KeyBindingInputManager.Add(content = new Container + KeyBindingInputManager.Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - }); - - AddInternal(KeyBindingInputManager); - KeyBindingInputManager.Add(Playfield); + content = new Container + { + RelativeSizeAxes = Axes.Both, + }, + Playfield + }; if (Cursor != null) KeyBindingInputManager.Add(Cursor); + InternalChildren = new Drawable[] + { + KeyBindingInputManager, + Overlays = new Container { RelativeSizeAxes = Axes.Both } + }; + // Apply mods applyRulesetMods(Mods, config); @@ -324,11 +324,6 @@ namespace osu.Game.Rulesets.UI mod.ApplyToDrawableHitObjects(Playfield.HitObjectContainer.Objects); } - /// - /// Returns the currently selected mods for this ruleset container. - /// - public IEnumerable ActiveMods { get => Mods; } - /// /// Creates and adds the visual representation of a to this . /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index fd2c94e814..19b49b099c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -184,7 +184,6 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, Child = RulesetContainer }, - RulesetContainer.Overlays, new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor) { Anchor = Anchor.Centre, From ef9d93ff6b93ec3eacd500e509728f2f9ca04eea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Dec 2018 19:46:39 +0900 Subject: [PATCH 30/54] Remove mod multipliers We decided that mods shouldn't be interacting with other mods. This can be added once we have the ability to have per-mod settings, as a difficulty setting local to blinds. --- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 71 +++------------------- osu.Game/Rulesets/UI/RulesetContainer.cs | 2 +- 2 files changed, 10 insertions(+), 63 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index f6c2c1215d..cc2102f0e9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -35,10 +34,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) { - bool hasEasy = rulesetContainer.Mods.Any(m => m is ModEasy); - bool hasHardrock = rulesetContainer.Mods.Any(m => m is ModHardRock); - - rulesetContainer.Overlays.Add(blinds = new DrawableOsuBlinds(rulesetContainer.Playfield.HitObjectContainer, hasEasy, hasHardrock, rulesetContainer.Beatmap)); + rulesetContainer.Overlays.Add(blinds = new DrawableOsuBlinds(rulesetContainer.Playfield.HitObjectContainer, rulesetContainer.Beatmap)); } public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) @@ -69,12 +65,7 @@ namespace osu.Game.Rulesets.Osu.Mods private readonly float targetBreakMultiplier = 0; private readonly float easing = 1; - private const float black_depth = 10; - private const float bg_panel_depth = 8; - private const float fg_panel_depth = 4; - private readonly CompositeDrawable restrictTo; - private readonly bool modEasy, modHardrock; /// /// @@ -89,13 +80,10 @@ namespace osu.Game.Rulesets.Osu.Mods /// private const float leniency = 0.1f; - public DrawableOsuBlinds(CompositeDrawable restrictTo, bool hasEasy, bool hasHardrock, Beatmap beatmap) + public DrawableOsuBlinds(CompositeDrawable restrictTo, Beatmap beatmap) { this.restrictTo = restrictTo; this.beatmap = beatmap; - - modEasy = hasEasy; - modHardrock = hasHardrock; } [BackgroundDependencyLoader] @@ -111,9 +99,6 @@ namespace osu.Game.Rulesets.Osu.Mods Origin = Anchor.TopLeft, Colour = Color4.Black, RelativeSizeAxes = Axes.Y, - Width = 0, - Height = 1, - Depth = black_depth }, blackBoxRight = new Box { @@ -121,60 +106,22 @@ namespace osu.Game.Rulesets.Osu.Mods Origin = Anchor.TopRight, Colour = Color4.Black, RelativeSizeAxes = Axes.Y, - Width = 0, - Height = 1, - Depth = black_depth }, bgPanelLeft = new ModBlindsPanel { Origin = Anchor.TopRight, Colour = Color4.Gray, - Depth = bg_panel_depth + 1 - }, - panelLeft = new ModBlindsPanel - { - Origin = Anchor.TopRight, - Depth = bg_panel_depth - }, - bgPanelRight = new ModBlindsPanel - { - Origin = Anchor.TopLeft, - Colour = Color4.Gray, - Depth = fg_panel_depth + 1 - }, - panelRight = new ModBlindsPanel - { - Origin = Anchor.TopLeft, - Depth = fg_panel_depth }, + panelLeft = new ModBlindsPanel { Origin = Anchor.TopRight, }, + bgPanelRight = new ModBlindsPanel { Colour = Color4.Gray }, + panelRight = new ModBlindsPanel() }; } - private float applyGap(float value) - { - const float easy_multiplier = 0.95f; - const float hardrock_multiplier = 1.1f; + private float calculateGap(float value) => MathHelper.Clamp(value, 0, target_clamp) * targetBreakMultiplier; - float multiplier = 1; - if (modEasy) - { - multiplier = easy_multiplier; - // TODO: include OD/CS - } - else if (modHardrock) - { - multiplier = hardrock_multiplier; - // TODO: include OD/CS - } - - return MathHelper.Clamp(value * multiplier, 0, target_clamp) * targetBreakMultiplier; - } - - private static float applyAdjustmentCurve(float value) - { - // lagrange polinominal for (0,0) (0.5,0.35) (1,1) should make a good curve - return 0.6f * value * value + 0.4f * value; - } + // lagrange polinominal for (0,0) (0.6,0.4) (1,1) should make a good curve + private static float applyAdjustmentCurve(float value) => 0.6f * value * value + 0.4f * value; protected override void Update() { @@ -186,7 +133,7 @@ namespace osu.Game.Rulesets.Osu.Mods start -= rawWidth * leniency * 0.5f; end += rawWidth * leniency * 0.5f; - float width = (end - start) * 0.5f * applyAdjustmentCurve(applyGap(easing)); + float width = (end - start) * 0.5f * applyAdjustmentCurve(calculateGap(easing)); // different values in case the playfield ever moves from center to somewhere else. blackBoxLeft.Width = start + width; diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 22d4eee5e4..56222ff282 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.UI /// /// The mods which are to be applied. /// - public IEnumerable Mods { get; protected set; } + protected IEnumerable Mods; /// /// The this was created with. From 36f57a7abbd4155a31c3db8f4d2b4d03b5cea7ce Mon Sep 17 00:00:00 2001 From: Paul Teng Date: Thu, 20 Dec 2018 05:49:05 -0500 Subject: [PATCH 31/54] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index baaba22726..a2f6472371 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ We are accepting bug reports (please report with as much detail as possible). Fe # Requirements -- A desktop platform with the [.NET Core SDK 2.1](https://www.microsoft.com/net/learn/get-started) or higher installed. +- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed. - When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio Code](https://code.visualstudio.com/) (with the C# plugin installed) or [Jetbrains Rider](https://www.jetbrains.com/rider/) (commercial). # Building and running From aaac45ab8cb384402c068dcc2226121fdf0affbf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Dec 2018 21:06:40 +0900 Subject: [PATCH 32/54] Add ability to select chat tabs with alt-1-9 --- osu.Game/Graphics/UserInterface/Nub.cs | 16 ++++++++++- osu.Game/Overlays/ChatOverlay.cs | 37 ++++++++++++++++++++++++- osu.Game/Overlays/Volume/MuteButton.cs | 16 ++++++++++- osu.Game/Screens/Play/HUD/ModDisplay.cs | 24 ++++++++++++---- 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index 974708af97..b715c80fb6 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osuTK; using osuTK.Graphics; using osu.Framework.Allocation; @@ -99,7 +100,20 @@ namespace osu.Game.Graphics.UserInterface } } - public Bindable Current { get; } = new Bindable(); + private readonly Bindable current = new Bindable(); + + public Bindable Current + { + get => current; + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + current.UnbindBindings(); + current.BindTo(value); + } + } private Color4 accentColour; public Color4 AccentColour diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 75e22f85d1..680e7ac416 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.Linq; using osuTK; using osuTK.Graphics; using osu.Framework.Allocation; @@ -19,6 +20,7 @@ using osu.Game.Online.Chat; using osu.Game.Overlays.Chat; using osu.Game.Overlays.Chat.Selection; using osu.Game.Overlays.Chat.Tabs; +using osuTK.Input; namespace osu.Game.Overlays { @@ -222,7 +224,7 @@ namespace osu.Game.Overlays else { currentChannelContainer.Clear(false); - Scheduler.Add(() => currentChannelContainer.Add(loaded)); + currentChannelContainer.Add(loaded); } } @@ -262,6 +264,39 @@ namespace osu.Game.Overlays return base.OnDragEnd(e); } + private void selectTab(int index) + { + var channel = channelTabControl.Items.Skip(index).FirstOrDefault(); + if (channel != null && channel.Name != "+") + channelTabControl.Current.Value = channel; + } + + protected override bool OnKeyDown(KeyDownEvent e) + { + if (e.AltPressed) + { + switch (e.Key) + { + case Key.Number1: + case Key.Number2: + case Key.Number3: + case Key.Number4: + case Key.Number5: + case Key.Number6: + case Key.Number7: + case Key.Number8: + case Key.Number9: + selectTab((int)e.Key - (int)Key.Number1); + return true; + case Key.Number0: + selectTab(9); + return true; + } + } + + return base.OnKeyDown(e); + } + public override bool AcceptsFocus => true; protected override void OnFocus(FocusEvent e) diff --git a/osu.Game/Overlays/Volume/MuteButton.cs b/osu.Game/Overlays/Volume/MuteButton.cs index e31b349827..dddfddedef 100644 --- a/osu.Game/Overlays/Volume/MuteButton.cs +++ b/osu.Game/Overlays/Volume/MuteButton.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; @@ -18,7 +19,20 @@ namespace osu.Game.Overlays.Volume { public class MuteButton : Container, IHasCurrentValue { - public Bindable Current { get; } = new Bindable(); + private readonly Bindable current = new Bindable(); + + public Bindable Current + { + get => current; + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + current.UnbindBindings(); + current.BindTo(value); + } + } private Color4 hoveredColour, unhoveredColour; private const float width = 100; diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 9509c7acae..c6f39b4705 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -1,7 +1,9 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -11,7 +13,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osuTK; using osu.Game.Graphics.Containers; -using System.Linq; using osu.Framework.Input.Events; namespace osu.Game.Screens.Play.HUD @@ -20,9 +21,20 @@ namespace osu.Game.Screens.Play.HUD { private const int fade_duration = 1000; - private readonly Bindable> mods = new Bindable>(); + private readonly Bindable> current = new Bindable>(); - public Bindable> Current => mods; + public Bindable> Current + { + get => current; + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + current.UnbindBindings(); + current.BindTo(value); + } + } private readonly FillFlowContainer iconsContainer; private readonly OsuSpriteText unrankedText; @@ -50,7 +62,7 @@ namespace osu.Game.Screens.Play.HUD } }; - mods.ValueChanged += mods => + Current.ValueChanged += mods => { iconsContainer.Clear(); foreach (Mod mod in mods) @@ -66,7 +78,7 @@ namespace osu.Game.Screens.Play.HUD protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - mods.UnbindAll(); + Current.UnbindAll(); } protected override void LoadComplete() @@ -77,7 +89,7 @@ namespace osu.Game.Screens.Play.HUD private void appearTransform() { - if (mods.Value.Any(m => !m.Ranked)) + if (Current.Value.Any(m => !m.Ranked)) unrankedText.FadeInFromZero(fade_duration, Easing.OutQuint); else unrankedText.Hide(); From e26958f90164e3979d97a72458061b4850731561 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Dec 2018 10:23:44 +0900 Subject: [PATCH 33/54] Fix targeting netcoreapp2.1 in many places --- .../runConfigurations/RulesetTests__catch_.xml | 2 +- .../runConfigurations/RulesetTests__mania_.xml | 2 +- .../runConfigurations/RulesetTests__osu__.xml | 2 +- .../runConfigurations/RulesetTests__taiko_.xml | 2 +- .../.idea/runConfigurations/VisualTests.xml | 2 +- .vscode/launch.json | 16 ++++++++-------- .vscode/tasks.json | 6 +----- .../.vscode/launch.json | 4 ++-- .../.vscode/launch.json | 4 ++-- osu.Game.Rulesets.Osu.Tests/.vscode/launch.json | 4 ++-- .../.vscode/launch.json | 4 ++-- 11 files changed, 22 insertions(+), 26 deletions(-) diff --git a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml index 1c988fe6fd..2eff16cc91 100644 --- a/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml +++ b/.idea/.idea.osu/.idea/runConfigurations/RulesetTests__catch_.xml @@ -1,6 +1,6 @@ - public class StandAloneChatDisplay : CompositeDrawable { + private readonly bool postingTextbox; + public readonly Bindable Channel = new Bindable(); - private readonly FillFlowContainer messagesFlow; - - private Channel lastChannel; - private readonly FocusedTextBox textbox; protected ChannelManager ChannelManager; + private ScrollContainer scroll; + + private DrawableChannel drawableChannel; + + private const float textbox_height = 30; + /// /// Construct a new instance. /// /// Whether a textbox for posting new messages should be displayed. public StandAloneChatDisplay(bool postingTextbox = false) { + this.postingTextbox = postingTextbox; CornerRadius = 10; Masking = true; @@ -50,23 +51,11 @@ namespace osu.Game.Online.Chat Alpha = 0.8f, RelativeSizeAxes = Axes.Both }, - messagesFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - LayoutEasing = Easing.Out, - LayoutDuration = 500, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Direction = FillDirection.Vertical - } }; - const float textbox_height = 30; if (postingTextbox) { - messagesFlow.Y -= textbox_height; AddInternal(textbox = new FocusedTextBox { RelativeSizeAxes = Axes.X, @@ -117,112 +106,43 @@ namespace osu.Game.Online.Chat this.MoveToY(100, 500, Easing.In); } - protected virtual Drawable CreateMessage(Message message) - { - return new StandAloneMessage(message); - } + protected virtual ChatLine CreateMessage(Message message) => new StandAloneMessage(message); private void channelChanged(Channel channel) { - if (lastChannel != null) - lastChannel.NewMessagesArrived -= newMessages; - - lastChannel = channel; - messagesFlow.Clear(); + drawableChannel?.Expire(); if (channel == null) return; - channel.NewMessagesArrived += newMessages; - - newMessages(channel.Messages); + AddInternal(drawableChannel = new StandAloneDrawableChannel(channel) + { + CreateChatLineAction = CreateMessage, + Padding = new MarginPadding { Bottom = postingTextbox ? textbox_height : 0 } + }); } - private void newMessages(IEnumerable messages) + protected class StandAloneDrawableChannel : DrawableChannel { - var excessChildren = messagesFlow.Children.Count - 10; - if (excessChildren > 0) - foreach (var c in messagesFlow.Children.Take(excessChildren)) - c.Expire(); + public Func CreateChatLineAction; - foreach (var message in messages) + protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m); + + public StandAloneDrawableChannel(Channel channel) + : base(channel) { - var formatted = MessageFormatter.FormatMessage(message); - var drawable = CreateMessage(formatted); - drawable.Y = messagesFlow.Height; - messagesFlow.Add(drawable); + ChatLineFlow.Padding = new MarginPadding { Horizontal = 0 }; } } - protected class StandAloneMessage : CompositeDrawable + protected class StandAloneMessage : ChatLine { - protected readonly Message Message; - protected OsuSpriteText SenderText; - protected Circle ColourBox; + protected override float TextSize => 15; - public StandAloneMessage(Message message) + protected override float HorizontalPadding => 10; + protected override float MessagePadding => 120; + + public StandAloneMessage(Message message) : base(message) { - Message = message; - } - - [BackgroundDependencyLoader] - private void load() - { - Margin = new MarginPadding(3); - - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - InternalChildren = new Drawable[] - { - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - Width = 0.2f, - Children = new Drawable[] - { - SenderText = new OsuSpriteText - { - Font = @"Exo2.0-Bold", - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Text = Message.Sender.ToString() - } - } - }, - new Container - { - Size = new Vector2(8, OsuSpriteText.FONT_SIZE), - Margin = new MarginPadding { Horizontal = 3 }, - Children = new Drawable[] - { - ColourBox = new Circle - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(8) - } - } - }, - new OsuTextFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Width = 0.5f, - Text = Message.DisplayContent - } - } - } - }; - - if (!string.IsNullOrEmpty(Message.Sender.Colour)) - SenderText.Colour = ColourBox.Colour = OsuColour.FromHex(Message.Sender.Colour); } } } diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index c11de48430..29579056bf 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -1,72 +1,38 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . +// Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; using System.Linq; -using osuTK; -using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.Chat; using osu.Game.Users; -using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.UserInterface; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays.Chat { - public class ChatLine : Container + public class ChatLine : CompositeDrawable { - private static readonly Color4[] username_colours = - { - OsuColour.FromHex("588c7e"), - OsuColour.FromHex("b2a367"), - OsuColour.FromHex("c98f65"), - OsuColour.FromHex("bc5151"), - OsuColour.FromHex("5c8bd6"), - OsuColour.FromHex("7f6ab7"), - OsuColour.FromHex("a368ad"), - OsuColour.FromHex("aa6880"), + public const float LEFT_PADDING = default_message_padding + default_horizontal_padding * 2; - OsuColour.FromHex("6fad9b"), - OsuColour.FromHex("f2e394"), - OsuColour.FromHex("f2ae72"), - OsuColour.FromHex("f98f8a"), - OsuColour.FromHex("7daef4"), - OsuColour.FromHex("a691f2"), - OsuColour.FromHex("c894d3"), - OsuColour.FromHex("d895b0"), + private const float default_message_padding = 200; - OsuColour.FromHex("53c4a1"), - OsuColour.FromHex("eace5c"), - OsuColour.FromHex("ea8c47"), - OsuColour.FromHex("fc4f4f"), - OsuColour.FromHex("3d94ea"), - OsuColour.FromHex("7760ea"), - OsuColour.FromHex("af52c6"), - OsuColour.FromHex("e25696"), + protected virtual float MessagePadding => default_message_padding; - OsuColour.FromHex("677c66"), - OsuColour.FromHex("9b8732"), - OsuColour.FromHex("8c5129"), - OsuColour.FromHex("8c3030"), - OsuColour.FromHex("1f5d91"), - OsuColour.FromHex("4335a5"), - OsuColour.FromHex("812a96"), - OsuColour.FromHex("992861"), - }; + private const float default_horizontal_padding = 15; - public const float LEFT_PADDING = message_padding + padding * 2; + protected virtual float HorizontalPadding => default_horizontal_padding; - private const float padding = 15; - private const float message_padding = 200; - private const float action_padding = 3; - private const float text_size = 20; + protected virtual float TextSize => 20; private Color4 customUsernameColour; @@ -75,14 +41,13 @@ namespace osu.Game.Overlays.Chat public ChatLine(Message message) { Message = message; - + Padding = new MarginPadding { Left = HorizontalPadding, Right = HorizontalPadding }; RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - - Padding = new MarginPadding { Left = padding, Right = padding }; } - private ChannelManager chatManager; + [Resolved(CanBeNull = true)] + private ChannelManager chatManager { get; set; } private Message message; private OsuSpriteText username; @@ -106,10 +71,9 @@ namespace osu.Game.Overlays.Chat } } - [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, ChannelManager chatManager) + [BackgroundDependencyLoader] + private void load(OsuColour colours) { - this.chatManager = chatManager; customUsernameColour = colours.ChatBlue; } @@ -125,7 +89,7 @@ namespace osu.Game.Overlays.Chat { Font = @"Exo2.0-BoldItalic", Colour = hasBackground ? customUsernameColour : username_colours[message.Sender.Id % username_colours.Length], - TextSize = text_size, + TextSize = TextSize, }; if (hasBackground) @@ -163,11 +127,11 @@ namespace osu.Game.Overlays.Chat }; } - Children = new Drawable[] + InternalChildren = new Drawable[] { new Container { - Size = new Vector2(message_padding, text_size), + Size = new Vector2(MessagePadding, TextSize), Children = new Drawable[] { timestamp = new OsuSpriteText @@ -176,7 +140,7 @@ namespace osu.Game.Overlays.Chat Origin = Anchor.CentreLeft, Font = @"Exo2.0-SemiBold", FixedWidth = true, - TextSize = text_size * 0.75f, + TextSize = TextSize * 0.75f, }, new MessageSender(message.Sender) { @@ -191,7 +155,7 @@ namespace osu.Game.Overlays.Chat { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Left = message_padding + padding }, + Padding = new MarginPadding { Left = MessagePadding + HorizontalPadding }, Children = new Drawable[] { contentFlow = new LinkFlowContainer(t => @@ -204,7 +168,7 @@ namespace osu.Game.Overlays.Chat t.Colour = OsuColour.FromHex(message.Sender.Colour); } - t.TextSize = text_size; + t.TextSize = TextSize; }) { AutoSizeAxes = Axes.Y, @@ -257,5 +221,44 @@ namespace osu.Game.Overlays.Chat new OsuMenuItem("Start Chat", MenuItemType.Standard, startChatAction), }; } + + private static readonly Color4[] username_colours = + { + OsuColour.FromHex("588c7e"), + OsuColour.FromHex("b2a367"), + OsuColour.FromHex("c98f65"), + OsuColour.FromHex("bc5151"), + OsuColour.FromHex("5c8bd6"), + OsuColour.FromHex("7f6ab7"), + OsuColour.FromHex("a368ad"), + OsuColour.FromHex("aa6880"), + + OsuColour.FromHex("6fad9b"), + OsuColour.FromHex("f2e394"), + OsuColour.FromHex("f2ae72"), + OsuColour.FromHex("f98f8a"), + OsuColour.FromHex("7daef4"), + OsuColour.FromHex("a691f2"), + OsuColour.FromHex("c894d3"), + OsuColour.FromHex("d895b0"), + + OsuColour.FromHex("53c4a1"), + OsuColour.FromHex("eace5c"), + OsuColour.FromHex("ea8c47"), + OsuColour.FromHex("fc4f4f"), + OsuColour.FromHex("3d94ea"), + OsuColour.FromHex("7760ea"), + OsuColour.FromHex("af52c6"), + OsuColour.FromHex("e25696"), + + OsuColour.FromHex("677c66"), + OsuColour.FromHex("9b8732"), + OsuColour.FromHex("8c5129"), + OsuColour.FromHex("8c3030"), + OsuColour.FromHex("1f5d91"), + OsuColour.FromHex("4335a5"), + OsuColour.FromHex("812a96"), + OsuColour.FromHex("992861"), + }; } } diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs index 2418eda2b6..c8fc15a0bc 100644 --- a/osu.Game/Overlays/Chat/DrawableChannel.cs +++ b/osu.Game/Overlays/Chat/DrawableChannel.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Chat public class DrawableChannel : Container { public readonly Channel Channel; - private readonly ChatLineContainer flow; + protected readonly ChatLineContainer ChatLineFlow; private readonly ScrollContainer scroll; public DrawableChannel(Channel channel) @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Chat { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Child = flow = new ChatLineContainer + Child = ChatLineFlow = new ChatLineContainer { Padding = new MarginPadding { Left = 20, Right = 20 }, RelativeSizeAxes = Axes.X, @@ -72,17 +72,19 @@ namespace osu.Game.Overlays.Chat Channel.PendingMessageResolved -= pendingMessageResolved; } + protected virtual ChatLine CreateChatLine(Message m) => new ChatLine(m); + private void newMessagesArrived(IEnumerable newMessages) { // Add up to last Channel.MAX_HISTORY messages var displayMessages = newMessages.Skip(Math.Max(0, newMessages.Count() - Channel.MaxHistory)); - flow.AddRange(displayMessages.Select(m => new ChatLine(m))); + ChatLineFlow.AddRange(displayMessages.Select(CreateChatLine)); - if (scroll.IsScrolledToEnd(10) || !flow.Children.Any() || newMessages.Any(m => m is LocalMessage)) + if (scroll.IsScrolledToEnd(10) || !ChatLineFlow.Children.Any() || newMessages.Any(m => m is LocalMessage)) scrollToEnd(); - var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); + var staleMessages = ChatLineFlow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray(); int count = staleMessages.Length - Channel.MaxHistory; for (int i = 0; i < count; i++) @@ -96,25 +98,25 @@ namespace osu.Game.Overlays.Chat private void pendingMessageResolved(Message existing, Message updated) { - var found = flow.Children.LastOrDefault(c => c.Message == existing); + var found = ChatLineFlow.Children.LastOrDefault(c => c.Message == existing); if (found != null) { Trace.Assert(updated.Id.HasValue, "An updated message was returned with no ID."); - flow.Remove(found); + ChatLineFlow.Remove(found); found.Message = updated; - flow.Add(found); + ChatLineFlow.Add(found); } } private void messageRemoved(Message removed) { - flow.Children.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire(); + ChatLineFlow.Children.FirstOrDefault(c => c.Message == removed)?.FadeColour(Color4.Red, 400).FadeOut(600).Expire(); } private void scrollToEnd() => ScheduleAfterChildren(() => scroll.ScrollToEnd()); - private class ChatLineContainer : FillFlowContainer + protected class ChatLineContainer : FillFlowContainer { protected override int Compare(Drawable x, Drawable y) { From 396caae0a90eace8b2b6da6bd91a516aadccc036 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Dec 2018 19:01:19 +0900 Subject: [PATCH 38/54] Remove redundant newline --- osu.Game/Online/Chat/StandAloneChatDisplay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Online/Chat/StandAloneChatDisplay.cs b/osu.Game/Online/Chat/StandAloneChatDisplay.cs index 46c6873063..1ec138ab57 100644 --- a/osu.Game/Online/Chat/StandAloneChatDisplay.cs +++ b/osu.Game/Online/Chat/StandAloneChatDisplay.cs @@ -53,7 +53,6 @@ namespace osu.Game.Online.Chat }, }; - if (postingTextbox) { AddInternal(textbox = new FocusedTextBox From 08d7c2df703bd60b364d43308f0bdb432f79b2e6 Mon Sep 17 00:00:00 2001 From: Poyo Date: Fri, 21 Dec 2018 16:08:08 -0800 Subject: [PATCH 39/54] Fix extra hard-rock flipping for sliders --- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 32d2d40671..1569e868b2 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -26,9 +26,6 @@ namespace osu.Game.Rulesets.Osu.Mods if (slider == null) return; - slider.HeadCircle.Position = new Vector2(slider.HeadCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.HeadCircle.Position.Y); - slider.TailCircle.Position = new Vector2(slider.TailCircle.Position.X, OsuPlayfield.BASE_SIZE.Y - slider.TailCircle.Position.Y); - slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); From f0dfc75bb2024ed110871e2222089e940c1d9319 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Dec 2018 09:48:55 +0900 Subject: [PATCH 40/54] Change osu! default keys back to Z/X A/S was no better as far as keyboard layout agnostic-ness. And people are confused if we change the defaults. Need to take a step back and reassess. --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index ecb0443f33..5cfc24bdde 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -31,8 +31,8 @@ namespace osu.Game.Rulesets.Osu public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] { - new KeyBinding(InputKey.A, OsuAction.LeftButton), - new KeyBinding(InputKey.S, OsuAction.RightButton), + new KeyBinding(InputKey.Z, OsuAction.LeftButton), + new KeyBinding(InputKey.X, OsuAction.RightButton), new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton), new KeyBinding(InputKey.MouseRight, OsuAction.RightButton), }; From 86ce0b551937f65f5b687430bf792d872a644ba1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 19 Dec 2018 12:44:51 +0900 Subject: [PATCH 41/54] Make DrawableDate adjustable --- osu.Game/Graphics/DrawableDate.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index 28f8bdf82f..639a549936 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -4,6 +4,7 @@ using System; using Humanizer; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Game.Graphics.Sprites; @@ -11,13 +12,27 @@ namespace osu.Game.Graphics { public class DrawableDate : OsuSpriteText, IHasTooltip { - protected readonly DateTimeOffset Date; + private DateTimeOffset date; + + public DateTimeOffset Date + { + get => date; + set + { + if (date == value) + return; + date = value.ToLocalTime(); + + if (LoadState >= LoadState.Ready) + updateTime(); + } + } public DrawableDate(DateTimeOffset date) { Font = "Exo2.0-RegularItalic"; - Date = date.ToLocalTime(); + Date = date; } [BackgroundDependencyLoader] From d28c754256c7258d59f0c5cd0ad4289b8787ad58 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 19 Dec 2018 13:07:43 +0900 Subject: [PATCH 42/54] Fix negative dates, and time moving in opposite direction --- osu.Game/Graphics/DrawableDate.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index 639a549936..87711c72c7 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -54,14 +54,14 @@ namespace osu.Game.Graphics var diffToNow = DateTimeOffset.Now.Subtract(Date); double timeUntilNextUpdate = 1000; - if (diffToNow.TotalSeconds > 60) + if (Math.Abs(diffToNow.TotalSeconds) > 120) { timeUntilNextUpdate *= 60; - if (diffToNow.TotalMinutes > 60) + if (Math.Abs(diffToNow.TotalMinutes) > 120) { timeUntilNextUpdate *= 60; - if (diffToNow.TotalHours > 24) + if (Math.Abs(diffToNow.TotalHours) > 48) timeUntilNextUpdate *= 24; } } From 621480af0214f2e4e86199a0f667b70d431c895b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Dec 2018 14:21:21 +0900 Subject: [PATCH 43/54] Add simple (non-automated) test --- osu.Game.Tests/Visual/TestCaseDrawableDate.cs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 osu.Game.Tests/Visual/TestCaseDrawableDate.cs diff --git a/osu.Game.Tests/Visual/TestCaseDrawableDate.cs b/osu.Game.Tests/Visual/TestCaseDrawableDate.cs new file mode 100644 index 0000000000..2e38f5eb28 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseDrawableDate.cs @@ -0,0 +1,67 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseDrawableDate : OsuTestCase + { + public TestCaseDrawableDate() + { + Child = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Children = new Drawable[] + { + new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(60))), + new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(55))), + new PokeyDrawableDate(DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(50))), + new PokeyDrawableDate(DateTimeOffset.Now), + new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(60))), + new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(65))), + new PokeyDrawableDate(DateTimeOffset.Now.Add(TimeSpan.FromSeconds(70))), + } + }; + } + + private class PokeyDrawableDate : CompositeDrawable + { + public PokeyDrawableDate(DateTimeOffset date) + { + const float box_size = 10; + + DrawableDate drawableDate; + Box flash; + + AutoSizeAxes = Axes.Both; + InternalChildren = new Drawable[] + { + flash = new Box + { + Colour = Color4.Yellow, + Size = new Vector2(box_size), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Alpha = 0 + }, + drawableDate = new DrawableDate(date) + { + X = box_size + 2, + } + }; + + drawableDate.Current.ValueChanged += v => flash.FadeOutFromOne(500); + } + } + } +} From f47ac3552218182fdbd7c6e928e48469df717e54 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 20 Dec 2018 20:08:22 +0900 Subject: [PATCH 44/54] Add click to avatar --- osu.Game/Overlays/BeatmapSet/AuthorInfo.cs | 12 +---- osu.Game/Overlays/Profile/ProfileHeader.cs | 1 + .../Overlays/Toolbar/ToolbarUserButton.cs | 1 + osu.Game/Users/Avatar.cs | 51 +++++++++++++++++-- osu.Game/Users/UpdateableAvatar.cs | 23 ++++++--- osu.Game/Users/UserPanel.cs | 1 + 6 files changed, 67 insertions(+), 22 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs index c2f03a4b66..0b260d4f39 100644 --- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs +++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs @@ -9,7 +9,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Users; using osuTK; using osuTK.Graphics; -using osu.Framework.Allocation; using osu.Game.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -20,7 +19,6 @@ namespace osu.Game.Overlays.BeatmapSet private const float height = 50; private readonly UpdateableAvatar avatar; - private readonly ClickableArea clickableArea; private readonly FillFlowContainer fields; private BeatmapSetInfo beatmapSet; @@ -73,7 +71,7 @@ namespace osu.Game.Overlays.BeatmapSet Children = new Drawable[] { - clickableArea = new ClickableArea + new Container { AutoSizeAxes = Axes.Both, CornerRadius = 3, @@ -100,14 +98,8 @@ namespace osu.Game.Overlays.BeatmapSet }; } - [BackgroundDependencyLoader(true)] - private void load(UserProfileOverlay profile) + private void load() { - clickableArea.Action = () => - { - if (avatar.User != null) profile?.ShowUser(avatar.User); - }; - updateDisplay(); } diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index a8075ec295..3c7fe0b1eb 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -82,6 +82,7 @@ namespace osu.Game.Overlays.Profile Origin = Anchor.BottomLeft, Masking = true, CornerRadius = 5, + OpenOnClick = { Value = false }, EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 017d748600..36299e51f0 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -31,6 +31,7 @@ namespace osu.Game.Overlays.Toolbar Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, CornerRadius = 4, + OpenOnClick = { Value = false }, EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, diff --git a/osu.Game/Users/Avatar.cs b/osu.Game/Users/Avatar.cs index e6e1ba3c53..24864cce51 100644 --- a/osu.Game/Users/Avatar.cs +++ b/osu.Game/Users/Avatar.cs @@ -3,17 +3,29 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Containers; namespace osu.Game.Users { public class Avatar : Container { + /// + /// Whether to open the user's profile when clicked. + /// + public readonly BindableBool OpenOnClick = new BindableBool(true); + private readonly User user; + [Resolved(CanBeNull = true)] + private OsuGame game { get; set; } + /// /// An avatar for specified user. /// @@ -33,14 +45,43 @@ namespace osu.Game.Users if (user != null && user.Id > 1) texture = textures.Get($@"https://a.ppy.sh/{user.Id}"); if (texture == null) texture = textures.Get(@"Online/avatar-guest"); - Add(new Sprite + ClickableArea clickableArea; + Add(clickableArea = new ClickableArea { RelativeSizeAxes = Axes.Both, - Texture = texture, - FillMode = FillMode.Fit, - Anchor = Anchor.Centre, - Origin = Anchor.Centre + Child = new Sprite + { + RelativeSizeAxes = Axes.Both, + Texture = texture, + FillMode = FillMode.Fit, + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }, + Action = openProfile }); + + clickableArea.Enabled.BindTo(OpenOnClick); + } + + private void openProfile() + { + if (!OpenOnClick) + return; + + if (user != null) + game?.ShowUser(user.Id); + } + + private class ClickableArea : OsuClickableContainer, IHasTooltip + { + public string TooltipText => Enabled.Value ? @"View Profile" : null; + + protected override bool OnClick(ClickEvent e) + { + if (!Enabled) + return false; + return base.OnClick(e); + } } } } diff --git a/osu.Game/Users/UpdateableAvatar.cs b/osu.Game/Users/UpdateableAvatar.cs index 6c0b841abf..a8d9d3d66b 100644 --- a/osu.Game/Users/UpdateableAvatar.cs +++ b/osu.Game/Users/UpdateableAvatar.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -35,6 +36,11 @@ namespace osu.Game.Users } } + /// + /// Whether to open the user's profile when clicked. + /// + public readonly BindableBool OpenOnClick = new BindableBool(true); + protected override void LoadComplete() { base.LoadComplete(); @@ -45,15 +51,18 @@ namespace osu.Game.Users { displayedAvatar?.FadeOut(300); displayedAvatar?.Expire(); + if (user != null || ShowGuestOnNull) { - Add(displayedAvatar = new DelayedLoadWrapper( - new Avatar(user) - { - RelativeSizeAxes = Axes.Both, - OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), - }) - ); + var avatar = new Avatar(user) + { + RelativeSizeAxes = Axes.Both, + OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), + }; + + avatar.OpenOnClick.BindTo(OpenOnClick); + + Add(displayedAvatar = new DelayedLoadWrapper(avatar)); } } } diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index d86f608bd1..1d302ef04f 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -99,6 +99,7 @@ namespace osu.Game.Users User = user, Masking = true, CornerRadius = 5, + OpenOnClick = { Value = false }, EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, From 870d843fff7a229e18edb03aaf07f5d078d81b27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Dec 2018 14:40:55 +0900 Subject: [PATCH 45/54] Fix username not displaying correctly in overlay --- osu.Game/Overlays/Profile/ProfileHeader.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 3c7fe0b1eb..23739d8ad1 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -115,7 +115,7 @@ namespace osu.Game.Overlays.Profile Y = -48, Children = new Drawable[] { - new OsuSpriteText + usernameText = new OsuSpriteText { Text = user.Username, Font = @"Exo2.0-RegularItalic", @@ -317,6 +317,8 @@ namespace osu.Game.Overlays.Profile levelBadge.Texture = textures.Get(@"Profile/levelbadge"); } + private readonly OsuSpriteText usernameText; + private User user; public User User @@ -344,6 +346,8 @@ namespace osu.Game.Overlays.Profile if (user.IsSupporter) SupporterTag.Show(); + usernameText.Text = user.Username; + if (!string.IsNullOrEmpty(user.Colour)) { colourBar.Colour = OsuColour.FromHex(user.Colour); From e657f13c1539fa6deace5405f61a4b1df12e4728 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 14 Dec 2018 19:51:27 +0900 Subject: [PATCH 46/54] Separate out Leaderboard into BeatmapLeaderboard --- osu.Game.Tests/Visual/TestCaseLeaderboard.cs | 5 +- .../Online/API/Requests/GetScoresRequest.cs | 6 +- .../Leaderboards/DrawableRank.cs | 4 +- .../Leaderboards/Leaderboard.cs | 113 +++++------------- .../Leaderboards/LeaderboardScore.cs | 79 +++++++----- .../Leaderboards/MessagePlaceholder.cs | 2 +- .../Leaderboards/Placeholder.cs | 2 +- .../Leaderboards/PlaceholderState.cs | 2 +- .../RetrievalFailurePlaceholder.cs | 2 +- .../BeatmapSet/Scores/DrawableScore.cs | 2 +- .../BeatmapSet/Scores/DrawableTopScore.cs | 2 +- .../Sections/Ranks/DrawableProfileScore.cs | 2 +- .../Sections/Recent/DrawableRecentActivity.cs | 2 +- .../Screens/Ranking/ResultsPageRanking.cs | 2 +- osu.Game/Screens/Ranking/ResultsPageScore.cs | 2 +- osu.Game/Screens/Select/BeatmapDetailArea.cs | 6 +- .../Select/Leaderboards/BeatmapLeaderboard.cs | 87 ++++++++++++++ ...ardScope.cs => BeatmapLeaderboardScope.cs} | 2 +- .../Leaderboards/BeatmapLeaderboardScore.cs | 34 ++++++ 19 files changed, 224 insertions(+), 132 deletions(-) rename osu.Game/{Screens/Select => Online}/Leaderboards/DrawableRank.cs (96%) rename osu.Game/{Screens/Select => Online}/Leaderboards/Leaderboard.cs (76%) rename osu.Game/{Screens/Select => Online}/Leaderboards/LeaderboardScore.cs (87%) rename osu.Game/{Screens/Select => Online}/Leaderboards/MessagePlaceholder.cs (94%) rename osu.Game/{Screens/Select => Online}/Leaderboards/Placeholder.cs (94%) rename osu.Game/{Screens/Select => Online}/Leaderboards/PlaceholderState.cs (88%) rename osu.Game/{Screens/Select => Online}/Leaderboards/RetrievalFailurePlaceholder.cs (97%) create mode 100644 osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs rename osu.Game/Screens/Select/Leaderboards/{LeaderboardScope.cs => BeatmapLeaderboardScope.cs} (87%) create mode 100644 osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScore.cs diff --git a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs index f7630f0902..10d7eaee8a 100644 --- a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osuTK; using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Online.Leaderboards; using osu.Game.Rulesets; using osu.Game.Scoring; @@ -36,7 +37,7 @@ namespace osu.Game.Tests.Visual Origin = Anchor.Centre, Anchor = Anchor.Centre, Size = new Vector2(550f, 450f), - Scope = LeaderboardScope.Global, + Scope = BeatmapLeaderboardScope.Global, }); AddStep(@"New Scores", newScores); @@ -275,7 +276,7 @@ namespace osu.Game.Tests.Visual }; } - private class FailableLeaderboard : Leaderboard + private class FailableLeaderboard : BeatmapLeaderboard { public void SetRetrievalState(PlaceholderState state) { diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 2751dd956b..ae2c7dc269 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -13,15 +13,15 @@ namespace osu.Game.Online.API.Requests public class GetScoresRequest : APIRequest { private readonly BeatmapInfo beatmap; - private readonly LeaderboardScope scope; + private readonly BeatmapLeaderboardScope scope; private readonly RulesetInfo ruleset; - public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, LeaderboardScope scope = LeaderboardScope.Global) + public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global) { if (!beatmap.OnlineBeatmapID.HasValue) throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); - if (scope == LeaderboardScope.Local) + if (scope == BeatmapLeaderboardScope.Local) throw new InvalidOperationException("Should not attempt to request online scores for a local scoped leaderboard"); this.beatmap = beatmap; diff --git a/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs b/osu.Game/Online/Leaderboards/DrawableRank.cs similarity index 96% rename from osu.Game/Screens/Select/Leaderboards/DrawableRank.cs rename to osu.Game/Online/Leaderboards/DrawableRank.cs index 3258a62adf..1c68c64180 100644 --- a/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs +++ b/osu.Game/Online/Leaderboards/DrawableRank.cs @@ -2,14 +2,14 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Extensions; using osu.Game.Scoring; -namespace osu.Game.Screens.Select.Leaderboards +namespace osu.Game.Online.Leaderboards { public class DrawableRank : Container { diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs similarity index 76% rename from osu.Game/Screens/Select/Leaderboards/Leaderboard.cs rename to osu.Game/Online/Leaderboards/Leaderboard.cs index a65cc6f096..8e83c8ad5a 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -3,38 +3,30 @@ using System; using System.Collections.Generic; -using osuTK; -using osuTK.Graphics; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Threading; -using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using System.Linq; -using osu.Framework.Configuration; -using osu.Game.Rulesets; -using osu.Game.Scoring; +using osu.Game.Screens.Select.Leaderboards; +using osuTK; +using osuTK.Graphics; -namespace osu.Game.Screens.Select.Leaderboards +namespace osu.Game.Online.Leaderboards { - public class Leaderboard : Container + public abstract class Leaderboard : Container { private const double fade_duration = 300; private readonly ScrollContainer scrollContainer; private readonly Container placeholderContainer; - private FillFlowContainer scrollFlow; - - private readonly IBindable ruleset = new Bindable(); - - public Action ScoreSelected; + private FillFlowContainer> scrollFlow; private readonly LoadingAnimation loading; @@ -42,9 +34,9 @@ namespace osu.Game.Screens.Select.Leaderboards private bool scoresLoadedOnce; - private IEnumerable scores; + private IEnumerable scores; - public IEnumerable Scores + public IEnumerable Scores { get { return scores; } set @@ -64,13 +56,13 @@ namespace osu.Game.Screens.Select.Leaderboards // ensure placeholder is hidden when displaying scores PlaceholderState = PlaceholderState.Successful; - var flow = scrollFlow = new FillFlowContainer + var flow = scrollFlow = new FillFlowContainer> { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Spacing = new Vector2(0f, 5f), Padding = new MarginPadding { Top = 10, Bottom = 5 }, - ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) }) + ChildrenEnumerable = scores.Select((s, index) => CreateScoreVisualiser(s, index + 1)) }; // schedule because we may not be loaded yet (LoadComponentAsync complains). @@ -96,18 +88,18 @@ namespace osu.Game.Screens.Select.Leaderboards } } - private LeaderboardScope scope; + private TScope scope; - public LeaderboardScope Scope + public TScope Scope { get { return scope; } set { - if (value == scope) + if (value.Equals(scope)) return; scope = value; - updateScores(); + UpdateScores(); } } @@ -137,7 +129,7 @@ namespace osu.Game.Screens.Select.Leaderboards case PlaceholderState.NetworkFailure: replacePlaceholder(new RetrievalFailurePlaceholder { - OnRetry = updateScores, + OnRetry = UpdateScores, }); break; case PlaceholderState.Unavailable: @@ -159,7 +151,7 @@ namespace osu.Game.Screens.Select.Leaderboards } } - public Leaderboard() + protected Leaderboard() { Children = new Drawable[] { @@ -177,36 +169,14 @@ namespace osu.Game.Screens.Select.Leaderboards } private APIAccess api; - private BeatmapInfo beatmap; - - [Resolved] - private ScoreManager scoreManager { get; set; } private ScheduledDelegate pendingUpdateScores; - public BeatmapInfo Beatmap - { - get { return beatmap; } - set - { - if (beatmap == value) - return; - - beatmap = value; - Scores = null; - - updateScores(); - } - } - - [BackgroundDependencyLoader(permitNulls: true)] - private void load(APIAccess api, IBindable parentRuleset) + [BackgroundDependencyLoader(true)] + private void load(APIAccess api) { this.api = api; - ruleset.BindTo(parentRuleset); - ruleset.ValueChanged += _ => updateScores(); - if (api != null) api.OnStateChange += handleApiStateChange; } @@ -219,21 +189,17 @@ namespace osu.Game.Screens.Select.Leaderboards api.OnStateChange -= handleApiStateChange; } - public void RefreshScores() => updateScores(); + public void RefreshScores() => UpdateScores(); - private GetScoresRequest getScoresRequest; + private APIRequest getScoresRequest; private void handleApiStateChange(APIState oldState, APIState newState) { - if (Scope == LeaderboardScope.Local) - // No need to respond to API state change while current scope is local - return; - if (newState == APIState.Online) - updateScores(); + UpdateScores(); } - private void updateScores() + protected void UpdateScores() { // don't display any scores or placeholder until the first Scores_Set has been called. // this avoids scope changes flickering a "no scores" placeholder before initialisation of song select is finished. @@ -245,40 +211,23 @@ namespace osu.Game.Screens.Select.Leaderboards pendingUpdateScores?.Cancel(); pendingUpdateScores = Schedule(() => { - if (Scope == LeaderboardScope.Local) - { - Scores = scoreManager.QueryScores(s => s.Beatmap.ID == Beatmap.ID).ToArray(); - PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; - return; - } - - if (Beatmap?.OnlineBeatmapID == null) - { - PlaceholderState = PlaceholderState.Unavailable; - return; - } - if (api?.IsLoggedIn != true) { PlaceholderState = PlaceholderState.NotLoggedIn; return; } - if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter) - { - PlaceholderState = PlaceholderState.NotSupporter; - return; - } - PlaceholderState = PlaceholderState.Retrieving; loading.Show(); - getScoresRequest = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope); - getScoresRequest.Success += r => Schedule(() => + getScoresRequest = FetchScores(scores => Schedule(() => { - Scores = r.Scores; + Scores = scores; PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; - }); + })); + + if (getScoresRequest == null) + return; getScoresRequest.Failure += e => Schedule(() => { @@ -292,6 +241,8 @@ namespace osu.Game.Screens.Select.Leaderboards }); } + protected abstract APIRequest FetchScores(Action> scoresCallback); + private Placeholder currentPlaceholder; private void replacePlaceholder(Placeholder placeholder) @@ -344,5 +295,7 @@ namespace osu.Game.Screens.Select.Leaderboards } } } + + protected abstract LeaderboardScore CreateScoreVisualiser(TScoreModel model, int index); } } diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs similarity index 87% rename from osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs rename to osu.Game/Online/Leaderboards/LeaderboardScore.cs index 1ba529c0bf..63352754a8 100644 --- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -1,9 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; using System.Linq; -using osuTK; -using osuTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -14,47 +13,60 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Users; +using osuTK; +using osuTK.Graphics; -namespace osu.Game.Screens.Select.Leaderboards +namespace osu.Game.Online.Leaderboards { - public class LeaderboardScore : OsuClickableContainer + public static class LeaderboardScore { - public static readonly float HEIGHT = 60; + public const float HEIGHT = 60; + } + public abstract class LeaderboardScore : OsuClickableContainer + { public readonly int RankPosition; - public readonly ScoreInfo Score; private const float corner_radius = 5; private const float edge_margin = 5; private const float background_alpha = 0.25f; private const float rank_width = 30; + protected Container RankContainer { get; private set; } + + private readonly TScoreModel score; + private Box background; private Container content; private Drawable avatar; - private DrawableRank scoreRank; + private Drawable scoreRank; private OsuSpriteText nameLabel; private GlowingSpriteText scoreLabel; - private ScoreComponentLabel maxCombo; - private ScoreComponentLabel accuracy; private Container flagBadgeContainer; private FillFlowContainer modsContainer; - public LeaderboardScore(ScoreInfo score, int rank) + private List statisticsLabels; + + protected LeaderboardScore(TScoreModel score, int rank) { - Score = score; + this.score = score; RankPosition = rank; RelativeSizeAxes = Axes.X; - Height = HEIGHT; + Height = LeaderboardScore.HEIGHT; } [BackgroundDependencyLoader] private void load() { + var user = GetUser(score); + + statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s.icon, s.value, s.name)).ToList(); + Children = new Drawable[] { new Container @@ -102,7 +114,7 @@ namespace osu.Game.Screens.Select.Leaderboards Children = new[] { avatar = new DelayedLoadWrapper( - new Avatar(Score.User) + new Avatar(user) { RelativeSizeAxes = Axes.Both, CornerRadius = corner_radius, @@ -117,18 +129,18 @@ namespace osu.Game.Screens.Select.Leaderboards }) { RelativeSizeAxes = Axes.None, - Size = new Vector2(HEIGHT - edge_margin * 2, HEIGHT - edge_margin * 2), + Size = new Vector2(LeaderboardScore.HEIGHT - edge_margin * 2, LeaderboardScore.HEIGHT - edge_margin * 2), }, new Container { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, - Position = new Vector2(HEIGHT - edge_margin, 0f), + Position = new Vector2(LeaderboardScore.HEIGHT - edge_margin, 0f), Children = new Drawable[] { nameLabel = new OsuSpriteText { - Text = Score.User.Username, + Text = user.Username, Font = @"Exo2.0-BoldItalic", TextSize = 23, }, @@ -149,7 +161,7 @@ namespace osu.Game.Screens.Select.Leaderboards Masking = true, Children = new Drawable[] { - new DrawableFlag(Score.User?.Country) + new DrawableFlag(user.Country) { Width = 30, RelativeSizeAxes = Axes.Y, @@ -164,11 +176,7 @@ namespace osu.Game.Screens.Select.Leaderboards Direction = FillDirection.Horizontal, Spacing = new Vector2(10f, 0f), Margin = new MarginPadding { Left = edge_margin }, - Children = new Drawable[] - { - maxCombo = new ScoreComponentLabel(FontAwesome.fa_link, Score.MaxCombo.ToString(), "Max Combo"), - accuracy = new ScoreComponentLabel(FontAwesome.fa_crosshairs, string.Format(Score.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", Score.Accuracy), "Accuracy"), - }, + Children = statisticsLabels }, }, }, @@ -183,17 +191,17 @@ namespace osu.Game.Screens.Select.Leaderboards Spacing = new Vector2(5f, 0f), Children = new Drawable[] { - scoreLabel = new GlowingSpriteText(Score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")), - new Container + scoreLabel = new GlowingSpriteText(GetTotalScore(score).ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")), + RankContainer = new Container { Size = new Vector2(40f, 20f), Children = new[] { - scoreRank = new DrawableRank(Score.Rank) + scoreRank = new DrawableRank(GetRank(score)) { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(40f), + Size = new Vector2(40f) }, }, }, @@ -205,7 +213,7 @@ namespace osu.Game.Screens.Select.Leaderboards Origin = Anchor.BottomRight, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - ChildrenEnumerable = Score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) }) + ChildrenEnumerable = GetMods(score).Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) }) }, }, }, @@ -216,7 +224,7 @@ namespace osu.Game.Screens.Select.Leaderboards public override void Show() { - foreach (var d in new[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, maxCombo, accuracy, modsContainer }) + foreach (var d in new[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, modsContainer }.Concat(statisticsLabels)) d.FadeOut(); Alpha = 0; @@ -243,7 +251,7 @@ namespace osu.Game.Screens.Select.Leaderboards using (BeginDelayedSequence(50, true)) { - var drawables = new Drawable[] { flagBadgeContainer, maxCombo, accuracy, modsContainer, }; + var drawables = new Drawable[] { flagBadgeContainer, modsContainer }.Concat(statisticsLabels).ToArray(); for (int i = 0; i < drawables.Length; i++) drawables[i].FadeIn(100 + i * 50); } @@ -263,6 +271,16 @@ namespace osu.Game.Screens.Select.Leaderboards base.OnHoverLost(e); } + protected abstract User GetUser(TScoreModel model); + + protected abstract IEnumerable GetMods(TScoreModel model); + + protected abstract IEnumerable<(FontAwesome icon, string value, string name)> GetStatistics(TScoreModel model); + + protected abstract int GetTotalScore(TScoreModel model); + + protected abstract ScoreRank GetRank(TScoreModel model); + private class GlowingSpriteText : Container { public GlowingSpriteText(string text, string font, int textSize, Color4 textColour, Color4 glowColour) @@ -324,8 +342,7 @@ namespace osu.Game.Screens.Select.Leaderboards public ScoreComponentLabel(FontAwesome icon, string value, string name) { this.name = name; - AutoSizeAxes = Axes.Y; - Width = 60; + AutoSizeAxes = Axes.Both; Child = content = new FillFlowContainer { diff --git a/osu.Game/Screens/Select/Leaderboards/MessagePlaceholder.cs b/osu.Game/Online/Leaderboards/MessagePlaceholder.cs similarity index 94% rename from osu.Game/Screens/Select/Leaderboards/MessagePlaceholder.cs rename to osu.Game/Online/Leaderboards/MessagePlaceholder.cs index f01a55b662..ea92836e6e 100644 --- a/osu.Game/Screens/Select/Leaderboards/MessagePlaceholder.cs +++ b/osu.Game/Online/Leaderboards/MessagePlaceholder.cs @@ -4,7 +4,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics; -namespace osu.Game.Screens.Select.Leaderboards +namespace osu.Game.Online.Leaderboards { public class MessagePlaceholder : Placeholder { diff --git a/osu.Game/Screens/Select/Leaderboards/Placeholder.cs b/osu.Game/Online/Leaderboards/Placeholder.cs similarity index 94% rename from osu.Game/Screens/Select/Leaderboards/Placeholder.cs rename to osu.Game/Online/Leaderboards/Placeholder.cs index 468b43e54f..4994ce0e99 100644 --- a/osu.Game/Screens/Select/Leaderboards/Placeholder.cs +++ b/osu.Game/Online/Leaderboards/Placeholder.cs @@ -5,7 +5,7 @@ using System; using osu.Framework.Graphics; using osu.Game.Graphics.Containers; -namespace osu.Game.Screens.Select.Leaderboards +namespace osu.Game.Online.Leaderboards { public abstract class Placeholder : OsuTextFlowContainer, IEquatable { diff --git a/osu.Game/Screens/Select/Leaderboards/PlaceholderState.cs b/osu.Game/Online/Leaderboards/PlaceholderState.cs similarity index 88% rename from osu.Game/Screens/Select/Leaderboards/PlaceholderState.cs rename to osu.Game/Online/Leaderboards/PlaceholderState.cs index 33a56540f3..504b03432f 100644 --- a/osu.Game/Screens/Select/Leaderboards/PlaceholderState.cs +++ b/osu.Game/Online/Leaderboards/PlaceholderState.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -namespace osu.Game.Screens.Select.Leaderboards +namespace osu.Game.Online.Leaderboards { public enum PlaceholderState { diff --git a/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs b/osu.Game/Online/Leaderboards/RetrievalFailurePlaceholder.cs similarity index 97% rename from osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs rename to osu.Game/Online/Leaderboards/RetrievalFailurePlaceholder.cs index 66a7793f7c..7fed40ed1a 100644 --- a/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs +++ b/osu.Game/Online/Leaderboards/RetrievalFailurePlaceholder.cs @@ -8,7 +8,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osuTK; -namespace osu.Game.Screens.Select.Leaderboards +namespace osu.Game.Online.Leaderboards { public class RetrievalFailurePlaceholder : Placeholder { diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs index f643e130aa..89416c1098 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs @@ -10,11 +10,11 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Leaderboards; using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; namespace osu.Game.Overlays.BeatmapSet.Scores diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs index 643839fa88..6259d85bee 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs @@ -12,12 +12,12 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Leaderboards; using osu.Game.Overlays.Profile.Sections.Ranks; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; -using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; namespace osu.Game.Overlays.BeatmapSet.Scores diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs index 1c39cb309c..18aa684664 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableProfileScore.cs @@ -6,8 +6,8 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Online.Leaderboards; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Select.Leaderboards; using osu.Game.Rulesets.UI; using osu.Game.Scoring; diff --git a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs index fefb289d17..8b4fb1a229 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs @@ -10,7 +10,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Chat; -using osu.Game.Screens.Select.Leaderboards; +using osu.Game.Online.Leaderboards; namespace osu.Game.Overlays.Profile.Sections.Recent { diff --git a/osu.Game/Screens/Ranking/ResultsPageRanking.cs b/osu.Game/Screens/Ranking/ResultsPageRanking.cs index c5a5cc6ad9..3a75daaf60 100644 --- a/osu.Game/Screens/Ranking/ResultsPageRanking.cs +++ b/osu.Game/Screens/Ranking/ResultsPageRanking.cs @@ -28,7 +28,7 @@ namespace osu.Game.Screens.Ranking Colour = colours.GrayE, RelativeSizeAxes = Axes.Both, }, - new Leaderboard + new BeatmapLeaderboard { Origin = Anchor.Centre, Anchor = Anchor.Centre, diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs index 62103314e1..0774a63e98 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -19,11 +19,11 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; -using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; using osu.Framework.Graphics.Shapes; using osu.Framework.Extensions; using osu.Framework.Localisation; +using osu.Game.Online.Leaderboards; using osu.Game.Scoring; namespace osu.Game.Screens.Ranking diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index a6fbd201d0..c5c4960ed4 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Select protected override Container Content => content; public readonly BeatmapDetails Details; - public readonly Leaderboard Leaderboard; + public readonly BeatmapLeaderboard Leaderboard; private WorkingBeatmap beatmap; public WorkingBeatmap Beatmap @@ -52,7 +52,7 @@ namespace osu.Game.Screens.Select default: Details.Hide(); - Leaderboard.Scope = (LeaderboardScope)tab - 1; + Leaderboard.Scope = (BeatmapLeaderboardScope)tab - 1; Leaderboard.Show(); break; } @@ -73,7 +73,7 @@ namespace osu.Game.Screens.Select Alpha = 0, Margin = new MarginPadding { Top = details_padding }, }, - Leaderboard = new Leaderboard + Leaderboard = new BeatmapLeaderboard { RelativeSizeAxes = Axes.Both, } diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs new file mode 100644 index 0000000000..b0cfad314c --- /dev/null +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Game.Beatmaps; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.Leaderboards; +using osu.Game.Rulesets; +using osu.Game.Scoring; + +namespace osu.Game.Screens.Select.Leaderboards +{ + public class BeatmapLeaderboard : Leaderboard + { + public Action ScoreSelected; + + private BeatmapInfo beatmap; + + public BeatmapInfo Beatmap + { + get { return beatmap; } + set + { + if (beatmap == value) + return; + + beatmap = value; + Scores = null; + + UpdateScores(); + } + } + + [Resolved] + private ScoreManager scoreManager { get; set; } + + [Resolved] + private IBindable ruleset { get; set; } + + [Resolved] + private APIAccess api { get; set; } + + [BackgroundDependencyLoader] + private void load() + { + ruleset.ValueChanged += _ => UpdateScores(); + } + + protected override APIRequest FetchScores(Action> scoresCallback) + { + if (Scope == BeatmapLeaderboardScope.Local) + { + Scores = scoreManager.QueryScores(s => s.Beatmap.ID == Beatmap.ID).ToArray(); + PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; + return null; + } + + if (Beatmap?.OnlineBeatmapID == null) + { + PlaceholderState = PlaceholderState.Unavailable; + return null; + } + + if (Scope != BeatmapLeaderboardScope.Global && !api.LocalUser.Value.IsSupporter) + { + PlaceholderState = PlaceholderState.NotSupporter; + return null; + } + + var req = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope); + + req.Success += r => scoresCallback?.Invoke(r.Scores); + + return req; + } + + protected override LeaderboardScore CreateScoreVisualiser(ScoreInfo model, int index) => new BeatmapLeaderboardScore(model, index) + { + Action = () => ScoreSelected?.Invoke(model) + }; + } +} diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScope.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs similarity index 87% rename from osu.Game/Screens/Select/Leaderboards/LeaderboardScope.cs rename to osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs index 761f53a5e8..39d9580792 100644 --- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScope.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs @@ -3,7 +3,7 @@ namespace osu.Game.Screens.Select.Leaderboards { - public enum LeaderboardScope + public enum BeatmapLeaderboardScope { Local, Country, diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScore.cs new file mode 100644 index 0000000000..098fff4052 --- /dev/null +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScore.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Graphics; +using osu.Game.Online.Leaderboards; +using osu.Game.Rulesets.Mods; +using osu.Game.Scoring; +using osu.Game.Users; + +namespace osu.Game.Screens.Select.Leaderboards +{ + public class BeatmapLeaderboardScore : LeaderboardScore + { + public BeatmapLeaderboardScore(ScoreInfo score, int rank) + : base(score, rank) + { + } + + protected override User GetUser(ScoreInfo model) => model.User; + + protected override IEnumerable GetMods(ScoreInfo model) => model.Mods; + + protected override IEnumerable<(FontAwesome icon, string value, string name)> GetStatistics(ScoreInfo model) => new[] + { + (FontAwesome.fa_link, model.MaxCombo.ToString(), "Max Combo"), + (FontAwesome.fa_crosshairs, string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy), "Accuracy") + }; + + protected override int GetTotalScore(ScoreInfo model) => model.TotalScore; + + protected override ScoreRank GetRank(ScoreInfo model) => model.Rank; + } +} From 23259b295cb5044be6923961d2e024d5b98fc895 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Dec 2018 14:45:35 +0900 Subject: [PATCH 47/54] Remove unnecessary using --- osu.Game/Online/Leaderboards/Leaderboard.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 8e83c8ad5a..83cc289bc1 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -13,7 +13,6 @@ using osu.Framework.Threading; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; -using osu.Game.Screens.Select.Leaderboards; using osuTK; using osuTK.Graphics; From 787e65c3c58bc8788c373de8e5ca5a4cd6007993 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Dec 2018 15:20:35 +0900 Subject: [PATCH 48/54] Reduce generic-ness --- osu.Game/Online/Leaderboards/Leaderboard.cs | 16 ++--- .../Online/Leaderboards/LeaderboardScore.cs | 68 ++++++++++--------- .../Select/Leaderboards/BeatmapLeaderboard.cs | 2 +- .../Leaderboards/BeatmapLeaderboardScore.cs | 34 ---------- 4 files changed, 46 insertions(+), 74 deletions(-) delete mode 100644 osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScore.cs diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 83cc289bc1..d9d78245bb 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -18,14 +18,14 @@ using osuTK.Graphics; namespace osu.Game.Online.Leaderboards { - public abstract class Leaderboard : Container + public abstract class Leaderboard : Container { private const double fade_duration = 300; private readonly ScrollContainer scrollContainer; private readonly Container placeholderContainer; - private FillFlowContainer> scrollFlow; + private FillFlowContainer scrollFlow; private readonly LoadingAnimation loading; @@ -33,9 +33,9 @@ namespace osu.Game.Online.Leaderboards private bool scoresLoadedOnce; - private IEnumerable scores; + private IEnumerable scores; - public IEnumerable Scores + public IEnumerable Scores { get { return scores; } set @@ -55,13 +55,13 @@ namespace osu.Game.Online.Leaderboards // ensure placeholder is hidden when displaying scores PlaceholderState = PlaceholderState.Successful; - var flow = scrollFlow = new FillFlowContainer> + var flow = scrollFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Spacing = new Vector2(0f, 5f), Padding = new MarginPadding { Top = 10, Bottom = 5 }, - ChildrenEnumerable = scores.Select((s, index) => CreateScoreVisualiser(s, index + 1)) + ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1)) }; // schedule because we may not be loaded yet (LoadComponentAsync complains). @@ -240,7 +240,7 @@ namespace osu.Game.Online.Leaderboards }); } - protected abstract APIRequest FetchScores(Action> scoresCallback); + protected abstract APIRequest FetchScores(Action> scoresCallback); private Placeholder currentPlaceholder; @@ -295,6 +295,6 @@ namespace osu.Game.Online.Leaderboards } } - protected abstract LeaderboardScore CreateScoreVisualiser(TScoreModel model, int index); + protected abstract LeaderboardScore CreateDrawableScore(ScoreInfo model, int index); } } diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 63352754a8..8269e02847 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -13,7 +13,6 @@ using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Users; @@ -22,15 +21,12 @@ using osuTK.Graphics; namespace osu.Game.Online.Leaderboards { - public static class LeaderboardScore - { - public const float HEIGHT = 60; - } - - public abstract class LeaderboardScore : OsuClickableContainer + public class LeaderboardScore : OsuClickableContainer { public readonly int RankPosition; + public const float HEIGHT = 60; + private const float corner_radius = 5; private const float edge_margin = 5; private const float background_alpha = 0.25f; @@ -38,7 +34,7 @@ namespace osu.Game.Online.Leaderboards protected Container RankContainer { get; private set; } - private readonly TScoreModel score; + private readonly ScoreInfo score; private Box background; private Container content; @@ -51,21 +47,27 @@ namespace osu.Game.Online.Leaderboards private List statisticsLabels; - protected LeaderboardScore(TScoreModel score, int rank) + public LeaderboardScore(ScoreInfo score, int rank) { this.score = score; RankPosition = rank; RelativeSizeAxes = Axes.X; - Height = LeaderboardScore.HEIGHT; + Height = HEIGHT; } + protected virtual IEnumerable GetStatistics(ScoreInfo model) => new[] + { + new LeaderboardScoreStatistic(FontAwesome.fa_link, "Max Combo", model.MaxCombo.ToString()), + new LeaderboardScoreStatistic(FontAwesome.fa_crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy)) + }; + [BackgroundDependencyLoader] private void load() { - var user = GetUser(score); + var user = score.User; - statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s.icon, s.value, s.name)).ToList(); + statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s)).ToList(); Children = new Drawable[] { @@ -129,13 +131,13 @@ namespace osu.Game.Online.Leaderboards }) { RelativeSizeAxes = Axes.None, - Size = new Vector2(LeaderboardScore.HEIGHT - edge_margin * 2, LeaderboardScore.HEIGHT - edge_margin * 2), + Size = new Vector2(HEIGHT - edge_margin * 2, HEIGHT - edge_margin * 2), }, new Container { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, - Position = new Vector2(LeaderboardScore.HEIGHT - edge_margin, 0f), + Position = new Vector2(HEIGHT - edge_margin, 0f), Children = new Drawable[] { nameLabel = new OsuSpriteText @@ -191,13 +193,13 @@ namespace osu.Game.Online.Leaderboards Spacing = new Vector2(5f, 0f), Children = new Drawable[] { - scoreLabel = new GlowingSpriteText(GetTotalScore(score).ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")), + scoreLabel = new GlowingSpriteText(score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")), RankContainer = new Container { Size = new Vector2(40f, 20f), Children = new[] { - scoreRank = new DrawableRank(GetRank(score)) + scoreRank = new DrawableRank(score.Rank) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -213,7 +215,7 @@ namespace osu.Game.Online.Leaderboards Origin = Anchor.BottomRight, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - ChildrenEnumerable = GetMods(score).Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) }) + ChildrenEnumerable = score.Mods.Select(mod => new ModIcon(mod) { Scale = new Vector2(0.375f) }) }, }, }, @@ -271,16 +273,6 @@ namespace osu.Game.Online.Leaderboards base.OnHoverLost(e); } - protected abstract User GetUser(TScoreModel model); - - protected abstract IEnumerable GetMods(TScoreModel model); - - protected abstract IEnumerable<(FontAwesome icon, string value, string name)> GetStatistics(TScoreModel model); - - protected abstract int GetTotalScore(TScoreModel model); - - protected abstract ScoreRank GetRank(TScoreModel model); - private class GlowingSpriteText : Container { public GlowingSpriteText(string text, string font, int textSize, Color4 textColour, Color4 glowColour) @@ -339,9 +331,9 @@ namespace osu.Game.Online.Leaderboards public string TooltipText => name; - public ScoreComponentLabel(FontAwesome icon, string value, string name) + public ScoreComponentLabel(LeaderboardScoreStatistic statistic) { - this.name = name; + name = statistic.Name; AutoSizeAxes = Axes.Both; Child = content = new FillFlowContainer @@ -373,11 +365,11 @@ namespace osu.Game.Online.Leaderboards Origin = Anchor.Centre, Size = new Vector2(icon_size - 6), Colour = OsuColour.FromHex(@"a4edff"), - Icon = icon, + Icon = statistic.Icon, }, }, }, - new GlowingSpriteText(value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa")) + new GlowingSpriteText(statistic.Value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa")) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -386,5 +378,19 @@ namespace osu.Game.Online.Leaderboards }; } } + + public class LeaderboardScoreStatistic + { + public FontAwesome Icon; + public string Value; + public string Name; + + public LeaderboardScoreStatistic(FontAwesome icon, string name, string value) + { + Icon = icon; + Name = name; + Value = value; + } + } } } diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index b0cfad314c..9f8726c86a 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -79,7 +79,7 @@ namespace osu.Game.Screens.Select.Leaderboards return req; } - protected override LeaderboardScore CreateScoreVisualiser(ScoreInfo model, int index) => new BeatmapLeaderboardScore(model, index) + protected override LeaderboardScore CreateDrawableScore(ScoreInfo model, int index) => new LeaderboardScore(model, index) { Action = () => ScoreSelected?.Invoke(model) }; diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScore.cs deleted file mode 100644 index 098fff4052..0000000000 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScore.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Graphics; -using osu.Game.Online.Leaderboards; -using osu.Game.Rulesets.Mods; -using osu.Game.Scoring; -using osu.Game.Users; - -namespace osu.Game.Screens.Select.Leaderboards -{ - public class BeatmapLeaderboardScore : LeaderboardScore - { - public BeatmapLeaderboardScore(ScoreInfo score, int rank) - : base(score, rank) - { - } - - protected override User GetUser(ScoreInfo model) => model.User; - - protected override IEnumerable GetMods(ScoreInfo model) => model.Mods; - - protected override IEnumerable<(FontAwesome icon, string value, string name)> GetStatistics(ScoreInfo model) => new[] - { - (FontAwesome.fa_link, model.MaxCombo.ToString(), "Max Combo"), - (FontAwesome.fa_crosshairs, string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy), "Accuracy") - }; - - protected override int GetTotalScore(ScoreInfo model) => model.TotalScore; - - protected override ScoreRank GetRank(ScoreInfo model) => model.Rank; - } -} From 52c6d5bfd418dbc3f6ab1e203f98b9b04d2edeb4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Dec 2018 15:23:32 +0900 Subject: [PATCH 49/54] Move protected method down --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 8269e02847..0c64105d5c 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -56,12 +56,6 @@ namespace osu.Game.Online.Leaderboards Height = HEIGHT; } - protected virtual IEnumerable GetStatistics(ScoreInfo model) => new[] - { - new LeaderboardScoreStatistic(FontAwesome.fa_link, "Max Combo", model.MaxCombo.ToString()), - new LeaderboardScoreStatistic(FontAwesome.fa_crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy)) - }; - [BackgroundDependencyLoader] private void load() { @@ -261,6 +255,12 @@ namespace osu.Game.Online.Leaderboards } } + protected virtual IEnumerable GetStatistics(ScoreInfo model) => new[] + { + new LeaderboardScoreStatistic(FontAwesome.fa_link, "Max Combo", model.MaxCombo.ToString()), + new LeaderboardScoreStatistic(FontAwesome.fa_crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy)) + }; + protected override bool OnHover(HoverEvent e) { background.FadeTo(0.5f, 300, Easing.OutQuint); From daa6292e087366f2cdde87c804e641dad09fb6f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 21 Dec 2018 16:28:33 +0900 Subject: [PATCH 50/54] Split results screen to allow for extensibility --- osu.Game.Tests/Visual/TestCaseResults.cs | 8 ++-- osu.Game/Screens/Play/Player.cs | 3 +- osu.Game/Screens/Play/SoloResults.cs | 24 +++++++++++ .../RankingResultsPage.cs} | 11 ++--- .../Ranking/{ => Pages}/ResultsPage.cs | 2 +- .../ScoreResultsPage.cs} | 23 +++++----- osu.Game/Screens/Ranking/ResultMode.cs | 12 ------ osu.Game/Screens/Ranking/ResultModeButton.cs | 19 +++----- .../Screens/Ranking/ResultModeTabControl.cs | 7 +-- osu.Game/Screens/Ranking/Results.cs | 43 ++++++++----------- osu.Game/Screens/Ranking/Types/IResultType.cs | 15 +++++++ .../Ranking/Types/RankingResultType.cs | 26 +++++++++++ .../Screens/Ranking/Types/ScoreResultType.cs | 26 +++++++++++ osu.Game/Screens/Select/SongSelect.cs | 4 +- 14 files changed, 147 insertions(+), 76 deletions(-) create mode 100644 osu.Game/Screens/Play/SoloResults.cs rename osu.Game/Screens/Ranking/{ResultsPageRanking.cs => Pages/RankingResultsPage.cs} (83%) rename osu.Game/Screens/Ranking/{ => Pages}/ResultsPage.cs (98%) rename osu.Game/Screens/Ranking/{ResultsPageScore.cs => Pages/ScoreResultsPage.cs} (98%) delete mode 100644 osu.Game/Screens/Ranking/ResultMode.cs create mode 100644 osu.Game/Screens/Ranking/Types/IResultType.cs create mode 100644 osu.Game/Screens/Ranking/Types/RankingResultType.cs create mode 100644 osu.Game/Screens/Ranking/Types/ScoreResultType.cs diff --git a/osu.Game.Tests/Visual/TestCaseResults.cs b/osu.Game.Tests/Visual/TestCaseResults.cs index 6a20a808b6..a954c6c57c 100644 --- a/osu.Game.Tests/Visual/TestCaseResults.cs +++ b/osu.Game.Tests/Visual/TestCaseResults.cs @@ -8,7 +8,9 @@ using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; +using osu.Game.Screens.Ranking.Pages; using osu.Game.Users; namespace osu.Game.Tests.Visual @@ -23,8 +25,8 @@ namespace osu.Game.Tests.Visual typeof(ScoreInfo), typeof(Results), typeof(ResultsPage), - typeof(ResultsPageScore), - typeof(ResultsPageRanking) + typeof(ScoreResultsPage), + typeof(RankingResultsPage) }; [BackgroundDependencyLoader] @@ -41,7 +43,7 @@ namespace osu.Game.Tests.Visual if (beatmapInfo != null) Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo); - Add(new Results(new ScoreInfo + Add(new SoloResults(new ScoreInfo { TotalScore = 2845370, Accuracy = 0.98, diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 19b49b099c..1429675ddd 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -28,7 +28,6 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; -using osu.Game.Screens.Ranking; using osu.Game.Skinning; using osu.Game.Storyboards.Drawables; @@ -288,7 +287,7 @@ namespace osu.Game.Screens.Play if (RulesetContainer.Replay == null) scoreManager.Import(score, true); - Push(new Results(score)); + Push(new SoloResults(score)); onCompletionEvent = null; }); diff --git a/osu.Game/Screens/Play/SoloResults.cs b/osu.Game/Screens/Play/SoloResults.cs new file mode 100644 index 0000000000..717efd118e --- /dev/null +++ b/osu.Game/Screens/Play/SoloResults.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Scoring; +using osu.Game.Screens.Ranking; +using osu.Game.Screens.Ranking.Types; + +namespace osu.Game.Screens.Play +{ + public class SoloResults : Results + { + public SoloResults(ScoreInfo score) + : base(score) + { + } + + protected override IEnumerable CreateResultTypes() => new IResultType[] + { + new ScoreResultType(Score, Beatmap), + new RankingResultType(Score, Beatmap) + }; + } +} diff --git a/osu.Game/Screens/Ranking/ResultsPageRanking.cs b/osu.Game/Screens/Ranking/Pages/RankingResultsPage.cs similarity index 83% rename from osu.Game/Screens/Ranking/ResultsPageRanking.cs rename to osu.Game/Screens/Ranking/Pages/RankingResultsPage.cs index 3a75daaf60..4c98e476c4 100644 --- a/osu.Game/Screens/Ranking/ResultsPageRanking.cs +++ b/osu.Game/Screens/Ranking/Pages/RankingResultsPage.cs @@ -3,18 +3,19 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Scoring; using osu.Game.Screens.Select.Leaderboards; using osuTK; -using osu.Framework.Graphics.Shapes; -using osu.Game.Scoring; -namespace osu.Game.Screens.Ranking +namespace osu.Game.Screens.Ranking.Pages { - public class ResultsPageRanking : ResultsPage + public class RankingResultsPage : ResultsPage { - public ResultsPageRanking(ScoreInfo score, WorkingBeatmap beatmap = null) : base(score, beatmap) + public RankingResultsPage(ScoreInfo score, WorkingBeatmap beatmap = null) + : base(score, beatmap) { } diff --git a/osu.Game/Screens/Ranking/ResultsPage.cs b/osu.Game/Screens/Ranking/Pages/ResultsPage.cs similarity index 98% rename from osu.Game/Screens/Ranking/ResultsPage.cs rename to osu.Game/Screens/Ranking/Pages/ResultsPage.cs index 5f68623e54..2e379d0a76 100644 --- a/osu.Game/Screens/Ranking/ResultsPage.cs +++ b/osu.Game/Screens/Ranking/Pages/ResultsPage.cs @@ -12,7 +12,7 @@ using osu.Game.Scoring; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.Ranking +namespace osu.Game.Screens.Ranking.Pages { public class ResultsPage : Container { diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs similarity index 98% rename from osu.Game/Screens/Ranking/ResultsPageScore.cs rename to osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs index 0774a63e98..8f5b21a7cb 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/Pages/ScoreResultsPage.cs @@ -4,36 +4,39 @@ using System; using System.Collections.Generic; using System.Linq; -using osuTK; -using osuTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Extensions; 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.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Online.Leaderboards; using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Users; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Extensions; -using osu.Framework.Localisation; -using osu.Game.Online.Leaderboards; -using osu.Game.Scoring; +using osuTK; +using osuTK.Graphics; -namespace osu.Game.Screens.Ranking +namespace osu.Game.Screens.Ranking.Pages { - public class ResultsPageScore : ResultsPage + public class ScoreResultsPage : ResultsPage { private Container scoreContainer; private ScoreCounter scoreCounter; - public ResultsPageScore(ScoreInfo score, WorkingBeatmap beatmap) : base(score, beatmap) { } + public ScoreResultsPage(ScoreInfo score, WorkingBeatmap beatmap) + : base(score, beatmap) + { + } private FillFlowContainer statisticsContainer; diff --git a/osu.Game/Screens/Ranking/ResultMode.cs b/osu.Game/Screens/Ranking/ResultMode.cs deleted file mode 100644 index 4742864b83..0000000000 --- a/osu.Game/Screens/Ranking/ResultMode.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Screens.Ranking -{ - public enum ResultMode - { - Summary, - Ranking, - Share - } -} diff --git a/osu.Game/Screens/Ranking/ResultModeButton.cs b/osu.Game/Screens/Ranking/ResultModeButton.cs index 5df3ff3afb..da0eb3dfcc 100644 --- a/osu.Game/Screens/Ranking/ResultModeButton.cs +++ b/osu.Game/Screens/Ranking/ResultModeButton.cs @@ -10,30 +10,21 @@ using osu.Game.Graphics; using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Game.Screens.Ranking.Types; namespace osu.Game.Screens.Ranking { - public class ResultModeButton : TabItem + public class ResultModeButton : TabItem { private readonly FontAwesome icon; private Color4 activeColour; private Color4 inactiveColour; private CircularContainer colouredPart; - public ResultModeButton(ResultMode mode) : base(mode) + public ResultModeButton(IResultType mode) + : base(mode) { - switch (mode) - { - case ResultMode.Summary: - icon = FontAwesome.fa_asterisk; - break; - case ResultMode.Ranking: - icon = FontAwesome.fa_list; - break; - case ResultMode.Share: - icon = FontAwesome.fa_camera; - break; - } + icon = mode.Icon; } [BackgroundDependencyLoader] diff --git a/osu.Game/Screens/Ranking/ResultModeTabControl.cs b/osu.Game/Screens/Ranking/ResultModeTabControl.cs index e5c6115085..cc1f7bc6b2 100644 --- a/osu.Game/Screens/Ranking/ResultModeTabControl.cs +++ b/osu.Game/Screens/Ranking/ResultModeTabControl.cs @@ -3,11 +3,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; +using osu.Game.Screens.Ranking.Types; using osuTK; namespace osu.Game.Screens.Ranking { - public class ResultModeTabControl : TabControl + public class ResultModeTabControl : TabControl { public ResultModeTabControl() { @@ -19,9 +20,9 @@ namespace osu.Game.Screens.Ranking TabContainer.Padding = new MarginPadding(5); } - protected override Dropdown CreateDropdown() => null; + protected override Dropdown CreateDropdown() => null; - protected override TabItem CreateTabItem(ResultMode value) => new ResultModeButton(value) + protected override TabItem CreateTabItem(IResultType value) => new ResultModeButton(value) { Anchor = TabContainer.Anchor, Origin = TabContainer.Origin diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 1ff5fc7bfd..e7340b2c33 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.IEnumerableExtensions; @@ -18,12 +19,12 @@ using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Sprites; using osu.Game.Scoring; +using osu.Game.Screens.Ranking.Types; namespace osu.Game.Screens.Ranking { - public class Results : OsuScreen + public abstract class Results : OsuScreen { - private readonly ScoreInfo score; private Container circleOuterBackground; private Container circleOuter; private Container circleInner; @@ -34,6 +35,8 @@ namespace osu.Game.Screens.Ranking public override bool AllowBeatmapRulesetChange => false; + protected readonly ScoreInfo Score; + private Container currentPage; private static readonly Vector2 background_blur = new Vector2(20); @@ -44,9 +47,9 @@ namespace osu.Game.Screens.Ranking private const float circle_outer_scale = 0.96f; - public Results(ScoreInfo score) + protected Results(ScoreInfo score) { - this.score = score; + Score = score; } private const float transition_time = 800; @@ -67,7 +70,7 @@ namespace osu.Game.Screens.Ranking backgroundParallax.FadeOut(); modeChangeButtons.FadeOut(); - currentPage.FadeOut(); + currentPage?.FadeOut(); circleOuterBackground .FadeIn(transition_time, Easing.OutQuint) @@ -90,7 +93,7 @@ namespace osu.Game.Screens.Ranking using (BeginDelayedSequence(transition_time * 0.4f, true)) { modeChangeButtons.FadeIn(transition_time, Easing.OutQuint); - currentPage.FadeIn(transition_time, Easing.OutQuint); + currentPage?.FadeIn(transition_time, Easing.OutQuint); } } } @@ -188,7 +191,7 @@ namespace osu.Game.Screens.Ranking }, new OsuSpriteText { - Text = $"{score.MaxCombo}x", + Text = $"{Score.MaxCombo}x", TextSize = 40, RelativePositionAxes = Axes.X, Font = @"Exo2.0-Bold", @@ -209,7 +212,7 @@ namespace osu.Game.Screens.Ranking }, new OsuSpriteText { - Text = $"{score.Accuracy:P2}", + Text = $"{Score.Accuracy:P2}", TextSize = 40, RelativePositionAxes = Axes.X, Font = @"Exo2.0-Bold", @@ -262,30 +265,22 @@ namespace osu.Game.Screens.Ranking }, }; - modeChangeButtons.AddItem(ResultMode.Summary); - modeChangeButtons.AddItem(ResultMode.Ranking); - //modeChangeButtons.AddItem(ResultMode.Share); + foreach (var t in CreateResultTypes()) + modeChangeButtons.AddItem(t); + modeChangeButtons.Current.Value = modeChangeButtons.Items.FirstOrDefault(); - modeChangeButtons.Current.ValueChanged += mode => + modeChangeButtons.Current.BindValueChanged(m => { currentPage?.FadeOut(); currentPage?.Expire(); - switch (mode) - { - case ResultMode.Summary: - currentPage = new ResultsPageScore(score, Beatmap.Value); - break; - case ResultMode.Ranking: - currentPage = new ResultsPageRanking(score, Beatmap.Value); - break; - } + currentPage = m?.CreatePage(); if (currentPage != null) circleInner.Add(currentPage); - }; - - modeChangeButtons.Current.TriggerChange(); + }, true); } + + protected abstract IEnumerable CreateResultTypes(); } } diff --git a/osu.Game/Screens/Ranking/Types/IResultType.cs b/osu.Game/Screens/Ranking/Types/IResultType.cs new file mode 100644 index 0000000000..df574e99f1 --- /dev/null +++ b/osu.Game/Screens/Ranking/Types/IResultType.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; +using osu.Game.Screens.Ranking.Pages; + +namespace osu.Game.Screens.Ranking.Types +{ + public interface IResultType + { + FontAwesome Icon { get; } + + ResultsPage CreatePage(); + } +} diff --git a/osu.Game/Screens/Ranking/Types/RankingResultType.cs b/osu.Game/Screens/Ranking/Types/RankingResultType.cs new file mode 100644 index 0000000000..a11732d324 --- /dev/null +++ b/osu.Game/Screens/Ranking/Types/RankingResultType.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Scoring; +using osu.Game.Screens.Ranking.Pages; + +namespace osu.Game.Screens.Ranking.Types +{ + public class RankingResultType : IResultType + { + private readonly ScoreInfo score; + private readonly WorkingBeatmap beatmap; + + public RankingResultType(ScoreInfo score, WorkingBeatmap beatmap) + { + this.score = score; + this.beatmap = beatmap; + } + + public FontAwesome Icon => FontAwesome.fa_list; + + public ResultsPage CreatePage() => new RankingResultsPage(score, beatmap); + } +} diff --git a/osu.Game/Screens/Ranking/Types/ScoreResultType.cs b/osu.Game/Screens/Ranking/Types/ScoreResultType.cs new file mode 100644 index 0000000000..0841365528 --- /dev/null +++ b/osu.Game/Screens/Ranking/Types/ScoreResultType.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Scoring; +using osu.Game.Screens.Ranking.Pages; + +namespace osu.Game.Screens.Ranking.Types +{ + public class ScoreResultType : IResultType + { + private readonly ScoreInfo score; + private readonly WorkingBeatmap beatmap; + + public ScoreResultType(ScoreInfo score, WorkingBeatmap beatmap) + { + this.score = score; + this.beatmap = beatmap; + } + + public FontAwesome Icon => FontAwesome.fa_asterisk; + + public ResultsPage CreatePage() => new ScoreResultsPage(score, beatmap); + } +} diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 71b63c8e5b..f65cc0e49d 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -28,7 +28,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; -using osu.Game.Screens.Ranking; +using osu.Game.Screens.Play; using osu.Game.Screens.Select.Options; using osu.Game.Skinning; @@ -210,7 +210,7 @@ namespace osu.Game.Screens.Select }); } - BeatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s)); + BeatmapDetails.Leaderboard.ScoreSelected += s => Push(new SoloResults(s)); } [BackgroundDependencyLoader(true)] From e404a0bc201d4a6e93b8ae47566998e78f5744f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Dec 2018 15:51:00 +0900 Subject: [PATCH 51/54] Clean-ups and renames --- osu.Game/Screens/Play/SoloResults.cs | 6 +++--- osu.Game/Screens/Ranking/{Types => }/IResultType.cs | 7 ++++--- osu.Game/Screens/Ranking/ResultModeButton.cs | 5 ++--- osu.Game/Screens/Ranking/ResultModeTabControl.cs | 7 +++---- osu.Game/Screens/Ranking/Results.cs | 3 +-- osu.Game/Screens/Ranking/{Pages => }/ResultsPage.cs | 6 +++--- ...{RankingResultType.cs => BeatmapLeaderboardPageInfo.cs} | 6 ++++-- .../Types/{ScoreResultType.cs => ScoreOverviewPageInfo.cs} | 6 ++++-- 8 files changed, 24 insertions(+), 22 deletions(-) rename osu.Game/Screens/Ranking/{Types => }/IResultType.cs (70%) rename osu.Game/Screens/Ranking/{Pages => }/ResultsPage.cs (94%) rename osu.Game/Screens/Ranking/Types/{RankingResultType.cs => BeatmapLeaderboardPageInfo.cs} (76%) rename osu.Game/Screens/Ranking/Types/{ScoreResultType.cs => ScoreOverviewPageInfo.cs} (77%) diff --git a/osu.Game/Screens/Play/SoloResults.cs b/osu.Game/Screens/Play/SoloResults.cs index 717efd118e..2828c758e0 100644 --- a/osu.Game/Screens/Play/SoloResults.cs +++ b/osu.Game/Screens/Play/SoloResults.cs @@ -15,10 +15,10 @@ namespace osu.Game.Screens.Play { } - protected override IEnumerable CreateResultTypes() => new IResultType[] + protected override IEnumerable CreateResultTypes() => new IResultPageInfo[] { - new ScoreResultType(Score, Beatmap), - new RankingResultType(Score, Beatmap) + new ScoreOverviewPageInfo(Score, Beatmap), + new BeatmapLeaderboardPageInfo(Score, Beatmap) }; } } diff --git a/osu.Game/Screens/Ranking/Types/IResultType.cs b/osu.Game/Screens/Ranking/IResultType.cs similarity index 70% rename from osu.Game/Screens/Ranking/Types/IResultType.cs rename to osu.Game/Screens/Ranking/IResultType.cs index df574e99f1..5c6d08d883 100644 --- a/osu.Game/Screens/Ranking/Types/IResultType.cs +++ b/osu.Game/Screens/Ranking/IResultType.cs @@ -2,14 +2,15 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Graphics; -using osu.Game.Screens.Ranking.Pages; -namespace osu.Game.Screens.Ranking.Types +namespace osu.Game.Screens.Ranking { - public interface IResultType + public interface IResultPageInfo { FontAwesome Icon { get; } + string Name { get; } + ResultsPage CreatePage(); } } diff --git a/osu.Game/Screens/Ranking/ResultModeButton.cs b/osu.Game/Screens/Ranking/ResultModeButton.cs index da0eb3dfcc..34a93236bd 100644 --- a/osu.Game/Screens/Ranking/ResultModeButton.cs +++ b/osu.Game/Screens/Ranking/ResultModeButton.cs @@ -10,18 +10,17 @@ using osu.Game.Graphics; using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Game.Screens.Ranking.Types; namespace osu.Game.Screens.Ranking { - public class ResultModeButton : TabItem + public class ResultModeButton : TabItem { private readonly FontAwesome icon; private Color4 activeColour; private Color4 inactiveColour; private CircularContainer colouredPart; - public ResultModeButton(IResultType mode) + public ResultModeButton(IResultPageInfo mode) : base(mode) { icon = mode.Icon; diff --git a/osu.Game/Screens/Ranking/ResultModeTabControl.cs b/osu.Game/Screens/Ranking/ResultModeTabControl.cs index cc1f7bc6b2..e87749d8dd 100644 --- a/osu.Game/Screens/Ranking/ResultModeTabControl.cs +++ b/osu.Game/Screens/Ranking/ResultModeTabControl.cs @@ -3,12 +3,11 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; -using osu.Game.Screens.Ranking.Types; using osuTK; namespace osu.Game.Screens.Ranking { - public class ResultModeTabControl : TabControl + public class ResultModeTabControl : TabControl { public ResultModeTabControl() { @@ -20,9 +19,9 @@ namespace osu.Game.Screens.Ranking TabContainer.Padding = new MarginPadding(5); } - protected override Dropdown CreateDropdown() => null; + protected override Dropdown CreateDropdown() => null; - protected override TabItem CreateTabItem(IResultType value) => new ResultModeButton(value) + protected override TabItem CreateTabItem(IResultPageInfo value) => new ResultModeButton(value) { Anchor = TabContainer.Anchor, Origin = TabContainer.Origin diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index e7340b2c33..86067d8cc5 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -19,7 +19,6 @@ using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Sprites; using osu.Game.Scoring; -using osu.Game.Screens.Ranking.Types; namespace osu.Game.Screens.Ranking { @@ -281,6 +280,6 @@ namespace osu.Game.Screens.Ranking }, true); } - protected abstract IEnumerable CreateResultTypes(); + protected abstract IEnumerable CreateResultTypes(); } } diff --git a/osu.Game/Screens/Ranking/Pages/ResultsPage.cs b/osu.Game/Screens/Ranking/ResultsPage.cs similarity index 94% rename from osu.Game/Screens/Ranking/Pages/ResultsPage.cs rename to osu.Game/Screens/Ranking/ResultsPage.cs index 2e379d0a76..08c6155557 100644 --- a/osu.Game/Screens/Ranking/Pages/ResultsPage.cs +++ b/osu.Game/Screens/Ranking/ResultsPage.cs @@ -12,9 +12,9 @@ using osu.Game.Scoring; using osuTK; using osuTK.Graphics; -namespace osu.Game.Screens.Ranking.Pages +namespace osu.Game.Screens.Ranking { - public class ResultsPage : Container + public abstract class ResultsPage : Container { protected readonly ScoreInfo Score; protected readonly WorkingBeatmap Beatmap; @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Ranking.Pages protected override Container Content => content; - public ResultsPage(ScoreInfo score, WorkingBeatmap beatmap) + protected ResultsPage(ScoreInfo score, WorkingBeatmap beatmap) { Score = score; Beatmap = beatmap; diff --git a/osu.Game/Screens/Ranking/Types/RankingResultType.cs b/osu.Game/Screens/Ranking/Types/BeatmapLeaderboardPageInfo.cs similarity index 76% rename from osu.Game/Screens/Ranking/Types/RankingResultType.cs rename to osu.Game/Screens/Ranking/Types/BeatmapLeaderboardPageInfo.cs index a11732d324..2b192d4bcd 100644 --- a/osu.Game/Screens/Ranking/Types/RankingResultType.cs +++ b/osu.Game/Screens/Ranking/Types/BeatmapLeaderboardPageInfo.cs @@ -8,12 +8,12 @@ using osu.Game.Screens.Ranking.Pages; namespace osu.Game.Screens.Ranking.Types { - public class RankingResultType : IResultType + public class BeatmapLeaderboardPageInfo : IResultPageInfo { private readonly ScoreInfo score; private readonly WorkingBeatmap beatmap; - public RankingResultType(ScoreInfo score, WorkingBeatmap beatmap) + public BeatmapLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap) { this.score = score; this.beatmap = beatmap; @@ -21,6 +21,8 @@ namespace osu.Game.Screens.Ranking.Types public FontAwesome Icon => FontAwesome.fa_list; + public string Name => @"Beatmap Leaderboard"; + public ResultsPage CreatePage() => new RankingResultsPage(score, beatmap); } } diff --git a/osu.Game/Screens/Ranking/Types/ScoreResultType.cs b/osu.Game/Screens/Ranking/Types/ScoreOverviewPageInfo.cs similarity index 77% rename from osu.Game/Screens/Ranking/Types/ScoreResultType.cs rename to osu.Game/Screens/Ranking/Types/ScoreOverviewPageInfo.cs index 0841365528..13930fe689 100644 --- a/osu.Game/Screens/Ranking/Types/ScoreResultType.cs +++ b/osu.Game/Screens/Ranking/Types/ScoreOverviewPageInfo.cs @@ -8,18 +8,20 @@ using osu.Game.Screens.Ranking.Pages; namespace osu.Game.Screens.Ranking.Types { - public class ScoreResultType : IResultType + public class ScoreOverviewPageInfo : IResultPageInfo { private readonly ScoreInfo score; private readonly WorkingBeatmap beatmap; - public ScoreResultType(ScoreInfo score, WorkingBeatmap beatmap) + public ScoreOverviewPageInfo(ScoreInfo score, WorkingBeatmap beatmap) { this.score = score; this.beatmap = beatmap; } public FontAwesome Icon => FontAwesome.fa_asterisk; + + public string Name => "Overview"; public ResultsPage CreatePage() => new ScoreResultsPage(score, beatmap); } From e7508cbd5efe7f1a74d6e1fbbccc33725697af04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Dec 2018 15:55:10 +0900 Subject: [PATCH 52/54] Fix CI issues --- .../Ranking/{IResultType.cs => IResultPageInfo.cs} | 0 .../Screens/Ranking/Types/ScoreOverviewPageInfo.cs | 12 +++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) rename osu.Game/Screens/Ranking/{IResultType.cs => IResultPageInfo.cs} (100%) diff --git a/osu.Game/Screens/Ranking/IResultType.cs b/osu.Game/Screens/Ranking/IResultPageInfo.cs similarity index 100% rename from osu.Game/Screens/Ranking/IResultType.cs rename to osu.Game/Screens/Ranking/IResultPageInfo.cs diff --git a/osu.Game/Screens/Ranking/Types/ScoreOverviewPageInfo.cs b/osu.Game/Screens/Ranking/Types/ScoreOverviewPageInfo.cs index 13930fe689..03998d2840 100644 --- a/osu.Game/Screens/Ranking/Types/ScoreOverviewPageInfo.cs +++ b/osu.Game/Screens/Ranking/Types/ScoreOverviewPageInfo.cs @@ -10,6 +10,9 @@ namespace osu.Game.Screens.Ranking.Types { public class ScoreOverviewPageInfo : IResultPageInfo { + public FontAwesome Icon => FontAwesome.fa_asterisk; + + public string Name => "Overview"; private readonly ScoreInfo score; private readonly WorkingBeatmap beatmap; @@ -19,10 +22,9 @@ namespace osu.Game.Screens.Ranking.Types this.beatmap = beatmap; } - public FontAwesome Icon => FontAwesome.fa_asterisk; - - public string Name => "Overview"; - - public ResultsPage CreatePage() => new ScoreResultsPage(score, beatmap); + public ResultsPage CreatePage() + { + return new ScoreResultsPage(score, beatmap); + } } } From a35d9178f05bb58ae1aac22e5d6849da55894d68 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Dec 2018 16:20:29 +0900 Subject: [PATCH 53/54] Quick rename --- osu.Game/Screens/Play/SoloResults.cs | 2 +- osu.Game/Screens/Ranking/Results.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/SoloResults.cs b/osu.Game/Screens/Play/SoloResults.cs index 2828c758e0..5e318e95d1 100644 --- a/osu.Game/Screens/Play/SoloResults.cs +++ b/osu.Game/Screens/Play/SoloResults.cs @@ -15,7 +15,7 @@ namespace osu.Game.Screens.Play { } - protected override IEnumerable CreateResultTypes() => new IResultPageInfo[] + protected override IEnumerable CreateResultPages() => new IResultPageInfo[] { new ScoreOverviewPageInfo(Score, Beatmap), new BeatmapLeaderboardPageInfo(Score, Beatmap) diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 86067d8cc5..bf9e3bcd27 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -264,7 +264,7 @@ namespace osu.Game.Screens.Ranking }, }; - foreach (var t in CreateResultTypes()) + foreach (var t in CreateResultPages()) modeChangeButtons.AddItem(t); modeChangeButtons.Current.Value = modeChangeButtons.Items.FirstOrDefault(); @@ -280,6 +280,6 @@ namespace osu.Game.Screens.Ranking }, true); } - protected abstract IEnumerable CreateResultTypes(); + protected abstract IEnumerable CreateResultPages(); } } From 005c6ff40c6212da2c4a279cb976a8f3edbcb747 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 22 Dec 2018 16:30:06 +0900 Subject: [PATCH 54/54] Add tooltip text to results screen pages --- osu.Game/Screens/Ranking/ResultModeButton.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ResultModeButton.cs b/osu.Game/Screens/Ranking/ResultModeButton.cs index 34a93236bd..f6a886418c 100644 --- a/osu.Game/Screens/Ranking/ResultModeButton.cs +++ b/osu.Game/Screens/Ranking/ResultModeButton.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics; using osuTK; @@ -13,7 +14,7 @@ using osu.Framework.Graphics.Shapes; namespace osu.Game.Screens.Ranking { - public class ResultModeButton : TabItem + public class ResultModeButton : TabItem, IHasTooltip { private readonly FontAwesome icon; private Color4 activeColour; @@ -24,6 +25,7 @@ namespace osu.Game.Screens.Ranking : base(mode) { icon = mode.Icon; + TooltipText = mode.Name; } [BackgroundDependencyLoader] @@ -85,5 +87,7 @@ namespace osu.Game.Screens.Ranking protected override void OnActivated() => colouredPart.FadeColour(activeColour, 200, Easing.OutQuint); protected override void OnDeactivated() => colouredPart.FadeColour(inactiveColour, 200, Easing.OutQuint); + + public string TooltipText { get; private set; } } }