mirror of
https://github.com/ppy/osu.git
synced 2024-11-06 09:07:25 +08:00
Merge branch 'master' into update-changelog-notification
This commit is contained in:
commit
630d67405f
@ -1 +1 @@
|
|||||||
Subproject commit f1527e5456cd228ddfb68cf6d56eb5d28dc360bf
|
Subproject commit 74f644bad039606e242d8782014d8c249a38b6a3
|
@ -115,18 +115,18 @@ namespace osu.Desktop.Tests.Visual
|
|||||||
Assert.AreEqual(1, speedAdjustments[3].ControlPoint.Multiplier);
|
Assert.AreEqual(1, speedAdjustments[3].ControlPoint.Multiplier);
|
||||||
|
|
||||||
// Check insertion of hit objects
|
// Check insertion of hit objects
|
||||||
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[0]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[4].Contains(hitObjects[0]));
|
||||||
Assert.IsTrue(speedAdjustments[0].Contains(hitObjects[1]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
|
||||||
Assert.IsTrue(speedAdjustments[1].Contains(hitObjects[2]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[2].Contains(hitObjects[2]));
|
||||||
Assert.IsTrue(speedAdjustments[2].Contains(hitObjects[3]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[1].Contains(hitObjects[3]));
|
||||||
Assert.IsTrue(speedAdjustments[3].Contains(hitObjects[4]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[4]));
|
||||||
Assert.IsTrue(speedAdjustments[3].Contains(hitObjects[5]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[0].Contains(hitObjects[5]));
|
||||||
|
|
||||||
hitObjectContainer.RemoveSpeedAdjustment(speedAdjustments[1]);
|
hitObjectContainer.RemoveSpeedAdjustment(hitObjectContainer.SpeedAdjustments[3]);
|
||||||
|
|
||||||
// The hit object contained in this speed adjustment should be resorted into the previous one
|
// The hit object contained in this speed adjustment should be resorted into the one occuring before it
|
||||||
|
|
||||||
Assert.IsTrue(speedAdjustments[0].Contains(hitObjects[2]));
|
Assert.IsTrue(hitObjectContainer.SpeedAdjustments[3].Contains(hitObjects[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestRulesetContainer : ScrollingRulesetContainer<TestPlayfield, TestHitObject, TestJudgement>
|
private class TestRulesetContainer : ScrollingRulesetContainer<TestPlayfield, TestHitObject, TestJudgement>
|
||||||
|
@ -28,12 +28,20 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
private Pattern lastPattern = new Pattern();
|
private Pattern lastPattern = new Pattern();
|
||||||
private FastRandom random;
|
private FastRandom random;
|
||||||
private Beatmap beatmap;
|
private Beatmap beatmap;
|
||||||
private bool isForCurrentRuleset;
|
|
||||||
|
|
||||||
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
|
private readonly int availableColumns;
|
||||||
|
private readonly bool isForCurrentRuleset;
|
||||||
|
|
||||||
|
public ManiaBeatmapConverter(bool isForCurrentRuleset, int availableColumns)
|
||||||
{
|
{
|
||||||
this.isForCurrentRuleset = isForCurrentRuleset;
|
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
|
||||||
|
|
||||||
|
this.isForCurrentRuleset = isForCurrentRuleset;
|
||||||
|
this.availableColumns = availableColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original)
|
||||||
|
{
|
||||||
beatmap = original;
|
beatmap = original;
|
||||||
|
|
||||||
BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty;
|
BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty;
|
||||||
@ -41,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
|
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
|
||||||
random = new FastRandom(seed);
|
random = new FastRandom(seed);
|
||||||
|
|
||||||
return base.ConvertBeatmap(original, isForCurrentRuleset);
|
return base.ConvertBeatmap(original);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
|
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
|
||||||
@ -89,7 +97,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
/// <returns>The hit objects generated.</returns>
|
/// <returns>The hit objects generated.</returns>
|
||||||
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original)
|
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original)
|
||||||
{
|
{
|
||||||
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern);
|
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
|
||||||
|
|
||||||
Pattern newPattern = generator.Generate();
|
Pattern newPattern = generator.Generate();
|
||||||
lastPattern = newPattern;
|
lastPattern = newPattern;
|
||||||
@ -113,14 +121,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
Patterns.PatternGenerator conversion = null;
|
Patterns.PatternGenerator conversion = null;
|
||||||
|
|
||||||
if (distanceData != null)
|
if (distanceData != null)
|
||||||
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern);
|
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern);
|
||||||
else if (endTimeData != null)
|
else if (endTimeData != null)
|
||||||
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap);
|
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, availableColumns);
|
||||||
else if (positionData != null)
|
else if (positionData != null)
|
||||||
{
|
{
|
||||||
computeDensity(original.StartTime);
|
computeDensity(original.StartTime);
|
||||||
|
|
||||||
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair);
|
conversion = new HitObjectPatternGenerator(random, original, beatmap, availableColumns, lastPattern, lastTime, lastPosition, density, lastStair);
|
||||||
|
|
||||||
recordNote(original.StartTime, positionData.Position);
|
recordNote(original.StartTime, positionData.Position);
|
||||||
}
|
}
|
||||||
@ -142,8 +150,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
|
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
|
||||||
{
|
{
|
||||||
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
|
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
|
||||||
: base(random, hitObject, beatmap, previousPattern)
|
: base(random, hitObject, beatmap, availableColumns, previousPattern)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,8 +29,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
private PatternType convertType;
|
private PatternType convertType;
|
||||||
|
|
||||||
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
|
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
|
||||||
: base(random, hitObject, beatmap, previousPattern)
|
: base(random, hitObject, beatmap, availableColumns, previousPattern)
|
||||||
{
|
{
|
||||||
convertType = PatternType.None;
|
convertType = PatternType.None;
|
||||||
if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
|
if (Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode)
|
||||||
|
@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
{
|
{
|
||||||
private readonly double endTime;
|
private readonly double endTime;
|
||||||
|
|
||||||
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap)
|
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns)
|
||||||
: base(random, hitObject, beatmap, new Pattern())
|
: base(random, hitObject, beatmap, availableColumns, new Pattern())
|
||||||
{
|
{
|
||||||
var endtimeData = HitObject as IHasEndTime;
|
var endtimeData = HitObject as IHasEndTime;
|
||||||
|
|
||||||
|
@ -20,9 +20,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
private readonly PatternType convertType;
|
private readonly PatternType convertType;
|
||||||
|
|
||||||
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
|
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair)
|
||||||
: base(random, hitObject, beatmap, previousPattern)
|
: base(random, hitObject, beatmap, availableColumns, previousPattern)
|
||||||
{
|
{
|
||||||
|
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
|
||||||
|
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
|
||||||
|
|
||||||
StairType = lastStair;
|
StairType = lastStair;
|
||||||
|
|
||||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
||||||
|
@ -25,11 +25,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly FastRandom Random;
|
protected readonly FastRandom Random;
|
||||||
|
|
||||||
protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
|
protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
|
||||||
: base(hitObject, beatmap, previousPattern)
|
: base(hitObject, beatmap, availableColumns, previousPattern)
|
||||||
{
|
{
|
||||||
Random = random;
|
if (random == null) throw new ArgumentNullException(nameof(random));
|
||||||
|
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
|
||||||
|
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
|
||||||
|
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
|
||||||
|
|
||||||
|
Random = random;
|
||||||
RandomStart = AvailableColumns == 8 ? 1 : 0;
|
RandomStart = AvailableColumns == 8 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +66,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// <returns>The amount of notes to be generated.</returns>
|
/// <returns>The amount of notes to be generated.</returns>
|
||||||
protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0)
|
protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0)
|
||||||
{
|
{
|
||||||
|
if (p2 < 0 || p2 > 1) throw new ArgumentOutOfRangeException(nameof(p2));
|
||||||
|
if (p3 < 0 || p3 > 1) throw new ArgumentOutOfRangeException(nameof(p3));
|
||||||
|
if (p4 < 0 || p4 > 1) throw new ArgumentOutOfRangeException(nameof(p4));
|
||||||
|
if (p5 < 0 || p5 > 1) throw new ArgumentOutOfRangeException(nameof(p5));
|
||||||
|
if (p6 < 0 || p6 > 1) throw new ArgumentOutOfRangeException(nameof(p6));
|
||||||
|
|
||||||
double val = Random.NextDouble();
|
double val = Random.NextDouble();
|
||||||
if (val >= 1 - p6)
|
if (val >= 1 - p6)
|
||||||
return 6;
|
return 6;
|
||||||
|
@ -32,13 +32,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Beatmap Beatmap;
|
protected readonly Beatmap Beatmap;
|
||||||
|
|
||||||
protected PatternGenerator(HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
|
protected PatternGenerator(HitObject hitObject, Beatmap beatmap, int availableColumns, Pattern previousPattern)
|
||||||
{
|
{
|
||||||
PreviousPattern = previousPattern;
|
if (hitObject == null) throw new ArgumentNullException(nameof(hitObject));
|
||||||
|
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap));
|
||||||
|
if (availableColumns <= 0) throw new ArgumentOutOfRangeException(nameof(availableColumns));
|
||||||
|
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern));
|
||||||
|
|
||||||
HitObject = hitObject;
|
HitObject = hitObject;
|
||||||
Beatmap = beatmap;
|
Beatmap = beatmap;
|
||||||
|
AvailableColumns = availableColumns;
|
||||||
AvailableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize);
|
PreviousPattern = previousPattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -6,6 +6,7 @@ using osu.Game.Rulesets.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania
|
namespace osu.Game.Rulesets.Mania
|
||||||
{
|
{
|
||||||
@ -21,6 +22,6 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
|
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,6 +125,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
for (int i = 0; i < columnCount; i++)
|
for (int i = 0; i < columnCount; i++)
|
||||||
{
|
{
|
||||||
var c = new Column();
|
var c = new Column();
|
||||||
|
c.Reversed.BindTo(Reversed);
|
||||||
c.VisibleTimeRange.BindTo(VisibleTimeRange);
|
c.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||||
|
|
||||||
columns.Add(c);
|
columns.Add(c);
|
||||||
|
@ -33,9 +33,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
public class ManiaRulesetContainer : ScrollingRulesetContainer<ManiaPlayfield, ManiaHitObject, ManiaJudgement>
|
public class ManiaRulesetContainer : ScrollingRulesetContainer<ManiaPlayfield, ManiaHitObject, ManiaJudgement>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Preferred column count. This will only have an effect during the initialization of the play field.
|
/// The number of columns which the <see cref="ManiaPlayfield"/> should display, and which
|
||||||
|
/// the beatmap converter will attempt to convert beatmaps to use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int PreferredColumns;
|
private int availableColumns;
|
||||||
|
|
||||||
public IEnumerable<DrawableBarLine> BarLines;
|
public IEnumerable<DrawableBarLine> BarLines;
|
||||||
|
|
||||||
@ -76,26 +77,35 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
BarLines.ForEach(Playfield.Add);
|
BarLines.ForEach(Playfield.Add);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyBeatmap()
|
protected sealed override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(availableColumns)
|
||||||
{
|
|
||||||
base.ApplyBeatmap();
|
|
||||||
|
|
||||||
PreferredColumns = (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.Difficulty.CircleSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected sealed override Playfield<ManiaHitObject, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(PreferredColumns)
|
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
// Invert by default for now (should be moved to config/skin later)
|
|
||||||
Scale = new Vector2(1, -1)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
||||||
|
|
||||||
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo);
|
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo);
|
||||||
|
|
||||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter();
|
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter()
|
||||||
|
{
|
||||||
|
if (IsForCurrentRuleset)
|
||||||
|
availableColumns = (int)Math.Max(1, Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
float percentSliderOrSpinner = (float)WorkingBeatmap.Beatmap.HitObjects.Count(h => h is IHasEndTime) / WorkingBeatmap.Beatmap.HitObjects.Count;
|
||||||
|
if (percentSliderOrSpinner < 0.2)
|
||||||
|
availableColumns = 7;
|
||||||
|
else if (percentSliderOrSpinner < 0.3 || Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.CircleSize) >= 5)
|
||||||
|
availableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 5 ? 7 : 6;
|
||||||
|
else if (percentSliderOrSpinner > 0.6)
|
||||||
|
availableColumns = Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) > 4 ? 5 : 4;
|
||||||
|
else
|
||||||
|
availableColumns = Math.Max(4, Math.Min((int)Math.Round(WorkingBeatmap.BeatmapInfo.Difficulty.OverallDifficulty) + 1, 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ManiaBeatmapConverter(IsForCurrentRuleset, availableColumns);
|
||||||
|
}
|
||||||
|
|
||||||
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
|
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
|
||||||
{
|
{
|
||||||
|
@ -39,19 +39,22 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const float taiko_base_distance = 100;
|
private const float taiko_base_distance = 100;
|
||||||
|
|
||||||
private bool isForCurrentRuleset;
|
private readonly bool isForCurrentRuleset;
|
||||||
|
|
||||||
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) };
|
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) };
|
||||||
|
|
||||||
protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
|
public TaikoBeatmapConverter(bool isForCurrentRuleset)
|
||||||
{
|
{
|
||||||
this.isForCurrentRuleset = isForCurrentRuleset;
|
this.isForCurrentRuleset = isForCurrentRuleset;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original)
|
||||||
|
{
|
||||||
// Rewrite the beatmap info to add the slider velocity multiplier
|
// Rewrite the beatmap info to add the slider velocity multiplier
|
||||||
BeatmapInfo info = original.BeatmapInfo.DeepClone();
|
BeatmapInfo info = original.BeatmapInfo.DeepClone();
|
||||||
info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier;
|
info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier;
|
||||||
|
|
||||||
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original, isForCurrentRuleset);
|
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original);
|
||||||
|
|
||||||
// Post processing step to transform hit objects with the same start time into strong hits
|
// Post processing step to transform hit objects with the same start time into strong hits
|
||||||
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>
|
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>
|
||||||
|
@ -28,14 +28,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
FillMode = FillMode.Fit;
|
FillMode = FillMode.Fit;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
// We need to set this here because RelativeSizeAxes won't/can't set our size by default with a different RelativeChildSize
|
|
||||||
Width *= Parent.RelativeChildSize.X;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void CheckJudgement(bool userTriggered)
|
protected override void CheckJudgement(bool userTriggered)
|
||||||
{
|
{
|
||||||
if (!userTriggered)
|
if (!userTriggered)
|
||||||
@ -71,6 +63,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
return UpdateJudgement(true);
|
return UpdateJudgement(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
Size = BaseSize * Parent.RelativeChildSize;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateState(ArmedState state)
|
protected override void UpdateState(ArmedState state)
|
||||||
{
|
{
|
||||||
var circlePiece = MainPiece as CirclePiece;
|
var circlePiece = MainPiece as CirclePiece;
|
||||||
|
@ -199,6 +199,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
|
Size = BaseSize * Parent.RelativeChildSize;
|
||||||
|
|
||||||
// Make the swell stop at the hit target
|
// Make the swell stop at the hit target
|
||||||
X = (float)Math.Max(Time.Current, HitObject.StartTime);
|
X = (float)Math.Max(Time.Current, HitObject.StartTime);
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
|
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
|
||||||
|
|
||||||
|
protected readonly Vector2 BaseSize;
|
||||||
|
|
||||||
protected readonly TaikoPiece MainPiece;
|
protected readonly TaikoPiece MainPiece;
|
||||||
|
|
||||||
public new TaikoHitType HitObject;
|
public new TaikoHitType HitObject;
|
||||||
@ -29,7 +31,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
Origin = Anchor.Custom;
|
Origin = Anchor.Custom;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
Size = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE);
|
Size = BaseSize = new Vector2(HitObject.IsStrong ? TaikoHitObject.DEFAULT_STRONG_SIZE : TaikoHitObject.DEFAULT_SIZE);
|
||||||
|
|
||||||
Add(MainPiece = CreateMainPiece());
|
Add(MainPiece = CreateMainPiece());
|
||||||
MainPiece.KiaiMode = HitObject.Kiai;
|
MainPiece.KiaiMode = HitObject.Kiai;
|
||||||
|
@ -135,6 +135,6 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
return difficulty;
|
return difficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter();
|
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
|
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
|
||||||
|
|
||||||
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter();
|
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(IsForCurrentRuleset);
|
||||||
|
|
||||||
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
|
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
|
||||||
|
|
||||||
|
@ -30,11 +30,15 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject
|
public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject
|
||||||
{
|
{
|
||||||
|
protected readonly Beatmap Beatmap;
|
||||||
|
|
||||||
protected List<T> Objects;
|
protected List<T> Objects;
|
||||||
|
|
||||||
protected DifficultyCalculator(Beatmap beatmap)
|
protected DifficultyCalculator(Beatmap beatmap)
|
||||||
{
|
{
|
||||||
Objects = CreateBeatmapConverter().Convert(beatmap, true).HitObjects;
|
Beatmap = beatmap;
|
||||||
|
|
||||||
|
Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects;
|
||||||
|
|
||||||
foreach (var h in Objects)
|
foreach (var h in Objects)
|
||||||
h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty);
|
h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.Difficulty);
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Cursor
|
namespace osu.Game.Graphics.Cursor
|
||||||
@ -21,20 +22,31 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
|
|
||||||
private bool dragging;
|
private bool dragging;
|
||||||
|
|
||||||
|
private bool startRotation;
|
||||||
|
|
||||||
protected override bool OnMouseMove(InputState state)
|
protected override bool OnMouseMove(InputState state)
|
||||||
{
|
{
|
||||||
if (dragging)
|
if (dragging)
|
||||||
{
|
{
|
||||||
Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown ?? state.Mouse.Delta;
|
Debug.Assert(state.Mouse.PositionMouseDown != null);
|
||||||
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
|
|
||||||
|
|
||||||
// Always rotate in the direction of least distance
|
// don't start rotating until we're moved a minimum distance away from the mouse down location,
|
||||||
float diff = (degrees - ActiveCursor.Rotation) % 360;
|
// else it can have an annoying effect.
|
||||||
if (diff < -180) diff += 360;
|
startRotation |= Vector2.Distance(state.Mouse.Position, state.Mouse.PositionMouseDown.Value) > 30;
|
||||||
if (diff > 180) diff -= 360;
|
|
||||||
degrees = ActiveCursor.Rotation + diff;
|
|
||||||
|
|
||||||
ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint);
|
if (startRotation)
|
||||||
|
{
|
||||||
|
Vector2 offset = state.Mouse.Position - state.Mouse.PositionMouseDown.Value;
|
||||||
|
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
|
||||||
|
|
||||||
|
// Always rotate in the direction of least distance
|
||||||
|
float diff = (degrees - ActiveCursor.Rotation) % 360;
|
||||||
|
if (diff < -180) diff += 360;
|
||||||
|
if (diff > 180) diff -= 360;
|
||||||
|
degrees = ActiveCursor.Rotation + diff;
|
||||||
|
|
||||||
|
ActiveCursor.RotateTo(degrees, 600, Easing.OutQuint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnMouseMove(state);
|
return base.OnMouseMove(state);
|
||||||
@ -61,6 +73,7 @@ namespace osu.Game.Graphics.Cursor
|
|||||||
if (!state.Mouse.HasMainButtonPressed)
|
if (!state.Mouse.HasMainButtonPressed)
|
||||||
{
|
{
|
||||||
dragging = false;
|
dragging = false;
|
||||||
|
startRotation = false;
|
||||||
|
|
||||||
((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint);
|
((Cursor)ActiveCursor).AdditiveLayer.FadeOut(500, Easing.OutQuint);
|
||||||
ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf);
|
ActiveCursor.RotateTo(0, 600 * (1 + Math.Abs(ActiveCursor.Rotation / 720)), Easing.OutElasticHalf);
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface.Volume
|
namespace osu.Game.Graphics.UserInterface.Volume
|
||||||
{
|
{
|
||||||
@ -64,15 +64,25 @@ namespace osu.Game.Graphics.UserInterface.Volume
|
|||||||
volumeMeterMusic.Bindable.ValueChanged -= volumeChanged;
|
volumeMeterMusic.Bindable.ValueChanged -= volumeChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Adjust(InputState state)
|
public bool Adjust(GlobalAction action)
|
||||||
{
|
{
|
||||||
if (State == Visibility.Hidden)
|
switch (action)
|
||||||
{
|
{
|
||||||
Show();
|
case GlobalAction.DecreaseVolume:
|
||||||
return;
|
if (State == Visibility.Hidden)
|
||||||
|
Show();
|
||||||
|
else
|
||||||
|
volumeMeterMaster.Decrease();
|
||||||
|
return true;
|
||||||
|
case GlobalAction.IncreaseVolume:
|
||||||
|
if (State == Visibility.Hidden)
|
||||||
|
Show();
|
||||||
|
else
|
||||||
|
volumeMeterMaster.Increase();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
volumeMeterMaster.TriggerOnWheel(state);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -3,32 +3,16 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input.Bindings;
|
||||||
using OpenTK.Input;
|
using osu.Game.Input.Bindings;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface.Volume
|
namespace osu.Game.Graphics.UserInterface.Volume
|
||||||
{
|
{
|
||||||
internal class VolumeControlReceptor : Container
|
internal class VolumeControlReceptor : Container, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
public Action<InputState> ActionRequested;
|
public Func<GlobalAction, bool> ActionRequested;
|
||||||
|
|
||||||
protected override bool OnWheel(InputState state)
|
public bool OnPressed(GlobalAction action) => ActionRequested?.Invoke(action) ?? false;
|
||||||
{
|
public bool OnReleased(GlobalAction action) => false;
|
||||||
ActionRequested?.Invoke(state);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
|
||||||
{
|
|
||||||
switch (args.Key)
|
|
||||||
{
|
|
||||||
case Key.Up:
|
|
||||||
case Key.Down:
|
|
||||||
ActionRequested?.Invoke(state);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnKeyDown(state, args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,16 @@
|
|||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface.Volume
|
namespace osu.Game.Graphics.UserInterface.Volume
|
||||||
{
|
{
|
||||||
internal class VolumeMeter : Container
|
internal class VolumeMeter : Container, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
private readonly Box meterFill;
|
private readonly Box meterFill;
|
||||||
public BindableDouble Bindable { get; } = new BindableDouble();
|
public BindableDouble Bindable { get; } = new BindableDouble();
|
||||||
@ -76,12 +77,35 @@ namespace osu.Game.Graphics.UserInterface.Volume
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnWheel(InputState state)
|
public void Increase()
|
||||||
{
|
{
|
||||||
Volume += 0.05f * state.Mouse.WheelDelta;
|
Volume += 0.05f;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
public void Decrease()
|
||||||
|
{
|
||||||
|
Volume -= 0.05f;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFill() => meterFill.ScaleTo(new Vector2(1, (float)Volume), 300, Easing.OutQuint);
|
private void updateFill() => meterFill.ScaleTo(new Vector2(1, (float)Volume), 300, Easing.OutQuint);
|
||||||
|
|
||||||
|
public bool OnPressed(GlobalAction action)
|
||||||
|
{
|
||||||
|
if (!IsHovered) return false;
|
||||||
|
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case GlobalAction.DecreaseVolume:
|
||||||
|
Decrease();
|
||||||
|
return true;
|
||||||
|
case GlobalAction.IncreaseVolume:
|
||||||
|
Increase();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(GlobalAction action) => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -26,7 +26,10 @@ namespace osu.Game.Input.Bindings
|
|||||||
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings),
|
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
|
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
|
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleDirect),
|
new KeyBinding(new[] { InputKey.Up }, GlobalAction.IncreaseVolume),
|
||||||
|
new KeyBinding(new[] { InputKey.MouseWheelUp }, GlobalAction.IncreaseVolume),
|
||||||
|
new KeyBinding(new[] { InputKey.Down }, GlobalAction.DecreaseVolume),
|
||||||
|
new KeyBinding(new[] { InputKey.MouseWheelDown }, GlobalAction.DecreaseVolume),
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override IEnumerable<Drawable> KeyBindingInputQueue =>
|
protected override IEnumerable<Drawable> KeyBindingInputQueue =>
|
||||||
@ -47,5 +50,9 @@ namespace osu.Game.Input.Bindings
|
|||||||
ToggleSettings,
|
ToggleSettings,
|
||||||
[Description("Toggle osu!direct")]
|
[Description("Toggle osu!direct")]
|
||||||
ToggleDirect,
|
ToggleDirect,
|
||||||
|
[Description("Increase Volume")]
|
||||||
|
IncreaseVolume,
|
||||||
|
[Description("Decrease Volume")]
|
||||||
|
DecreaseVolume,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ using osu.Game.Configuration;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Framework.Input;
|
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Game.Graphics.UserInterface.Volume;
|
using osu.Game.Graphics.UserInterface.Volume;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -160,7 +159,7 @@ namespace osu.Game
|
|||||||
new VolumeControlReceptor
|
new VolumeControlReceptor
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
ActionRequested = delegate(InputState state) { volume.Adjust(state); }
|
ActionRequested = action => volume.Adjust(action)
|
||||||
},
|
},
|
||||||
mainContent = new Container
|
mainContent = new Container
|
||||||
{
|
{
|
||||||
|
@ -136,40 +136,49 @@ namespace osu.Game.Overlays.KeyBinding
|
|||||||
|
|
||||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||||
{
|
{
|
||||||
if (HasFocus)
|
if (!HasFocus || !bindTarget.IsHovered)
|
||||||
{
|
return base.OnMouseDown(state, args);
|
||||||
if (bindTarget.IsHovered)
|
|
||||||
{
|
|
||||||
if (!AllowMainMouseButtons)
|
|
||||||
{
|
|
||||||
switch (args.Button)
|
|
||||||
{
|
|
||||||
case MouseButton.Left:
|
|
||||||
case MouseButton.Right:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
|
if (!AllowMainMouseButtons)
|
||||||
return true;
|
{
|
||||||
|
switch (args.Button)
|
||||||
|
{
|
||||||
|
case MouseButton.Left:
|
||||||
|
case MouseButton.Right:
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnMouseDown(state, args);
|
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||||
{
|
{
|
||||||
if (HasFocus && !state.Mouse.Buttons.Any())
|
// don't do anything until the last button is released.
|
||||||
|
if (!HasFocus || state.Mouse.Buttons.Any())
|
||||||
|
return base.OnMouseUp(state, args);
|
||||||
|
|
||||||
|
if (bindTarget.IsHovered)
|
||||||
|
finalise();
|
||||||
|
else
|
||||||
|
updateBindTarget();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnWheel(InputState state)
|
||||||
|
{
|
||||||
|
if (HasFocus)
|
||||||
{
|
{
|
||||||
if (bindTarget.IsHovered)
|
if (bindTarget.IsHovered)
|
||||||
|
{
|
||||||
|
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
|
||||||
finalise();
|
finalise();
|
||||||
else
|
return true;
|
||||||
updateBindTarget();
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnMouseUp(state, args);
|
return base.OnWheel(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||||
@ -196,13 +205,10 @@ namespace osu.Game.Overlays.KeyBinding
|
|||||||
|
|
||||||
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
|
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
|
||||||
{
|
{
|
||||||
if (HasFocus)
|
if (!HasFocus) return base.OnKeyUp(state, args);
|
||||||
{
|
|
||||||
finalise();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnKeyUp(state, args);
|
finalise();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void finalise()
|
private void finalise()
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Overlays
|
|||||||
private ScrollContainer scrollContainer;
|
private ScrollContainer scrollContainer;
|
||||||
private FlowContainer<NotificationSection> sections;
|
private FlowContainer<NotificationSection> sections;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(permitNulls: true)]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Width = width;
|
Width = width;
|
||||||
@ -72,6 +72,13 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private int runningDepth;
|
private int runningDepth;
|
||||||
|
|
||||||
|
private void notificationClosed()
|
||||||
|
{
|
||||||
|
// hide ourselves if all notifications have been dismissed.
|
||||||
|
if (sections.Select(c => c.DisplayedCount).Sum() > 0)
|
||||||
|
State = Visibility.Hidden;
|
||||||
|
}
|
||||||
|
|
||||||
public void Post(Notification notification)
|
public void Post(Notification notification)
|
||||||
{
|
{
|
||||||
Schedule(() =>
|
Schedule(() =>
|
||||||
@ -81,6 +88,8 @@ namespace osu.Game.Overlays
|
|||||||
++runningDepth;
|
++runningDepth;
|
||||||
notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth;
|
notification.Depth = notification.DisplayOnTop ? runningDepth : -runningDepth;
|
||||||
|
|
||||||
|
notification.Closed += notificationClosed;
|
||||||
|
|
||||||
var hasCompletionTarget = notification as IHasCompletionTarget;
|
var hasCompletionTarget = notification as IHasCompletionTarget;
|
||||||
if (hasCompletionTarget != null)
|
if (hasCompletionTarget != null)
|
||||||
hasCompletionTarget.CompletionTarget = Post;
|
hasCompletionTarget.CompletionTarget = Post;
|
||||||
|
@ -19,9 +19,9 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
public abstract class Notification : Container
|
public abstract class Notification : Container
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use requested close.
|
/// User requested close.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action Closed;
|
public event Action Closed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Run on user activating the notification. Return true to close.
|
/// Run on user activating the notification. Return true to close.
|
||||||
|
@ -24,6 +24,8 @@ namespace osu.Game.Overlays.Notifications
|
|||||||
|
|
||||||
private FlowContainer<Notification> notifications;
|
private FlowContainer<Notification> notifications;
|
||||||
|
|
||||||
|
public int DisplayedCount => notifications.Count;
|
||||||
|
|
||||||
public void Add(Notification notification)
|
public void Add(Notification notification)
|
||||||
{
|
{
|
||||||
notifications.Add(notification);
|
notifications.Add(notification);
|
||||||
|
@ -26,21 +26,19 @@ namespace osu.Game.Rulesets.Beatmaps
|
|||||||
/// Converts a Beatmap using this Beatmap Converter.
|
/// Converts a Beatmap using this Beatmap Converter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="original">The un-converted Beatmap.</param>
|
/// <param name="original">The un-converted Beatmap.</param>
|
||||||
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
|
|
||||||
/// <returns>The converted Beatmap.</returns>
|
/// <returns>The converted Beatmap.</returns>
|
||||||
public Beatmap<T> Convert(Beatmap original, bool isForCurrentRuleset)
|
public Beatmap<T> Convert(Beatmap original)
|
||||||
{
|
{
|
||||||
// We always operate on a clone of the original beatmap, to not modify it game-wide
|
// We always operate on a clone of the original beatmap, to not modify it game-wide
|
||||||
return ConvertBeatmap(new Beatmap(original), isForCurrentRuleset);
|
return ConvertBeatmap(new Beatmap(original));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs the conversion of a Beatmap using this Beatmap Converter.
|
/// Performs the conversion of a Beatmap using this Beatmap Converter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="original">The un-converted Beatmap.</param>
|
/// <param name="original">The un-converted Beatmap.</param>
|
||||||
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
|
|
||||||
/// <returns>The converted Beatmap.</returns>
|
/// <returns>The converted Beatmap.</returns>
|
||||||
protected virtual Beatmap<T> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
|
protected virtual Beatmap<T> ConvertBeatmap(Beatmap original)
|
||||||
{
|
{
|
||||||
return new Beatmap<T>
|
return new Beatmap<T>
|
||||||
{
|
{
|
||||||
|
@ -10,12 +10,10 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class LinearScrollingContainer : ScrollingContainer
|
internal class LinearScrollingContainer : ScrollingContainer
|
||||||
{
|
{
|
||||||
private readonly Axes scrollingAxes;
|
|
||||||
private readonly MultiplierControlPoint controlPoint;
|
private readonly MultiplierControlPoint controlPoint;
|
||||||
|
|
||||||
public LinearScrollingContainer(Axes scrollingAxes, MultiplierControlPoint controlPoint)
|
public LinearScrollingContainer(MultiplierControlPoint controlPoint)
|
||||||
{
|
{
|
||||||
this.scrollingAxes = scrollingAxes;
|
|
||||||
this.controlPoint = controlPoint;
|
this.controlPoint = controlPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,8 +21,8 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
if ((scrollingAxes & Axes.X) > 0) X = (float)(controlPoint.StartTime - Time.Current);
|
if ((ScrollingAxes & Axes.X) > 0) X = (float)(controlPoint.StartTime - Time.Current);
|
||||||
if ((scrollingAxes & Axes.Y) > 0) Y = (float)(controlPoint.StartTime - Time.Current);
|
if ((ScrollingAxes & Axes.Y) > 0) Y = (float)(controlPoint.StartTime - Time.Current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
@ -45,50 +46,34 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
RelativePositionAxes = Axes.Both;
|
RelativePositionAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void InvalidateFromChild(Invalidation invalidation)
|
protected override int Compare(Drawable x, Drawable y)
|
||||||
{
|
{
|
||||||
// We only want to re-compute our size when a child's size or position has changed
|
var hX = (DrawableHitObject)x;
|
||||||
if ((invalidation & Invalidation.RequiredParentSizeToFit) == 0)
|
var hY = (DrawableHitObject)y;
|
||||||
{
|
|
||||||
base.InvalidateFromChild(invalidation);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
int result = hY.HitObject.StartTime.CompareTo(hX.HitObject.StartTime);
|
||||||
|
if (result != 0)
|
||||||
|
return result;
|
||||||
|
return base.Compare(y, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Add(DrawableHitObject drawable)
|
||||||
|
{
|
||||||
durationBacking.Invalidate();
|
durationBacking.Invalidate();
|
||||||
|
base.Add(drawable);
|
||||||
base.InvalidateFromChild(invalidation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private double computeDuration()
|
public override bool Remove(DrawableHitObject drawable)
|
||||||
{
|
{
|
||||||
if (!Children.Any())
|
durationBacking.Invalidate();
|
||||||
return 0;
|
return base.Remove(drawable);
|
||||||
|
|
||||||
double baseDuration = Children.Max(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime) - ControlPoint.StartTime;
|
|
||||||
|
|
||||||
// If we have a singular hit object at the timing section's start time, let's set a sane default duration
|
|
||||||
if (baseDuration == 0)
|
|
||||||
baseDuration = 1;
|
|
||||||
|
|
||||||
// This container needs to resize such that it completely encloses the hit objects to avoid masking optimisations. This is done by converting the largest
|
|
||||||
// absolutely-sized element along the scrolling axes and adding a corresponding duration value. This introduces a bit of error, but will never under-estimate.ion.
|
|
||||||
|
|
||||||
// Find the largest element that is absolutely-sized along ScrollingAxes
|
|
||||||
float maxAbsoluteSize = Children.Where(c => (c.RelativeSizeAxes & ScrollingAxes) == 0)
|
|
||||||
.Select(c => (ScrollingAxes & Axes.X) > 0 ? c.Width : c.Height)
|
|
||||||
.DefaultIfEmpty().Max();
|
|
||||||
|
|
||||||
float ourAbsoluteSize = (ScrollingAxes & Axes.X) > 0 ? DrawWidth : DrawHeight;
|
|
||||||
|
|
||||||
// Add the extra duration to account for the absolute size
|
|
||||||
baseDuration *= 1 + maxAbsoluteSize / ourAbsoluteSize;
|
|
||||||
|
|
||||||
return baseDuration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Todo: This may underestimate the size of the hit object in some cases, but won't be too much of a problem for now
|
||||||
|
private double computeDuration() => Math.Max(0, Children.Select(c => (c.HitObject as IHasEndTime)?.EndTime ?? c.HitObject.StartTime).DefaultIfEmpty().Max() - ControlPoint.StartTime) + 1000;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum duration of any one hit object inside this <see cref="ScrollingContainer"/>. This is calculated as the maximum
|
/// An approximate total duration of this scrolling container.
|
||||||
/// duration of all hit objects relative to this <see cref="ScrollingContainer"/>'s <see cref="MultiplierControlPoint.StartTime"/>.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double Duration => durationBacking.IsValid ? durationBacking : (durationBacking.Value = computeDuration());
|
public double Duration => durationBacking.IsValid ? durationBacking : (durationBacking.Value = computeDuration());
|
||||||
|
|
||||||
@ -96,6 +81,8 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
|
RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)ControlPoint.StartTime : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)ControlPoint.StartTime : 0);
|
||||||
|
|
||||||
// We want our size and position-space along the scrolling axes to span our duration to completely enclose all the hit objects
|
// We want our size and position-space along the scrolling axes to span our duration to completely enclose all the hit objects
|
||||||
Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)Duration : Size.X, (ScrollingAxes & Axes.Y) > 0 ? (float)Duration : Size.Y);
|
Size = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)Duration : Size.X, (ScrollingAxes & Axes.Y) > 0 ? (float)Duration : Size.Y);
|
||||||
// And we need to make sure the hit object's position-space doesn't change due to our resizing
|
// And we need to make sure the hit object's position-space doesn't change due to our resizing
|
||||||
|
@ -31,7 +31,11 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The axes which the content of this container will scroll through.
|
/// The axes which the content of this container will scroll through.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Axes ScrollingAxes { get; internal set; }
|
public Axes ScrollingAxes
|
||||||
|
{
|
||||||
|
get { return scrollingContainer.ScrollingAxes; }
|
||||||
|
set { scrollingContainer.ScrollingAxes = value; }
|
||||||
|
}
|
||||||
|
|
||||||
public override bool RemoveWhenNotAlive => false;
|
public override bool RemoveWhenNotAlive => false;
|
||||||
|
|
||||||
@ -52,11 +56,8 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
scrollingContainer = CreateScrollingContainer();
|
scrollingContainer = CreateScrollingContainer();
|
||||||
|
|
||||||
scrollingContainer.ScrollingAxes = ScrollingAxes;
|
|
||||||
scrollingContainer.ControlPoint = ControlPoint;
|
scrollingContainer.ControlPoint = ControlPoint;
|
||||||
scrollingContainer.VisibleTimeRange.BindTo(VisibleTimeRange);
|
scrollingContainer.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||||
scrollingContainer.RelativeChildOffset = new Vector2((ScrollingAxes & Axes.X) > 0 ? (float)ControlPoint.StartTime : 0, (ScrollingAxes & Axes.Y) > 0 ? (float)ControlPoint.StartTime : 0);
|
|
||||||
|
|
||||||
AddInternal(content = scrollingContainer);
|
AddInternal(content = scrollingContainer);
|
||||||
}
|
}
|
||||||
@ -98,11 +99,6 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
base.Add(drawable);
|
base.Add(drawable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether a <see cref="DrawableHitObject"/> falls within this <see cref="SpeedAdjustmentContainer"/>s affecting timespan.
|
|
||||||
/// </summary>
|
|
||||||
public bool CanContain(DrawableHitObject hitObject) => CanContain(hitObject.HitObject.StartTime);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether a point in time falls within this <see cref="SpeedAdjustmentContainer"/>s affecting timespan.
|
/// Whether a point in time falls within this <see cref="SpeedAdjustmentContainer"/>s affecting timespan.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -112,6 +108,6 @@ namespace osu.Game.Rulesets.Timing
|
|||||||
/// Creates the <see cref="ScrollingContainer"/> which contains the scrolling <see cref="DrawableHitObject"/>s of this container.
|
/// Creates the <see cref="ScrollingContainer"/> which contains the scrolling <see cref="DrawableHitObject"/>s of this container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="ScrollingContainer"/>.</returns>
|
/// <returns>The <see cref="ScrollingContainer"/>.</returns>
|
||||||
protected virtual ScrollingContainer CreateScrollingContainer() => new LinearScrollingContainer(ScrollingAxes, ControlPoint);
|
protected virtual ScrollingContainer CreateScrollingContainer() => new LinearScrollingContainer(ControlPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,16 +139,30 @@ namespace osu.Game.Rulesets.UI
|
|||||||
protected IEnumerable<Mod> Mods;
|
protected IEnumerable<Mod> Mods;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// The <see cref="WorkingBeatmap"/> this <see cref="RulesetContainer{TObject}"/> was created with.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly WorkingBeatmap WorkingBeatmap;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the specified beatmap is assumed to be specific to the current ruleset.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly bool IsForCurrentRuleset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to assume the beatmap passed into this <see cref="RulesetContainer{TObject}"/> is for the current ruleset.
|
||||||
/// Creates a hit renderer for a beatmap.
|
/// Creates a hit renderer for a beatmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ruleset">The ruleset being repesented.</param>
|
/// <param name="ruleset">The ruleset being repesented.</param>
|
||||||
/// <param name="beatmap">The beatmap to create the hit renderer for.</param>
|
/// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param>
|
||||||
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
|
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
|
||||||
internal RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) : base(ruleset)
|
internal RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap, bool isForCurrentRuleset)
|
||||||
|
: base(ruleset)
|
||||||
{
|
{
|
||||||
Debug.Assert(beatmap != null, "RulesetContainer initialized with a null beatmap.");
|
Debug.Assert(workingBeatmap != null, "RulesetContainer initialized with a null beatmap.");
|
||||||
|
|
||||||
Mods = beatmap.Mods.Value;
|
WorkingBeatmap = workingBeatmap;
|
||||||
|
IsForCurrentRuleset = isForCurrentRuleset;
|
||||||
|
Mods = workingBeatmap.Mods.Value;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
@ -156,11 +170,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
BeatmapProcessor<TObject> processor = CreateBeatmapProcessor();
|
BeatmapProcessor<TObject> processor = CreateBeatmapProcessor();
|
||||||
|
|
||||||
// Check if the beatmap can be converted
|
// Check if the beatmap can be converted
|
||||||
if (!converter.CanConvert(beatmap.Beatmap))
|
if (!converter.CanConvert(workingBeatmap.Beatmap))
|
||||||
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can not be converted for the current ruleset (converter: {converter}).");
|
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can not be converted for the current ruleset (converter: {converter}).");
|
||||||
|
|
||||||
// Convert the beatmap
|
// Convert the beatmap
|
||||||
Beatmap = converter.Convert(beatmap.Beatmap, isForCurrentRuleset);
|
Beatmap = converter.Convert(workingBeatmap.Beatmap);
|
||||||
|
|
||||||
// Apply difficulty adjustments from mods before using Difficulty.
|
// Apply difficulty adjustments from mods before using Difficulty.
|
||||||
foreach (var mod in Mods.OfType<IApplicableToDifficulty>())
|
foreach (var mod in Mods.OfType<IApplicableToDifficulty>())
|
||||||
@ -173,8 +187,6 @@ namespace osu.Game.Rulesets.UI
|
|||||||
// Post-process the beatmap
|
// Post-process the beatmap
|
||||||
processor.PostProcess(Beatmap);
|
processor.PostProcess(Beatmap);
|
||||||
|
|
||||||
ApplyBeatmap();
|
|
||||||
|
|
||||||
// Add mods, should always be the last thing applied to give full control to mods
|
// Add mods, should always be the last thing applied to give full control to mods
|
||||||
applyMods(Mods);
|
applyMods(Mods);
|
||||||
}
|
}
|
||||||
@ -192,11 +204,6 @@ namespace osu.Game.Rulesets.UI
|
|||||||
mod.ApplyToRulesetContainer(this);
|
mod.ApplyToRulesetContainer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the beatmap of this hit renderer has been set. Used to apply any default values from the beatmap.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void ApplyBeatmap() { }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a processor to perform post-processing operations
|
/// Creates a processor to perform post-processing operations
|
||||||
/// on HitObjects in converted Beatmaps.
|
/// on HitObjects in converted Beatmaps.
|
||||||
|
@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly BindableBool Reversed = new BindableBool();
|
public readonly BindableBool Reversed = new BindableBool();
|
||||||
|
|
||||||
private readonly Container<SpeedAdjustmentContainer> speedAdjustments;
|
private readonly SortedContainer speedAdjustments;
|
||||||
public IReadOnlyList<SpeedAdjustmentContainer> SpeedAdjustments => speedAdjustments;
|
public IReadOnlyList<SpeedAdjustmentContainer> SpeedAdjustments => speedAdjustments;
|
||||||
|
|
||||||
private readonly SpeedAdjustmentContainer defaultSpeedAdjustment;
|
private readonly SpeedAdjustmentContainer defaultSpeedAdjustment;
|
||||||
@ -166,14 +166,15 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
this.scrollingAxes = scrollingAxes;
|
this.scrollingAxes = scrollingAxes;
|
||||||
|
|
||||||
AddInternal(speedAdjustments = new Container<SpeedAdjustmentContainer> { RelativeSizeAxes = Axes.Both });
|
AddInternal(speedAdjustments = new SortedContainer { RelativeSizeAxes = Axes.Both });
|
||||||
|
|
||||||
// Default speed adjustment
|
// Default speed adjustment
|
||||||
AddSpeedAdjustment(defaultSpeedAdjustment = new SpeedAdjustmentContainer(new MultiplierControlPoint(0)));
|
AddSpeedAdjustment(defaultSpeedAdjustment = new SpeedAdjustmentContainer(new MultiplierControlPoint(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a <see cref="SpeedAdjustmentContainer"/> to this container.
|
/// Adds a <see cref="SpeedAdjustmentContainer"/> to this container, re-sorting all hit objects
|
||||||
|
/// in the last <see cref="SpeedAdjustmentContainer"/> that occurred (time-wise) before it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="speedAdjustment">The <see cref="SpeedAdjustmentContainer"/>.</param>
|
/// <param name="speedAdjustment">The <see cref="SpeedAdjustmentContainer"/>.</param>
|
||||||
public void AddSpeedAdjustment(SpeedAdjustmentContainer speedAdjustment)
|
public void AddSpeedAdjustment(SpeedAdjustmentContainer speedAdjustment)
|
||||||
@ -181,26 +182,27 @@ namespace osu.Game.Rulesets.UI
|
|||||||
speedAdjustment.ScrollingAxes = scrollingAxes;
|
speedAdjustment.ScrollingAxes = scrollingAxes;
|
||||||
speedAdjustment.VisibleTimeRange.BindTo(VisibleTimeRange);
|
speedAdjustment.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||||
speedAdjustment.Reversed.BindTo(Reversed);
|
speedAdjustment.Reversed.BindTo(Reversed);
|
||||||
speedAdjustments.Add(speedAdjustment);
|
|
||||||
|
|
||||||
// We now need to re-sort the hit objects in the last speed adjustment prior to this one, to see if they need a new parent
|
if (speedAdjustments.Count > 0)
|
||||||
var previousSpeedAdjustment = speedAdjustments.LastOrDefault(s => s != speedAdjustment && s.ControlPoint.StartTime <= speedAdjustment.ControlPoint.StartTime);
|
|
||||||
if (previousSpeedAdjustment == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = 0; i < previousSpeedAdjustment.Children.Count; i++)
|
|
||||||
{
|
{
|
||||||
DrawableHitObject hitObject = previousSpeedAdjustment[i];
|
// We need to re-sort all hit objects in the speed adjustment container prior to figure out if they
|
||||||
|
// should now lie within this one
|
||||||
|
var existingAdjustment = adjustmentContainerAt(speedAdjustment.ControlPoint.StartTime);
|
||||||
|
for (int i = 0; i < existingAdjustment.Count; i++)
|
||||||
|
{
|
||||||
|
DrawableHitObject hitObject = existingAdjustment[i];
|
||||||
|
|
||||||
var newSpeedAdjustment = adjustmentContainerFor(hitObject);
|
if (!speedAdjustment.CanContain(hitObject.HitObject.StartTime))
|
||||||
if (newSpeedAdjustment == previousSpeedAdjustment)
|
continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
previousSpeedAdjustment.Remove(hitObject);
|
existingAdjustment.Remove(hitObject);
|
||||||
newSpeedAdjustment.Add(hitObject);
|
speedAdjustment.Add(hitObject);
|
||||||
|
|
||||||
i--;
|
i--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
speedAdjustments.Add(speedAdjustment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -237,27 +239,32 @@ namespace osu.Game.Rulesets.UI
|
|||||||
if (!(hitObject is IScrollingHitObject))
|
if (!(hitObject is IScrollingHitObject))
|
||||||
throw new InvalidOperationException($"Hit objects added to a {nameof(ScrollingHitObjectContainer)} must implement {nameof(IScrollingHitObject)}.");
|
throw new InvalidOperationException($"Hit objects added to a {nameof(ScrollingHitObjectContainer)} must implement {nameof(IScrollingHitObject)}.");
|
||||||
|
|
||||||
adjustmentContainerFor(hitObject).Add(hitObject);
|
adjustmentContainerAt(hitObject.HitObject.StartTime).Add(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Remove(DrawableHitObject hitObject) => speedAdjustments.Any(s => s.Remove(hitObject));
|
public override bool Remove(DrawableHitObject hitObject) => speedAdjustments.Any(s => s.Remove(hitObject));
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds the <see cref="SpeedAdjustmentContainer"/> which provides the speed adjustment active at the start time
|
|
||||||
/// of a hit object. If there is no <see cref="SpeedAdjustmentContainer"/> active at the start time of the hit object,
|
|
||||||
/// then the first (time-wise) speed adjustment is returned.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hitObject">The hit object to find the active <see cref="SpeedAdjustmentContainer"/> for.</param>
|
|
||||||
/// <returns>The <see cref="SpeedAdjustmentContainer"/> active at <paramref name="hitObject"/>'s start time. Null if there are no speed adjustments.</returns>
|
|
||||||
private SpeedAdjustmentContainer adjustmentContainerFor(DrawableHitObject hitObject) => speedAdjustments.LastOrDefault(c => c.CanContain(hitObject)) ?? defaultSpeedAdjustment;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the <see cref="SpeedAdjustmentContainer"/> which provides the speed adjustment active at a time.
|
/// Finds the <see cref="SpeedAdjustmentContainer"/> which provides the speed adjustment active at a time.
|
||||||
/// If there is no <see cref="SpeedAdjustmentContainer"/> active at the time, then the first (time-wise) speed adjustment is returned.
|
/// If there is no <see cref="SpeedAdjustmentContainer"/> active at the time, then the first (time-wise) speed adjustment is returned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The time to find the active <see cref="SpeedAdjustmentContainer"/> at.</param>
|
/// <param name="time">The time to find the active <see cref="SpeedAdjustmentContainer"/> at.</param>
|
||||||
/// <returns>The <see cref="SpeedAdjustmentContainer"/> active at <paramref name="time"/>. Null if there are no speed adjustments.</returns>
|
/// <returns>The <see cref="SpeedAdjustmentContainer"/> active at <paramref name="time"/>. Null if there are no speed adjustments.</returns>
|
||||||
private SpeedAdjustmentContainer adjustmentContainerAt(double time) => speedAdjustments.LastOrDefault(c => c.CanContain(time)) ?? defaultSpeedAdjustment;
|
private SpeedAdjustmentContainer adjustmentContainerAt(double time) => speedAdjustments.FirstOrDefault(c => c.CanContain(time)) ?? defaultSpeedAdjustment;
|
||||||
|
|
||||||
|
private class SortedContainer : Container<SpeedAdjustmentContainer>
|
||||||
|
{
|
||||||
|
protected override int Compare(Drawable x, Drawable y)
|
||||||
|
{
|
||||||
|
var sX = (SpeedAdjustmentContainer)x;
|
||||||
|
var sY = (SpeedAdjustmentContainer)y;
|
||||||
|
|
||||||
|
int result = sY.ControlPoint.StartTime.CompareTo(sX.ControlPoint.StartTime);
|
||||||
|
if (result != 0)
|
||||||
|
return result;
|
||||||
|
return base.Compare(y, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,17 +39,6 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
|
||||||
DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield<TObject, TJudgement> playfield)
|
|
||||||
{
|
|
||||||
playfield.HitObjects.AddSpeedAdjustment(CreateSpeedAdjustmentContainer(controlPoint));
|
|
||||||
playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ApplyBeatmap()
|
|
||||||
{
|
{
|
||||||
// Calculate default multiplier control points
|
// Calculate default multiplier control points
|
||||||
var lastTimingPoint = new TimingControlPoint();
|
var lastTimingPoint = new TimingControlPoint();
|
||||||
@ -95,6 +84,14 @@ namespace osu.Game.Rulesets.UI
|
|||||||
// If we have no control points, add a default one
|
// If we have no control points, add a default one
|
||||||
if (DefaultControlPoints.Count == 0)
|
if (DefaultControlPoints.Count == 0)
|
||||||
DefaultControlPoints.Add(new MultiplierControlPoint());
|
DefaultControlPoints.Add(new MultiplierControlPoint());
|
||||||
|
|
||||||
|
DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySpeedAdjustment(MultiplierControlPoint controlPoint, ScrollingPlayfield<TObject, TJudgement> playfield)
|
||||||
|
{
|
||||||
|
playfield.HitObjects.AddSpeedAdjustment(CreateSpeedAdjustmentContainer(controlPoint));
|
||||||
|
playfield.NestedPlayfields.ForEach(p => applySpeedAdjustment(controlPoint, p));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Loading…
Reference in New Issue
Block a user