osu/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs

212 lines
8.2 KiB
C#
Raw Normal View History

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
2018-08-05 12:20:56 +00:00
using System;
2018-12-07 12:11:35 +00:00
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.Framework.Utils;
2018-12-07 12:11:35 +00:00
using osu.Game.Beatmaps;
2018-08-05 12:20:56 +00:00
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
2018-12-07 12:11:35 +00:00
using osuTK.Graphics;
2018-08-05 12:20:56 +00:00
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModBlinds : Mod, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToHealthProcessor
2018-08-05 12:20:56 +00:00
{
2018-12-07 11:12:56 +00:00
public override string Name => "Blinds";
2018-12-07 12:11:35 +00:00
public override string Description => "Play with blinds on your screen.";
2018-12-07 11:12:56 +00:00
public override string Acronym => "BL";
2018-12-07 12:11:35 +00:00
2020-01-14 13:22:00 +00:00
public override IconUsage? Icon => FontAwesome.Solid.Adjust;
2018-12-07 11:12:56 +00:00
public override ModType Type => ModType.DifficultyIncrease;
2018-12-07 12:11:35 +00:00
2018-08-05 12:20:56 +00:00
public override double ScoreMultiplier => 1.12;
2018-12-07 12:11:35 +00:00
private DrawableOsuBlinds blinds;
2018-08-05 12:20:56 +00:00
2019-03-20 02:22:34 +00:00
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
2018-08-05 12:20:56 +00:00
{
drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield, drawableRuleset.Beatmap));
2018-08-05 12:20:56 +00:00
}
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
2018-08-05 12:20:56 +00:00
{
healthProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); };
2018-12-07 12:11:35 +00:00
}
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
2018-12-07 12:11:35 +00:00
/// <summary>
/// Element for the Blinds mod drawing 2 black boxes covering the whole screen which resize inside a restricted area with some leniency.
/// </summary>
public class DrawableOsuBlinds : Container
{
/// <summary>
/// Black background boxes behind blind panel textures.
/// </summary>
private Box blackBoxLeft, blackBoxRight;
private Drawable panelLeft, panelRight, bgPanelLeft, bgPanelRight;
private readonly Beatmap<OsuHitObject> beatmap;
/// <summary>
/// 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.
/// </summary>
private const float target_clamp = 1;
2020-11-01 19:51:23 +00:00
private readonly float targetBreakMultiplier;
private readonly float easing;
2018-12-07 12:11:35 +00:00
private readonly CompositeDrawable restrictTo;
/// <summary>
/// <para>
/// Percentage of playfield to extend blinds over. Basically moves the origin points where the blinds start.
/// </para>
/// <para>
/// -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.
/// </para>
/// </summary>
private const float leniency = 0.1f;
public DrawableOsuBlinds(CompositeDrawable restrictTo, Beatmap<OsuHitObject> beatmap)
2018-12-07 12:11:35 +00:00
{
this.restrictTo = restrictTo;
this.beatmap = beatmap;
2020-11-01 19:51:23 +00:00
targetBreakMultiplier = 0;
easing = 1;
2018-12-07 12:11:35 +00:00
}
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.Both;
Children = new[]
{
blackBoxLeft = new Box
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Colour = Color4.Black,
RelativeSizeAxes = Axes.Y,
},
blackBoxRight = new Box
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = Color4.Black,
RelativeSizeAxes = Axes.Y,
},
bgPanelLeft = new ModBlindsPanel
{
Origin = Anchor.TopRight,
Colour = Color4.Gray,
},
panelLeft = new ModBlindsPanel { Origin = Anchor.TopRight, },
bgPanelRight = new ModBlindsPanel { Colour = Color4.Gray },
panelRight = new ModBlindsPanel()
2018-12-07 12:11:35 +00:00
};
}
private float calculateGap(float value) => Math.Clamp(value, 0, target_clamp) * targetBreakMultiplier;
2018-12-07 12:11:35 +00:00
// 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;
2018-12-07 12:11:35 +00:00
protected override void Update()
{
float start, end;
if (Precision.AlmostEquals(restrictTo.Rotation, 0))
{
start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X;
end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X;
}
else
{
float center = restrictTo.ToSpaceOfOtherDrawable(restrictTo.OriginPosition, Parent).X;
float halfDiagonal = (restrictTo.DrawSize / 2).LengthFast;
start = center - halfDiagonal;
end = center + halfDiagonal;
}
2018-12-07 12:11:35 +00:00
float rawWidth = end - start;
start -= rawWidth * leniency * 0.5f;
end += rawWidth * leniency * 0.5f;
float width = (end - start) * 0.5f * applyAdjustmentCurve(calculateGap(easing));
2018-12-07 12:11:35 +00:00
// 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];
double startDelay = firstObj.StartTime - firstObj.TimePreempt;
2018-12-07 12:11:35 +00:00
2021-07-05 15:52:39 +00:00
using (BeginAbsoluteSequence(startDelay + break_close_late))
2018-12-07 12:11:35 +00:00
leaveBreak();
foreach (var breakInfo in beatmap.Breaks)
{
if (breakInfo.HasEffect)
{
2021-07-05 15:52:39 +00:00
using (BeginAbsoluteSequence(breakInfo.StartTime - break_open_early))
2018-12-07 12:11:35 +00:00
{
enterBreak();
2021-07-05 15:52:39 +00:00
using (BeginDelayedSequence(breakInfo.Duration + break_open_early + break_close_late))
2018-12-07 12:11:35 +00:00
leaveBreak();
}
}
}
}
private void enterBreak() => this.TransformTo(nameof(targetBreakMultiplier), 0f, 1000, Easing.OutSine);
private void leaveBreak() => this.TransformTo(nameof(targetBreakMultiplier), 1f, 2500, Easing.OutBounce);
/// <summary>
/// 0 is open, 1 is closed.
/// </summary>
public void AnimateClosedness(float value) => this.TransformTo(nameof(easing), value, 200, Easing.OutQuint);
public class ModBlindsPanel : Sprite
2018-12-07 12:11:35 +00:00
{
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
2019-08-30 06:10:11 +00:00
Texture = textures.Get("Gameplay/osu/blinds-panel");
2018-12-07 12:11:35 +00:00
}
}
2018-08-05 12:20:56 +00:00
}
}
}