Split ControlPoint into different types.

# Conflicts:
#	osu.Game.Rulesets.Mania/UI/Column.cs
This commit is contained in:
smoogipooo
2017-05-23 13:55:18 +09:00
parent d1eb8937b7
commit 3cdfd2eef5
38 changed files with 307 additions and 205 deletions

View File

@ -7,6 +7,7 @@ using osu.Game.Database;
using osu.Game.Rulesets.Objects;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Beatmaps
{
@ -17,7 +18,7 @@ namespace osu.Game.Beatmaps
where T : HitObject
{
public BeatmapInfo BeatmapInfo;
public TimingInfo TimingInfo = new TimingInfo();
public ControlPointInfo ControlPointInfo = new ControlPointInfo();
public List<BreakPeriod> Breaks = new List<BreakPeriod>();
public readonly List<Color4> ComboColors = new List<Color4>
{
@ -46,7 +47,7 @@ namespace osu.Game.Beatmaps
public Beatmap(Beatmap original = null)
{
BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo;
TimingInfo = original?.TimingInfo ?? TimingInfo;
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
Breaks = original?.Breaks ?? Breaks;
ComboColors = original?.ComboColors ?? ComboColors;
}

View File

@ -0,0 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Beatmaps.ControlPoints
{
public class ControlPoint
{
public double Time;
}
}

View File

@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Beatmaps.ControlPoints
{
public class ControlPointInfo
{
/// <summary>
/// All the control points.
/// </summary>
public readonly List<ControlPoint> ControlPoints = new List<ControlPoint>();
/// <summary>
/// Finds the difficulty control point that is active at <paramref name="time"/>.
/// </summary>
/// <param name="time">The time to find the difficulty control point at.</param>
/// <returns>The difficulty control point.</returns>
public DifficultyControlPoint DifficultyPointAt(double time) =>
ControlPoints.OfType<DifficultyControlPoint>().LastOrDefault(t => t.Time <= time) ?? new DifficultyControlPoint();
/// <summary>
/// Finds the effect control point that is active at <paramref name="time"/>.
/// </summary>
/// <param name="time">The time to find the effect control point at.</param>
/// <returns>The effect control point.</returns>
public EffectControlPoint EffectPointAt(double time) =>
ControlPoints.OfType<EffectControlPoint>().LastOrDefault(t => t.Time <= time) ?? new EffectControlPoint();
/// <summary>
/// Finds the sound control point that is active at <paramref name="time"/>.
/// </summary>
/// <param name="time">The time to find the sound control point at.</param>
/// <returns>The sound control point.</returns>
public SoundControlPoint SoundPointAt(double time) =>
ControlPoints.OfType<SoundControlPoint>().LastOrDefault(t => t.Time <= time) ?? new SoundControlPoint();
/// <summary>
/// Finds the timing control point that is active at <paramref name="time"/>.
/// </summary>
/// <param name="time">The time to find the timing control point at.</param>
/// <returns>The timing control point.</returns>
public TimingControlPoint TimingPointAt(double time) =>
ControlPoints.OfType<TimingControlPoint>().LastOrDefault(t => t.Time <= time) ?? new TimingControlPoint();
/// <summary>
/// Finds the maximum BPM represented by any timing control point.
/// </summary>
public double BPMMaximum =>
60000 / (ControlPoints.OfType<TimingControlPoint>().OrderBy(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength;
/// <summary>
/// Finds the minimum BPM represented by any timing control point.
/// </summary>
public double BPMMinimum =>
60000 / (ControlPoints.OfType<TimingControlPoint>().OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new TimingControlPoint()).BeatLength;
/// <summary>
/// Finds the mode BPM (most common BPM) represented by the control points.
/// </summary>
public double BPMMode =>
60000 / (ControlPoints.OfType<TimingControlPoint>().GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).FirstOrDefault()?.FirstOrDefault() ?? new TimingControlPoint()).BeatLength;
}
}

View File

@ -0,0 +1,7 @@
namespace osu.Game.Beatmaps.ControlPoints
{
public class DifficultyControlPoint : ControlPoint
{
public double SpeedMultiplier = 1;
}
}

View File

@ -0,0 +1,8 @@
namespace osu.Game.Beatmaps.ControlPoints
{
public class EffectControlPoint : ControlPoint
{
public bool KiaiMode;
public bool OmitFirstBarLine;
}
}

View File

@ -0,0 +1,8 @@
namespace osu.Game.Beatmaps.ControlPoints
{
public class SoundControlPoint : ControlPoint
{
public string SampleBank;
public int SampleVolume;
}
}

View File

@ -0,0 +1,10 @@
using osu.Game.Beatmaps.Timing;
namespace osu.Game.Beatmaps.ControlPoints
{
public class TimingControlPoint : ControlPoint
{
public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple;
public double BeatLength = 500;
}
}

View File

@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps
Objects = CreateBeatmapConverter().Convert(beatmap, true).HitObjects;
foreach (var h in Objects)
h.ApplyDefaults(beatmap.TimingInfo, beatmap.BeatmapInfo.Difficulty);
h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty);
PreprocessHitObjects();
}

View File

@ -8,6 +8,8 @@ using OpenTK.Graphics;
using osu.Game.Beatmaps.Timing;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Objects.Legacy;
using System.Linq;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Beatmaps.Formats
{
@ -241,6 +243,7 @@ namespace osu.Game.Beatmaps.Formats
double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo);
double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo);
double speedMultiplier = beatLength < 0 ? -beatLength / 100.0 : 1;
TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple;
if (split.Length >= 3)
@ -275,18 +278,49 @@ namespace osu.Game.Beatmaps.Formats
if (stringSampleSet == @"none")
stringSampleSet = @"normal";
beatmap.TimingInfo.ControlPoints.Add(new ControlPoint
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(time);
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time);
SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time);
EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time);
if (timingChange && (beatLength != timingPoint.BeatLength || timeSignature != timingPoint.TimeSignature))
{
Time = time,
BeatLength = beatLength,
SpeedMultiplier = beatLength < 0 ? -beatLength / 100.0 : 1,
TimingChange = timingChange,
TimeSignature = timeSignature,
SampleBank = stringSampleSet,
SampleVolume = sampleVolume,
KiaiMode = kiaiMode,
OmitFirstBarLine = omitFirstBarSignature
});
beatmap.ControlPointInfo.ControlPoints.Add(new TimingControlPoint
{
Time = time,
BeatLength = beatLength,
TimeSignature = timeSignature
});
}
if (speedMultiplier != difficultyPoint.SpeedMultiplier)
{
beatmap.ControlPointInfo.ControlPoints.Add(new DifficultyControlPoint
{
Time = time,
SpeedMultiplier = speedMultiplier
});
}
if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume)
{
beatmap.ControlPointInfo.ControlPoints.Add(new SoundControlPoint
{
Time = time,
SampleBank = stringSampleSet,
SampleVolume = sampleVolume
});
}
if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine)
{
beatmap.ControlPointInfo.ControlPoints.Add(new EffectControlPoint
{
Time = time,
KiaiMode = kiaiMode,
OmitFirstBarLine = omitFirstBarSignature
});
}
}
private void handleColours(Beatmap beatmap, string key, string val, ref bool hasCustomColours)

