Move flashlight code from OsuModFlashlight to ModFlashlight and implement other modes and break time

This commit is contained in:
jorolf 2018-11-11 18:38:12 +01:00
parent 5c09662c14
commit 023924396d
11 changed files with 388 additions and 80 deletions

View File

@ -1,12 +1,78 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using JetBrains.Annotations;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModFlashlight : ModFlashlight
public class CatchModFlashlight : ModFlashlight<CatchHitObject>
{
public override double ScoreMultiplier => 1.12;
private const float default_flashlight_size = 350;
public override Flashlight CreateFlashlight() => new CatchFlashlight(playfield);
private CatchPlayfield playfield;
public override void ApplyToRulesetContainer(RulesetContainer<CatchHitObject> rulesetContainer)
{
playfield = (CatchPlayfield)rulesetContainer.Playfield;
base.ApplyToRulesetContainer(rulesetContainer);
}
private class CatchFlashlight : Flashlight
{
private readonly CatchPlayfield playfield;
public CatchFlashlight(CatchPlayfield playfield)
{
this.playfield = playfield;
MousePosWrapper.CircularFlashlightSize = getSizeFor(0);
MousePosWrapper.Rectangular = false;
}
protected override void Update()
{
base.Update();
MousePosWrapper.FlashlightPosition = (playfield.CatcherArea.MovableCatcher.ScreenSpaceDrawQuad.TopLeft + playfield.CatcherArea.MovableCatcher.ScreenSpaceDrawQuad.TopRight) / 2;
MousePosWrapper.FlashlightPositionChanged = true;
}
[UsedImplicitly]
private float flashlightSize
{
set
{
if (MousePosWrapper.CircularFlashlightSize == value) return;
MousePosWrapper.CircularFlashlightSize = value;
MousePosWrapper.CircularFlashlightSizeChanged = true;
}
get => MousePosWrapper.CircularFlashlightSize;
}
private float getSizeFor(int combo)
{
if (combo > 200)
return default_flashlight_size * 0.8f;
else if (combo > 100)
return default_flashlight_size * 0.9f;
else
return default_flashlight_size;
}
protected override void OnComboChange(int newCombo)
{
this.TransformTo(nameof(flashlightSize), getSizeFor(newCombo), FLASHLIGHT_FADE_DURATION);
}
}
}
}

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
public const float BASE_WIDTH = 512;
private readonly CatcherArea catcherArea;
internal readonly CatcherArea CatcherArea;
protected override bool UserScrollSpeedAdjustment => false;
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
RelativeSizeAxes = Axes.Both,
},
catcherArea = new CatcherArea(difficulty)
CatcherArea = new CatcherArea(difficulty)
{
GetVisualRepresentation = getVisualRepresentation,
ExplodingFruitTarget = explodingFruitContainer,
@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.UI
VisibleTimeRange.Value = BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
}
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
public bool CheckIfWeCanCatch(CatchHitObject obj) => CatcherArea.AttemptCatch(obj);
public override void Add(DrawableHitObject h)
{
@ -72,6 +72,6 @@ namespace osu.Game.Rulesets.Catch.UI
}
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
=> catcherArea.OnResult((DrawableCatchHitObject)judgedObject, result);
=> CatcherArea.OnResult((DrawableCatchHitObject)judgedObject, result);
}
}

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
public const float CATCHER_SIZE = 100;
protected readonly Catcher MovableCatcher;
protected internal readonly Catcher MovableCatcher;
public Func<CatchHitObject, DrawableHitObject<CatchHitObject>> GetVisualRepresentation;

View File

@ -3,6 +3,7 @@
using System;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
@ -16,6 +17,6 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Description => @"Keys appear out of nowhere!";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
}
}

View File

