1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-16 13:17:26 +08:00
osu-lazer/osu.Game/Rulesets/Mods/ModFlashlight.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

325 lines
11 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-04-13 17:19:50 +08:00
using System;
using System.Diagnostics;
2023-02-25 00:21:37 +08:00
using System.Runtime.InteropServices;
using osu.Framework.Allocation;
2019-02-21 18:04:31 +08:00
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
2022-07-29 21:33:34 +08:00
using osu.Framework.Graphics.Rendering;
2022-08-05 19:36:28 +08:00
using osu.Framework.Graphics.Rendering.Vertices;
using osu.Framework.Graphics.Shaders;
2023-02-25 00:21:37 +08:00
using osu.Framework.Graphics.Shaders.Types;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Framework.Utils;
2022-01-16 04:43:28 +08:00
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.OpenGL.Vertices;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
2018-11-20 15:51:59 +08:00
using osuTK;
using osuTK.Graphics;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Rulesets.Mods
{
2018-11-30 13:48:19 +08:00
public abstract class ModFlashlight : Mod
{
public override string Name => "Flashlight";
public override string Acronym => "FL";
2020-01-14 21:22:00 +08:00
public override IconUsage? Icon => OsuIcon.ModFlashlight;
2017-05-03 02:36:55 +08:00
public override ModType Type => ModType.DifficultyIncrease;
public override LocalisableString Description => "Restricted view area.";
2024-01-31 21:59:35 +08:00
public override bool Ranked => UsesDefaultConfiguration;
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
public abstract BindableFloat SizeMultiplier { get; }
2022-01-16 04:43:28 +08:00
[SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")]
public abstract BindableBool ComboBasedSize { get; }
/// <summary>
/// The default size of the flashlight in ruleset-appropriate dimensions.
/// <see cref="SizeMultiplier"/> and <see cref="ComboBasedSize"/> will apply their adjustments on top of this size.
/// </summary>
public abstract float DefaultFlashlightSize { get; }
2018-11-30 13:48:19 +08:00
}
public abstract partial class ModFlashlight<T> : ModFlashlight, IApplicableToDrawableRuleset<T>, IApplicableToScoreProcessor
2018-11-30 13:48:19 +08:00
where T : HitObject
{
public const double FLASHLIGHT_FADE_DURATION = 800;
protected readonly BindableInt Combo = new BindableInt();
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{
Combo.BindTo(scoreProcessor.Combo);
}
2020-07-03 03:39:37 +08:00
public ScoreRank AdjustRank(ScoreRank rank, double accuracy)
{
switch (rank)
{
case ScoreRank.X:
return ScoreRank.XH;
case ScoreRank.S:
return ScoreRank.SH;
default:
return rank;
}
}
2019-03-20 10:22:34 +08:00
public virtual void ApplyToDrawableRuleset(DrawableRuleset<T> drawableRuleset)
{
var flashlight = CreateFlashlight();
flashlight.RelativeSizeAxes = Axes.Both;
flashlight.Colour = Color4.Black;
// Flashlight mods should always draw above any other mod adding overlays.
flashlight.Depth = float.MinValue;
flashlight.Combo.BindTo(Combo);
flashlight.GetPlayfieldScale = () => drawableRuleset.Playfield.Scale;
2022-12-26 04:32:47 +08:00
drawableRuleset.Overlays.Add(new Container
{
RelativeSizeAxes = Axes.Both,
// workaround for 1px gaps on the edges of the playfield which would sometimes show with "gameplay" screen scaling active.
Padding = new MarginPadding(-1),
Child = flashlight,
});
}
protected abstract Flashlight CreateFlashlight();
public abstract partial class Flashlight : Drawable
{
public readonly BindableInt Combo = new BindableInt();
private IShader shader = null!;
2019-04-02 10:56:22 +08:00
protected override DrawNode CreateDrawNode() => new FlashlightDrawNode(this);
public override bool RemoveCompletedTransforms => false;
internal Func<Vector2>? GetPlayfieldScale;
private readonly float defaultFlashlightSize;
private readonly float sizeMultiplier;
private readonly bool comboBasedSize;
2022-01-16 04:43:28 +08:00
protected Flashlight(ModFlashlight modFlashlight)
2022-01-16 04:43:28 +08:00
{
defaultFlashlightSize = modFlashlight.DefaultFlashlightSize;
sizeMultiplier = modFlashlight.SizeMultiplier.Value;
comboBasedSize = modFlashlight.ComboBasedSize.Value;
2022-01-16 04:43:28 +08:00
}
[BackgroundDependencyLoader]
private void load(ShaderManager shaderManager)
{
shader = shaderManager.Load("PositionAndColour", FragmentShader);
}
[Resolved]
private Player? player { get; set; }
private readonly IBindable<bool> isBreakTime = new BindableBool();
protected override void LoadComplete()
{
base.LoadComplete();
Combo.ValueChanged += _ => UpdateFlashlightSize(GetSize());
if (player != null)
{
isBreakTime.BindTo(player.IsBreakTime);
isBreakTime.BindValueChanged(_ => UpdateFlashlightSize(GetSize()), true);
}
}
protected abstract void UpdateFlashlightSize(float size);
protected abstract string FragmentShader { get; }
public float GetSize()
2022-01-16 04:43:28 +08:00
{
float size = defaultFlashlightSize * sizeMultiplier;
if (GetPlayfieldScale != null)
{
Vector2 playfieldScale = GetPlayfieldScale();
Debug.Assert(Precision.AlmostEquals(Math.Abs(playfieldScale.X), Math.Abs(playfieldScale.Y)),
@"Playfield has non-proportional scaling. Flashlight implementations should be revisited with regard to balance.");
size *= Math.Abs(playfieldScale.X);
}
if (isBreakTime.Value)
size *= 2.5f;
else if (comboBasedSize)
size *= GetComboScaleFor(Combo.Value);
2022-01-16 04:43:28 +08:00
return size;
2022-01-16 04:43:28 +08:00
}
protected virtual float GetComboScaleFor(int combo)
{
if (combo >= 200)
return 0.625f;
if (combo >= 100)
return 0.8125f;
return 1.0f;
}
private Vector2 flashlightPosition;
2019-02-28 12:31:40 +08:00
protected Vector2 FlashlightPosition
{
get => flashlightPosition;
set
{
if (flashlightPosition == value) return;
flashlightPosition = value;
Invalidate(Invalidation.DrawNode);
}
}
private Vector2 flashlightSize;
2019-02-28 12:31:40 +08:00
protected Vector2 FlashlightSize
{
get => flashlightSize;
set
{
if (flashlightSize == value) return;
flashlightSize = value;
Invalidate(Invalidation.DrawNode);
}
}
2019-04-12 10:23:40 +08:00
private float flashlightDim;
2019-04-12 10:23:40 +08:00
public float FlashlightDim
{
2019-04-12 10:23:40 +08:00
get => flashlightDim;
set
{
2019-04-12 10:23:40 +08:00
if (flashlightDim == value) return;
2019-04-12 10:23:40 +08:00
flashlightDim = value;
Invalidate(Invalidation.DrawNode);
}
}
private float flashlightSmoothness = 1.1f;
public float FlashlightSmoothness
{
get => flashlightSmoothness;
set
{
if (flashlightSmoothness == value) return;
flashlightSmoothness = value;
Invalidate(Invalidation.DrawNode);
}
}
2019-04-02 10:56:22 +08:00
private class FlashlightDrawNode : DrawNode
{
2019-04-02 10:56:22 +08:00
protected new Flashlight Source => (Flashlight)base.Source;
private IShader shader = null!;
2019-04-02 10:56:22 +08:00
private Quad screenSpaceDrawQuad;
private Vector2 flashlightPosition;
private Vector2 flashlightSize;
private float flashlightDim;
private float flashlightSmoothness;
2022-07-29 22:32:06 +08:00
private IVertexBatch<PositionAndColourVertex>? quadBatch;
private Action<TexturedVertex2D>? addAction;
2019-04-02 10:56:22 +08:00
public FlashlightDrawNode(Flashlight source)
: base(source)
{
}
2019-04-02 10:56:22 +08:00
public override void ApplyState()
{
base.ApplyState();
shader = Source.shader;
screenSpaceDrawQuad = Source.ScreenSpaceDrawQuad;
flashlightPosition = Vector2Extensions.Transform(Source.FlashlightPosition, DrawInfo.Matrix);
flashlightSize = Source.FlashlightSize * DrawInfo.Matrix.ExtractScale().Xy;
flashlightDim = Source.FlashlightDim;
flashlightSmoothness = Source.flashlightSmoothness;
2019-04-02 10:56:22 +08:00
}
2023-02-25 00:21:37 +08:00
private IUniformBuffer<FlashlightParameters>? flashlightParametersBuffer;
2023-12-04 07:51:21 +08:00
protected override void Draw(IRenderer renderer)
2019-04-02 10:56:22 +08:00
{
2022-07-29 21:33:34 +08:00
base.Draw(renderer);
2022-07-29 22:32:06 +08:00
if (quadBatch == null)
{
quadBatch = renderer.CreateQuadBatch<PositionAndColourVertex>(1, 1);
addAction = v => quadBatch.Add(new PositionAndColourVertex
{
Position = v.Position,
Colour = v.Colour
});
}
2023-02-25 00:21:37 +08:00
flashlightParametersBuffer ??= renderer.CreateUniformBuffer<FlashlightParameters>();
flashlightParametersBuffer.Data = flashlightParametersBuffer.Data with
{
Position = flashlightPosition,
Size = flashlightSize,
Dim = flashlightDim,
Smoothness = flashlightSmoothness
};
2023-02-25 00:21:37 +08:00
shader.Bind();
shader.BindUniformBlock(@"m_FlashlightParameters", flashlightParametersBuffer);
2022-08-02 18:50:57 +08:00
renderer.DrawQuad(renderer.WhitePixel, screenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: addAction);
2019-04-02 10:56:22 +08:00
shader.Unbind();
}
2019-08-07 16:33:30 +08:00
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
2022-07-29 22:32:06 +08:00
quadBatch?.Dispose();
2023-02-25 00:21:37 +08:00
flashlightParametersBuffer?.Dispose();
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private record struct FlashlightParameters
{
public UniformVector2 Position;
public UniformVector2 Size;
public UniformFloat Dim;
public UniformFloat Smoothness;
private readonly UniformPadding8 pad1;
2019-08-07 16:33:30 +08:00
}
}
}
}
2018-01-05 19:21:19 +08:00
}