View File

@ -1,20 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Beatmaps.Timing
{
public class ControlPoint
{
public string SampleBank;
public int SampleVolume;
public TimeSignatures TimeSignature = TimeSignatures.SimpleQuadruple;
public double Time;
public double BeatLength = 500;
public double SpeedMultiplier = 1;
public bool TimingChange = true;
public bool KiaiMode;
public bool OmitFirstBarLine;
public ControlPoint Clone() => (ControlPoint)MemberwiseClone();
}
}

View File

@ -1,80 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Beatmaps.Timing
{
public class TimingInfo
{
public readonly List<ControlPoint> ControlPoints = new List<ControlPoint>();
public double BPMMaximum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderBy(c => c.BeatLength).FirstOrDefault() ?? new ControlPoint()).BeatLength;
public double BPMMinimum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderByDescending(c => c.BeatLength).FirstOrDefault() ?? new ControlPoint()).BeatLength;
public double BPMMode => BPMAt(ControlPoints.Where(c => c.BeatLength != 0).GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).First().First().Time);
public double BPMAt(double time)
{
return 60000 / BeatLengthAt(time);
}
/// <summary>
/// Finds the speed multiplier at a time.
/// </summary>
/// <param name="time">The time to find the speed multiplier at.</param>
/// <returns>The speed multiplier.</returns>
public double SpeedMultiplierAt(double time)
{
ControlPoint overridePoint;
ControlPoint timingPoint = TimingPointAt(time, out overridePoint);
return overridePoint?.SpeedMultiplier ?? timingPoint?.SpeedMultiplier ?? 1;
}
/// <summary>
/// Finds the beat length at a time. This is expressed in milliseconds.
/// </summary>
/// <param name="time">The time to find the beat length at.</param>
/// <returns>The beat length.</returns>
public double BeatLengthAt(double time)
{
ControlPoint overridePoint;
ControlPoint timingPoint = TimingPointAt(time, out overridePoint);
return timingPoint.BeatLength;
}
/// <summary>
/// Finds the timing point at a time.
/// </summary>
/// <param name="time">The time to find the timing point at.</param>
/// <param name="overridePoint">The timing point containing the velocity change of the returned timing point.</param>
/// <returns>The timing point.</returns>
public ControlPoint TimingPointAt(double time, out ControlPoint overridePoint)
{
overridePoint = null;
ControlPoint timingPoint = null;
foreach (var controlPoint in ControlPoints)
{
// Some beatmaps have the first timingPoint (accidentally) start after the first HitObject(s).
// This null check makes it so that the first ControlPoint that makes a timing change is used as
// the timingPoint for those HitObject(s).
if (controlPoint.Time <= time || timingPoint == null)
{
if (controlPoint.TimingChange)
{
timingPoint = controlPoint;
overridePoint = null;
}
else
overridePoint = controlPoint;
}
else break;
}
return timingPoint ?? new ControlPoint();
}
}
}