@ -2,13 +2,59 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
using OpenTK;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModFlashlight : ModFlashlight
public class ManiaModFlashlight : ModFlashlight<ManiaHitObject>
{
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModHidden) };
private const float default_flashlight_size = 180;
public override Flashlight CreateFlashlight() => new ManiaFlashlight();
private class ManiaFlashlight : Flashlight
{
public ManiaFlashlight()
{
MousePosWrapper.Rectangular = true;
MousePosWrapper.RectangularFlashlightSize = new Vector2(0, default_flashlight_size);
}
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) > 0)
{
Schedule(() =>
{
MousePosWrapper.RectangularFlashlightSize.X = DrawWidth;
MousePosWrapper.RectangularFlashlightSizeChanged = true;
MousePosWrapper.FlashlightPosition = ScreenSpaceDrawQuad.Centre;
MousePosWrapper.FlashlightPositionChanged = true;
});
}
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void OnComboChange(int newCombo)
{
}
protected override void LoadComplete()
{
MousePosWrapper.RectangularFlashlightSize.X = DrawWidth;
MousePosWrapper.RectangularFlashlightSizeChanged = true;
MousePosWrapper.FlashlightPosition = ScreenSpaceDrawQuad.Centre;
MousePosWrapper.FlashlightPositionChanged = true;
}
}
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
@ -10,6 +11,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string Description => @"Keys fade out before you hit them!";
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
}
}

View File

@ -1,95 +1,65 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using JetBrains.Annotations;
using osu.Framework.Graphics;
using osu.Framework.Graphics.OpenGL.Vertices;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.UI;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModFlashlight : ModFlashlight, IApplicableToRulesetContainer<OsuHitObject>
public class OsuModFlashlight : ModFlashlight<OsuHitObject>
{
public override double ScoreMultiplier => 1.12;
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)
private const float default_flashlight_size = 180;
public override Flashlight CreateFlashlight() => new OsuFlashlight();
private class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition
{
rulesetContainer.KeyBindingInputManager.Add(new Flashlight
public OsuFlashlight()
{
RelativeSizeAxes = Axes.Both,
});
}
private class Flashlight : Drawable, IRequireHighFrequencyMousePosition
{
private Shader shader;
private readonly MousePositionWrapper mousePosWrapper = new MousePositionWrapper
{
FlashlightSize = 300f
};
protected override DrawNode CreateDrawNode() => new FlashlightDrawNode();
protected override void ApplyDrawNode(DrawNode node)
{
base.ApplyDrawNode(node);
var flashNode = (FlashlightDrawNode)node;
flashNode.Shader = shader;
flashNode.ScreenSpaceDrawQuad = ScreenSpaceDrawQuad;
flashNode.MousePosWrapper = mousePosWrapper;
}
[BackgroundDependencyLoader]
private void load(ShaderManager shaderManager)
{
shader = shaderManager.Load(VertexShaderDescriptor.POSITION, "Flashlight");
MousePosWrapper.CircularFlashlightSize = getSizeFor(0);
MousePosWrapper.Rectangular = false;
}
protected override bool OnMouseMove(MouseMoveEvent e)
{
mousePosWrapper.MousePosition = e.ScreenSpaceMousePosition;
MousePosWrapper.FlashlightPosition = e.ScreenSpaceMousePosition;
MousePosWrapper.FlashlightPositionChanged = true;
return base.OnMouseMove(e);
}
}
private class MousePositionWrapper
{
public Vector2 MousePosition;
public float FlashlightSize;
public bool FlashlightUniformUpdated;
}
private class FlashlightDrawNode : DrawNode
{
public Shader Shader;
public Quad ScreenSpaceDrawQuad;
public MousePositionWrapper MousePosWrapper;
public override void Draw(Action<TexturedVertex2D> vertexAction)
[UsedImplicitly]
private float flashlightSize
{
base.Draw(vertexAction);
set
{
if (MousePosWrapper.CircularFlashlightSize == value) return;
Shader.Bind();
// ReSharper disable once AssignmentInConditionalExpression
if(MousePosWrapper.FlashlightUniformUpdated = !MousePosWrapper.FlashlightUniformUpdated)
Shader.GetUniform<float>("flashlightSize").UpdateValue(ref MousePosWrapper.FlashlightSize);
MousePosWrapper.CircularFlashlightSize = value;
MousePosWrapper.CircularFlashlightSizeChanged = true;
}
Shader.GetUniform<Vector2>("mousePos").UpdateValue(ref MousePosWrapper.MousePosition);
get => MousePosWrapper.CircularFlashlightSize;
}
Texture.WhitePixel.DrawQuad(ScreenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction);
private float getSizeFor(int combo)
{
if (combo > 200)
return default_flashlight_size * 0.8f;
else if (combo > 100)
return default_flashlight_size * 0.9f;
else
return default_flashlight_size;
}
Shader.Unbind();
protected override void OnComboChange(int newCombo)
{
this.TransformTo(nameof(flashlightSize), getSizeFor(newCombo), FLASHLIGHT_FADE_DURATION);
}
}
}

