mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 15:03:10 +08:00
Merge remote-tracking branch 'upstream/master' into discord-rich-presence
This commit is contained in:
commit
4e1cbadcbd
@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
base.CreateNestedHitObjects();
|
base.CreateNestedHitObjects();
|
||||||
|
|
||||||
var tickSamples = Samples.Select(s => new SampleInfo
|
var tickSamples = Samples.Select(s => new HitSampleInfo
|
||||||
{
|
{
|
||||||
Bank = s.Bank,
|
Bank = s.Bank,
|
||||||
Name = @"slidertick",
|
Name = @"slidertick",
|
||||||
@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public double Distance => Path.Distance;
|
public double Distance => Path.Distance;
|
||||||
|
|
||||||
public List<List<SampleInfo>> NodeSamples { get; set; } = new List<List<SampleInfo>>();
|
public List<List<HitSampleInfo>> NodeSamples { get; set; } = new List<List<HitSampleInfo>>();
|
||||||
|
|
||||||
public double? LegacyLastTickOffset { get; set; }
|
public double? LegacyLastTickOffset { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -255,7 +255,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The time to retrieve the sample info list from.</param>
|
/// <param name="time">The time to retrieve the sample info list from.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private List<SampleInfo> sampleInfoListAt(double time)
|
private List<HitSampleInfo> sampleInfoListAt(double time)
|
||||||
{
|
{
|
||||||
var curveData = HitObject as IHasCurve;
|
var curveData = HitObject as IHasCurve;
|
||||||
|
|
||||||
|
@ -364,7 +364,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP || sample.Name == SampleInfo.HIT_FINISH;
|
bool isDoubleSample(HitSampleInfo sample) => sample.Name == HitSampleInfo.HIT_CLAP || sample.Name == HitSampleInfo.HIT_FINISH;
|
||||||
|
|
||||||
bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
|
bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
|
||||||
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
|
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
|
||||||
@ -443,7 +443,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
noteCount = 0;
|
noteCount = 0;
|
||||||
noteCount = Math.Min(TotalColumns - 1, noteCount);
|
noteCount = Math.Min(TotalColumns - 1, noteCount);
|
||||||
|
|
||||||
bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP);
|
bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == HitSampleInfo.HIT_WHISTLE || s.Name == HitSampleInfo.HIT_FINISH || s.Name == HitSampleInfo.HIT_CLAP);
|
||||||
|
|
||||||
var rowPattern = new Pattern();
|
var rowPattern = new Pattern();
|
||||||
|
|
||||||
@ -472,7 +472,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The time to retrieve the sample info list from.</param>
|
/// <param name="time">The time to retrieve the sample info list from.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private List<SampleInfo> sampleInfoListAt(double time)
|
private List<HitSampleInfo> sampleInfoListAt(double time)
|
||||||
{
|
{
|
||||||
var curveData = HitObject as IHasCurve;
|
var curveData = HitObject as IHasCurve;
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
switch (TotalColumns)
|
switch (TotalColumns)
|
||||||
{
|
{
|
||||||
case 8 when HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000:
|
case 8 when HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000:
|
||||||
addToPattern(pattern, 0, generateHold);
|
addToPattern(pattern, 0, generateHold);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -72,9 +72,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (hold.Head.Samples == null)
|
if (hold.Head.Samples == null)
|
||||||
hold.Head.Samples = new List<SampleInfo>();
|
hold.Head.Samples = new List<HitSampleInfo>();
|
||||||
|
|
||||||
hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL });
|
hold.Head.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_NORMAL });
|
||||||
|
|
||||||
hold.Tail.Samples = HitObject.Samples;
|
hold.Tail.Samples = HitObject.Samples;
|
||||||
|
|
||||||
|
@ -79,9 +79,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
if (!convertType.HasFlag(PatternType.KeepSingle))
|
if (!convertType.HasFlag(PatternType.KeepSingle))
|
||||||
{
|
{
|
||||||
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
|
if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && TotalColumns != 8)
|
||||||
convertType |= PatternType.Mirror;
|
convertType |= PatternType.Mirror;
|
||||||
else if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP))
|
else if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP))
|
||||||
convertType |= PatternType.Gathered;
|
convertType |= PatternType.Gathered;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,7 +263,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this hit object can generate a note in the special column.
|
/// Whether this hit object can generate a note in the special column.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
|
private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a random pattern.
|
/// Generates a random pattern.
|
||||||
@ -364,7 +364,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP))
|
if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP))
|
||||||
p2 = 1;
|
p2 = 1;
|
||||||
|
|
||||||
return GetRandomNoteCount(p2, p3, p4, p5);
|
return GetRandomNoteCount(p2, p3, p4, p5);
|
||||||
|
@ -248,9 +248,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private void createCatmull(int repeats = 0)
|
private void createCatmull(int repeats = 0)
|
||||||
{
|
{
|
||||||
var repeatSamples = new List<List<SampleInfo>>();
|
var repeatSamples = new List<List<HitSampleInfo>>();
|
||||||
for (int i = 0; i < repeats; i++)
|
for (int i = 0; i < repeats; i++)
|
||||||
repeatSamples.Add(new List<SampleInfo>());
|
repeatSamples.Add(new List<HitSampleInfo>());
|
||||||
|
|
||||||
var slider = new Slider
|
var slider = new Slider
|
||||||
{
|
{
|
||||||
@ -270,11 +270,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
addSlider(slider, 3, 1);
|
addSlider(slider, 3, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<List<SampleInfo>> createEmptySamples(int repeats)
|
private List<List<HitSampleInfo>> createEmptySamples(int repeats)
|
||||||
{
|
{
|
||||||
var repeatSamples = new List<List<SampleInfo>>();
|
var repeatSamples = new List<List<HitSampleInfo>>();
|
||||||
for (int i = 0; i < repeats; i++)
|
for (int i = 0; i < repeats; i++)
|
||||||
repeatSamples.Add(new List<SampleInfo>());
|
repeatSamples.Add(new List<HitSampleInfo>());
|
||||||
return repeatSamples;
|
return repeatSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
private readonly ShakeContainer shakeContainer;
|
private readonly ShakeContainer shakeContainer;
|
||||||
|
|
||||||
|
// Must be set to update IsHovered as it's used in relax mdo to detect osu hit objects.
|
||||||
|
public override bool HandlePositionalInput => true;
|
||||||
|
|
||||||
protected DrawableOsuHitObject(OsuHitObject hitObject)
|
protected DrawableOsuHitObject(OsuHitObject hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
|
@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal float LazyTravelDistance;
|
internal float LazyTravelDistance;
|
||||||
|
|
||||||
public List<List<SampleInfo>> NodeSamples { get; set; } = new List<List<SampleInfo>>();
|
public List<List<HitSampleInfo>> NodeSamples { get; set; } = new List<List<HitSampleInfo>>();
|
||||||
|
|
||||||
private int repeatCount;
|
private int repeatCount;
|
||||||
|
|
||||||
@ -157,12 +157,12 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
foreach (var e in
|
foreach (var e in
|
||||||
SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset))
|
SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset))
|
||||||
{
|
{
|
||||||
var firstSample = Samples.Find(s => s.Name == SampleInfo.HIT_NORMAL)
|
var firstSample = Samples.Find(s => s.Name == HitSampleInfo.HIT_NORMAL)
|
||||||
?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
|
?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
|
||||||
var sampleList = new List<SampleInfo>();
|
var sampleList = new List<HitSampleInfo>();
|
||||||
|
|
||||||
if (firstSample != null)
|
if (firstSample != null)
|
||||||
sampleList.Add(new SampleInfo
|
sampleList.Add(new HitSampleInfo
|
||||||
{
|
{
|
||||||
Bank = firstSample.Bank,
|
Bank = firstSample.Bank,
|
||||||
Volume = firstSample.Volume,
|
Volume = firstSample.Volume,
|
||||||
@ -225,7 +225,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SampleInfo> getNodeSamples(int nodeIndex) =>
|
private List<HitSampleInfo> getNodeSamples(int nodeIndex) =>
|
||||||
nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples;
|
nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples;
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new OsuJudgement();
|
public override Judgement CreateJudgement() => new OsuJudgement();
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
{
|
{
|
||||||
typeof(InputDrum),
|
typeof(InputDrum),
|
||||||
typeof(DrumSampleMapping),
|
typeof(DrumSampleMapping),
|
||||||
typeof(SampleInfo),
|
typeof(HitSampleInfo),
|
||||||
typeof(SampleControlPoint)
|
typeof(SampleControlPoint)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Audio
|
|||||||
foreach (var s in samplePoints)
|
foreach (var s in samplePoints)
|
||||||
{
|
{
|
||||||
var centre = s.GetSampleInfo();
|
var centre = s.GetSampleInfo();
|
||||||
var rim = s.GetSampleInfo(SampleInfo.HIT_CLAP);
|
var rim = s.GetSampleInfo(HitSampleInfo.HIT_CLAP);
|
||||||
|
|
||||||
// todo: this is ugly
|
// todo: this is ugly
|
||||||
centre.Namespace = "taiko";
|
centre.Namespace = "taiko";
|
||||||
@ -43,9 +43,9 @@ namespace osu.Game.Rulesets.Taiko.Audio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SkinnableSound addSound(SampleInfo sampleInfo)
|
private SkinnableSound addSound(HitSampleInfo hitSampleInfo)
|
||||||
{
|
{
|
||||||
var drawable = new SkinnableSound(sampleInfo);
|
var drawable = new SkinnableSound(hitSampleInfo);
|
||||||
Sounds.Add(drawable);
|
Sounds.Add(drawable);
|
||||||
return drawable;
|
return drawable;
|
||||||
}
|
}
|
||||||
|
@ -79,9 +79,9 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
var curveData = obj as IHasCurve;
|
var curveData = obj as IHasCurve;
|
||||||
|
|
||||||
// Old osu! used hit sounding to determine various hit type information
|
// Old osu! used hit sounding to determine various hit type information
|
||||||
List<SampleInfo> samples = obj.Samples;
|
List<HitSampleInfo> samples = obj.Samples;
|
||||||
|
|
||||||
bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
|
bool strong = samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
|
||||||
|
|
||||||
if (distanceData != null)
|
if (distanceData != null)
|
||||||
{
|
{
|
||||||
@ -117,15 +117,15 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
|
|
||||||
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
||||||
{
|
{
|
||||||
List<List<SampleInfo>> allSamples = curveData != null ? curveData.NodeSamples : new List<List<SampleInfo>>(new[] { samples });
|
List<List<HitSampleInfo>> allSamples = curveData != null ? curveData.NodeSamples : new List<List<HitSampleInfo>>(new[] { samples });
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
|
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
|
||||||
{
|
{
|
||||||
List<SampleInfo> currentSamples = allSamples[i];
|
List<HitSampleInfo> currentSamples = allSamples[i];
|
||||||
bool isRim = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
|
bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
|
||||||
strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH);
|
strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
|
||||||
|
|
||||||
if (isRim)
|
if (isRim)
|
||||||
{
|
{
|
||||||
@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool isRim = samples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
|
bool isRim = samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
|
||||||
|
|
||||||
if (isRim)
|
if (isRim)
|
||||||
{
|
{
|
||||||
|
@ -9,5 +9,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public override string Description => @"Beats fade out before you hit them!";
|
public override string Description => @"Beats fade out before you hit them!";
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => 1.06;
|
||||||
|
public override bool HasImplementation => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Normal and clap samples are handled by the drum
|
// Normal and clap samples are handled by the drum
|
||||||
protected override IEnumerable<SampleInfo> GetSamples() => HitObject.Samples.Where(s => s.Name != SampleInfo.HIT_NORMAL && s.Name != SampleInfo.HIT_CLAP);
|
protected override IEnumerable<HitSampleInfo> GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP);
|
||||||
|
|
||||||
protected override string SampleNamespace => "Taiko";
|
protected override string SampleNamespace => "Taiko";
|
||||||
|
|
||||||
|
@ -354,14 +354,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.IsNotNull(curveData);
|
Assert.IsNotNull(curveData);
|
||||||
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
|
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
|
||||||
Assert.AreEqual(956, hitObjects[0].StartTime);
|
Assert.AreEqual(956, hitObjects[0].StartTime);
|
||||||
Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
|
Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL));
|
||||||
|
|
||||||
positionData = hitObjects[1] as IHasPosition;
|
positionData = hitObjects[1] as IHasPosition;
|
||||||
|
|
||||||
Assert.IsNotNull(positionData);
|
Assert.IsNotNull(positionData);
|
||||||
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
|
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
|
||||||
Assert.AreEqual(1285, hitObjects[1].StartTime);
|
Assert.AreEqual(1285, hitObjects[1].StartTime);
|
||||||
Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
|
Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,7 +384,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual("soft-hitnormal8", getTestableSampleInfo(hitObjects[4]).LookupNames.First());
|
Assert.AreEqual("soft-hitnormal8", getTestableSampleInfo(hitObjects[4]).LookupNames.First());
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -402,7 +402,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual("normal-hitnormal3", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
|
Assert.AreEqual("normal-hitnormal3", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -422,7 +422,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume);
|
Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -438,34 +438,34 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
var slider1 = (ConvertSlider)hitObjects[0];
|
var slider1 = (ConvertSlider)hitObjects[0];
|
||||||
|
|
||||||
Assert.AreEqual(1, slider1.NodeSamples[0].Count);
|
Assert.AreEqual(1, slider1.NodeSamples[0].Count);
|
||||||
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[0][0].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[0][0].Name);
|
||||||
Assert.AreEqual(1, slider1.NodeSamples[1].Count);
|
Assert.AreEqual(1, slider1.NodeSamples[1].Count);
|
||||||
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[1][0].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[1][0].Name);
|
||||||
Assert.AreEqual(1, slider1.NodeSamples[2].Count);
|
Assert.AreEqual(1, slider1.NodeSamples[2].Count);
|
||||||
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[2][0].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[2][0].Name);
|
||||||
|
|
||||||
var slider2 = (ConvertSlider)hitObjects[1];
|
var slider2 = (ConvertSlider)hitObjects[1];
|
||||||
|
|
||||||
Assert.AreEqual(2, slider2.NodeSamples[0].Count);
|
Assert.AreEqual(2, slider2.NodeSamples[0].Count);
|
||||||
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[0][0].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[0][0].Name);
|
||||||
Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[0][1].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[0][1].Name);
|
||||||
Assert.AreEqual(2, slider2.NodeSamples[1].Count);
|
Assert.AreEqual(2, slider2.NodeSamples[1].Count);
|
||||||
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[1][0].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[1][0].Name);
|
||||||
Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[1][1].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[1][1].Name);
|
||||||
Assert.AreEqual(2, slider2.NodeSamples[2].Count);
|
Assert.AreEqual(2, slider2.NodeSamples[2].Count);
|
||||||
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[2][0].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[2][0].Name);
|
||||||
Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[2][1].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[2][1].Name);
|
||||||
|
|
||||||
var slider3 = (ConvertSlider)hitObjects[2];
|
var slider3 = (ConvertSlider)hitObjects[2];
|
||||||
|
|
||||||
Assert.AreEqual(2, slider3.NodeSamples[0].Count);
|
Assert.AreEqual(2, slider3.NodeSamples[0].Count);
|
||||||
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[0][0].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[0][0].Name);
|
||||||
Assert.AreEqual(SampleInfo.HIT_WHISTLE, slider3.NodeSamples[0][1].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_WHISTLE, slider3.NodeSamples[0][1].Name);
|
||||||
Assert.AreEqual(1, slider3.NodeSamples[1].Count);
|
Assert.AreEqual(1, slider3.NodeSamples[1].Count);
|
||||||
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[1][0].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[1][0].Name);
|
||||||
Assert.AreEqual(2, slider3.NodeSamples[2].Count);
|
Assert.AreEqual(2, slider3.NodeSamples[2].Count);
|
||||||
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[2][0].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[2][0].Name);
|
||||||
Assert.AreEqual(SampleInfo.HIT_CLAP, slider3.NodeSamples[2][1].Name);
|
Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider3.NodeSamples[2][1].Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,14 +101,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
Assert.IsNotNull(curveData);
|
Assert.IsNotNull(curveData);
|
||||||
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
|
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
|
||||||
Assert.AreEqual(956, beatmap.HitObjects[0].StartTime);
|
Assert.AreEqual(956, beatmap.HitObjects[0].StartTime);
|
||||||
Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
|
Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL));
|
||||||
|
|
||||||
positionData = beatmap.HitObjects[1] as IHasPosition;
|
positionData = beatmap.HitObjects[1] as IHasPosition;
|
||||||
|
|
||||||
Assert.IsNotNull(positionData);
|
Assert.IsNotNull(positionData);
|
||||||
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
|
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
|
||||||
Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime);
|
Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime);
|
||||||
Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
|
Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(normal)]
|
[TestCase(normal)]
|
||||||
|
@ -3,19 +3,23 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Online.Chat;
|
using osu.Game.Online.Chat;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Chat;
|
using osu.Game.Overlays.Chat;
|
||||||
|
using osu.Game.Overlays.Chat.Selection;
|
||||||
using osu.Game.Overlays.Chat.Tabs;
|
using osu.Game.Overlays.Chat.Tabs;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Online
|
namespace osu.Game.Tests.Visual.Online
|
||||||
{
|
{
|
||||||
[Description("Testing chat api and overlay")]
|
public class TestSceneChatOverlay : ManualInputManagerTestScene
|
||||||
public class TestSceneChatOverlay : OsuTestScene
|
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
@ -28,17 +32,126 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
typeof(TabCloseButton)
|
typeof(TabCloseButton)
|
||||||
};
|
};
|
||||||
|
|
||||||
[Cached]
|
private TestChatOverlay chatOverlay;
|
||||||
private readonly ChannelManager channelManager = new ChannelManager();
|
private ChannelManager channelManager;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
private readonly Channel channel1 = new Channel(new User()) { Name = "test1" };
|
||||||
private void load()
|
private readonly Channel channel2 = new Channel(new User()) { Name = "test2" };
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Schedule(() =>
|
||||||
{
|
{
|
||||||
channelManager,
|
ChannelManagerContainer container;
|
||||||
new ChatOverlay { State = { Value = Visibility.Visible } }
|
|
||||||
};
|
Child = container = new ChannelManagerContainer(new List<Channel> { channel1, channel2 })
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
};
|
||||||
|
|
||||||
|
chatOverlay = container.ChatOverlay;
|
||||||
|
channelManager = container.ChannelManager;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHideOverlay()
|
||||||
|
{
|
||||||
|
AddAssert("Chat overlay is visible", () => chatOverlay.State.Value == Visibility.Visible);
|
||||||
|
AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
|
||||||
|
|
||||||
|
AddStep("Close chat overlay", () => chatOverlay.Hide());
|
||||||
|
|
||||||
|
AddAssert("Chat overlay was hidden", () => chatOverlay.State.Value == Visibility.Hidden);
|
||||||
|
AddAssert("Channel selection overlay was hidden", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSelectingChannelClosesSelector()
|
||||||
|
{
|
||||||
|
AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
|
||||||
|
|
||||||
|
AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
|
||||||
|
AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
|
||||||
|
|
||||||
|
AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
|
||||||
|
AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCloseChannelWhileSelectorClosed()
|
||||||
|
{
|
||||||
|
AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
|
||||||
|
AddStep("Join channel 2", () => channelManager.JoinChannel(channel2));
|
||||||
|
|
||||||
|
AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2]));
|
||||||
|
AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child));
|
||||||
|
|
||||||
|
AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
|
||||||
|
AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
|
||||||
|
|
||||||
|
AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child));
|
||||||
|
|
||||||
|
AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clickDrawable(Drawable d)
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(d);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ChannelManagerContainer : Container
|
||||||
|
{
|
||||||
|
public TestChatOverlay ChatOverlay { get; private set; }
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
public ChannelManager ChannelManager { get; } = new ChannelManager();
|
||||||
|
|
||||||
|
private readonly List<Channel> channels;
|
||||||
|
|
||||||
|
public ChannelManagerContainer(List<Channel> channels)
|
||||||
|
{
|
||||||
|
this.channels = channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
((BindableList<Channel>)ChannelManager.AvailableChannels).AddRange(channels);
|
||||||
|
|
||||||
|
Child = ChatOverlay = new TestChatOverlay { RelativeSizeAxes = Axes.Both, };
|
||||||
|
ChatOverlay.Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestChatOverlay : ChatOverlay
|
||||||
|
{
|
||||||
|
public Visibility SelectionOverlayState => ChannelSelectionOverlay.State.Value;
|
||||||
|
|
||||||
|
public new ChannelSelectionOverlay ChannelSelectionOverlay => base.ChannelSelectionOverlay;
|
||||||
|
|
||||||
|
protected override ChannelTabControl CreateChannelTabControl() => new TestTabControl();
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<Channel, TabItem<Channel>> TabMap => ((TestTabControl)ChannelTabControl).TabMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestTabControl : ChannelTabControl
|
||||||
|
{
|
||||||
|
protected override TabItem<Channel> CreateTabItem(Channel value) => new TestChannelTabItem(value);
|
||||||
|
|
||||||
|
public new IReadOnlyDictionary<Channel, TabItem<Channel>> TabMap => base.TabMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestChannelTabItem : PrivateChannelTabItem
|
||||||
|
{
|
||||||
|
public TestChannelTabItem(Channel channel)
|
||||||
|
: base(channel)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public new ClickableContainer CloseButton => base.CloseButton;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,9 @@ namespace osu.Game.Tests
|
|||||||
trackStore = audioManager.GetTrackStore(reader);
|
trackStore = audioManager.GetTrackStore(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose();
|
base.Dispose(isDisposing);
|
||||||
stream?.Dispose();
|
stream?.Dispose();
|
||||||
reader?.Dispose();
|
reader?.Dispose();
|
||||||
trackStore?.Dispose();
|
trackStore?.Dispose();
|
||||||
|
70
osu.Game/Audio/HitSampleInfo.cs
Normal file
70
osu.Game/Audio/HitSampleInfo.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace osu.Game.Audio
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Describes a gameplay hit sample.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
public class HitSampleInfo : ISampleInfo
|
||||||
|
{
|
||||||
|
public const string HIT_WHISTLE = @"hitwhistle";
|
||||||
|
public const string HIT_FINISH = @"hitfinish";
|
||||||
|
public const string HIT_NORMAL = @"hitnormal";
|
||||||
|
public const string HIT_CLAP = @"hitclap";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An optional ruleset namespace.
|
||||||
|
/// </summary>
|
||||||
|
public string Namespace;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The bank to load the sample from.
|
||||||
|
/// </summary>
|
||||||
|
public string Bank;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the sample to load.
|
||||||
|
/// </summary>
|
||||||
|
public string Name;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An optional suffix to provide priority lookup. Falls back to non-suffixed <see cref="Name"/>.
|
||||||
|
/// </summary>
|
||||||
|
public string Suffix;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sample volume.
|
||||||
|
/// </summary>
|
||||||
|
public int Volume { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first).
|
||||||
|
/// </summary>
|
||||||
|
public virtual IEnumerable<string> LookupNames
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Namespace))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Suffix))
|
||||||
|
yield return $"{Namespace}/{Bank}-{Name}{Suffix}";
|
||||||
|
|
||||||
|
yield return $"{Namespace}/{Bank}-{Name}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// check non-namespace as a fallback even when we have a namespace
|
||||||
|
if (!string.IsNullOrEmpty(Suffix))
|
||||||
|
yield return $"{Bank}-{Name}{Suffix}";
|
||||||
|
|
||||||
|
yield return $"{Bank}-{Name}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HitSampleInfo Clone() => (HitSampleInfo)MemberwiseClone();
|
||||||
|
}
|
||||||
|
}
|
17
osu.Game/Audio/ISampleInfo.cs
Normal file
17
osu.Game/Audio/ISampleInfo.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace osu.Game.Audio
|
||||||
|
{
|
||||||
|
public interface ISampleInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first).
|
||||||
|
/// </summary>
|
||||||
|
IEnumerable<string> LookupNames { get; }
|
||||||
|
|
||||||
|
int Volume { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,67 +1,24 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace osu.Game.Audio
|
namespace osu.Game.Audio
|
||||||
{
|
{
|
||||||
[Serializable]
|
/// <summary>
|
||||||
public class SampleInfo
|
/// Describes a gameplay sample.
|
||||||
|
/// </summary>
|
||||||
|
public class SampleInfo : ISampleInfo
|
||||||
{
|
{
|
||||||
public const string HIT_WHISTLE = @"hitwhistle";
|
private readonly string sampleName;
|
||||||
public const string HIT_FINISH = @"hitfinish";
|
|
||||||
public const string HIT_NORMAL = @"hitnormal";
|
|
||||||
public const string HIT_CLAP = @"hitclap";
|
|
||||||
|
|
||||||
/// <summary>
|
public SampleInfo(string sampleName)
|
||||||
/// An optional ruleset namespace.
|
|
||||||
/// </summary>
|
|
||||||
public string Namespace;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The bank to load the sample from.
|
|
||||||
/// </summary>
|
|
||||||
public string Bank;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the sample to load.
|
|
||||||
/// </summary>
|
|
||||||
public string Name;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An optional suffix to provide priority lookup. Falls back to non-suffixed <see cref="Name"/>.
|
|
||||||
/// </summary>
|
|
||||||
public string Suffix;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The sample volume.
|
|
||||||
/// </summary>
|
|
||||||
public int Volume;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first).
|
|
||||||
/// </summary>
|
|
||||||
public virtual IEnumerable<string> LookupNames
|
|
||||||
{
|
{
|
||||||
get
|
this.sampleName = sampleName;
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(Namespace))
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(Suffix))
|
|
||||||
yield return $"{Namespace}/{Bank}-{Name}{Suffix}";
|
|
||||||
|
|
||||||
yield return $"{Namespace}/{Bank}-{Name}";
|
|
||||||
}
|
|
||||||
|
|
||||||
// check non-namespace as a fallback even when we have a namespace
|
|
||||||
if (!string.IsNullOrEmpty(Suffix))
|
|
||||||
yield return $"{Bank}-{Name}{Suffix}";
|
|
||||||
|
|
||||||
yield return $"{Bank}-{Name}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SampleInfo Clone() => (SampleInfo)MemberwiseClone();
|
public IEnumerable<string> LookupNames => new[] { sampleName };
|
||||||
|
|
||||||
|
public int Volume { get; set; } = 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Audio;
|
|||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Lists;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
@ -159,6 +160,8 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <param name="beatmap">The beatmap difficulty to restore.</param>
|
/// <param name="beatmap">The beatmap difficulty to restore.</param>
|
||||||
public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);
|
public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);
|
||||||
|
|
||||||
|
private readonly WeakList<WorkingBeatmap> workingCache = new WeakList<WorkingBeatmap>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a <see cref="WorkingBeatmap"/> instance for the provided <see cref="BeatmapInfo"/>
|
/// Retrieve a <see cref="WorkingBeatmap"/> instance for the provided <see cref="BeatmapInfo"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -173,12 +176,18 @@ namespace osu.Game.Beatmaps
|
|||||||
if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
|
if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
|
||||||
return DefaultBeatmap;
|
return DefaultBeatmap;
|
||||||
|
|
||||||
|
var cached = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID);
|
||||||
|
|
||||||
|
if (cached != null)
|
||||||
|
return cached;
|
||||||
|
|
||||||
if (beatmapInfo.Metadata == null)
|
if (beatmapInfo.Metadata == null)
|
||||||
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
|
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
|
||||||
|
|
||||||
WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager);
|
WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager);
|
||||||
|
|
||||||
previous?.TransferTo(working);
|
previous?.TransferTo(working);
|
||||||
|
workingCache.Add(working);
|
||||||
|
|
||||||
return working;
|
return working;
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,8 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// Create a SampleInfo based on the sample settings in this control point.
|
/// Create a SampleInfo based on the sample settings in this control point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sampleName">The name of the same.</param>
|
/// <param name="sampleName">The name of the same.</param>
|
||||||
/// <returns>A populated <see cref="SampleInfo"/>.</returns>
|
/// <returns>A populated <see cref="HitSampleInfo"/>.</returns>
|
||||||
public SampleInfo GetSampleInfo(string sampleName = SampleInfo.HIT_NORMAL) => new SampleInfo
|
public HitSampleInfo GetSampleInfo(string sampleName = HitSampleInfo.HIT_NORMAL) => new HitSampleInfo
|
||||||
{
|
{
|
||||||
Bank = SampleBank,
|
Bank = SampleBank,
|
||||||
Name = sampleName,
|
Name = sampleName,
|
||||||
@ -33,15 +33,15 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies <see cref="SampleBank"/> and <see cref="SampleVolume"/> to a <see cref="SampleInfo"/> if necessary, returning the modified <see cref="SampleInfo"/>.
|
/// Applies <see cref="SampleBank"/> and <see cref="SampleVolume"/> to a <see cref="HitSampleInfo"/> if necessary, returning the modified <see cref="HitSampleInfo"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sampleInfo">The <see cref="SampleInfo"/>. This will not be modified.</param>
|
/// <param name="hitSampleInfo">The <see cref="HitSampleInfo"/>. This will not be modified.</param>
|
||||||
/// <returns>The modified <see cref="SampleInfo"/>. This does not share a reference with <paramref name="sampleInfo"/>.</returns>
|
/// <returns>The modified <see cref="HitSampleInfo"/>. This does not share a reference with <paramref name="hitSampleInfo"/>.</returns>
|
||||||
public virtual SampleInfo ApplyTo(SampleInfo sampleInfo)
|
public virtual HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo)
|
||||||
{
|
{
|
||||||
var newSampleInfo = sampleInfo.Clone();
|
var newSampleInfo = hitSampleInfo.Clone();
|
||||||
newSampleInfo.Bank = sampleInfo.Bank ?? SampleBank;
|
newSampleInfo.Bank = hitSampleInfo.Bank ?? SampleBank;
|
||||||
newSampleInfo.Volume = sampleInfo.Volume > 0 ? sampleInfo.Volume : SampleVolume;
|
newSampleInfo.Volume = hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume;
|
||||||
return newSampleInfo;
|
return newSampleInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,9 +193,9 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
public int CustomSampleBank;
|
public int CustomSampleBank;
|
||||||
|
|
||||||
public override SampleInfo ApplyTo(SampleInfo sampleInfo)
|
public override HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo)
|
||||||
{
|
{
|
||||||
var baseInfo = base.ApplyTo(sampleInfo);
|
var baseInfo = base.ApplyTo(hitSampleInfo);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(baseInfo.Suffix) && CustomSampleBank > 1)
|
if (string.IsNullOrEmpty(baseInfo.Suffix) && CustomSampleBank > 1)
|
||||||
baseInfo.Suffix = CustomSampleBank.ToString();
|
baseInfo.Suffix = CustomSampleBank.ToString();
|
||||||
|
@ -11,7 +11,9 @@ using osu.Framework.IO.File;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Statistics;
|
||||||
using osu.Game.IO.Serialization;
|
using osu.Game.IO.Serialization;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -31,6 +33,8 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
protected AudioManager AudioManager { get; }
|
protected AudioManager AudioManager { get; }
|
||||||
|
|
||||||
|
private static readonly GlobalStatistic<int> total_count = GlobalStatistics.Get<int>(nameof(Beatmaps), $"Total {nameof(WorkingBeatmap)}s");
|
||||||
|
|
||||||
protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager)
|
protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager)
|
||||||
{
|
{
|
||||||
AudioManager = audioManager;
|
AudioManager = audioManager;
|
||||||
@ -38,24 +42,13 @@ namespace osu.Game.Beatmaps
|
|||||||
BeatmapSetInfo = beatmapInfo.BeatmapSet;
|
BeatmapSetInfo = beatmapInfo.BeatmapSet;
|
||||||
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
||||||
|
|
||||||
beatmap = new RecyclableLazy<IBeatmap>(() =>
|
|
||||||
{
|
|
||||||
var b = GetBeatmap() ?? new Beatmap();
|
|
||||||
|
|
||||||
// The original beatmap version needs to be preserved as the database doesn't contain it
|
|
||||||
BeatmapInfo.BeatmapVersion = b.BeatmapInfo.BeatmapVersion;
|
|
||||||
|
|
||||||
// Use the database-backed info for more up-to-date values (beatmap id, ranked status, etc)
|
|
||||||
b.BeatmapInfo = BeatmapInfo;
|
|
||||||
|
|
||||||
return b;
|
|
||||||
});
|
|
||||||
|
|
||||||
track = new RecyclableLazy<Track>(() => GetTrack() ?? GetVirtualTrack());
|
track = new RecyclableLazy<Track>(() => GetTrack() ?? GetVirtualTrack());
|
||||||
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
|
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
|
||||||
waveform = new RecyclableLazy<Waveform>(GetWaveform);
|
waveform = new RecyclableLazy<Waveform>(GetWaveform);
|
||||||
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
|
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
|
||||||
skin = new RecyclableLazy<Skin>(GetSkin);
|
skin = new RecyclableLazy<Skin>(GetSkin);
|
||||||
|
|
||||||
|
total_count.Value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Track GetVirtualTrack()
|
protected virtual Track GetVirtualTrack()
|
||||||
@ -153,10 +146,40 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public override string ToString() => BeatmapInfo.ToString();
|
public override string ToString() => BeatmapInfo.ToString();
|
||||||
|
|
||||||
public bool BeatmapLoaded => beatmap.IsResultAvailable;
|
public bool BeatmapLoaded => beatmapLoadTask?.IsCompleted ?? false;
|
||||||
public IBeatmap Beatmap => beatmap.Value;
|
|
||||||
|
public Task<IBeatmap> LoadBeatmapAsync() => (beatmapLoadTask ?? (beatmapLoadTask = Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
// Todo: Handle cancellation during beatmap parsing
|
||||||
|
var b = GetBeatmap() ?? new Beatmap();
|
||||||
|
|
||||||
|
// The original beatmap version needs to be preserved as the database doesn't contain it
|
||||||
|
BeatmapInfo.BeatmapVersion = b.BeatmapInfo.BeatmapVersion;
|
||||||
|
|
||||||
|
// Use the database-backed info for more up-to-date values (beatmap id, ranked status, etc)
|
||||||
|
b.BeatmapInfo = BeatmapInfo;
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}, beatmapCancellation.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default)));
|
||||||
|
|
||||||
|
public IBeatmap Beatmap
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return LoadBeatmapAsync().Result;
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly CancellationTokenSource beatmapCancellation = new CancellationTokenSource();
|
||||||
protected abstract IBeatmap GetBeatmap();
|
protected abstract IBeatmap GetBeatmap();
|
||||||
private readonly RecyclableLazy<IBeatmap> beatmap;
|
private Task<IBeatmap> beatmapLoadTask;
|
||||||
|
|
||||||
public bool BackgroundLoaded => background.IsResultAvailable;
|
public bool BackgroundLoaded => background.IsResultAvailable;
|
||||||
public Texture Background => background.Value;
|
public Texture Background => background.Value;
|
||||||
@ -195,20 +218,46 @@ namespace osu.Game.Beatmaps
|
|||||||
other.track = track;
|
other.track = track;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Dispose()
|
|
||||||
{
|
|
||||||
background.Recycle();
|
|
||||||
waveform.Recycle();
|
|
||||||
storyboard.Recycle();
|
|
||||||
skin.Recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Eagerly dispose of the audio track associated with this <see cref="WorkingBeatmap"/> (if any).
|
/// Eagerly dispose of the audio track associated with this <see cref="WorkingBeatmap"/> (if any).
|
||||||
/// Accessing track again will load a fresh instance.
|
/// Accessing track again will load a fresh instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void RecycleTrack() => track.Recycle();
|
public virtual void RecycleTrack() => track.Recycle();
|
||||||
|
|
||||||
|
#region Disposal
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isDisposed;
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
if (isDisposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
isDisposed = true;
|
||||||
|
|
||||||
|
// recycling logic is not here for the time being, as components which use
|
||||||
|
// retrieved objects from WorkingBeatmap may not hold a reference to the WorkingBeatmap itself.
|
||||||
|
// this should be fine as each retrieved component do have their own finalizers.
|
||||||
|
|
||||||
|
// cancelling the beatmap load is safe for now since the retrieval is a synchronous
|
||||||
|
// operation. if we add an async retrieval method this may need to be reconsidered.
|
||||||
|
beatmapCancellation.Cancel();
|
||||||
|
total_count.Value--;
|
||||||
|
}
|
||||||
|
|
||||||
|
~WorkingBeatmap()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
public class RecyclableLazy<T>
|
public class RecyclableLazy<T>
|
||||||
{
|
{
|
||||||
private Lazy<T> lazy;
|
private Lazy<T> lazy;
|
||||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Microsoft.EntityFrameworkCore.Storage;
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
|
using osu.Framework.Statistics;
|
||||||
|
|
||||||
namespace osu.Game.Database
|
namespace osu.Game.Database
|
||||||
{
|
{
|
||||||
@ -31,11 +32,20 @@ namespace osu.Game.Database
|
|||||||
recycleThreadContexts();
|
recycleThreadContexts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly GlobalStatistic<int> reads = GlobalStatistics.Get<int>("Database", "Get (Read)");
|
||||||
|
private static readonly GlobalStatistic<int> writes = GlobalStatistics.Get<int>("Database", "Get (Write)");
|
||||||
|
private static readonly GlobalStatistic<int> commits = GlobalStatistics.Get<int>("Database", "Commits");
|
||||||
|
private static readonly GlobalStatistic<int> rollbacks = GlobalStatistics.Get<int>("Database", "Rollbacks");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a context for the current thread for read-only usage.
|
/// Get a context for the current thread for read-only usage.
|
||||||
/// If a <see cref="DatabaseWriteUsage"/> is in progress, the existing write-safe context will be returned.
|
/// If a <see cref="DatabaseWriteUsage"/> is in progress, the existing write-safe context will be returned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public OsuDbContext Get() => threadContexts.Value;
|
public OsuDbContext Get()
|
||||||
|
{
|
||||||
|
reads.Value++;
|
||||||
|
return threadContexts.Value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context).
|
/// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context).
|
||||||
@ -45,6 +55,7 @@ namespace osu.Game.Database
|
|||||||
/// <returns>A usage containing a usable context.</returns>
|
/// <returns>A usage containing a usable context.</returns>
|
||||||
public DatabaseWriteUsage GetForWrite(bool withTransaction = true)
|
public DatabaseWriteUsage GetForWrite(bool withTransaction = true)
|
||||||
{
|
{
|
||||||
|
writes.Value++;
|
||||||
Monitor.Enter(writeLock);
|
Monitor.Enter(writeLock);
|
||||||
OsuDbContext context;
|
OsuDbContext context;
|
||||||
|
|
||||||
@ -90,9 +101,15 @@ namespace osu.Game.Database
|
|||||||
if (usages == 0)
|
if (usages == 0)
|
||||||
{
|
{
|
||||||
if (currentWriteDidError)
|
if (currentWriteDidError)
|
||||||
|
{
|
||||||
|
rollbacks.Value++;
|
||||||
currentWriteTransaction?.Rollback();
|
currentWriteTransaction?.Rollback();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
commits.Value++;
|
||||||
currentWriteTransaction?.Commit();
|
currentWriteTransaction?.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
if (currentWriteDidWrite || currentWriteDidError)
|
if (currentWriteDidWrite || currentWriteDidError)
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
|
using osu.Framework.Statistics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
@ -34,6 +35,8 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
private static readonly Lazy<OsuDbLoggerFactory> logger = new Lazy<OsuDbLoggerFactory>(() => new OsuDbLoggerFactory());
|
private static readonly Lazy<OsuDbLoggerFactory> logger = new Lazy<OsuDbLoggerFactory>(() => new OsuDbLoggerFactory());
|
||||||
|
|
||||||
|
private static readonly GlobalStatistic<int> contexts = GlobalStatistics.Get<int>("Database", "Contexts");
|
||||||
|
|
||||||
static OsuDbContext()
|
static OsuDbContext()
|
||||||
{
|
{
|
||||||
// required to initialise native SQLite libraries on some platforms.
|
// required to initialise native SQLite libraries on some platforms.
|
||||||
@ -76,6 +79,8 @@ namespace osu.Game.Database
|
|||||||
connection.Close();
|
connection.Close();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contexts.Value++;
|
||||||
}
|
}
|
||||||
|
|
||||||
~OsuDbContext()
|
~OsuDbContext()
|
||||||
@ -85,6 +90,20 @@ namespace osu.Game.Database
|
|||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool isDisposed;
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
if (isDisposed) return;
|
||||||
|
|
||||||
|
isDisposed = true;
|
||||||
|
|
||||||
|
base.Dispose();
|
||||||
|
|
||||||
|
contexts.Value--;
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
base.OnConfiguring(optionsBuilder);
|
base.OnConfiguring(optionsBuilder);
|
||||||
|
@ -6,23 +6,28 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Graphics.Transforms;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Backgrounds
|
namespace osu.Game.Graphics.Backgrounds
|
||||||
{
|
{
|
||||||
public class Background : BufferedContainer
|
/// <summary>
|
||||||
|
/// A background which offers blurring via a <see cref="BufferedContainer"/> on demand.
|
||||||
|
/// </summary>
|
||||||
|
public class Background : CompositeDrawable
|
||||||
{
|
{
|
||||||
public Sprite Sprite;
|
public Sprite Sprite;
|
||||||
|
|
||||||
private readonly string textureName;
|
private readonly string textureName;
|
||||||
|
|
||||||
|
private BufferedContainer bufferedContainer;
|
||||||
|
|
||||||
public Background(string textureName = @"")
|
public Background(string textureName = @"")
|
||||||
{
|
{
|
||||||
CacheDrawnFrameBuffer = true;
|
|
||||||
|
|
||||||
this.textureName = textureName;
|
this.textureName = textureName;
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
Add(Sprite = new Sprite
|
AddInternal(Sprite = new Sprite
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -37,5 +42,28 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
if (!string.IsNullOrEmpty(textureName))
|
if (!string.IsNullOrEmpty(textureName))
|
||||||
Sprite.Texture = textures.Get(textureName);
|
Sprite.Texture = textures.Get(textureName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector2 BlurSigma => bufferedContainer?.BlurSigma ?? Vector2.Zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Smoothly adjusts <see cref="IBufferedContainer.BlurSigma"/> over time.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||||
|
public void BlurTo(Vector2 newBlurSigma, double duration = 0, Easing easing = Easing.None)
|
||||||
|
{
|
||||||
|
if (bufferedContainer == null)
|
||||||
|
{
|
||||||
|
RemoveInternal(Sprite);
|
||||||
|
|
||||||
|
AddInternal(bufferedContainer = new BufferedContainer
|
||||||
|
{
|
||||||
|
CacheDrawnFrameBuffer = true,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = Sprite
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferedContainer.BlurTo(newBlurSigma, duration, easing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,6 +295,10 @@ namespace osu.Game
|
|||||||
var nextBeatmap = beatmap.NewValue;
|
var nextBeatmap = beatmap.NewValue;
|
||||||
if (nextBeatmap?.Track != null)
|
if (nextBeatmap?.Track != null)
|
||||||
nextBeatmap.Track.Completed += currentTrackCompleted;
|
nextBeatmap.Track.Completed += currentTrackCompleted;
|
||||||
|
|
||||||
|
beatmap.OldValue?.Dispose();
|
||||||
|
|
||||||
|
nextBeatmap?.LoadBeatmapAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void currentTrackCompleted()
|
private void currentTrackCompleted()
|
||||||
|
@ -154,6 +154,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera"));
|
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera"));
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Light"));
|
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Light"));
|
||||||
|
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Medium"));
|
||||||
|
|
||||||
runMigrations();
|
runMigrations();
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@ namespace osu.Game.Overlays.Chat.Selection
|
|||||||
private readonly SearchTextBox search;
|
private readonly SearchTextBox search;
|
||||||
private readonly SearchContainer<ChannelSection> sectionsFlow;
|
private readonly SearchContainer<ChannelSection> sectionsFlow;
|
||||||
|
|
||||||
|
protected override bool DimMainContent => false;
|
||||||
|
|
||||||
public Action<Channel> OnRequestJoin;
|
public Action<Channel> OnRequestJoin;
|
||||||
public Action<Channel> OnRequestLeave;
|
public Action<Channel> OnRequestLeave;
|
||||||
|
|
||||||
|
@ -49,6 +49,8 @@ namespace osu.Game.Overlays.Chat.Tabs
|
|||||||
// performTabSort might've made selectorTab's position wonky, fix it
|
// performTabSort might've made selectorTab's position wonky, fix it
|
||||||
TabContainer.SetLayoutPosition(selectorTab, float.MaxValue);
|
TabContainer.SetLayoutPosition(selectorTab, float.MaxValue);
|
||||||
|
|
||||||
|
((ChannelTabItem)item).OnRequestClose += tabCloseRequested;
|
||||||
|
|
||||||
base.AddTabItem(item, addToDropdown);
|
base.AddTabItem(item, addToDropdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,10 +59,10 @@ namespace osu.Game.Overlays.Chat.Tabs
|
|||||||
switch (value.Type)
|
switch (value.Type)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
return new ChannelTabItem(value) { OnRequestClose = tabCloseRequested };
|
return new ChannelTabItem(value);
|
||||||
|
|
||||||
case ChannelType.PM:
|
case ChannelType.PM:
|
||||||
return new PrivateChannelTabItem(value) { OnRequestClose = tabCloseRequested };
|
return new PrivateChannelTabItem(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,9 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
public const float TAB_AREA_HEIGHT = 50;
|
public const float TAB_AREA_HEIGHT = 50;
|
||||||
|
|
||||||
private ChannelTabControl channelTabControl;
|
protected ChannelTabControl ChannelTabControl;
|
||||||
|
|
||||||
|
protected virtual ChannelTabControl CreateChannelTabControl() => new ChannelTabControl();
|
||||||
|
|
||||||
private Container chatContainer;
|
private Container chatContainer;
|
||||||
private TabsArea tabsArea;
|
private TabsArea tabsArea;
|
||||||
@ -55,9 +57,10 @@ namespace osu.Game.Overlays
|
|||||||
public Bindable<double> ChatHeight { get; set; }
|
public Bindable<double> ChatHeight { get; set; }
|
||||||
|
|
||||||
private Container channelSelectionContainer;
|
private Container channelSelectionContainer;
|
||||||
private ChannelSelectionOverlay channelSelectionOverlay;
|
protected ChannelSelectionOverlay ChannelSelectionOverlay;
|
||||||
|
|
||||||
public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || (channelSelectionOverlay.State.Value == Visibility.Visible && channelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos));
|
public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos)
|
||||||
|
|| (ChannelSelectionOverlay.State.Value == Visibility.Visible && ChannelSelectionOverlay.ReceivePositionalInputAt(screenSpacePos));
|
||||||
|
|
||||||
public ChatOverlay()
|
public ChatOverlay()
|
||||||
{
|
{
|
||||||
@ -81,7 +84,7 @@ namespace osu.Game.Overlays
|
|||||||
Masking = true,
|
Masking = true,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
channelSelectionOverlay = new ChannelSelectionOverlay
|
ChannelSelectionOverlay = new ChannelSelectionOverlay
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
@ -154,31 +157,25 @@ namespace osu.Game.Overlays
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4.Black,
|
Colour = Color4.Black,
|
||||||
},
|
},
|
||||||
channelTabControl = new ChannelTabControl
|
ChannelTabControl = CreateChannelTabControl().With(d =>
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
d.Anchor = Anchor.BottomLeft;
|
||||||
Origin = Anchor.BottomLeft,
|
d.Origin = Anchor.BottomLeft;
|
||||||
RelativeSizeAxes = Axes.Both,
|
d.RelativeSizeAxes = Axes.Both;
|
||||||
OnRequestLeave = channelManager.LeaveChannel
|
d.OnRequestLeave = channelManager.LeaveChannel;
|
||||||
},
|
}),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
channelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue;
|
ChannelTabControl.Current.ValueChanged += current => channelManager.CurrentChannel.Value = current.NewValue;
|
||||||
channelTabControl.ChannelSelectorActive.ValueChanged += active => channelSelectionOverlay.State.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden;
|
ChannelTabControl.ChannelSelectorActive.ValueChanged += active => ChannelSelectionOverlay.State.Value = active.NewValue ? Visibility.Visible : Visibility.Hidden;
|
||||||
channelSelectionOverlay.State.ValueChanged += state =>
|
ChannelSelectionOverlay.State.ValueChanged += state =>
|
||||||
{
|
{
|
||||||
if (state.NewValue == Visibility.Hidden && channelManager.JoinedChannels.Count == 0)
|
// Propagate the visibility state to ChannelSelectorActive
|
||||||
{
|
ChannelTabControl.ChannelSelectorActive.Value = state.NewValue == Visibility.Visible;
|
||||||
channelSelectionOverlay.Show();
|
|
||||||
Hide();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
channelTabControl.ChannelSelectorActive.Value = state.NewValue == Visibility.Visible;
|
|
||||||
|
|
||||||
if (state.NewValue == Visibility.Visible)
|
if (state.NewValue == Visibility.Visible)
|
||||||
{
|
{
|
||||||
@ -190,8 +187,8 @@ namespace osu.Game.Overlays
|
|||||||
textbox.HoldFocus = true;
|
textbox.HoldFocus = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
channelSelectionOverlay.OnRequestJoin = channel => channelManager.JoinChannel(channel);
|
ChannelSelectionOverlay.OnRequestJoin = channel => channelManager.JoinChannel(channel);
|
||||||
channelSelectionOverlay.OnRequestLeave = channelManager.LeaveChannel;
|
ChannelSelectionOverlay.OnRequestLeave = channelManager.LeaveChannel;
|
||||||
|
|
||||||
ChatHeight = config.GetBindable<double>(OsuSetting.ChatDisplayHeight);
|
ChatHeight = config.GetBindable<double>(OsuSetting.ChatDisplayHeight);
|
||||||
ChatHeight.ValueChanged += height =>
|
ChatHeight.ValueChanged += height =>
|
||||||
@ -217,11 +214,11 @@ namespace osu.Game.Overlays
|
|||||||
channelManager.JoinedChannels.ItemsAdded += onChannelAddedToJoinedChannels;
|
channelManager.JoinedChannels.ItemsAdded += onChannelAddedToJoinedChannels;
|
||||||
channelManager.JoinedChannels.ItemsRemoved += onChannelRemovedFromJoinedChannels;
|
channelManager.JoinedChannels.ItemsRemoved += onChannelRemovedFromJoinedChannels;
|
||||||
foreach (Channel channel in channelManager.JoinedChannels)
|
foreach (Channel channel in channelManager.JoinedChannels)
|
||||||
channelTabControl.AddChannel(channel);
|
ChannelTabControl.AddChannel(channel);
|
||||||
|
|
||||||
channelManager.AvailableChannels.ItemsAdded += availableChannelsChanged;
|
channelManager.AvailableChannels.ItemsAdded += availableChannelsChanged;
|
||||||
channelManager.AvailableChannels.ItemsRemoved += availableChannelsChanged;
|
channelManager.AvailableChannels.ItemsRemoved += availableChannelsChanged;
|
||||||
channelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels);
|
ChannelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels);
|
||||||
|
|
||||||
currentChannel = channelManager.CurrentChannel.GetBoundCopy();
|
currentChannel = channelManager.CurrentChannel.GetBoundCopy();
|
||||||
currentChannel.BindValueChanged(currentChannelChanged, true);
|
currentChannel.BindValueChanged(currentChannelChanged, true);
|
||||||
@ -236,7 +233,7 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
textbox.Current.Disabled = true;
|
textbox.Current.Disabled = true;
|
||||||
currentChannelContainer.Clear(false);
|
currentChannelContainer.Clear(false);
|
||||||
channelSelectionOverlay.Show();
|
ChannelSelectionOverlay.Show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,8 +242,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
textbox.Current.Disabled = e.NewValue.ReadOnly;
|
textbox.Current.Disabled = e.NewValue.ReadOnly;
|
||||||
|
|
||||||
if (channelTabControl.Current.Value != e.NewValue)
|
if (ChannelTabControl.Current.Value != e.NewValue)
|
||||||
Scheduler.Add(() => channelTabControl.Current.Value = e.NewValue);
|
Scheduler.Add(() => ChannelTabControl.Current.Value = e.NewValue);
|
||||||
|
|
||||||
var loaded = loadedChannels.Find(d => d.Channel == e.NewValue);
|
var loaded = loadedChannels.Find(d => d.Channel == e.NewValue);
|
||||||
|
|
||||||
@ -294,7 +291,7 @@ namespace osu.Game.Overlays
|
|||||||
double targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y;
|
double targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y;
|
||||||
|
|
||||||
// If the channel selection screen is shown, mind its minimum height
|
// If the channel selection screen is shown, mind its minimum height
|
||||||
if (channelSelectionOverlay.State.Value == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height)
|
if (ChannelSelectionOverlay.State.Value == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height)
|
||||||
targetChatHeight = 1f - channel_selection_min_height;
|
targetChatHeight = 1f - channel_selection_min_height;
|
||||||
|
|
||||||
ChatHeight.Value = targetChatHeight;
|
ChatHeight.Value = targetChatHeight;
|
||||||
@ -311,9 +308,9 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private void selectTab(int index)
|
private void selectTab(int index)
|
||||||
{
|
{
|
||||||
var channel = channelTabControl.Items.Skip(index).FirstOrDefault();
|
var channel = ChannelTabControl.Items.Skip(index).FirstOrDefault();
|
||||||
if (channel != null && !(channel is ChannelSelectorTabItem.ChannelSelectorTabChannel))
|
if (channel != null && !(channel is ChannelSelectorTabItem.ChannelSelectorTabChannel))
|
||||||
channelTabControl.Current.Value = channel;
|
ChannelTabControl.Current.Value = channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
@ -358,6 +355,7 @@ namespace osu.Game.Overlays
|
|||||||
this.FadeIn(transition_length, Easing.OutQuint);
|
this.FadeIn(transition_length, Easing.OutQuint);
|
||||||
|
|
||||||
textbox.HoldFocus = true;
|
textbox.HoldFocus = true;
|
||||||
|
|
||||||
base.PopIn();
|
base.PopIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,7 +364,7 @@ namespace osu.Game.Overlays
|
|||||||
this.MoveToY(Height, transition_length, Easing.InSine);
|
this.MoveToY(Height, transition_length, Easing.InSine);
|
||||||
this.FadeOut(transition_length, Easing.InSine);
|
this.FadeOut(transition_length, Easing.InSine);
|
||||||
|
|
||||||
channelSelectionOverlay.Hide();
|
ChannelSelectionOverlay.Hide();
|
||||||
|
|
||||||
textbox.HoldFocus = false;
|
textbox.HoldFocus = false;
|
||||||
base.PopOut();
|
base.PopOut();
|
||||||
@ -375,20 +373,20 @@ namespace osu.Game.Overlays
|
|||||||
private void onChannelAddedToJoinedChannels(IEnumerable<Channel> channels)
|
private void onChannelAddedToJoinedChannels(IEnumerable<Channel> channels)
|
||||||
{
|
{
|
||||||
foreach (Channel channel in channels)
|
foreach (Channel channel in channels)
|
||||||
channelTabControl.AddChannel(channel);
|
ChannelTabControl.AddChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onChannelRemovedFromJoinedChannels(IEnumerable<Channel> channels)
|
private void onChannelRemovedFromJoinedChannels(IEnumerable<Channel> channels)
|
||||||
{
|
{
|
||||||
foreach (Channel channel in channels)
|
foreach (Channel channel in channels)
|
||||||
{
|
{
|
||||||
channelTabControl.RemoveChannel(channel);
|
ChannelTabControl.RemoveChannel(channel);
|
||||||
loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel));
|
loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void availableChannelsChanged(IEnumerable<Channel> channels)
|
private void availableChannelsChanged(IEnumerable<Channel> channels)
|
||||||
=> channelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels);
|
=> ChannelSelectionOverlay.UpdateAvailableChannels(channelManager.AvailableChannels);
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
|
@ -66,24 +66,64 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Header.Tabs.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch);
|
Header.Tabs.Current.ValueChanged += _ => queueUpdate();
|
||||||
|
|
||||||
Filter.Tabs.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch);
|
Filter.Tabs.Current.ValueChanged += _ => queueUpdate();
|
||||||
|
|
||||||
Filter.DisplayStyleControl.DisplayStyle.ValueChanged += style => recreatePanels(style.NewValue);
|
Filter.DisplayStyleControl.DisplayStyle.ValueChanged += style => recreatePanels(style.NewValue);
|
||||||
Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => Scheduler.AddOnce(updateSearch);
|
Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += _ => queueUpdate();
|
||||||
|
|
||||||
|
currentQuery.BindTo(Filter.Search.Current);
|
||||||
currentQuery.ValueChanged += query =>
|
currentQuery.ValueChanged += query =>
|
||||||
{
|
{
|
||||||
queryChangedDebounce?.Cancel();
|
queryChangedDebounce?.Cancel();
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(query.NewValue))
|
if (string.IsNullOrEmpty(query.NewValue))
|
||||||
Scheduler.AddOnce(updateSearch);
|
queueUpdate();
|
||||||
else
|
else
|
||||||
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500);
|
queryChangedDebounce = Scheduler.AddDelayed(updateSearch, 500);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
currentQuery.BindTo(Filter.Search.Current);
|
private APIRequest getUsersRequest;
|
||||||
|
|
||||||
|
private readonly Bindable<string> currentQuery = new Bindable<string>();
|
||||||
|
|
||||||
|
private ScheduledDelegate queryChangedDebounce;
|
||||||
|
|
||||||
|
private void queueUpdate() => Scheduler.AddOnce(updateSearch);
|
||||||
|
|
||||||
|
private void updateSearch()
|
||||||
|
{
|
||||||
|
queryChangedDebounce?.Cancel();
|
||||||
|
|
||||||
|
if (!IsLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Users = null;
|
||||||
|
clearPanels();
|
||||||
|
loading.Hide();
|
||||||
|
getUsersRequest?.Cancel();
|
||||||
|
|
||||||
|
if (API?.IsLoggedIn != true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (Header.Tabs.Current.Value)
|
||||||
|
{
|
||||||
|
case SocialTab.Friends:
|
||||||
|
var friendRequest = new GetFriendsRequest(); // TODO filter arguments?
|
||||||
|
friendRequest.Success += updateUsers;
|
||||||
|
API.Queue(getUsersRequest = friendRequest);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var userRequest = new GetUsersRequest(); // TODO filter arguments!
|
||||||
|
userRequest.Success += response => updateUsers(response.Select(r => r.User));
|
||||||
|
API.Queue(getUsersRequest = userRequest);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recreatePanels(PanelDisplayStyle displayStyle)
|
private void recreatePanels(PanelDisplayStyle displayStyle)
|
||||||
@ -133,45 +173,6 @@ namespace osu.Game.Overlays
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private APIRequest getUsersRequest;
|
|
||||||
|
|
||||||
private readonly Bindable<string> currentQuery = new Bindable<string>();
|
|
||||||
|
|
||||||
private ScheduledDelegate queryChangedDebounce;
|
|
||||||
|
|
||||||
private void updateSearch()
|
|
||||||
{
|
|
||||||
queryChangedDebounce?.Cancel();
|
|
||||||
|
|
||||||
if (!IsLoaded)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Users = null;
|
|
||||||
clearPanels();
|
|
||||||
loading.Hide();
|
|
||||||
getUsersRequest?.Cancel();
|
|
||||||
|
|
||||||
if (API?.IsLoggedIn != true)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (Header.Tabs.Current.Value)
|
|
||||||
{
|
|
||||||
case SocialTab.Friends:
|
|
||||||
var friendRequest = new GetFriendsRequest(); // TODO filter arguments?
|
|
||||||
friendRequest.Success += updateUsers;
|
|
||||||
API.Queue(getUsersRequest = friendRequest);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
var userRequest = new GetUsersRequest(); // TODO filter arguments!
|
|
||||||
userRequest.Success += response => updateUsers(response.Select(r => r.User));
|
|
||||||
API.Queue(getUsersRequest = userRequest);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
loading.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateUsers(IEnumerable<User> newUsers)
|
private void updateUsers(IEnumerable<User> newUsers)
|
||||||
{
|
{
|
||||||
Users = newUsers;
|
Users = newUsers;
|
||||||
@ -193,7 +194,7 @@ namespace osu.Game.Overlays
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case APIState.Online:
|
case APIState.Online:
|
||||||
Scheduler.AddOnce(updateSearch);
|
queueUpdate();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
|
|
||||||
protected SkinnableSound Samples;
|
protected SkinnableSound Samples;
|
||||||
|
|
||||||
protected virtual IEnumerable<SampleInfo> GetSamples() => HitObject.Samples;
|
protected virtual IEnumerable<HitSampleInfo> GetSamples() => HitObject.Samples;
|
||||||
|
|
||||||
private readonly Lazy<List<DrawableHitObject>> nestedHitObjects = new Lazy<List<DrawableHitObject>>();
|
private readonly Lazy<List<DrawableHitObject>> nestedHitObjects = new Lazy<List<DrawableHitObject>>();
|
||||||
public IEnumerable<DrawableHitObject> NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : Enumerable.Empty<DrawableHitObject>();
|
public IEnumerable<DrawableHitObject> NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : Enumerable.Empty<DrawableHitObject>();
|
||||||
@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
|
|
||||||
var samples = GetSamples().ToArray();
|
var samples = GetSamples().ToArray();
|
||||||
|
|
||||||
if (samples.Any())
|
if (samples.Length > 0)
|
||||||
{
|
{
|
||||||
if (HitObject.SampleControlPoint == null)
|
if (HitObject.SampleControlPoint == null)
|
||||||
throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
|
throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual double StartTime { get; set; }
|
public virtual double StartTime { get; set; }
|
||||||
|
|
||||||
private List<SampleInfo> samples;
|
private List<HitSampleInfo> samples;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The samples to be played when this hit object is hit.
|
/// The samples to be played when this hit object is hit.
|
||||||
@ -38,9 +38,9 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// and can be treated as the default samples for the hit object.
|
/// and can be treated as the default samples for the hit object.
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<SampleInfo> Samples
|
public List<HitSampleInfo> Samples
|
||||||
{
|
{
|
||||||
get => samples ?? (samples = new List<SampleInfo>());
|
get => samples ?? (samples = new List<HitSampleInfo>());
|
||||||
set => samples = value;
|
set => samples = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<SampleInfo>> nodeSamples)
|
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<HitSampleInfo>> nodeSamples)
|
||||||
{
|
{
|
||||||
newCombo |= forceNewCombo;
|
newCombo |= forceNewCombo;
|
||||||
comboOffset += extraComboOffset;
|
comboOffset += extraComboOffset;
|
||||||
|
@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate the final per-node samples
|
// Generate the final per-node samples
|
||||||
var nodeSamples = new List<List<SampleInfo>>(nodes);
|
var nodeSamples = new List<List<HitSampleInfo>>(nodes);
|
||||||
for (int i = 0; i < nodes; i++)
|
for (int i = 0; i < nodes; i++)
|
||||||
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
|
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
|
||||||
|
|
||||||
@ -291,7 +291,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
/// <param name="repeatCount">The slider repeat count.</param>
|
/// <param name="repeatCount">The slider repeat count.</param>
|
||||||
/// <param name="nodeSamples">The samples to be played when the slider nodes are hit. This includes the head and tail of the slider.</param>
|
/// <param name="nodeSamples">The samples to be played when the slider nodes are hit. This includes the head and tail of the slider.</param>
|
||||||
/// <returns>The hit object.</returns>
|
/// <returns>The hit object.</returns>
|
||||||
protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<SampleInfo>> nodeSamples);
|
protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<HitSampleInfo>> nodeSamples);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a legacy Spinner-type hit object.
|
/// Creates a legacy Spinner-type hit object.
|
||||||
@ -312,14 +312,14 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
/// <param name="endTime">The hold end time.</param>
|
/// <param name="endTime">The hold end time.</param>
|
||||||
protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime);
|
protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime);
|
||||||
|
|
||||||
private List<SampleInfo> convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
|
private List<HitSampleInfo> convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
|
||||||
{
|
{
|
||||||
// Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario
|
// Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario
|
||||||
if (!string.IsNullOrEmpty(bankInfo.Filename))
|
if (!string.IsNullOrEmpty(bankInfo.Filename))
|
||||||
{
|
{
|
||||||
return new List<SampleInfo>
|
return new List<HitSampleInfo>
|
||||||
{
|
{
|
||||||
new FileSampleInfo
|
new FileHitSampleInfo
|
||||||
{
|
{
|
||||||
Filename = bankInfo.Filename,
|
Filename = bankInfo.Filename,
|
||||||
Volume = bankInfo.Volume
|
Volume = bankInfo.Volume
|
||||||
@ -327,12 +327,12 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var soundTypes = new List<SampleInfo>
|
var soundTypes = new List<HitSampleInfo>
|
||||||
{
|
{
|
||||||
new LegacySampleInfo
|
new LegacyHitSampleInfo
|
||||||
{
|
{
|
||||||
Bank = bankInfo.Normal,
|
Bank = bankInfo.Normal,
|
||||||
Name = SampleInfo.HIT_NORMAL,
|
Name = HitSampleInfo.HIT_NORMAL,
|
||||||
Volume = bankInfo.Volume,
|
Volume = bankInfo.Volume,
|
||||||
CustomSampleBank = bankInfo.CustomSampleBank
|
CustomSampleBank = bankInfo.CustomSampleBank
|
||||||
}
|
}
|
||||||
@ -340,10 +340,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
if (type.HasFlag(LegacySoundType.Finish))
|
if (type.HasFlag(LegacySoundType.Finish))
|
||||||
{
|
{
|
||||||
soundTypes.Add(new LegacySampleInfo
|
soundTypes.Add(new LegacyHitSampleInfo
|
||||||
{
|
{
|
||||||
Bank = bankInfo.Add,
|
Bank = bankInfo.Add,
|
||||||
Name = SampleInfo.HIT_FINISH,
|
Name = HitSampleInfo.HIT_FINISH,
|
||||||
Volume = bankInfo.Volume,
|
Volume = bankInfo.Volume,
|
||||||
CustomSampleBank = bankInfo.CustomSampleBank
|
CustomSampleBank = bankInfo.CustomSampleBank
|
||||||
});
|
});
|
||||||
@ -351,10 +351,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
if (type.HasFlag(LegacySoundType.Whistle))
|
if (type.HasFlag(LegacySoundType.Whistle))
|
||||||
{
|
{
|
||||||
soundTypes.Add(new LegacySampleInfo
|
soundTypes.Add(new LegacyHitSampleInfo
|
||||||
{
|
{
|
||||||
Bank = bankInfo.Add,
|
Bank = bankInfo.Add,
|
||||||
Name = SampleInfo.HIT_WHISTLE,
|
Name = HitSampleInfo.HIT_WHISTLE,
|
||||||
Volume = bankInfo.Volume,
|
Volume = bankInfo.Volume,
|
||||||
CustomSampleBank = bankInfo.CustomSampleBank
|
CustomSampleBank = bankInfo.CustomSampleBank
|
||||||
});
|
});
|
||||||
@ -362,10 +362,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
if (type.HasFlag(LegacySoundType.Clap))
|
if (type.HasFlag(LegacySoundType.Clap))
|
||||||
{
|
{
|
||||||
soundTypes.Add(new LegacySampleInfo
|
soundTypes.Add(new LegacyHitSampleInfo
|
||||||
{
|
{
|
||||||
Bank = bankInfo.Add,
|
Bank = bankInfo.Add,
|
||||||
Name = SampleInfo.HIT_CLAP,
|
Name = HitSampleInfo.HIT_CLAP,
|
||||||
Volume = bankInfo.Volume,
|
Volume = bankInfo.Volume,
|
||||||
CustomSampleBank = bankInfo.CustomSampleBank
|
CustomSampleBank = bankInfo.CustomSampleBank
|
||||||
});
|
});
|
||||||
@ -387,7 +387,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
|
public SampleBankInfo Clone() => (SampleBankInfo)MemberwiseClone();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LegacySampleInfo : SampleInfo
|
private class LegacyHitSampleInfo : HitSampleInfo
|
||||||
{
|
{
|
||||||
public int CustomSampleBank
|
public int CustomSampleBank
|
||||||
{
|
{
|
||||||
@ -399,7 +399,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FileSampleInfo : SampleInfo
|
private class FileHitSampleInfo : HitSampleInfo
|
||||||
{
|
{
|
||||||
public string Filename;
|
public string Filename;
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
public double Distance => Path.Distance;
|
public double Distance => Path.Distance;
|
||||||
|
|
||||||
public List<List<SampleInfo>> NodeSamples { get; set; }
|
public List<List<HitSampleInfo>> NodeSamples { get; set; }
|
||||||
public int RepeatCount { get; set; }
|
public int RepeatCount { get; set; }
|
||||||
|
|
||||||
public double EndTime => StartTime + this.SpanCount() * Distance / Velocity;
|
public double EndTime => StartTime + this.SpanCount() * Distance / Velocity;
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<SampleInfo>> nodeSamples)
|
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<HitSampleInfo>> nodeSamples)
|
||||||
{
|
{
|
||||||
return new ConvertSlider
|
return new ConvertSlider
|
||||||
{
|
{
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<SampleInfo>> nodeSamples)
|
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<HitSampleInfo>> nodeSamples)
|
||||||
{
|
{
|
||||||
newCombo |= forceNewCombo;
|
newCombo |= forceNewCombo;
|
||||||
comboOffset += extraComboOffset;
|
comboOffset += extraComboOffset;
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
|
|||||||
return new ConvertHit();
|
return new ConvertHit();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<SampleInfo>> nodeSamples)
|
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<HitSampleInfo>> nodeSamples)
|
||||||
{
|
{
|
||||||
return new ConvertSlider
|
return new ConvertSlider
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Objects.Types
|
|||||||
/// n-1: The last repeat.<br />
|
/// n-1: The last repeat.<br />
|
||||||
/// n: The last node.
|
/// n: The last node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
List<List<SampleInfo>> NodeSamples { get; }
|
List<List<HitSampleInfo>> NodeSamples { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class HasRepeatsExtensions
|
public static class HasRepeatsExtensions
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
private Background background;
|
private Background background;
|
||||||
|
|
||||||
private int currentDisplay;
|
private int currentDisplay;
|
||||||
private const int background_count = 5;
|
private const int background_count = 7;
|
||||||
|
|
||||||
private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}";
|
private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}";
|
||||||
|
|
||||||
|
@ -24,11 +24,13 @@ using osu.Game.Screens.Edit.Design;
|
|||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit
|
namespace osu.Game.Screens.Edit
|
||||||
{
|
{
|
||||||
public class Editor : OsuScreen
|
public class Editor : OsuScreen, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
|
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
|
||||||
|
|
||||||
@ -206,6 +208,20 @@ namespace osu.Game.Screens.Edit
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(GlobalAction action)
|
||||||
|
{
|
||||||
|
if (action == GlobalAction.Back)
|
||||||
|
{
|
||||||
|
// as we don't want to display the back button, manual handling of exit action is required.
|
||||||
|
this.Exit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(GlobalAction action) => action == GlobalAction.Back;
|
||||||
|
|
||||||
public override void OnResuming(IScreen last)
|
public override void OnResuming(IScreen last)
|
||||||
{
|
{
|
||||||
Beatmap.Value.Track?.Stop();
|
Beatmap.Value.Track?.Stop();
|
||||||
|
@ -86,6 +86,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
if (!resuming)
|
if (!resuming)
|
||||||
{
|
{
|
||||||
Beatmap.Value = introBeatmap;
|
Beatmap.Value = introBeatmap;
|
||||||
|
introBeatmap = null;
|
||||||
|
|
||||||
if (menuVoice.Value)
|
if (menuVoice.Value)
|
||||||
welcome.Play();
|
welcome.Play();
|
||||||
@ -94,7 +95,10 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu.
|
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu.
|
||||||
if (menuMusic.Value)
|
if (menuMusic.Value)
|
||||||
|
{
|
||||||
track.Start();
|
track.Start();
|
||||||
|
track = null;
|
||||||
|
}
|
||||||
|
|
||||||
LoadComponentAsync(mainMenu = new MainMenu());
|
LoadComponentAsync(mainMenu = new MainMenu());
|
||||||
|
|
||||||
|
@ -96,13 +96,13 @@ namespace osu.Game.Screens.Menu
|
|||||||
var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null;
|
var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null;
|
||||||
var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null;
|
var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null;
|
||||||
|
|
||||||
float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes ?? new float[256];
|
float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes;
|
||||||
|
|
||||||
for (int i = 0; i < bars_per_visualiser; i++)
|
for (int i = 0; i < bars_per_visualiser; i++)
|
||||||
{
|
{
|
||||||
if (track?.IsRunning ?? false)
|
if (track?.IsRunning ?? false)
|
||||||
{
|
{
|
||||||
float targetAmplitude = temporalAmplitudes[(i + indexOffset) % bars_per_visualiser] * (effect?.KiaiMode == true ? 1 : 0.5f);
|
float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f);
|
||||||
if (targetAmplitude > frequencyAmplitudes[i])
|
if (targetAmplitude > frequencyAmplitudes[i])
|
||||||
frequencyAmplitudes[i] = targetAmplitude;
|
frequencyAmplitudes[i] = targetAmplitude;
|
||||||
}
|
}
|
||||||
@ -115,7 +115,6 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
|
|
||||||
indexOffset = (indexOffset + index_change) % bars_per_visualiser;
|
indexOffset = (indexOffset + index_change) % bars_per_visualiser;
|
||||||
Scheduler.AddDelayed(updateAmplitudes, time_between_updates);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateColour()
|
private void updateColour()
|
||||||
@ -131,7 +130,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
updateAmplitudes();
|
|
||||||
|
Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
42
osu.Game/Screens/Play/ComboEffects.cs
Normal file
42
osu.Game/Screens/Play/ComboEffects.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play
|
||||||
|
{
|
||||||
|
public class ComboEffects : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly ScoreProcessor processor;
|
||||||
|
|
||||||
|
private SkinnableSound comboBreakSample;
|
||||||
|
|
||||||
|
public ComboEffects(ScoreProcessor processor)
|
||||||
|
{
|
||||||
|
this.processor = processor;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
InternalChild = comboBreakSample = new SkinnableSound(new SampleInfo("combobreak"));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
processor.Combo.BindValueChanged(onComboChange, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onComboChange(ValueChangedEvent<int> combo)
|
||||||
|
{
|
||||||
|
if (combo.NewValue == 0 && combo.OldValue > 20)
|
||||||
|
comboBreakSample?.Play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -127,7 +127,11 @@ namespace osu.Game.Screens.Play
|
|||||||
Child = new LocalSkinOverrideContainer(working.Skin)
|
Child = new LocalSkinOverrideContainer(working.Skin)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = DrawableRuleset
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
DrawableRuleset,
|
||||||
|
new ComboEffects(ScoreProcessor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
|
new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
|
||||||
|
@ -11,7 +11,6 @@ using osu.Game.Configuration;
|
|||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
@ -64,35 +63,29 @@ namespace osu.Game.Screens.Select
|
|||||||
public IEnumerable<BeatmapSetInfo> BeatmapSets
|
public IEnumerable<BeatmapSetInfo> BeatmapSets
|
||||||
{
|
{
|
||||||
get => beatmapSets.Select(g => g.BeatmapSet);
|
get => beatmapSets.Select(g => g.BeatmapSet);
|
||||||
set => loadBeatmapSets(() => value);
|
set => loadBeatmapSets(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadBeatmapSetsFromManager(BeatmapManager manager) => loadBeatmapSets(manager.GetAllUsableBeatmapSetsEnumerable);
|
private void loadBeatmapSets(IEnumerable<BeatmapSetInfo> beatmapSets)
|
||||||
|
|
||||||
private void loadBeatmapSets(Func<IEnumerable<BeatmapSetInfo>> beatmapSets)
|
|
||||||
{
|
{
|
||||||
CarouselRoot newRoot = new CarouselRoot(this);
|
CarouselRoot newRoot = new CarouselRoot(this);
|
||||||
|
|
||||||
Task.Run(() =>
|
beatmapSets.Select(createCarouselSet).Where(g => g != null).ForEach(newRoot.AddChild);
|
||||||
{
|
newRoot.Filter(activeCriteria);
|
||||||
beatmapSets().Select(createCarouselSet).Where(g => g != null).ForEach(newRoot.AddChild);
|
|
||||||
newRoot.Filter(activeCriteria);
|
|
||||||
|
|
||||||
// preload drawables as the ctor overhead is quite high currently.
|
// preload drawables as the ctor overhead is quite high currently.
|
||||||
var _ = newRoot.Drawables;
|
var _ = newRoot.Drawables;
|
||||||
}).ContinueWith(_ => Schedule(() =>
|
|
||||||
{
|
|
||||||
root = newRoot;
|
|
||||||
scrollableContent.Clear(false);
|
|
||||||
itemsCache.Invalidate();
|
|
||||||
scrollPositionCache.Invalidate();
|
|
||||||
|
|
||||||
Schedule(() =>
|
root = newRoot;
|
||||||
{
|
scrollableContent.Clear(false);
|
||||||
BeatmapSetsChanged?.Invoke();
|
itemsCache.Invalidate();
|
||||||
BeatmapSetsLoaded = true;
|
scrollPositionCache.Invalidate();
|
||||||
});
|
|
||||||
}));
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
BeatmapSetsChanged?.Invoke();
|
||||||
|
BeatmapSetsLoaded = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<float> yPositions = new List<float>();
|
private readonly List<float> yPositions = new List<float>();
|
||||||
@ -125,13 +118,15 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(permitNulls: true)]
|
[BackgroundDependencyLoader(permitNulls: true)]
|
||||||
private void load(OsuConfigManager config)
|
private void load(OsuConfigManager config, BeatmapManager beatmaps)
|
||||||
{
|
{
|
||||||
config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm);
|
config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm);
|
||||||
config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled);
|
config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled);
|
||||||
|
|
||||||
RightClickScrollingEnabled.ValueChanged += enabled => RightMouseScrollbar = enabled.NewValue;
|
RightClickScrollingEnabled.ValueChanged += enabled => RightMouseScrollbar = enabled.NewValue;
|
||||||
RightClickScrollingEnabled.TriggerChange();
|
RightClickScrollingEnabled.TriggerChange();
|
||||||
|
|
||||||
|
loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
|
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
|
||||||
|
@ -244,8 +244,6 @@ namespace osu.Game.Screens.Select
|
|||||||
sampleChangeBeatmap = audio.Samples.Get(@"SongSelect/select-expand");
|
sampleChangeBeatmap = audio.Samples.Get(@"SongSelect/select-expand");
|
||||||
SampleConfirm = audio.Samples.Get(@"SongSelect/confirm-selection");
|
SampleConfirm = audio.Samples.Get(@"SongSelect/confirm-selection");
|
||||||
|
|
||||||
Carousel.LoadBeatmapSetsFromManager(this.beatmaps);
|
|
||||||
|
|
||||||
if (dialogOverlay != null)
|
if (dialogOverlay != null)
|
||||||
{
|
{
|
||||||
Schedule(() =>
|
Schedule(() =>
|
||||||
|
@ -81,7 +81,10 @@ namespace osu.Game.Skinning
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var texture = GetTexture(componentName);
|
// temporary allowance is given for skins the fact that stable handles non-animatable items such as hitcircles (incorrectly)
|
||||||
|
// by (incorrectly) displaying the first frame of animation rather than the non-animated version.
|
||||||
|
// users have used this to "hide" certain elements like hit300.
|
||||||
|
var texture = GetTexture($"{componentName}-0") ?? GetTexture(componentName);
|
||||||
|
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
return null;
|
return null;
|
||||||
|
@ -23,6 +23,7 @@ namespace osu.Game.Skinning
|
|||||||
private readonly Bindable<bool> beatmapHitsounds = new Bindable<bool>();
|
private readonly Bindable<bool> beatmapHitsounds = new Bindable<bool>();
|
||||||
|
|
||||||
private readonly ISkin skin;
|
private readonly ISkin skin;
|
||||||
|
|
||||||
private ISkinSource fallbackSource;
|
private ISkinSource fallbackSource;
|
||||||
|
|
||||||
public LocalSkinOverrideContainer(ISkin skin)
|
public LocalSkinOverrideContainer(ISkin skin)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
@ -13,14 +14,19 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
public class SkinnableSound : SkinReloadableDrawable
|
public class SkinnableSound : SkinReloadableDrawable
|
||||||
{
|
{
|
||||||
private readonly SampleInfo[] samples;
|
private readonly ISampleInfo[] hitSamples;
|
||||||
private SampleChannel[] channels;
|
private SampleChannel[] channels;
|
||||||
|
|
||||||
private AudioManager audio;
|
private AudioManager audio;
|
||||||
|
|
||||||
public SkinnableSound(params SampleInfo[] samples)
|
public SkinnableSound(IEnumerable<ISampleInfo> hitSamples)
|
||||||
{
|
{
|
||||||
this.samples = samples;
|
this.hitSamples = hitSamples.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkinnableSound(ISampleInfo hitSamples)
|
||||||
|
{
|
||||||
|
this.hitSamples = new[] { hitSamples };
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -35,7 +41,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||||
{
|
{
|
||||||
channels = samples.Select(s =>
|
channels = hitSamples.Select(s =>
|
||||||
{
|
{
|
||||||
var ch = loadChannel(s, skin.GetSample);
|
var ch = loadChannel(s, skin.GetSample);
|
||||||
if (ch == null && allowFallback)
|
if (ch == null && allowFallback)
|
||||||
@ -44,7 +50,7 @@ namespace osu.Game.Skinning
|
|||||||
}).Where(c => c != null).ToArray();
|
}).Where(c => c != null).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SampleChannel loadChannel(SampleInfo info, Func<string, SampleChannel> getSampleFunction)
|
private SampleChannel loadChannel(ISampleInfo info, Func<string, SampleChannel> getSampleFunction)
|
||||||
{
|
{
|
||||||
foreach (var lookup in info.LookupNames)
|
foreach (var lookup in info.LookupNames)
|
||||||
{
|
{
|
||||||
|
@ -137,9 +137,9 @@ namespace osu.Game.Tests.Visual
|
|||||||
track = audio?.Tracks.GetVirtual(length);
|
track = audio?.Tracks.GetVirtual(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
base.Dispose();
|
base.Dispose(isDisposing);
|
||||||
store?.Dispose();
|
store?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.4" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.4" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.609.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2019.628.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2019.703.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.23.0" />
|
<PackageReference Include="SharpCompress" Version="0.23.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
@ -104,9 +104,9 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.609.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2019.628.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2019.703.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.628.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.703.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.22.0" />
|
<PackageReference Include="SharpCompress" Version="0.22.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user