View File

@ -2,9 +2,11 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
namespace osu.Game.Graphics.Containers
@ -14,7 +16,8 @@ namespace osu.Game.Graphics.Containers
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private int lastBeat;
private ControlPoint lastControlPoint;
private TimingControlPoint lastTimingPoint;
private EffectControlPoint lastEffectPoint;
protected override void Update()
{
@ -22,29 +25,29 @@ namespace osu.Game.Graphics.Containers
return;
double currentTrackTime = beatmap.Value.Track.CurrentTime;
ControlPoint overridePoint;
ControlPoint controlPoint = beatmap.Value.Beatmap.TimingInfo.TimingPointAt(currentTrackTime, out overridePoint);
if (controlPoint.BeatLength == 0)
TimingControlPoint timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
EffectControlPoint effectPoint = beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
if (timingPoint.BeatLength == 0)
return;
bool kiai = (overridePoint ?? controlPoint).KiaiMode;
int beat = (int)((currentTrackTime - controlPoint.Time) / controlPoint.BeatLength);
int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength);
// The beats before the start of the first control point are off by 1, this should do the trick
if (currentTrackTime < controlPoint.Time)
beat--;
if (currentTrackTime < timingPoint.Time)
beatIndex--;
if (controlPoint == lastControlPoint && beat == lastBeat)
if (timingPoint == lastTimingPoint && beatIndex == lastBeat)
return;
double offsetFromBeat = (controlPoint.Time - currentTrackTime) % controlPoint.BeatLength;
double offsetFromBeat = (timingPoint.Time - currentTrackTime) % timingPoint.BeatLength;
using (BeginDelayedSequence(offsetFromBeat, true))
OnNewBeat(beat, controlPoint.BeatLength, controlPoint.TimeSignature, kiai);
OnNewBeat(beatIndex, timingPoint, effectPoint, beatmap.Value.Track.CurrentAmplitudes);
lastBeat = beat;
lastControlPoint = controlPoint;
lastBeat = beatIndex;
lastTimingPoint = timingPoint;
}
[BackgroundDependencyLoader]
@ -53,7 +56,7 @@ namespace osu.Game.Graphics.Containers
beatmap.BindTo(game.Beatmap);
}
protected virtual void OnNewBeat(int newBeat, double beatLength, TimeSignatures timeSignature, bool kiai)
protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
{
}
}

View File

@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Beatmaps
return new Beatmap<T>
{
BeatmapInfo = original.BeatmapInfo,
TimingInfo = original.TimingInfo,
ControlPointInfo = original.ControlPointInfo,
HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList()
};
}

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
using osu.Game.Rulesets.Objects.Types;
@ -33,31 +34,28 @@ namespace osu.Game.Rulesets.Objects
/// <summary>
/// Applies default values to this HitObject.
/// </summary>
/// <param name="controlPointInfo">The control points.</param>
/// <param name="difficulty">The difficulty settings to use.</param>
/// <param name="timing">The timing settings to use.</param>
public virtual void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty)
public virtual void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
ControlPoint overridePoint;
ControlPoint timingPoint = timing.TimingPointAt(StartTime, out overridePoint);
ControlPoint samplePoint = overridePoint ?? timingPoint;
SoundControlPoint soundPoint = controlPointInfo.SoundPointAt(StartTime);
// Initialize first sample
Samples.ForEach(s => initializeSampleInfo(s, samplePoint));
Samples.ForEach(s => initializeSampleInfo(s, soundPoint));
// Initialize any repeat samples
var repeatData = this as IHasRepeats;
repeatData?.RepeatSamples?.ForEach(r => r.ForEach(s => initializeSampleInfo(s, samplePoint)));
repeatData?.RepeatSamples?.ForEach(r => r.ForEach(s => initializeSampleInfo(s, soundPoint)));
}
private void initializeSampleInfo(SampleInfo sample, ControlPoint controlPoint)
private void initializeSampleInfo(SampleInfo sample, SoundControlPoint soundPoint)
{
if (sample.Volume == 0)
sample.Volume = controlPoint?.SampleVolume ?? 0;
sample.Volume = soundPoint?.SampleVolume ?? 0;
// If the bank is not assigned a name, assign it from the control point
if (string.IsNullOrEmpty(sample.Bank))
sample.Bank = controlPoint?.SampleBank ?? @"normal";
sample.Bank = soundPoint?.SampleBank ?? @"normal";
}
}
}