View File

@ -8,6 +8,8 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Mods;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.Difficulty
{
@ -82,7 +84,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
if (mods.Any(m => m is ModHidden))
strainValue *= 1.025;
if (mods.Any(m => m is ModFlashlight))
if (mods.Any(m => m is ModFlashlight<TaikoHitObject>))
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
strainValue *= 1.05 * lengthBonus;

View File

@ -1,12 +1,93 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using JetBrains.Annotations;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModFlashlight : ModFlashlight
public class TaikoModFlashlight : ModFlashlight<TaikoHitObject>
{
public override double ScoreMultiplier => 1.12;
private const float default_flashlight_size = 250;
public override Flashlight CreateFlashlight() => new TaikoFlashlight(playfield);
private TaikoPlayfield playfield;
public override void ApplyToRulesetContainer(RulesetContainer<TaikoHitObject> rulesetContainer)
{
playfield = (TaikoPlayfield)rulesetContainer.Playfield;
base.ApplyToRulesetContainer(rulesetContainer);
}
private class TaikoFlashlight : Flashlight
{
private readonly TaikoPlayfield taikoPlayfield;
public TaikoFlashlight(TaikoPlayfield taikoPlayfield)
{
this.taikoPlayfield = taikoPlayfield;
MousePosWrapper.CircularFlashlightSize = getSizeFor(0);
MousePosWrapper.Rectangular = false;
}
[UsedImplicitly]
private float flashlightSize
{
set
{
if (MousePosWrapper.CircularFlashlightSize == value) return;
MousePosWrapper.CircularFlashlightSize = value;
MousePosWrapper.CircularFlashlightSizeChanged = true;
}
get => MousePosWrapper.CircularFlashlightSize;
}
private float getSizeFor(int combo)
{
if (combo > 200)
return default_flashlight_size * 0.8f;
else if (combo > 100)
return default_flashlight_size * 0.9f;
else
return default_flashlight_size;
}
protected override void OnComboChange(int newCombo)
{
this.TransformTo(nameof(flashlightSize), getSizeFor(newCombo), FLASHLIGHT_FADE_DURATION);
}
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.DrawSize) > 0)
{
Schedule(() =>
{
MousePosWrapper.FlashlightPosition = taikoPlayfield.HitExplosionContainer.ScreenSpaceDrawQuad.Centre;
MousePosWrapper.FlashlightPositionChanged = true;
});
}
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void LoadComplete()
{
base.LoadComplete();
MousePosWrapper.FlashlightPosition = taikoPlayfield.HitExplosionContainer.ScreenSpaceDrawQuad.Centre;
MousePosWrapper.FlashlightPositionChanged = true;
}
}
}
}

View File

@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Overlapping;
private readonly Container<HitExplosion> hitExplosionContainer;
internal readonly Container<HitExplosion> HitExplosionContainer;
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
private readonly JudgementContainer<DrawableTaikoJudgement> judgementContainer;
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko.UI
Masking = true,
Children = new Drawable[]
{
hitExplosionContainer = new Container<HitExplosion>
HitExplosionContainer = new Container<HitExplosion>
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
@ -243,7 +243,7 @@ namespace osu.Game.Rulesets.Taiko.UI
{
case TaikoStrongJudgement _:
if (result.IsHit)
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit();
HitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit();
break;
default:
judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject)
@ -259,7 +259,7 @@ namespace osu.Game.Rulesets.Taiko.UI
bool isRim = judgedObject.HitObject is RimHit;
hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
HitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
if (judgedObject.HitObject.Kiai)
kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim));

View File

