osu/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs

216 lines
6.7 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.
2020-05-15 06:49:50 +00:00
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
2020-05-18 12:27:26 +00:00
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Screens.Edit;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Edit
{
/// <summary>
/// A grid which displays coloured beat divisor lines in proximity to the selection or placement cursor.
/// </summary>
2020-05-18 08:47:47 +00:00
public class ManiaBeatSnapGrid : Component
{
2020-05-18 12:27:26 +00:00
private const double visible_range = 750;
2020-05-15 06:51:54 +00:00
/// <summary>
/// The range of time values of the current selection.
/// </summary>
public (double start, double end)? SelectionTimeRange
{
set
{
if (value == selectionTimeRange)
return;
selectionTimeRange = value;
lineCache.Invalidate();
}
}
[Resolved]
private EditorBeatmap beatmap { get; set; }
[Resolved]
private IScrollingInfo scrollingInfo { get; set; }
[Resolved]
private Bindable<WorkingBeatmap> working { get; set; }
[Resolved]
private OsuColour colours { get; set; }
[Resolved]
private BindableBeatDivisor beatDivisor { get; set; }
2020-05-18 08:52:04 +00:00
private readonly List<ScrollingHitObjectContainer> grids = new List<ScrollingHitObjectContainer>();
2020-05-18 12:27:26 +00:00
private readonly Cached lineCache = new Cached();
private (double start, double end)? selectionTimeRange;
[BackgroundDependencyLoader]
private void load(HitObjectComposer composer)
{
foreach (var stage in ((ManiaPlayfield)composer.Playfield).Stages)
{
2020-05-18 08:47:47 +00:00
foreach (var column in stage.Columns)
{
2020-05-18 08:52:04 +00:00
var lineContainer = new ScrollingHitObjectContainer();
2020-05-18 08:52:04 +00:00
grids.Add(lineContainer);
column.UnderlayElements.Add(lineContainer);
2020-05-18 08:47:47 +00:00
}
}
beatDivisor.BindValueChanged(_ => createLines(), true);
}
2020-05-19 14:02:20 +00:00
protected override void Update()
{
base.Update();
if (!lineCache.IsValid)
{
lineCache.Validate();
createLines();
}
}
2020-05-19 10:07:35 +00:00
private readonly Stack<DrawableGridLine> availableLines = new Stack<DrawableGridLine>();
private void createLines()
{
foreach (var grid in grids)
2020-05-19 10:07:35 +00:00
{
foreach (var line in grid.Objects.OfType<DrawableGridLine>())
availableLines.Push(line);
grid.Clear(false);
}
2020-05-18 12:27:26 +00:00
if (selectionTimeRange == null)
return;
var range = selectionTimeRange.Value;
var timingPoint = beatmap.ControlPointInfo.TimingPointAt(range.start - visible_range);
double time = timingPoint.Time;
int beat = 0;
// progress time until in the visible range.
while (time < range.start - visible_range)
{
time += timingPoint.BeatLength / beatDivisor.Value;
beat++;
}
while (time < range.end + visible_range)
{
2020-05-18 12:27:26 +00:00
var nextTimingPoint = beatmap.ControlPointInfo.TimingPointAt(time);
2020-05-18 12:27:26 +00:00
// switch to the next timing point if we have reached it.
if (nextTimingPoint.Time > timingPoint.Time)
2020-05-18 12:27:26 +00:00
{
beat = 0;
time = nextTimingPoint.Time;
2020-05-18 12:27:26 +00:00
timingPoint = nextTimingPoint;
}
Color4 colour = BindableBeatDivisor.GetColourFor(
BindableBeatDivisor.GetDivisorForBeatIndex(Math.Max(1, beat), beatDivisor.Value), colours);
foreach (var grid in grids)
2020-05-19 10:07:35 +00:00
{
if (!availableLines.TryPop(out var line))
line = new DrawableGridLine();
line.HitObject.StartTime = time;
line.Colour = colour;
grid.Add(line);
}
2020-05-18 12:27:26 +00:00
beat++;
time += timingPoint.BeatLength / beatDivisor.Value;
}
foreach (var grid in grids)
{
// required to update ScrollingHitObjectContainer's cache.
grid.UpdateSubTree();
foreach (var line in grid.Objects.OfType<DrawableGridLine>())
{
2020-05-18 12:27:26 +00:00
time = line.HitObject.StartTime;
2020-05-18 12:27:26 +00:00
if (time >= range.start && time <= range.end)
line.Alpha = 1;
else
{
2020-05-18 12:27:26 +00:00
double timeSeparation = time < range.start ? range.start - time : time - range.end;
line.Alpha = (float)Math.Max(0, 1 - timeSeparation / visible_range);
}
}
}
}
private class DrawableGridLine : DrawableHitObject
{
[Resolved]
private IScrollingInfo scrollingInfo { get; set; }
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
2020-05-19 10:07:35 +00:00
public DrawableGridLine()
: base(new HitObject())
{
RelativeSizeAxes = Axes.X;
Height = 2;
2020-05-19 10:07:35 +00:00
AddInternal(new Box { RelativeSizeAxes = Axes.Both });
}
[BackgroundDependencyLoader]
private void load()
{
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(onDirectionChanged, true);
}
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
{
Origin = Anchor = direction.NewValue == ScrollingDirection.Up
? Anchor.TopLeft
: Anchor.BottomLeft;
}
protected override void UpdateInitialTransforms()
{
// don't perform any fading we are handling that ourselves.
}
protected override void UpdateStateTransforms(ArmedState state)
{
LifetimeEnd = HitObject.StartTime + visible_range;
}
}
}
}