diff --git a/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs b/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs new file mode 100644 index 0000000000..1545f4cf89 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Mods/IGenerateSpeedAdjustments.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Rulesets.Mania.UI; +using osu.Game.Rulesets.Timing; + +namespace osu.Game.Rulesets.Mania.Mods +{ + internal interface IGenerateSpeedAdjustments + { + void ApplyToHitRenderer(ManiaHitRenderer hitRenderer, ref List[] hitObjectTimingChanges, ref List barlineTimingChanges); + } +} diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs index 0a118b1e04..0560780be9 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModGravity.cs @@ -5,12 +5,6 @@ using System.Collections.Generic; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.Objects.Types; -using System.Linq; -using osu.Framework.Lists; -using osu.Game.Beatmaps.ControlPoints; -using osu.Framework.MathUtils; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Timing; using osu.Game.Rulesets.Objects; @@ -18,7 +12,7 @@ using osu.Game.Rulesets.Timing; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModGravity : Mod, IApplicableMod + public class ManiaModGravity : Mod, IGenerateSpeedAdjustments { public override string Name => "Gravity"; @@ -26,45 +20,26 @@ namespace osu.Game.Rulesets.Mania.Mods public override FontAwesome Icon => FontAwesome.fa_sort_desc; - public void ApplyToHitRenderer(HitRenderer hitRenderer) + public void ApplyToHitRenderer(ManiaHitRenderer hitRenderer, ref List[] hitObjectTimingChanges, ref List barlineTimingChanges) { - var maniaHitRenderer = (ManiaHitRenderer)hitRenderer; - - maniaHitRenderer.HitObjectTimingChanges = new List[maniaHitRenderer.PreferredColumns]; - maniaHitRenderer.BarlineTimingChanges = new List(); - - for (int i = 0; i < maniaHitRenderer.PreferredColumns; i++) - maniaHitRenderer.HitObjectTimingChanges[i] = new List(); - - foreach (HitObject obj in maniaHitRenderer.Objects) + foreach (HitObject obj in hitRenderer.Objects) { var maniaObject = obj as ManiaHitObject; if (maniaObject == null) continue; - maniaHitRenderer.HitObjectTimingChanges[maniaObject.Column].Add(new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(obj.StartTime) - { - TimingPoint = { BeatLength = 1000 } - }, ScrollingAlgorithm.Gravity)); + MultiplierControlPoint controlPoint = hitRenderer.CreateControlPointAt(obj.StartTime); + controlPoint.TimingPoint.BeatLength = 1000; + + hitObjectTimingChanges[maniaObject.Column].Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity)); } - double lastObjectTime = (maniaHitRenderer.Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? maniaHitRenderer.Objects.LastOrDefault()?.StartTime ?? double.MaxValue; - - SortedList timingPoints = maniaHitRenderer.Beatmap.ControlPointInfo.TimingPoints; - for (int i = 0; i < timingPoints.Count; i++) + foreach (BarLine barLine in hitRenderer.BarLines) { - TimingControlPoint point = timingPoints[i]; + var controlPoint = hitRenderer.CreateControlPointAt(barLine.StartTime); + controlPoint.TimingPoint.BeatLength = 1000; - // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object - double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; - - for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength) - { - maniaHitRenderer.BarlineTimingChanges.Add(new ManiaSpeedAdjustmentContainer(new MultiplierControlPoint(t) - { - TimingPoint = { BeatLength = 1000 } - }, ScrollingAlgorithm.Gravity)); - } + barlineTimingChanges.Add(new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Gravity)); } } } diff --git a/osu.Game.Rulesets.Mania/Timing/BasicScrollingDrawableTimingSection.cs b/osu.Game.Rulesets.Mania/Timing/BasicScrollingDrawableTimingSection.cs index 219c0bbd37..58f7e522be 100644 --- a/osu.Game.Rulesets.Mania/Timing/BasicScrollingDrawableTimingSection.cs +++ b/osu.Game.Rulesets.Mania/Timing/BasicScrollingDrawableTimingSection.cs @@ -3,7 +3,6 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Timing; -using osu.Game.Rulesets.Timing.Drawables; namespace osu.Game.Rulesets.Mania.Timing { diff --git a/osu.Game.Rulesets.Mania/Timing/GravityScrollingDrawableTimingSection.cs b/osu.Game.Rulesets.Mania/Timing/GravityScrollingDrawableTimingSection.cs index ddc174821e..b0e41cfbae 100644 --- a/osu.Game.Rulesets.Mania/Timing/GravityScrollingDrawableTimingSection.cs +++ b/osu.Game.Rulesets.Mania/Timing/GravityScrollingDrawableTimingSection.cs @@ -3,7 +3,6 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Timing; -using osu.Game.Rulesets.Timing.Drawables; namespace osu.Game.Rulesets.Mania.Timing { diff --git a/osu.Game.Rulesets.Mania/Timing/ManiaSpeedAdjustmentContainer.cs b/osu.Game.Rulesets.Mania/Timing/ManiaSpeedAdjustmentContainer.cs index 228ddc889b..41bb001531 100644 --- a/osu.Game.Rulesets.Mania/Timing/ManiaSpeedAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Mania/Timing/ManiaSpeedAdjustmentContainer.cs @@ -3,7 +3,6 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Timing; -using osu.Game.Rulesets.Timing.Drawables; namespace osu.Game.Rulesets.Mania.Timing { diff --git a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs index a1c17511ba..57ad9dcb45 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaHitRenderer.cs @@ -6,17 +6,17 @@ using System.Collections.Generic; using System.Linq; using OpenTK; using OpenTK.Input; -using osu.Framework.Allocation; using osu.Framework.Configuration; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Lists; using osu.Framework.MathUtils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.IO.Serialization; using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Judgements; +using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Scoring; @@ -36,42 +36,103 @@ namespace osu.Game.Rulesets.Mania.UI /// public int PreferredColumns; + public readonly List BarLines = new List(); + /// /// Per-column timing changes. /// - public List[] HitObjectTimingChanges; + private readonly List[] hitObjectTimingChanges; /// /// Bar line timing changes. /// - public List BarlineTimingChanges; + private readonly List barlineTimingChanges = new List(); - /// - /// Number of columns in the playfield of this hit renderer. Null if the play field hasn't been generated yet. - /// - public int? Columns { get; private set; } + private readonly SortedList defaultControlPoints = new SortedList(Comparer.Default); public ManiaHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset) : base(beatmap, isForCurrentRuleset) { - Columns = PreferredColumns; + // Generate the speed adjustment container lists + hitObjectTimingChanges = new List[PreferredColumns]; + for (int i = 0; i < PreferredColumns; i++) + hitObjectTimingChanges[i] = new List(); - generateDefaultTimingChanges(); + // Generate the bar line list + double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; + SortedList timingPoints = Beatmap.ControlPointInfo.TimingPoints; + for (int i = 0; i < timingPoints.Count; i++) + { + TimingControlPoint point = timingPoints[i]; + + // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object + double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; + + int index = 0; + for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) + { + BarLines.Add(new BarLine + { + StartTime = t, + ControlPoint = point, + BeatIndex = index + }); + } + } + + // Generate speed adjustments from mods first + bool useDefaultSpeedAdjustments = true; + + if (Mods != null) + { + foreach (var speedAdjustmentMod in Mods.OfType()) + { + useDefaultSpeedAdjustments = false; + speedAdjustmentMod.ApplyToHitRenderer(this, ref hitObjectTimingChanges, ref barlineTimingChanges); + } + } + + // Generate the default speed adjustments + if (useDefaultSpeedAdjustments) + generateDefaultSpeedAdjustments(); } - private void generateDefaultTimingChanges() + private void generateDefaultSpeedAdjustments() { - if (HitObjectTimingChanges != null || BarlineTimingChanges != null) - return; + defaultControlPoints.ForEach(c => + { + foreach (List t in hitObjectTimingChanges) + t.Add(new ManiaSpeedAdjustmentContainer(c, ScrollingAlgorithm.Basic)); + barlineTimingChanges.Add(new ManiaSpeedAdjustmentContainer(c, ScrollingAlgorithm.Basic)); + }); + } - HitObjectTimingChanges = new List[PreferredColumns]; - BarlineTimingChanges = new List(); + /// + /// Generates a control point at a point in time with the relevant timing change/difficulty change from the beatmap. + /// + /// The time to create the control point at. + /// The at . + public MultiplierControlPoint CreateControlPointAt(double time) + { + if (defaultControlPoints.Count == 0) + return new MultiplierControlPoint(time); - for (int i = 0; i < PreferredColumns; i++) - HitObjectTimingChanges[i] = new List(); + int index = defaultControlPoints.BinarySearch(new MultiplierControlPoint(time)); + if (index < 0) + return new MultiplierControlPoint(time); - double lastSpeedMultiplier = 1; - double lastBeatLength = 500; + return new MultiplierControlPoint(time, defaultControlPoints[index].DeepClone()); + } + + protected override void ApplyBeatmap() + { + base.ApplyBeatmap(); + + PreferredColumns = (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize); + + // Calculate default multiplier control points + var lastTimingPoint = new TimingControlPoint(); + var lastDifficultyPoint = new DifficultyControlPoint(); // Merge timing + difficulty points var allPoints = new SortedList(Comparer.Default); @@ -85,15 +146,15 @@ namespace osu.Game.Rulesets.Mania.UI var difficultyPoint = c as DifficultyControlPoint; if (timingPoint != null) - lastBeatLength = timingPoint.BeatLength; + lastTimingPoint = timingPoint; if (difficultyPoint != null) - lastSpeedMultiplier = difficultyPoint.SpeedMultiplier; + lastDifficultyPoint = difficultyPoint; return new MultiplierControlPoint(c.Time) { - TimingPoint = { BeatLength = lastBeatLength }, - DifficultyPoint = { SpeedMultiplier = lastSpeedMultiplier } + TimingPoint = lastTimingPoint, + DifficultyPoint = lastDifficultyPoint }; }); @@ -109,19 +170,7 @@ namespace osu.Game.Rulesets.Mania.UI .GroupBy(s => s.TimingPoint.BeatLength * s.DifficultyPoint.SpeedMultiplier).Select(g => g.First()) .ToList(); - timingChanges.ForEach(t => - { - for (int i = 0; i < PreferredColumns; i++) - HitObjectTimingChanges[i].Add(new ManiaSpeedAdjustmentContainer(t, ScrollingAlgorithm.Basic)); - - BarlineTimingChanges.Add(new ManiaSpeedAdjustmentContainer(t, ScrollingAlgorithm.Basic)); - }); - } - - protected override void ApplyBeatmap() - { - base.ApplyBeatmap(); - PreferredColumns = (int)Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize); + defaultControlPoints.AddRange(timingChanges); } protected override Playfield CreatePlayfield() @@ -136,44 +185,16 @@ namespace osu.Game.Rulesets.Mania.UI for (int i = 0; i < PreferredColumns; i++) { - foreach (var change in HitObjectTimingChanges[i]) + foreach (var change in hitObjectTimingChanges[i]) playfield.Columns.ElementAt(i).Add(change); } - foreach (var change in BarlineTimingChanges) + foreach (var change in barlineTimingChanges) playfield.Add(change); return playfield; } - [BackgroundDependencyLoader] - private void load() - { - var maniaPlayfield = (ManiaPlayfield)Playfield; - - double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; - - SortedList timingPoints = Beatmap.ControlPointInfo.TimingPoints; - for (int i = 0; i < timingPoints.Count; i++) - { - TimingControlPoint point = timingPoints[i]; - - // Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object - double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; - - int index = 0; - for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) - { - maniaPlayfield.Add(new DrawableBarLine(new BarLine - { - StartTime = t, - ControlPoint = point, - BeatIndex = index - })); - } - } - } - public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); protected override BeatmapConverter CreateBeatmapConverter() => new ManiaBeatmapConverter(); diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 1560d780be..c504b57310 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -62,6 +62,7 @@ + diff --git a/osu.Game/Rulesets/Timing/DrawableTimingSection.cs b/osu.Game/Rulesets/Timing/DrawableTimingSection.cs index 06c7a201ab..adcf8219d1 100644 --- a/osu.Game/Rulesets/Timing/DrawableTimingSection.cs +++ b/osu.Game/Rulesets/Timing/DrawableTimingSection.cs @@ -5,14 +5,14 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Caching; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; -using osu.Framework.Configuration; -namespace osu.Game.Rulesets.Timing.Drawables +namespace osu.Game.Rulesets.Timing { /// /// A collection of hit objects which scrolls within a . diff --git a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs index 893b4b43ca..2037174a5c 100644 --- a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs +++ b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs @@ -1,11 +1,13 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.IO.Serialization; namespace osu.Game.Rulesets.Timing { - public class MultiplierControlPoint + public class MultiplierControlPoint : IJsonSerializable, IComparable { /// /// The time in milliseconds at which this control point starts. @@ -20,9 +22,22 @@ namespace osu.Game.Rulesets.Timing public TimingControlPoint TimingPoint = new TimingControlPoint(); public DifficultyControlPoint DifficultyPoint = new DifficultyControlPoint(); + public MultiplierControlPoint() + { + } + public MultiplierControlPoint(double startTime) { StartTime = startTime; } + + public MultiplierControlPoint(double startTime, MultiplierControlPoint other) + : this(startTime) + { + TimingPoint = other.TimingPoint; + DifficultyPoint = other.DifficultyPoint; + } + + public int CompareTo(MultiplierControlPoint other) => StartTime.CompareTo(other?.StartTime); } } \ No newline at end of file diff --git a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs index ba623e769c..a6f94ccf6c 100644 --- a/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs +++ b/osu.Game/Rulesets/Timing/SpeedAdjustmentContainer.cs @@ -7,7 +7,6 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Timing.Drawables; using OpenTK; namespace osu.Game.Rulesets.Timing diff --git a/osu.Game/Rulesets/UI/HitRenderer.cs b/osu.Game/Rulesets/UI/HitRenderer.cs index 7001567f25..43c350184e 100644 --- a/osu.Game/Rulesets/UI/HitRenderer.cs +++ b/osu.Game/Rulesets/UI/HitRenderer.cs @@ -120,6 +120,11 @@ namespace osu.Game.Rulesets.UI /// public Beatmap Beatmap; + /// + /// The mods which are to be applied. + /// + protected IEnumerable Mods; + /// /// Creates a hit renderer for a beatmap. /// @@ -129,6 +134,8 @@ namespace osu.Game.Rulesets.UI { Debug.Assert(beatmap != null, "HitRenderer initialized with a null beatmap."); + Mods = beatmap.Mods.Value; + RelativeSizeAxes = Axes.Both; BeatmapConverter converter = CreateBeatmapConverter();