@ -1,11 +1,27 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.OpenGL.Vertices;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps.Timing;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mods
{
public abstract class ModFlashlight : Mod
public abstract class ModFlashlight<T> : Mod, IApplicableToRulesetContainer<T>, IApplicableToScoreProcessor
where T : HitObject
{
public override string Name => "Flashlight";
public override string ShortenedName => "FL";
@ -13,5 +29,130 @@ namespace osu.Game.Rulesets.Mods
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => "Restricted view area.";
public override bool Ranked => true;
public const double FLASHLIGHT_FADE_DURATION = 800;
protected readonly BindableInt Combo = new BindableInt();
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{
Combo.BindTo(scoreProcessor.Combo);
}
public virtual void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer)
{
var flashlight = CreateFlashlight();
flashlight.Combo = Combo;
flashlight.RelativeSizeAxes = Axes.Both;
flashlight.Colour = Color4.Black;
rulesetContainer.KeyBindingInputManager.Add(flashlight);
flashlight.Breaks = rulesetContainer.Beatmap.Breaks;
}
public abstract Flashlight CreateFlashlight();
public abstract class Flashlight : Drawable
{
internal BindableInt Combo;
private Shader shader;
protected readonly FlashlightUniformWrapper MousePosWrapper = new FlashlightUniformWrapper();
protected override DrawNode CreateDrawNode() => new FlashlightDrawNode();
public override bool RemoveCompletedTransforms => false;
public List<BreakPeriod> Breaks;
protected override void ApplyDrawNode(DrawNode node)
{
base.ApplyDrawNode(node);
var flashNode = (FlashlightDrawNode)node;
flashNode.Shader = shader;
flashNode.ScreenSpaceDrawQuad = ScreenSpaceDrawQuad;
flashNode.MousePosWrapper = MousePosWrapper;
}
[BackgroundDependencyLoader]
private void load(ShaderManager shaderManager)
{
shader = shaderManager.Load("PositionAndColour", "Flashlight");
}
protected override void LoadComplete()
{
base.LoadComplete();
Combo.ValueChanged += OnComboChange;
this.FadeInFromZero(FLASHLIGHT_FADE_DURATION);
foreach (var breakPeriod in Breaks)
{
this.Delay(breakPeriod.StartTime + FLASHLIGHT_FADE_DURATION).FadeOutFromOne(FLASHLIGHT_FADE_DURATION);
this.Delay(breakPeriod.EndTime - FLASHLIGHT_FADE_DURATION).FadeInFromZero(FLASHLIGHT_FADE_DURATION);
}
}
protected abstract void OnComboChange(int newCombo);
}
public class FlashlightUniformWrapper
{
public bool Rectangular;
public bool RectangularChanged = true;
public Vector2 FlashlightPosition;
public bool FlashlightPositionChanged = true;
public float CircularFlashlightSize;
public bool CircularFlashlightSizeChanged = true;
public Vector2 RectangularFlashlightSize;
public bool RectangularFlashlightSizeChanged = true;
}
private class FlashlightDrawNode : DrawNode
{
public Shader Shader;
public Quad ScreenSpaceDrawQuad;
public FlashlightUniformWrapper MousePosWrapper;
public override void Draw(Action<TexturedVertex2D> vertexAction)
{
base.Draw(vertexAction);
Shader.Bind();
if (MousePosWrapper.RectangularChanged)
{
Shader.GetUniform<bool>("rectangular").UpdateValue(ref MousePosWrapper.Rectangular);
MousePosWrapper.RectangularChanged = false;
}
if (MousePosWrapper.FlashlightPositionChanged)
{
Shader.GetUniform<Vector2>("flashlightPos").UpdateValue(ref MousePosWrapper.FlashlightPosition);
MousePosWrapper.FlashlightPositionChanged = false;
}
if (MousePosWrapper.CircularFlashlightSizeChanged)
{
Shader.GetUniform<float>("circularFlashlightSize").UpdateValue(ref MousePosWrapper.CircularFlashlightSize);
MousePosWrapper.CircularFlashlightSizeChanged = false;
}
if (MousePosWrapper.RectangularFlashlightSizeChanged)
{
Shader.GetUniform<Vector2>("rectangularFlashlightSize").UpdateValue(ref MousePosWrapper.RectangularFlashlightSize);
MousePosWrapper.RectangularFlashlightSizeChanged = false;
}
Texture.WhitePixel.DrawQuad(ScreenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction);
Shader.Unbind();
}
}
}
}