From b011edd85d7aefaa00b6d8fe83ca7d8def901149 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 5 Aug 2018 13:12:31 +0200 Subject: [PATCH 01/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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 07f8b8e334d398de787475881cf34b698c7ba879 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Dec 2018 15:47:26 +0900 Subject: [PATCH 16/19] 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 17/19] 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 d3368df94dd3338cbe611304185a773b89bf7df6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 20 Dec 2018 19:35:32 +0900 Subject: [PATCH 18/19] 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 19/19] 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.