diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index d2fccb2c58..681c5a5ef2 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Colour; using osu.Framework.Input; using osu.Game.Graphics; +using osu.Game.Rulesets.Taiko.Timing; namespace osu.Game.Rulesets.Mania.UI { @@ -29,11 +30,17 @@ namespace osu.Game.Rulesets.Mania.UI private const float column_width = 45; private const float special_column_width = 70; + private const double time_span_default = 2000; + private const double time_span_min = 10; + private const double time_span_max = 20000; + private const double time_span_step = 100; + public Key Key; private readonly Box background; private readonly Container hitTargetBar; private readonly Container keyIcon; + private readonly Container timingSectionContainer; public Column() { @@ -130,6 +137,14 @@ namespace osu.Game.Rulesets.Mania.UI } } } + }, + timingSectionContainer = new Container + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.Both, + Y = -hit_target_bar_height, + RelativeCoordinateSpace = new Vector2(1, (float)time_span_default) } }; } @@ -176,14 +191,32 @@ namespace osu.Game.Rulesets.Mania.UI } } + public void AddTimingSection(TimingSection timingSection) => timingSectionContainer.Add(new DrawableTimingSection(timingSection)); + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { - if (args.Key == Key && !args.Repeat) + if (args.Repeat) + return false; + + if (args.Key == Key) { background.FadeTo(background.Alpha + 0.2f, 50, EasingTypes.OutQuint); keyIcon.ScaleTo(1.4f, 50, EasingTypes.OutQuint); } + if (state.Keyboard.ControlPressed) + { + switch (args.Key) + { + case Key.Minus: + timeSpan += time_span_step; + break; + case Key.Plus: + timeSpan -= time_span_step; + break; + } + } + return false; } @@ -197,6 +230,15 @@ namespace osu.Game.Rulesets.Mania.UI return false; } + + /// + /// The amount of time which the length of this column spans. + /// + private double timeSpan + { + get { return timingSectionContainer.RelativeCoordinateSpace.Y; } + set { timingSectionContainer.RelativeCoordinateSpace = new Vector2(1, (float)MathHelper.Clamp(value, time_span_min, time_span_max)); } + } } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index c007cdc80e..f72ddf3227 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -1,14 +1,21 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Timing; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mania.UI @@ -21,6 +28,60 @@ namespace osu.Game.Rulesets.Mania.UI : base(beatmap) { this.columns = columns; + // Has to be done before drawable hit objects are generated in load() + loadTimingSections(); + } + + private void loadTimingSections() + { + var maniaPlayfield = Playfield as ManiaPlayfield; + if (maniaPlayfield == null) + return; + + var sections = new List(); + + // Construct all the relevant timing sections + ControlPoint lastTimingChange = null; + foreach (ControlPoint point in Beatmap.TimingInfo.ControlPoints) + { + if (point.TimingChange) + lastTimingChange = point; + + sections.Add(new TimingSection + { + StartTime = point.Time, + // Todo: Should this be dividing by beatlength? + BeatLength = point.SpeedMultiplier * lastTimingChange.BeatLength, + TimeSignature = point.TimeSignature + }); + } + + double lastObjectTime = (Objects.Last() as IHasEndTime)?.EndTime ?? Objects.Last().StartTime; + + // Perform some post processing of the timing sections + sections = sections + // Collapse sections after the last hit object + .Where(s => s.StartTime <= lastObjectTime) + // Collapse sections with the same start time + .GroupBy(s => s.StartTime).Select(g => g.Last()).OrderBy(s => s.StartTime) + // Collapse sections with the same beat length + .GroupBy(s => s.BeatLength).Select(g => g.First()) + .ToList(); + + // Determine duration of timing sections + for (int i = 0; i < sections.Count; i++) + { + if (i < sections.Count - 1) + sections[i].Duration = sections[i + 1].StartTime - sections[i].StartTime; + else + { + // Extra length added for the last timing section to extend past the last hitobject + double extraLength = sections[i].BeatLength * (int)sections[i].TimeSignature; + sections[i].Duration = lastObjectTime + extraLength - sections[i].StartTime; + } + } + + sections.ForEach(s => maniaPlayfield.Columns.Children.ForEach(c => c.AddTimingSection(s))); } public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 438c1f4b5f..6999ae3952 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -16,6 +16,10 @@ using osu.Framework.Allocation; using OpenTK.Input; using System.Linq; using System.Collections.Generic; +using osu.Game.Rulesets.Taiko.Timing; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Mania.Objects.Drawables; namespace osu.Game.Rulesets.Mania.UI { @@ -156,5 +160,7 @@ namespace osu.Game.Rulesets.Mania.UI return column == columnCount - 1; } } + + public void Add(TimingSection timingSection) => columns.Children.ForEach(c => c.Add(timingSection)); } } diff --git a/osu.Game.Rulesets.Taiko/Timing/DrawableTimingSection.cs b/osu.Game.Rulesets.Taiko/Timing/DrawableTimingSection.cs new file mode 100644 index 0000000000..e9c403302c --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Timing/DrawableTimingSection.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Rulesets.Taiko.Timing +{ + public class DrawableTimingSection : Container + { + private readonly TimingSection section; + + public DrawableTimingSection(TimingSection section) + { + this.section = section; + + RelativePositionAxes = Axes.Y; + Y = -(float)section.StartTime; + + RelativeSizeAxes = Axes.Both; + Height = (float)section.Duration; + + RelativeCoordinateSpace = new Vector2(1, Height); + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/Timing/TimingSection.cs b/osu.Game.Rulesets.Taiko/Timing/TimingSection.cs new file mode 100644 index 0000000000..418ccac8c8 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Timing/TimingSection.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps.Timing; + +namespace osu.Game.Rulesets.Taiko.Timing +{ + public class TimingSection + { + public double StartTime; + public double Duration; + public double BeatLength; + public TimeSignatures TimeSignature; + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 983dc72d9e..5308de741a 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -85,6 +85,8 @@ + +