View File

@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.UI
// Apply defaults
foreach (var h in Beatmap.HitObjects)
h.ApplyDefaults(Beatmap.TimingInfo, Beatmap.BeatmapInfo.Difficulty);
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.Difficulty);
// Post-process the beatmap
processor.PostProcess(Beatmap);

View File

@ -123,7 +123,7 @@ namespace osu.Game.Screens.Play
decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
var firstObjectTime = HitRenderer.Objects.First().StartTime;
decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(Beatmap.Beatmap.TimingInfo.BeatLengthAt(firstObjectTime) * 4, Beatmap.BeatmapInfo.AudioLeadIn)));
decoupledClock.Seek(Math.Min(0, firstObjectTime - Math.Max(Beatmap.Beatmap.ControlPointInfo.TimingPointAt(firstObjectTime).BeatLength * 4, Beatmap.BeatmapInfo.AudioLeadIn)));
decoupledClock.ProcessFrame();
offsetClock = new FramedOffsetClock(decoupledClock);

View File

@ -238,12 +238,12 @@ namespace osu.Game.Screens.Select
private string getBPMRange(Beatmap beatmap)
{
double bpmMax = beatmap.TimingInfo.BPMMaximum;
double bpmMin = beatmap.TimingInfo.BPMMinimum;
double bpmMax = beatmap.ControlPointInfo.BPMMaximum;
double bpmMin = beatmap.ControlPointInfo.BPMMinimum;
if (Precision.AlmostEquals(bpmMin, bpmMax)) return Math.Round(bpmMin) + "bpm";
return Math.Round(bpmMin) + "-" + Math.Round(bpmMax) + "bpm (mostly " + Math.Round(beatmap.TimingInfo.BPMMode) + "bpm)";
return Math.Round(bpmMin) + "-" + Math.Round(bpmMax) + "bpm (mostly " + Math.Round(beatmap.ControlPointInfo.BPMMode) + "bpm)";
}
public class InfoLabel : Container

View File

@ -87,10 +87,15 @@
<Compile Include="Overlays\Toolbar\ToolbarChatButton.cs" />
<Compile Include="Rulesets\Beatmaps\BeatmapConverter.cs" />
<Compile Include="Rulesets\Beatmaps\BeatmapProcessor.cs" />
<Compile Include="Beatmaps\ControlPoints\ControlPoint.cs" />
<Compile Include="Beatmaps\ControlPoints\ControlPointInfo.cs" />
<Compile Include="Beatmaps\ControlPoints\DifficultyControlPoint.cs" />
<Compile Include="Beatmaps\ControlPoints\EffectControlPoint.cs" />
<Compile Include="Beatmaps\ControlPoints\SoundControlPoint.cs" />
<Compile Include="Beatmaps\ControlPoints\TimingControlPoint.cs" />
<Compile Include="Beatmaps\Legacy\LegacyBeatmap.cs" />
<Compile Include="Beatmaps\Timing\BreakPeriod.cs" />
<Compile Include="Beatmaps\Timing\TimeSignatures.cs" />
<Compile Include="Beatmaps\Timing\TimingInfo.cs" />
<Compile Include="Database\BeatmapMetrics.cs" />
<Compile Include="Database\Database.cs" />
<Compile Include="Database\RulesetInfo.cs" />
@ -202,7 +207,6 @@
<Compile Include="Beatmaps\Drawables\Panel.cs" />
<Compile Include="Rulesets\Objects\Drawables\DrawableHitObject.cs" />
<Compile Include="Rulesets\Objects\HitObject.cs" />
<Compile Include="Beatmaps\Timing\ControlPoint.cs" />
<Compile Include="Configuration\OsuConfigManager.cs" />
<Compile Include="Overlays\Notifications\IHasCompletionTarget.cs" />
<Compile Include="Overlays\Notifications\Notification.cs" />