2019-01-24 16:43:03 +08:00
|
|
|
// 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
|
|
|
|
2018-11-12 01:38:12 +08:00
|
|
|
using System;
|
2024-02-26 17:37:03 +08:00
|
|
|
using System.Diagnostics;
|
2023-02-25 00:21:37 +08:00
|
|
|
using System.Runtime.InteropServices;
|
2018-11-12 01:38:12 +08:00
|
|
|
using osu.Framework.Allocation;
|
2019-02-21 18:04:31 +08:00
|
|
|
using osu.Framework.Bindables;
|
2018-11-12 01:38:12 +08:00
|
|
|
using osu.Framework.Graphics;
|
|
|
|
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;
|
2018-11-12 01:38:12 +08:00
|
|
|
using osu.Framework.Graphics.Shaders;
|
2023-02-25 00:21:37 +08:00
|
|
|
using osu.Framework.Graphics.Shaders.Types;
|
2019-03-27 18:29:27 +08:00
|
|
|
using osu.Framework.Graphics.Sprites;
|
2022-08-11 03:54:48 +08:00
|
|
|
using osu.Framework.Localisation;
|
2024-02-26 17:37:03 +08:00
|
|
|
using osu.Framework.Utils;
|
2022-01-16 04:43:28 +08:00
|
|
|
using osu.Game.Configuration;
|
2017-04-21 16:33:20 +08:00
|
|
|
using osu.Game.Graphics;
|
2019-07-23 14:17:02 +08:00
|
|
|
using osu.Game.Graphics.OpenGL.Vertices;
|
2018-11-12 01:38:12 +08:00
|
|
|
using osu.Game.Rulesets.Objects;
|
|
|
|
using osu.Game.Rulesets.Scoring;
|
|
|
|
using osu.Game.Rulesets.UI;
|
2019-04-25 18:56:57 +08:00
|
|
|
using osu.Game.Scoring;
|
2022-10-11 22:16:57 +08:00
|
|
|
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
|
|
|
|
2017-04-21 16:33:20 +08:00
|
|
|
namespace osu.Game.Rulesets.Mods
|
|
|
|
{
|
2018-11-30 13:48:19 +08:00
|
|
|
public abstract class ModFlashlight : Mod
|
2017-04-21 16:33:20 +08:00
|
|
|
{
|
|
|
|
public override string Name => "Flashlight";
|
2018-11-30 16:16:00 +08:00
|
|
|
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;
|
2022-08-11 03:54:48 +08:00
|
|
|
public override LocalisableString Description => "Restricted view area.";
|
2024-01-31 21:59:35 +08:00
|
|
|
public override bool Ranked => UsesDefaultConfiguration;
|
2018-11-12 01:38:12 +08:00
|
|
|
|
2022-01-25 03:44:25 +08:00
|
|
|
[SettingSource("Flashlight size", "Multiplier applied to the default flashlight size.")]
|
2022-02-03 03:41:25 +08:00
|
|
|
public abstract BindableFloat SizeMultiplier { get; }
|
2022-01-16 04:43:28 +08:00
|
|
|
|
2022-01-25 03:44:25 +08:00
|
|
|
[SettingSource("Change size based on combo", "Decrease the flashlight size as combo increases.")]
|
|
|
|
public abstract BindableBool ComboBasedSize { get; }
|
2022-01-25 03:46:54 +08:00
|
|
|
|
|
|
|
/// <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
|
|
|
}
|
|
|
|
|
2019-03-19 22:44:15 +08:00
|
|
|
public abstract partial class ModFlashlight<T> : ModFlashlight, IApplicableToDrawableRuleset<T>, IApplicableToScoreProcessor
|
2018-11-30 13:48:19 +08:00
|
|
|
where T : HitObject
|
|
|
|
{
|
2018-11-12 01:38:12 +08:00
|
|
|
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-04-25 18:56:57 +08:00
|
|
|
|
2019-03-20 10:22:34 +08:00
|
|
|
public virtual void ApplyToDrawableRuleset(DrawableRuleset<T> drawableRuleset)
|
2018-11-12 01:38:12 +08:00
|
|
|
{
|
|
|
|
var flashlight = CreateFlashlight();
|
2021-09-17 18:15:14 +08:00
|
|
|
|
2018-11-12 01:38:12 +08:00
|
|
|
flashlight.RelativeSizeAxes = Axes.Both;
|
|
|
|
flashlight.Colour = Color4.Black;
|
2023-05-02 16:00:54 +08:00
|
|
|
// Flashlight mods should always draw above any other mod adding overlays.
|
|
|
|
flashlight.Depth = float.MinValue;
|
2021-09-17 18:15:14 +08:00
|
|
|
|
|
|
|
flashlight.Combo.BindTo(Combo);
|
2023-12-28 15:02:45 +08:00
|
|
|
flashlight.GetPlayfieldScale = () => drawableRuleset.Playfield.Scale;
|
2022-12-26 04:32:47 +08:00
|
|
|
|
2023-02-14 16:36:01 +08:00
|
|
|
drawableRuleset.Overlays.Add(flashlight);
|
2018-11-12 01:38:12 +08:00
|
|
|
}
|
|
|
|
|
2022-01-25 03:55:24 +08:00
|
|
|
protected abstract Flashlight CreateFlashlight();
|
2018-11-12 01:38:12 +08:00
|
|
|
|
|
|
|
public abstract partial class Flashlight : Drawable
|
|
|
|
{
|
2021-09-17 18:15:14 +08:00
|
|
|
public readonly BindableInt Combo = new BindableInt();
|
|
|
|
|
2022-07-10 23:37:11 +08:00
|
|
|
private IShader shader = null!;
|
2018-11-12 01:38:12 +08:00
|
|
|
|
2019-04-02 10:56:22 +08:00
|
|
|
protected override DrawNode CreateDrawNode() => new FlashlightDrawNode(this);
|
2018-11-12 01:38:12 +08:00
|
|
|
|
|
|
|
public override bool RemoveCompletedTransforms => false;
|
|
|
|
|
2023-12-28 15:02:45 +08:00
|
|
|
internal Func<Vector2>? GetPlayfieldScale;
|
|
|
|
|
2022-01-25 03:55:24 +08:00
|
|
|
private readonly float defaultFlashlightSize;
|
|
|
|
private readonly float sizeMultiplier;
|
|
|
|
private readonly bool comboBasedSize;
|
2022-01-16 04:43:28 +08:00
|
|
|
|
2022-01-25 03:55:24 +08:00
|
|
|
protected Flashlight(ModFlashlight modFlashlight)
|
2022-01-16 04:43:28 +08:00
|
|
|
{
|
2022-01-25 03:55:24 +08:00
|
|
|
defaultFlashlightSize = modFlashlight.DefaultFlashlightSize;
|
|
|
|
sizeMultiplier = modFlashlight.SizeMultiplier.Value;
|
|
|
|
comboBasedSize = modFlashlight.ComboBasedSize.Value;
|
2022-01-16 04:43:28 +08:00
|
|
|
}
|
|
|
|
|
2018-11-12 01:38:12 +08:00
|
|
|
[BackgroundDependencyLoader]
|
|
|
|
private void load(ShaderManager shaderManager)
|
|
|
|
{
|
2018-11-15 07:33:13 +08:00
|
|
|
shader = shaderManager.Load("PositionAndColour", FragmentShader);
|
2018-11-12 01:38:12 +08:00
|
|
|
}
|
|
|
|
|
2022-10-11 22:16:57 +08:00
|
|
|
[Resolved]
|
|
|
|
private Player? player { get; set; }
|
|
|
|
|
|
|
|
private readonly IBindable<bool> isBreakTime = new BindableBool();
|
|
|
|
|
2018-11-12 01:38:12 +08:00
|
|
|
protected override void LoadComplete()
|
|
|
|
{
|
|
|
|
base.LoadComplete();
|
|
|
|
|
2022-10-11 22:16:57 +08:00
|
|
|
Combo.ValueChanged += _ => UpdateFlashlightSize(GetSize());
|
2018-11-12 01:38:12 +08:00
|
|
|
|
2022-10-11 22:16:57 +08:00
|
|
|
if (player != null)
|
2018-11-12 01:38:12 +08:00
|
|
|
{
|
2022-10-11 22:16:57 +08:00
|
|
|
isBreakTime.BindTo(player.IsBreakTime);
|
|
|
|
isBreakTime.BindValueChanged(_ => UpdateFlashlightSize(GetSize()), true);
|
2018-11-12 01:38:12 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-11 22:16:57 +08:00
|
|
|
protected abstract void UpdateFlashlightSize(float size);
|
2018-11-12 01:38:12 +08:00
|
|
|
|
2018-11-15 07:33:13 +08:00
|
|
|
protected abstract string FragmentShader { get; }
|
2018-11-12 01:38:12 +08:00
|
|
|
|
2023-12-28 15:02:45 +08:00
|
|
|
public float GetSize()
|
2022-01-16 04:43:28 +08:00
|
|
|
{
|
2022-01-25 03:55:24 +08:00
|
|
|
float size = defaultFlashlightSize * sizeMultiplier;
|
|
|
|
|
2023-12-28 15:02:45 +08:00
|
|
|
if (GetPlayfieldScale != null)
|
|
|
|
{
|
|
|
|
Vector2 playfieldScale = GetPlayfieldScale();
|
|
|
|
|
2024-02-26 17:37:03 +08:00
|
|
|
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);
|
2023-12-28 15:02:45 +08:00
|
|
|
}
|
|
|
|
|
2022-10-11 22:16:57 +08:00
|
|
|
if (isBreakTime.Value)
|
|
|
|
size *= 2.5f;
|
|
|
|
else if (comboBasedSize)
|
|
|
|
size *= GetComboScaleFor(Combo.Value);
|
2022-01-16 04:43:28 +08:00
|
|
|
|
2022-09-18 12:38:01 +08:00
|
|
|
return size;
|
2022-01-16 04:43:28 +08:00
|
|
|
}
|
|
|
|
|
2022-10-07 13:26:19 +08:00
|
|
|
protected virtual float GetComboScaleFor(int combo)
|
|
|
|
{
|
|
|
|
if (combo >= 200)
|
|
|
|
return 0.625f;
|
|
|
|
if (combo >= 100)
|
|
|
|
return 0.8125f;
|
|
|
|
|
|
|
|
return 1.0f;
|
|
|
|
}
|
|
|
|
|
2018-11-15 07:33:13 +08:00
|
|
|
private Vector2 flashlightPosition;
|
2019-02-28 12:31:40 +08:00
|
|
|
|
2018-11-15 07:33:13 +08:00
|
|
|
protected Vector2 FlashlightPosition
|
|
|
|
{
|
|
|
|
get => flashlightPosition;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (flashlightPosition == value) return;
|
|
|
|
|
|
|
|
flashlightPosition = value;
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
}
|
|
|
|
}
|
2018-11-12 01:38:12 +08:00
|
|
|
|
2018-11-15 07:33:13 +08:00
|
|
|
private Vector2 flashlightSize;
|
2019-02-28 12:31:40 +08:00
|
|
|
|
2018-11-15 07:33:13 +08:00
|
|
|
protected Vector2 FlashlightSize
|
|
|
|
{
|
|
|
|
get => flashlightSize;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (flashlightSize == value) return;
|
|
|
|
|
|
|
|
flashlightSize = value;
|
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
}
|
|
|
|
}
|
2019-04-12 09:47:22 +08:00
|
|
|
|
2019-04-12 10:23:40 +08:00
|
|
|
private float flashlightDim;
|
2019-04-12 09:47:22 +08:00
|
|
|
|
2019-04-12 10:23:40 +08:00
|
|
|
public float FlashlightDim
|
2019-04-12 09:47:22 +08:00
|
|
|
{
|
2019-04-12 10:23:40 +08:00
|
|
|
get => flashlightDim;
|
2019-04-12 09:47:22 +08:00
|
|
|
set
|
|
|
|
{
|
2019-04-12 10:23:40 +08:00
|
|
|
if (flashlightDim == value) return;
|
2019-04-12 09:47:22 +08:00
|
|
|
|
2019-04-12 10:23:40 +08:00
|
|
|
flashlightDim = value;
|
2019-04-12 09:47:22 +08:00
|
|
|
Invalidate(Invalidation.DrawNode);
|
|
|
|
}
|
|
|
|
}
|
2018-11-12 01:38:12 +08:00
|
|
|
|
2022-10-03 17:24:56 +08:00
|
|
|
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
|
2018-11-12 01:38:12 +08:00
|
|
|
{
|
2019-04-02 10:56:22 +08:00
|
|
|
protected new Flashlight Source => (Flashlight)base.Source;
|
2018-11-12 01:38:12 +08:00
|
|
|
|
2022-07-10 23:37:11 +08:00
|
|
|
private IShader shader = null!;
|
2019-04-02 10:56:22 +08:00
|
|
|
private Quad screenSpaceDrawQuad;
|
|
|
|
private Vector2 flashlightPosition;
|
|
|
|
private Vector2 flashlightSize;
|
2019-05-07 11:04:58 +08:00
|
|
|
private float flashlightDim;
|
2022-10-03 17:24:56 +08:00
|
|
|
private float flashlightSmoothness;
|
2018-11-12 01:38:12 +08:00
|
|
|
|
2022-07-29 22:32:06 +08:00
|
|
|
private IVertexBatch<PositionAndColourVertex>? quadBatch;
|
|
|
|
private Action<TexturedVertex2D>? addAction;
|
2019-07-23 14:17:02 +08:00
|
|
|
|
2019-04-02 10:56:22 +08:00
|
|
|
public FlashlightDrawNode(Flashlight source)
|
|
|
|
: base(source)
|
|
|
|
{
|
|
|
|
}
|
2018-11-12 01:38:12 +08:00
|
|
|
|
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);
|
2019-05-07 11:04:58 +08:00
|
|
|
flashlightSize = Source.FlashlightSize * DrawInfo.Matrix.ExtractScale().Xy;
|
|
|
|
flashlightDim = Source.FlashlightDim;
|
2022-10-03 17:24:56 +08:00
|
|
|
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);
|
2018-11-12 01:38:12 +08:00
|
|
|
|
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
|
|
|
|
};
|
2018-11-12 01:38:12 +08:00
|
|
|
|
2023-02-25 00:21:37 +08:00
|
|
|
shader.Bind();
|
2023-03-16 19:06:35 +08:00
|
|
|
shader.BindUniformBlock(@"m_FlashlightParameters", flashlightParametersBuffer);
|
2018-11-12 01:38:12 +08:00
|
|
|
|
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-11-12 01:38:12 +08:00
|
|
|
}
|
|
|
|
}
|
2017-04-21 16:33:20 +08:00
|
|
|
}
|
2018-01-05 19:21:19 +08:00
|
|
|
}
|