mirror of
https://github.com/SK-la/Ez2Lazer.git
synced 2026-03-13 11:20:28 +00:00
[同步更新]
This commit is contained in:
@@ -300,7 +300,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
Description = "Affects how early fruits fade in on the screen.",
|
||||
AdditionalMetrics =
|
||||
[
|
||||
new RulesetBeatmapAttribute.AdditionalMetric("Fade-in time", LocalisableString.Interpolate($@"{IBeatmapDifficultyInfo.DifficultyRange(effectiveDifficulty.ApproachRate, CatchHitObject.PREEMPT_RANGE):#,0.##} ms"))
|
||||
new RulesetBeatmapAttribute.AdditionalMetric("Fade-in time", LocalisableString.Interpolate($@"{IBeatmapDifficultyInfo.DifficultyRangeInt(effectiveDifficulty.ApproachRate, CatchHitObject.PREEMPT_RANGE):#,0.##} ms"))
|
||||
]
|
||||
};
|
||||
yield return new RulesetBeatmapAttribute(SongSelectStrings.HPDrain, @"HP", originalDifficulty.DrainRate, effectiveDifficulty.DrainRate, 10)
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, PREEMPT_RANGE);
|
||||
TimePreempt = IBeatmapDifficultyInfo.DifficultyRangeInt(difficulty.ApproachRate, PREEMPT_RANGE);
|
||||
|
||||
Scale = LegacyRulesetExtensions.CalculateScaleFromCircleSize(difficulty.CircleSize);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
typeof(ManiaModFadeIn)
|
||||
}).ToArray();
|
||||
|
||||
public override bool Ranked => false;
|
||||
public override bool Ranked => true;
|
||||
|
||||
public override bool ValidForFreestyleAsRequiredMod => false;
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase("multi-segment-slider")]
|
||||
[TestCase("nan-slider")]
|
||||
[TestCase("1124896")]
|
||||
[TestCase("1341554")]
|
||||
[TestCase("2593923")]
|
||||
[TestCase("801165")]
|
||||
public void Test(string name) => base.Test(name);
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,941 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
AudioLeadIn: 0
|
||||
PreviewTime: 76429
|
||||
Countdown: 0
|
||||
SampleSet: Soft
|
||||
StackLeniency: 0.2
|
||||
Mode: 0
|
||||
LetterboxInBreaks: 0
|
||||
WidescreenStoryboard: 1
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:5
|
||||
CircleSize:4.3
|
||||
OverallDifficulty:8
|
||||
ApproachRate:9.3
|
||||
SliderMultiplier:2.99999995231628
|
||||
SliderTickRate:1
|
||||
|
||||
[Events]
|
||||
//Background and Video events
|
||||
//Break Periods
|
||||
//Storyboard Layer 0 (Background)
|
||||
//Storyboard Layer 1 (Fail)
|
||||
//Storyboard Layer 2 (Pass)
|
||||
//Storyboard Layer 3 (Foreground)
|
||||
//Storyboard Sound Samples
|
||||
|
||||
[TimingPoints]
|
||||
763,444.444444444444,4,2,1,60,1,0
|
||||
763,-111.111111111111,4,2,1,60,0,0
|
||||
1929,-100,4,2,1,5,0,0
|
||||
1985,-100,4,2,1,60,0,0
|
||||
2040,-100,4,2,1,5,0,0
|
||||
2096,-153.846153846153,4,2,1,60,0,0
|
||||
2429,-133.333333333333,4,2,1,5,0,0
|
||||
2540,-71.4285714285714,4,2,1,70,0,1
|
||||
2985,-100,4,2,1,70,0,1
|
||||
4485,-100,4,2,1,5,0,1
|
||||
4540,-100,4,2,1,70,0,1
|
||||
4707,-100,4,2,1,5,0,1
|
||||
4762,-100,4,2,1,70,0,1
|
||||
4929,-100,4,2,1,5,0,1
|
||||
4985,-100,4,2,1,70,0,1
|
||||
5096,-83.3333333333333,4,2,1,70,0,1
|
||||
5429,-133.333333333333,4,2,1,70,0,1
|
||||
5596,-133.333333333333,4,2,1,70,0,1
|
||||
5652,-133.333333333333,4,2,1,70,0,1
|
||||
5818,-133.333333333333,4,2,1,70,0,1
|
||||
5874,-133.333333333333,4,2,1,70,0,1
|
||||
6040,-133.333333333333,4,2,1,70,0,1
|
||||
6096,-100,4,2,1,70,0,1
|
||||
6540,-100,4,2,1,70,0,1
|
||||
8040,-100,4,2,1,5,0,1
|
||||
8096,-100,4,2,1,70,0,1
|
||||
8262,-100,4,2,1,5,0,1
|
||||
8318,-100,4,2,1,70,0,1
|
||||
8485,-100,4,2,1,5,0,1
|
||||
8540,-133.333333333333,4,2,1,70,0,1
|
||||
8874,-100,4,2,1,5,0,1
|
||||
8985,-100,4,2,1,70,0,1
|
||||
9651,-100,4,2,1,70,0,1
|
||||
10096,-100,4,2,1,70,0,1
|
||||
11596,-100,4,2,1,5,0,1
|
||||
11651,-100,4,2,1,70,0,1
|
||||
11818,-100,4,2,1,5,0,1
|
||||
11873,-100,4,2,1,70,0,1
|
||||
11874,-80,4,2,1,70,0,1
|
||||
12040,-80,4,2,1,5,0,1
|
||||
12096,-80,4,2,1,70,0,1
|
||||
12207,-133.333333333333,4,2,1,70,0,1
|
||||
12429,-100,4,2,1,5,0,1
|
||||
12540,-100,4,2,1,70,0,1
|
||||
12707,-100,4,2,1,70,0,1
|
||||
12763,-100,4,2,1,70,0,1
|
||||
12929,-100,4,2,1,70,0,1
|
||||
12985,-100,4,2,1,70,0,1
|
||||
13429,-300,4,2,1,70,0,1
|
||||
13651,-83.3333333333333,4,2,1,70,0,1
|
||||
13874,-100,4,2,1,70,0,1
|
||||
15151,-100,4,2,1,5,0,1
|
||||
15207,-100,4,2,1,70,0,1
|
||||
15373,-100,4,2,1,5,0,1
|
||||
15429,-100,4,2,1,70,0,1
|
||||
15596,-100,4,2,1,5,0,1
|
||||
15651,-100,4,2,1,70,0,1
|
||||
15985,-100,4,2,1,5,0,1
|
||||
16096,-100,4,2,1,70,0,1
|
||||
16262,-100,4,2,1,70,0,1
|
||||
16318,-83.3333333333333,4,2,1,70,0,1
|
||||
16651,-100,4,2,1,70,0,1
|
||||
16762,-133.333333333333,4,2,1,60,0,0
|
||||
17096,-133.333333333333,4,2,1,5,0,0
|
||||
17207,-200,4,2,1,60,0,0
|
||||
18096,-66.6666666666667,4,2,1,60,0,0
|
||||
18262,-66.6666666666667,4,2,1,5,0,0
|
||||
18318,-66.6666666666667,4,2,1,60,0,0
|
||||
18540,-100,4,2,1,60,0,0
|
||||
18874,-100,4,2,1,5,0,0
|
||||
18985,-100,4,2,1,60,0,0
|
||||
19985,-100,4,2,1,60,0,0
|
||||
20485,-100,4,2,1,5,0,0
|
||||
20540,-100,4,2,1,60,0,0
|
||||
20707,-100,4,2,1,5,0,0
|
||||
20762,-200,4,2,1,60,0,0
|
||||
20985,-100,4,2,1,60,0,0
|
||||
21095,-100,4,2,1,60,0,0
|
||||
21374,-100,4,2,1,5,0,0
|
||||
21429,-100,4,2,1,60,0,0
|
||||
21596,-100,4,2,1,5,0,0
|
||||
21651,-100,4,2,1,60,0,0
|
||||
21818,-100,4,2,1,5,0,0
|
||||
21874,-66.6666666666667,4,2,1,60,0,0
|
||||
21985,-100,4,2,1,60,0,0
|
||||
22096,-100,4,2,1,60,0,0
|
||||
22985,-200,4,2,1,60,0,0
|
||||
23318,-100,4,2,1,60,0,0
|
||||
23429,-100,4,2,1,60,0,0
|
||||
23540,-100,4,2,1,60,0,0
|
||||
23651,-100,4,2,1,60,0,0
|
||||
23762,-100,4,2,1,60,0,0
|
||||
23874,-133.333333333333,4,2,1,60,0,0
|
||||
24208,-133.333333333333,4,2,1,5,0,0
|
||||
24318,-200,4,2,1,5,0,0
|
||||
24319,-200,4,2,1,60,0,0
|
||||
24540,-100,4,2,1,60,0,0
|
||||
24651,-66.6666666666667,4,2,1,60,0,0
|
||||
24874,-100,4,2,1,60,0,0
|
||||
25374,-100,4,2,1,5,0,0
|
||||
25429,-100,4,2,1,60,0,0
|
||||
27096,-100,4,2,1,60,0,0
|
||||
27596,-100,4,2,1,5,0,0
|
||||
27651,-100,4,2,1,60,0,0
|
||||
27818,-100,4,2,1,5,0,0
|
||||
27873,-133.333333333333,4,2,1,60,0,0
|
||||
28096,-100,4,2,1,60,0,0
|
||||
28206,-100,4,2,1,60,0,0
|
||||
28485,-100,4,2,1,5,0,0
|
||||
28540,-100,4,2,1,60,0,0
|
||||
28707,-100,4,2,1,5,0,0
|
||||
28762,-100,4,2,1,60,0,0
|
||||
28929,-100,4,2,1,5,0,0
|
||||
28985,-66.6666666666667,4,2,1,60,0,0
|
||||
29151,-100,4,2,1,60,0,0
|
||||
29207,-100,4,2,1,60,0,0
|
||||
29651,-100,4,2,1,60,0,0
|
||||
30429,-100,4,2,1,60,0,0
|
||||
30540,-58.8235294117647,4,2,1,60,0,0
|
||||
30874,-58.8235294117647,4,2,1,5,0,0
|
||||
30985,-58.8235294117647,4,2,1,60,0,0
|
||||
31040,-100,4,2,1,5,0,0
|
||||
31429,-100,4,2,1,60,0,0
|
||||
32485,-100,4,2,1,60,0,0
|
||||
32540,-100,4,2,1,60,0,0
|
||||
32707,-100,4,2,1,60,0,0
|
||||
32762,-100,4,2,1,60,0,0
|
||||
32985,-100,4,2,1,60,0,0
|
||||
34318,-50,4,2,1,60,0,0
|
||||
34485,-100,4,2,1,5,0,0
|
||||
34540,-100,4,2,1,60,0,0
|
||||
35151,-100,4,2,1,5,0,0
|
||||
35207,-100,4,2,1,60,0,0
|
||||
35374,-100,4,2,1,5,0,0
|
||||
35430,-100,4,2,1,60,0,0
|
||||
35818,-100,4,2,1,5,0,0
|
||||
35874,-200,4,2,1,60,0,0
|
||||
36429,-100,4,2,1,60,0,0
|
||||
37818,-100,4,2,1,5,0,0
|
||||
37874,-100,4,2,1,60,0,0
|
||||
38040,-100,4,2,1,5,0,0
|
||||
38096,-50,4,2,1,60,0,0
|
||||
38151,-100,4,2,1,5,0,0
|
||||
38540,-100,4,2,1,60,0,0
|
||||
39596,-100,4,2,1,5,0,0
|
||||
39651,-100,4,2,1,60,0,0
|
||||
39818,-100,4,2,1,60,0,0
|
||||
39873,-100,4,2,1,60,0,0
|
||||
40096,-100,4,2,1,60,0,0
|
||||
41429,-50,4,2,1,60,0,0
|
||||
41596,-100,4,2,1,5,0,0
|
||||
41651,-100,4,2,1,60,0,0
|
||||
41818,-100,4,2,1,5,0,0
|
||||
41874,-100,4,2,1,60,0,0
|
||||
42040,-100,4,2,1,5,0,0
|
||||
42096,-100,4,2,1,60,0,0
|
||||
44318,-100,4,2,1,60,0,0
|
||||
44762,-83.3333333333333,4,2,1,60,0,0
|
||||
45207,-66.6666666666667,4,2,1,45,0,0
|
||||
45651,-133.333333333333,4,2,1,45,0,0
|
||||
51540,-133.333333333333,4,2,1,50,0,0
|
||||
51651,-133.333333333333,4,2,1,45,0,0
|
||||
52318,-133.333333333333,4,2,1,45,0,0
|
||||
58540,-76.9230769230769,4,2,1,45,0,0
|
||||
58818,-100,4,2,1,45,0,0
|
||||
58874,-111.111111111111,4,2,1,45,0,0
|
||||
59318,-111.111111111111,4,2,1,45,0,0
|
||||
59429,-83.3333333333333,4,2,1,60,0,0
|
||||
59540,-83.3333333333333,4,2,1,5,0,0
|
||||
59874,-100,4,2,1,60,0,0
|
||||
60096,-100,4,2,1,5,0,0
|
||||
60207,-100,4,2,1,60,0,0
|
||||
60707,-100,4,2,1,5,0,0
|
||||
60763,-100,4,2,1,60,0,0
|
||||
60818,-100,4,2,1,5,0,0
|
||||
60874,-100,4,2,1,60,0,0
|
||||
60929,-100,4,2,1,5,0,0
|
||||
60985,-100,4,2,1,60,0,0
|
||||
61040,-100,4,2,1,5,0,0
|
||||
61096,-100,4,2,1,60,0,0
|
||||
61151,-100,4,2,1,5,0,0
|
||||
61207,-100,4,2,1,60,0,0
|
||||
61596,-100,4,2,1,5,0,0
|
||||
61651,-100,4,2,1,60,0,0
|
||||
61762,-83.3333333333333,4,2,1,60,0,0
|
||||
61985,-100,4,2,1,5,0,0
|
||||
62096,-100,4,2,1,60,0,0
|
||||
62151,-100,4,2,1,5,0,0
|
||||
62207,-100,4,2,1,60,0,0
|
||||
62262,-100,4,2,1,5,0,0
|
||||
62318,-100,4,2,1,60,0,0
|
||||
62374,-100,4,2,1,5,0,0
|
||||
62430,-100,4,2,1,60,0,0
|
||||
62485,-100,4,2,1,5,0,0
|
||||
62540,-100,4,2,1,60,0,0
|
||||
62596,-100,4,2,1,5,0,0
|
||||
62651,-100,4,2,1,60,0,0
|
||||
62707,-100,4,2,1,5,0,0
|
||||
62762,-100,4,2,1,60,0,0
|
||||
62818,-100,4,2,1,5,0,0
|
||||
62874,-100,4,2,1,60,0,0
|
||||
62929,-100,4,2,1,60,0,0
|
||||
62930,-100,4,2,1,5,0,0
|
||||
62985,-100,4,2,1,60,0,0
|
||||
63707,-100,4,2,1,5,0,0
|
||||
63762,-100,4,2,1,60,0,0
|
||||
64262,-100,4,2,1,5,0,0
|
||||
64318,-100,4,2,1,60,0,0
|
||||
64485,-100,4,2,1,5,0,0
|
||||
64540,-100,4,2,1,60,0,0
|
||||
64596,-100,4,2,1,5,0,0
|
||||
64651,-100,4,2,1,60,0,0
|
||||
64707,-100,4,2,1,5,0,0
|
||||
64762,-71.4285714285714,4,2,1,60,0,0
|
||||
64929,-71.4285714285714,4,2,1,5,0,0
|
||||
64984,-133.333333333333,4,2,1,60,0,0
|
||||
65151,-133.333333333333,4,2,1,5,0,0
|
||||
65206,-71.4285714285714,4,2,1,60,0,0
|
||||
65374,-71.4285714285714,4,2,1,5,0,0
|
||||
65429,-133.333333333333,4,2,1,60,0,0
|
||||
65596,-133.333333333333,4,2,1,5,0,0
|
||||
65651,-100,4,2,1,60,0,0
|
||||
66540,-66.6666666666667,4,2,1,60,0,0
|
||||
66596,-66.6666666666667,4,2,1,5,0,0
|
||||
66929,-100,4,2,1,5,0,0
|
||||
66985,-200,4,2,1,60,0,0
|
||||
67207,-200,4,2,1,5,0,0
|
||||
67318,-100,4,2,1,60,0,0
|
||||
67818,-100,4,2,1,5,0,0
|
||||
67874,-100,4,2,1,60,0,0
|
||||
67929,-100,4,2,1,5,0,0
|
||||
67985,-100,4,2,1,60,0,0
|
||||
68040,-100,4,2,1,5,0,0
|
||||
68096,-100,4,2,1,60,0,0
|
||||
68151,-100,4,2,1,5,0,0
|
||||
68207,-100,4,2,1,60,0,0
|
||||
68262,-100,4,2,1,5,0,0
|
||||
68318,-100,4,2,1,60,0,0
|
||||
68874,-83.3333333333333,4,2,1,60,0,0
|
||||
69096,-100,4,2,1,60,0,0
|
||||
69097,-100,4,2,1,5,0,0
|
||||
69207,-100,4,2,1,60,0,0
|
||||
69263,-100,4,2,1,5,0,0
|
||||
69319,-100,4,2,1,60,0,0
|
||||
69374,-100,4,2,1,5,0,0
|
||||
69430,-100,4,2,1,60,0,0
|
||||
69486,-100,4,2,1,5,0,0
|
||||
69542,-100,4,2,1,60,0,0
|
||||
69597,-100,4,2,1,5,0,0
|
||||
69651,-100,4,2,1,60,0,0
|
||||
69707,-100,4,2,1,5,0,0
|
||||
69762,-100,4,2,1,60,0,0
|
||||
69818,-100,4,2,1,5,0,0
|
||||
69874,-100,4,2,1,60,0,0
|
||||
69929,-100,4,2,1,5,0,0
|
||||
69985,-100,4,2,1,60,0,0
|
||||
70040,-100,4,2,1,60,0,0
|
||||
70041,-100,4,2,1,5,0,0
|
||||
70096,-100,4,2,1,60,0,0
|
||||
70818,-100,4,2,1,5,0,0
|
||||
70873,-100,4,2,1,60,0,0
|
||||
71207,-71.4285714285714,4,2,1,60,0,0
|
||||
71429,-100,4,2,1,60,0,0
|
||||
71874,-71.4285714285714,4,2,1,60,0,0
|
||||
72041,-71.4285714285714,4,2,1,5,0,0
|
||||
72096,-133.333333333333,4,2,1,60,0,0
|
||||
72263,-133.333333333333,4,2,1,5,0,0
|
||||
72318,-71.4285714285714,4,2,1,60,0,0
|
||||
72485,-71.4285714285714,4,2,1,5,0,0
|
||||
72540,-133.333333333333,4,2,1,60,0,0
|
||||
72985,-66.6666666666667,4,2,1,60,0,0
|
||||
73207,-100,4,2,1,60,0,0
|
||||
73651,-133.333333333333,4,2,1,45,0,0
|
||||
75318,-133.333333333333,4,2,1,5,0,0
|
||||
75429,-133.333333333333,4,2,1,45,0,0
|
||||
76762,-100,4,2,1,45,0,0
|
||||
77096,-100,4,2,1,5,0,0
|
||||
77207,-100,4,2,1,70,0,1
|
||||
77818,-100,4,2,1,5,0,1
|
||||
77874,-100,4,2,1,70,0,1
|
||||
78262,-100,4,2,1,5,0,1
|
||||
78318,-100,4,2,1,70,0,1
|
||||
78540,-83.3333333333333,4,2,1,70,0,1
|
||||
78985,-100,4,2,1,70,0,1
|
||||
79596,-100,4,2,1,5,0,1
|
||||
79651,-100,4,2,1,70,0,1
|
||||
80040,-100,4,2,1,5,0,1
|
||||
80096,-100,4,2,1,70,0,1
|
||||
80318,-83.3333333333333,4,2,1,70,0,1
|
||||
84318,-100,4,2,1,70,0,1
|
||||
84929,-100,4,2,1,5,0,1
|
||||
84985,-100,4,2,1,70,0,1
|
||||
85207,-100,4,2,1,70,0,1
|
||||
85374,-100,4,2,1,5,0,1
|
||||
85429,-100,4,2,1,70,0,1
|
||||
85651,-83.3333333333333,4,2,1,70,0,1
|
||||
86096,-100,4,2,1,70,0,1
|
||||
86707,-100,4,2,1,5,0,1
|
||||
86762,-100,4,2,1,70,0,1
|
||||
88818,-100,4,2,1,5,0,1
|
||||
88874,-100,4,2,1,70,0,1
|
||||
88929,-100,4,2,1,5,0,1
|
||||
88985,-100,4,2,1,70,0,1
|
||||
89040,-100,4,2,1,5,0,1
|
||||
89096,-100,4,2,1,70,0,1
|
||||
92040,-100,4,2,1,5,0,1
|
||||
92096,-100,4,2,1,70,0,1
|
||||
92485,-100,4,2,1,5,0,1
|
||||
92540,-100,4,2,1,70,0,1
|
||||
97651,-200,4,2,1,70,0,1
|
||||
97818,-200,4,2,1,5,0,1
|
||||
97874,-66.6666666666667,4,2,1,70,0,1
|
||||
97985,-66.6666666666667,4,2,1,70,0,1
|
||||
98040,-66.6666666666667,4,2,1,5,0,1
|
||||
98096,-133.333333333333,4,2,1,70,0,1
|
||||
98262,-133.333333333333,4,2,1,5,0,1
|
||||
98318,-66.6666666666667,4,2,1,70,0,1
|
||||
98540,-100,4,2,1,70,0,1
|
||||
99151,-100,4,2,1,5,0,1
|
||||
99207,-100,4,2,1,70,0,1
|
||||
99596,-100,4,2,1,5,0,1
|
||||
99651,-100,4,2,1,70,0,1
|
||||
103040,-100,4,2,1,5,0,1
|
||||
103096,-100,4,2,1,70,0,1
|
||||
103151,-100,4,2,1,5,0,1
|
||||
103207,-100,4,2,1,70,0,1
|
||||
103262,-100,4,2,1,5,0,1
|
||||
103318,-100,4,2,1,70,0,1
|
||||
105207,-83.3333333333333,4,2,1,70,0,1
|
||||
105540,-83.3333333333333,4,2,1,70,0,1
|
||||
105651,-133.333333333333,4,2,1,60,0,0
|
||||
105985,-133.333333333333,4,2,1,5,0,0
|
||||
106096,-200,4,2,1,60,0,0
|
||||
106985,-66.6666666666667,4,2,1,60,0,0
|
||||
107151,-66.6666666666667,4,2,1,5,0,0
|
||||
107207,-66.6666666666667,4,2,1,60,0,0
|
||||
107429,-100,4,2,1,60,0,0
|
||||
107763,-100,4,2,1,5,0,0
|
||||
107874,-100,4,2,1,60,0,0
|
||||
108874,-100,4,2,1,60,0,0
|
||||
109374,-100,4,2,1,5,0,0
|
||||
109429,-100,4,2,1,60,0,0
|
||||
109596,-100,4,2,1,5,0,0
|
||||
109651,-200,4,2,1,60,0,0
|
||||
109929,-100,4,2,1,60,0,0
|
||||
109984,-100,4,2,1,60,0,0
|
||||
110262,-100,4,2,1,5,0,0
|
||||
110318,-100,4,2,1,60,0,0
|
||||
110485,-100,4,2,1,5,0,0
|
||||
110540,-100,4,2,1,60,0,0
|
||||
110707,-100,4,2,1,5,0,0
|
||||
110762,-66.6666666666667,4,2,1,60,0,0
|
||||
110929,-100,4,2,1,60,0,0
|
||||
110985,-133.333333333333,4,2,1,60,0,0
|
||||
111429,-133.333333333333,4,2,1,60,0,0
|
||||
111596,-133.333333333333,4,2,1,60,0,0
|
||||
111651,-133.333333333333,4,2,1,60,0,0
|
||||
111818,-133.333333333333,4,2,1,60,0,0
|
||||
111874,-100,4,2,1,60,0,1
|
||||
112318,-83.3333333333333,4,2,1,60,0,1
|
||||
112429,-100,4,2,1,5,0,0
|
||||
|
||||
|
||||
[Colours]
|
||||
Combo1 : 112,75,180
|
||||
Combo2 : 0,255,255
|
||||
Combo3 : 255,15,117
|
||||
Combo4 : 255,135,15
|
||||
|
||||
[HitObjects]
|
||||
309,230,763,37,0,3:0:0:0:
|
||||
485,146,985,2,0,L|406:167,1,67.4999968671799,8|0,3:0|0:0,0:0:0:0:
|
||||
374,249,1207,2,0,L|299:227,1,67.4999968671799,8|0,3:0|0:0,0:0:0:0:
|
||||
196,91,1429,2,0,L|191:44,3,33.7499984335899,0|0|0|0,3:0|3:0|3:0|3:0,0:0:0:0:
|
||||
124,173,1651,2,0,L|131:222,2,44.9999979114532,0|0|0,3:0|3:0|3:0,0:0:0:0:
|
||||
221,284,1874,2,0,L|213:208,1,67.4999968671799,0|0,3:0|3:0,0:0:0:0:
|
||||
292,86,2096,38,0,L|310:234,1,146.249990980625,12|0,3:0|0:0,0:0:0:0:
|
||||
314,328,2540,38,0,B|280:359|280:359|230:320|252:242|313:230,1,209.999990253448,0|0,3:0|0:0,0:0:0:0:
|
||||
421,300,2874,1,0,0:0:0:0:
|
||||
421,300,2985,2,0,P|461:288|491:253,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
309,231,3207,2,0,P|297:190|305:153,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
394,22,3429,5,0,3:0:0:0:
|
||||
461,72,3540,2,0,B|477:103|477:103|461:148,1,74.999998807907,0|4,0:0|0:0,0:0:0:0:
|
||||
378,183,3762,2,0,L|206:157,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
229,161,4096,2,0,P|227:202|211:250,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
61,384,4318,38,0,P|101:359|134:322,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
317,310,4540,2,0,P|267:305|226:288,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
141,110,4762,2,0,B|121:175|152:226|152:226|152:202|161:183,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
155,196,5096,6,0,P|67:211|79:286,1,179.999991645813,0|0,0:0|0:0,0:0:0:0:
|
||||
212,366,5429,38,0,P|207:335|174:281,1,56.2500012516975,4|0,0:0|0:0,0:0:0:0:
|
||||
206,286,5651,2,0,P|236:297|299:295,1,56.2500012516975,8|0,3:0|0:0,0:0:0:0:
|
||||
281,321,5874,2,0,P|257:340|227:396,1,56.2500012516975,4|0,0:0|0:0,0:0:0:0:
|
||||
124,246,6096,6,0,P|198:198|277:232,1,149.999997615814,0|0,3:0|0:0,0:0:0:0:
|
||||
253,211,6429,1,0,0:0:0:0:
|
||||
276,99,6540,2,0,P|335:139|369:215,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
368,208,6874,1,0,0:0:0:0:
|
||||
430,96,6985,37,0,3:0:0:0:
|
||||
497,147,7096,2,0,P|507:189|488:244,1,74.999998807907,0|4,0:0|0:0,0:0:0:0:
|
||||
414,379,7318,2,0,B|383:322|421:267|421:267|421:308,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
421,298,7651,2,0,P|378:312|336:304,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
270,170,7874,6,0,P|275:228|236:278,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
94,300,8096,2,0,P|133:263|208:274,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
261,374,8318,2,0,L|176:365,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
38,377,8540,2,0,L|55:197,1,168.750003755093,4|0,0:0|0:0,0:0:0:0:
|
||||
123,25,8985,38,0,L|132:110,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
217,242,9207,2,0,L|237:168,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
48,92,9429,5,4,0:0:0:0:
|
||||
63,176,9540,1,0,0:0:0:0:
|
||||
83,259,9651,38,0,P|167:223|231:255,1,149.999997615814,0|0,3:0|0:0,0:0:0:0:
|
||||
274,312,9985,1,0,0:0:0:0:
|
||||
274,312,10096,2,0,L|354:292,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
459,225,10318,2,0,L|375:204,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
269,107,10540,1,0,3:0:0:0:
|
||||
276,54,10651,1,0,0:0:0:0:
|
||||
313,17,10762,1,4,0:0:0:0:
|
||||
363,9,10874,1,0,0:0:0:0:
|
||||
363,9,11096,5,0,0:0:0:0:
|
||||
432,68,11207,2,0,P|444:107|425:154,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
309,252,11429,38,0,P|297:195|321:158,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
450,316,11651,2,0,L|361:312,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
160,341,11874,2,0,B|187:380|187:380|233:309|177:235,1,187.499997019767,8|4,3:0|0:0,0:0:0:0:
|
||||
116,200,12207,6,0,P|52:224|122:264,1,168.750003755093,0|4,0:0|0:0,0:0:0:0:
|
||||
297,91,12762,37,8,3:0:0:0:
|
||||
276,44,12874,1,0,0:0:0:0:
|
||||
226,27,12985,1,4,0:0:0:0:
|
||||
187,63,13096,1,0,0:0:0:0:
|
||||
196,115,13207,1,0,0:0:0:0:
|
||||
376,144,13429,2,0,L|378:121,2,16.6666664017571,0|0|0,0:0|0:0|0:0,0:0:0:0:
|
||||
436,220,13651,6,0,B|395:211|373:164|373:164|332:208|264:185,1,179.999991645813,8|4,3:0|0:0,0:0:0:0:
|
||||
276,44,13985,1,0,0:0:0:0:
|
||||
196,115,14096,38,0,L|139:124,4,37.4999994039535,0|0|0|0|4,3:0|0:0|0:0|0:0|0:0,0:0:0:0:
|
||||
82,69,14429,1,0,0:0:0:0:
|
||||
106,190,14540,2,0,L|126:276,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
218,383,14762,2,0,L|234:309,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
26,231,14985,5,0,3:0:0:0:
|
||||
253,202,15207,37,0,0:0:0:0:
|
||||
331,271,15318,1,0,0:0:0:0:
|
||||
233,309,15429,1,8,3:0:0:0:
|
||||
389,73,15651,6,0,P|410:22|447:112,1,224.999996423721,4|0,3:0|0:0,0:0:0:0:
|
||||
391,165,16096,1,0,0:0:0:0:
|
||||
377,177,16207,1,0,0:0:0:0:
|
||||
365,187,16318,38,0,B|253:261|221:119|94:192,1,269.999987468719,0|0,0:0|0:0,0:0:0:0:
|
||||
73,319,16762,22,0,P|133:336|116:236,1,168.750003755093,4|0,3:0|0:0,0:0:0:0:
|
||||
139,258,17207,6,0,P|138:315|69:283,1,112.500002503395,8|0,3:0|0:0,0:0:0:0:
|
||||
92,323,17762,37,0,0:0:0:0:
|
||||
43,245,17874,1,4,0:0:0:0:
|
||||
4,322,17985,1,0,0:0:0:0:
|
||||
133,245,18096,1,0,3:0:0:0:
|
||||
29,105,18318,6,0,L|38:40,3,56.2500012516975,4|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
50,30,18540,38,0,P|111:56|193:25,1,149.999997615814,0|0,3:0|0:0,0:0:0:0:
|
||||
240,120,18985,2,0,P|328:91|394:125,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
409,213,19318,2,0,B|377:226|377:226|243:200,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
119,187,19651,2,0,L|127:286,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
179,338,19874,1,8,3:0:0:0:
|
||||
45,307,19985,6,0,L|3:297,2,37.4999994039535,0|0|4,0:0|0:0|0:0,0:0:0:0:
|
||||
103,380,20207,1,0,3:0:0:0:
|
||||
212,257,20318,38,0,P|233:218|231:171,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
111,118,20540,1,4,0:0:0:0:
|
||||
111,118,20762,6,0,L|197:109,1,74.999998807907,8|4,3:0|0:0,0:0:0:0:
|
||||
256,18,21096,37,0,0:0:0:0:
|
||||
337,121,21207,2,0,P|350:60|403:16,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
384,26,21429,2,0,P|406:86|465:122,1,112.49999821186,0|0,0:0|0:0,0:0:0:0:
|
||||
443,114,21651,2,0,P|377:105|327:131,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
352,223,21874,6,0,B|369:230|369:230|391:228|391:228|416:239|416:239|440:235|440:235|462:244|462:244|489:249,1,112.500002503395,4|0,0:0|0:0,0:0:0:0:
|
||||
322,343,22096,37,0,3:0:0:0:
|
||||
259,270,22207,2,0,P|223:276|182:263,2,74.999998807907,0|4|0,0:0|0:0|0:0,0:0:0:0:
|
||||
86,360,22540,5,8,3:0:0:0:
|
||||
15,295,22651,2,0,L|0:201,2,74.999998807907,0|4|0,0:0|0:0|0:0,0:0:0:0:
|
||||
94,384,22985,38,0,P|118:328|112:277,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
0,211,23429,22,0,L|76:196,1,74.999998807907,12|0,3:0|0:0,0:0:0:0:
|
||||
215,134,23651,2,0,L|114:110,1,74.999998807907,12|0,3:0|0:0,0:0:0:0:
|
||||
33,124,23874,22,0,L|43:2,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
150,269,24318,2,0,L|162:194,1,74.999998807907,0|4,3:0|0:0,0:0:0:0:
|
||||
229,134,24651,6,0,L|386:164,1,112.500002503395,12|0,3:0|0:0,0:0:0:0:
|
||||
486,268,24874,37,0,0:0:0:0:
|
||||
410,119,24985,1,4,0:0:0:0:
|
||||
381,213,25096,1,0,0:0:0:0:
|
||||
512,120,25207,1,0,3:0:0:0:
|
||||
247,36,25429,6,0,L|191:25,3,37.4999994039535,4|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
185,24,25651,2,0,B|145:72|145:72|174:164,1,149.999997615814,0|0,3:0|0:0,0:0:0:0:
|
||||
253,219,26096,2,0,B|281:311|281:311|228:382,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
100,363,26429,38,0,L|259:354,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
404,262,26762,1,4,0:0:0:0:
|
||||
390,352,26874,1,0,0:0:0:0:
|
||||
314,295,26985,1,8,3:0:0:0:
|
||||
425,256,27096,6,0,L|492:246,2,37.4999994039535,0|0|4,0:0|0:0|0:0,0:0:0:0:
|
||||
329,216,27318,1,0,3:0:0:0:
|
||||
193,177,27429,38,0,L|266:161,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
322,107,27651,1,4,0:0:0:0:
|
||||
322,107,27874,2,0,L|310:238,1,112.500002503395,8|4,3:0|0:0,0:0:0:0:
|
||||
110,299,28207,5,0,0:0:0:0:
|
||||
164,231,28318,2,0,B|168:303|168:303|121:338,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
30,284,28540,2,0,B|90:244|90:244|144:267,1,112.49999821186,0|0,0:0|0:0,0:0:0:0:
|
||||
148,371,28762,2,0,B|83:338|83:338|76:280,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
194,201,28985,38,0,B|207:210|207:210|227:210|227:210|243:217|243:217|265:218|265:218|282:227|282:227|305:225|305:225|325:238,1,112.500002503395,4|0,0:0|0:0,0:0:0:0:
|
||||
492,114,29207,6,0,P|445:136|410:138,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
324,102,29429,2,0,P|291:68|280:29,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
418,17,29651,1,8,3:0:0:0:
|
||||
495,201,29874,1,4,3:2:0:0:
|
||||
221,136,30096,37,0,3:0:0:0:
|
||||
299,188,30207,2,0,B|316:251|316:251|271:352,1,149.999997615814,4|0,3:0|0:0,0:0:0:0:
|
||||
115,334,30540,6,0,P|11:252|167:266,1,382.500001215934,0|0,0:0|0:0,0:0:0:0:
|
||||
216,326,30985,38,0,L|304:331,1,63.7500002026557,4|0,3:0|0:0,0:0:0:0:
|
||||
280,330,31429,6,0,L|293:241,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
426,252,31651,2,0,L|439:163,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
253,158,31874,37,0,3:0:0:0:
|
||||
258,132,31985,1,0,0:0:0:0:
|
||||
337,111,32096,5,4,0:0:0:0:
|
||||
341,85,32207,1,0,0:0:0:0:
|
||||
271,30,32318,38,0,B|212:42|212:42|141:19,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
163,26,32540,2,0,L|144:181,1,149.999997615814,4|0,0:0|3:0,0:0:0:0:
|
||||
445,343,32985,22,0,B|439:234|439:234|384:269,1,149.999997615814,4|8,0:0|3:0,0:0:0:0:
|
||||
240,257,33429,2,0,B|263:148|263:148|291:205,1,149.999997615814,4|0,0:0|3:0,0:0:0:0:
|
||||
68,333,33874,2,0,B|83:233|83:233|41:256,1,149.999997615814,4|8,0:0|3:0,0:0:0:0:
|
||||
344,347,34318,22,0,B|368:372|368:372|455:355|455:355|472:308,1,149.999997615814,4|0,0:0|0:0,0:0:0:0:
|
||||
452,255,34540,2,0,B|389:212|389:212|332:273,1,149.999997615814,0|4,3:0|0:0,0:0:0:0:
|
||||
256,220,34874,5,0,0:0:0:0:
|
||||
256,220,34985,2,0,B|256:128,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
256,70,35207,2,0,B|256:162,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
112,312,35429,37,0,3:0:0:0:
|
||||
60,255,35540,2,0,B|123:212|123:212|180:273,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
169,350,35874,6,0,B|144:375|144:375|57:358|57:358|40:311,1,149.999997615814,8|0,3:0|3:0,0:0:0:0:
|
||||
62,169,36429,6,0,L|76:267,2,74.999998807907,0|4|0,0:0|0:0|0:0,0:0:0:0:
|
||||
134,61,36762,1,8,3:0:0:0:
|
||||
201,113,36874,2,0,L|215:211,2,74.999998807907,0|4|0,0:0|0:0|0:0,0:0:0:0:
|
||||
298,272,37207,6,0,L|315:184,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
330,114,37429,1,4,0:0:0:0:
|
||||
446,176,37540,2,0,B|404:214|404:214|307:197,1,149.999997615814,4|0,3:0|0:0,0:0:0:0:
|
||||
231,240,37874,2,0,P|223:199|231:162,1,74.999998807907,4|0,3:0|0:0,0:0:0:0:
|
||||
325,285,38096,6,0,L|154:300,1,149.999997615814,4|0,3:0|0:0,0:0:0:0:
|
||||
175,298,38540,6,0,L|163:396,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
75,208,38762,2,0,L|63:306,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
233,74,38985,37,0,3:0:0:0:
|
||||
231,98,39096,1,0,0:0:0:0:
|
||||
156,139,39207,5,4,0:0:0:0:
|
||||
155,165,39318,1,0,0:0:0:0:
|
||||
227,215,39429,38,0,P|282:209|352:230,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
336,222,39651,2,0,L|366:67,1,149.999997615814,4|0,0:0|3:0,0:0:0:0:
|
||||
81,35,40096,22,0,B|82:105|82:105|118:136|118:136|132:89,1,149.999997615814,4|8,0:0|3:0,0:0:0:0:
|
||||
272,158,40540,2,0,B|270:228|270:228|234:259|234:259|220:212,1,149.999997615814,4|0,0:0|3:0,0:0:0:0:
|
||||
423,36,40985,2,0,B|400:102|400:102|423:143|423:143|453:104,1,149.999997615814,4|8,0:0|3:0,0:0:0:0:
|
||||
512,278,41429,6,0,P|415:258|361:293,1,149.999997615814,4|0,0:0|0:0,0:0:0:0:
|
||||
359,302,41651,6,0,B|320:264|320:264|310:187,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
322,190,41874,2,0,L|449:171,1,112.49999821186,4|0,0:0|0:0,0:0:0:0:
|
||||
443,159,42096,1,8,3:0:0:0:
|
||||
240,52,42318,6,0,B|255:79|255:79|241:135,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
177,166,42540,1,0,3:0:0:0:
|
||||
163,151,42651,2,0,B|161:207|161:207|192:240|192:240|189:299,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
131,365,42985,2,0,P|198:322|280:345,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
335,377,43318,1,0,0:0:0:0:
|
||||
442,239,43429,38,0,B|456:178|456:178|422:136|422:136|427:68|427:68|449:112,1,224.999996423721,0|0,3:0|0:0,0:0:0:0:
|
||||
444,103,43874,2,0,P|402:118|356:120,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
249,28,44096,2,0,P|295:35|324:48,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
364,201,44318,5,0,0:0:0:0:
|
||||
332,195,44429,1,0,0:0:0:0:
|
||||
251,135,44540,37,0,0:0:0:0:
|
||||
281,123,44651,1,0,0:0:0:0:
|
||||
332,195,44762,6,0,B|356:269|324:333|324:333|303:293,1,179.999991645813,4|0,0:3|0:0,0:0:0:0:
|
||||
61,25,45207,38,0,L|88:158,1,112.500002503395,0|0,3:0|0:0,0:0:0:0:
|
||||
84,136,45651,1,8,3:0:0:0:
|
||||
84,136,46096,1,0,3:0:0:0:
|
||||
176,33,46207,2,0,L|164:103,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
219,207,46429,2,0,L|232:152,1,56.2500012516975,0|8,0:0|3:0,0:0:0:0:
|
||||
312,65,46651,1,0,0:0:0:0:
|
||||
312,65,46762,2,0,L|398:94,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
512,176,46985,5,0,3:0:0:0:
|
||||
421,192,47096,1,0,0:0:0:0:
|
||||
421,192,47429,1,8,3:0:0:0:
|
||||
402,357,47651,37,0,0:0:0:0:
|
||||
394,277,47762,1,0,0:0:0:0:
|
||||
328,324,47874,1,0,3:0:0:0:
|
||||
328,324,48318,1,8,3:0:0:0:
|
||||
110,357,48540,5,0,0:0:0:0:
|
||||
118,277,48651,1,0,0:0:0:0:
|
||||
184,324,48763,1,0,3:0:0:0:
|
||||
110,357,48874,1,0,0:0:0:0:
|
||||
110,357,49207,1,8,3:0:0:0:
|
||||
110,357,49651,1,0,3:0:0:0:
|
||||
0,283,49762,38,0,P|41:301|97:295,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
188,219,49985,2,0,P|168:236|137:246,1,56.2500012516975,0|8,0:0|3:0,0:0:0:0:
|
||||
49,137,50207,1,0,0:0:0:0:
|
||||
49,137,50318,2,0,P|65:184|93:205,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
107,67,50540,5,0,3:0:0:0:
|
||||
32,15,50651,1,0,0:0:0:0:
|
||||
32,15,50985,1,8,3:0:0:0:
|
||||
265,114,51207,37,0,0:0:0:0:
|
||||
254,196,51318,1,0,0:0:0:0:
|
||||
241,279,51429,1,0,3:0:0:0:
|
||||
241,279,51651,1,0,0:0:0:0:
|
||||
336,207,51762,6,0,P|397:191|371:274,1,168.750003755093,0|0,0:0|0:0,0:0:0:0:
|
||||
83,206,52318,5,0,3:0:0:0:
|
||||
83,206,52429,2,0,L|101:260,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
40,383,52651,2,0,P|70:355|90:324,1,56.2500012516975,0|8,0:0|3:0,0:0:0:0:
|
||||
214,334,52874,1,0,0:0:0:0:
|
||||
214,334,52985,2,0,P|171:322|140:304,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
151,160,53207,5,0,3:0:0:0:
|
||||
188,135,53318,1,0,0:0:0:0:
|
||||
232,129,53429,1,0,0:0:0:0:
|
||||
273,146,53540,1,0,0:0:0:0:
|
||||
339,198,53651,37,8,3:0:0:0:
|
||||
383,199,53762,1,0,0:0:0:0:
|
||||
426,185,53874,1,0,0:0:0:0:
|
||||
450,147,53985,1,0,0:0:0:0:
|
||||
444,61,54096,6,0,P|414:28|377:15,1,56.2500012516975,0|0,3:0|0:0,0:0:0:0:
|
||||
301,28,54318,2,0,P|268:48|255:77,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
189,271,54540,38,0,P|209:222|204:198,1,56.2500012516975,8|0,3:0|0:0,0:0:0:0:
|
||||
186,114,54762,2,0,P|152:74|124:68,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
27,137,54985,5,0,3:0:0:0:
|
||||
34,167,55096,1,0,0:0:0:0:
|
||||
122,204,55207,37,0,0:0:0:0:
|
||||
116,178,55318,1,0,0:0:0:0:
|
||||
48,249,55429,5,8,3:0:0:0:
|
||||
54,274,55540,1,0,0:0:0:0:
|
||||
124,329,55651,38,0,P|157:326|200:310,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
320,185,55874,5,0,3:0:0:0:
|
||||
287,175,55985,1,0,0:0:0:0:
|
||||
254,181,56096,2,0,P|258:221|264:241,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
337,347,56318,2,0,P|348:321|350:293,1,56.2500012516975,8|0,3:0|0:0,0:0:0:0:
|
||||
418,197,56540,37,0,0:0:0:0:
|
||||
418,197,56651,2,0,L|492:180,1,56.2500012516975,0|0,0:0|3:0,0:0:0:0:
|
||||
329,114,56874,2,0,L|262:94,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
436,59,57096,6,0,L|413:126,1,56.2500012516975,0|8,0:0|3:0,0:0:0:0:
|
||||
332,194,57318,2,0,L|353:259,2,56.2500012516975,0|0|0,0:0|0:0|0:0,0:0:0:0:
|
||||
202,194,57651,37,0,3:0:0:0:
|
||||
224,233,57762,1,0,0:0:0:0:
|
||||
222,279,57874,1,0,0:0:0:0:
|
||||
193,314,57985,1,0,0:0:0:0:
|
||||
144,244,58096,5,0,0:0:0:0:
|
||||
127,214,58207,1,0,0:0:0:0:
|
||||
126,180,58318,1,0,0:0:0:0:
|
||||
139,149,58429,1,0,0:0:0:0:
|
||||
224,113,58540,38,0,B|262:88|235:70|189:83|189:83|224:138|194:193,1,194.999987974167
|
||||
299,319,58874,1,0,0:0:0:0:
|
||||
299,319,58985,2,0,B|316:283|314:237|314:237|278:226|278:226|320:243|359:227,1,202.49999060154,4|0,0:0|0:0,0:0:0:0:
|
||||
428,181,59429,22,0,P|454:129|399:4,1,179.999991645813,0|0,3:0|0:0,0:0:0:0:
|
||||
418,18,59874,2,0,L|373:15,6,24.9999996026357,8|0|0|0|0|0|4,3:0|0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0:
|
||||
428,181,60207,5,0,0:0:0:0:
|
||||
352,209,60318,1,0,3:0:0:0:
|
||||
278,177,60429,1,0,0:0:0:0:
|
||||
208,225,60540,2,0,L|222:267,2,37.4999994039535,4|0|0,0:0|0:0|0:0,0:0:0:0:
|
||||
71,144,60762,38,0,L|65:109,3,24.9999996026357,8|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
145,86,60985,1,4,0:0:0:0:
|
||||
163,127,61096,1,0,0:0:0:0:
|
||||
161,171,61207,1,0,3:0:0:0:
|
||||
136,208,61318,1,0,0:0:0:0:
|
||||
99,231,61429,2,0,B|91:279|91:279|117:314,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
177,378,61651,5,8,3:0:0:0:
|
||||
177,378,61762,2,0,B|231:371|231:371|272:326|272:326|345:319,1,179.999991645813
|
||||
417,293,62096,1,0,3:0:0:0:
|
||||
438,263,62207,1,0,0:0:0:0:
|
||||
436,225,62318,1,4,0:0:0:0:
|
||||
412,196,62429,1,0,0:0:0:0:
|
||||
320,172,62540,6,0,P|307:192|291:204,1,37.4999994039535,8|0,3:0|0:0,0:0:0:0:
|
||||
291,147,62651,2,0,P|274:156|245:153,1,37.4999994039535,0|0,0:0|0:0,0:0:0:0:
|
||||
276,114,62762,2,0,P|250:107|234:94,1,37.4999994039535,4|0,0:0|0:0,0:0:0:0:
|
||||
283,81,62874,2,0,P|265:61|260:45,1,37.4999994039535,0|0,0:0|0:0,0:0:0:0:
|
||||
365,31,62985,38,0,P|398:44|442:49,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
512,169,63207,2,0,P|466:163|421:176,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
350,107,63429,1,8,3:0:0:0:
|
||||
293,237,63540,38,0,L|276:158,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
428,269,63762,2,0,B|373:275|373:275|338:249|338:249|267:255,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
191,318,64096,2,0,B|182:355|182:355|212:395,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
192,186,64318,5,8,3:0:0:0:
|
||||
135,253,64429,2,0,L|56:270,2,74.999998807907,0|4|0,0:0|0:0|0:0,0:0:0:0:
|
||||
24,136,64762,38,0,P|69:76|158:75,1,157.499992690086,0|0,3:0|0:0,0:0:0:0:
|
||||
160,80,64985,6,0,P|193:102|255:102,1,84.3750018775463,4|0,0:0|0:0,0:0:0:0:
|
||||
276,34,65207,38,0,L|290:212,1,157.499992690086,8|0,3:0|0:0,0:0:0:0:
|
||||
291,219,65429,6,0,L|311:132,1,84.3750018775463,4|0,0:0|0:0,0:0:0:0:
|
||||
381,111,65651,38,0,B|418:126|418:126|460:126,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
221,163,65874,2,0,B|186:143|186:143|139:153,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
41,231,66096,1,8,3:0:0:0:
|
||||
49,267,66207,1,0,0:0:0:0:
|
||||
56,303,66318,1,4,0:0:0:0:
|
||||
67,288,66429,1,4,0:0:0:0:
|
||||
77,270,66540,6,0,P|171:255|72:350,1,337.500007510185,0|0,0:0|0:0,0:0:0:0:
|
||||
95,356,66985,38,0,L|185:343,1,74.999998807907,8|4,3:0|0:0,0:0:0:0:
|
||||
274,286,67318,6,0,B|289:324|289:324|268:378,1,74.999998807907,0|0,0:0|3:0,0:0:0:0:
|
||||
191,227,67540,1,0,0:0:0:0:
|
||||
255,168,67651,2,0,L|264:116,2,37.4999994039535,4|0|0,0:0|0:0|0:0,0:0:0:0:
|
||||
147,83,67874,2,0,L|154:108,3,24.9999996026357,8|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
80,148,68096,38,0,L|98:224,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
125,356,68318,1,0,3:0:0:0:
|
||||
0,319,68429,1,0,0:0:0:0:
|
||||
0,319,68540,2,0,L|76:294,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
277,219,68762,5,8,3:0:0:0:
|
||||
277,219,68874,2,0,B|327:199|327:199|293:138|197:173,1,179.999991645813,0|0,0:0|0:0,0:0:0:0:
|
||||
157,273,69207,37,0,3:0:0:0:
|
||||
175,316,69318,1,0,0:0:0:0:
|
||||
212,334,69429,1,4,0:0:0:0:
|
||||
254,333,69540,1,0,0:0:0:0:
|
||||
332,268,69651,38,0,P|333:237|343:213,1,37.4999994039535,8|0,3:0|0:0,0:0:0:0:
|
||||
373,265,69762,2,0,P|386:239|404:232,1,37.4999994039535,0|0,0:0|0:0,0:0:0:0:
|
||||
413,284,69874,2,0,P|430:269|454:269,1,37.4999994039535,4|0,0:0|0:0,0:0:0:0:
|
||||
433,318,69985,2,0,P|452:320|474:337,1,37.4999994039535,4|0,0:0|0:0,0:0:0:0:
|
||||
401,384,70096,6,0,P|353:378|319:346,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
251,251,70318,2,0,P|240:196|260:154,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
401,18,70540,1,8,3:0:0:0:
|
||||
401,18,70651,2,0,P|409:54|398:90,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
327,193,70874,2,0,L|304:45,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
290,26,71207,6,0,L|308:144,1,104.999995126724,0|0,0:0|0:0,0:0:0:0:
|
||||
272,302,71429,2,0,L|187:288,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
33,217,71651,37,4,0:0:0:0:
|
||||
27,187,71762,1,4,0:0:0:0:
|
||||
20,157,71874,2,0,B|103:140|103:140|162:58,1,157.499992690086,0|0,3:0|0:0,0:0:0:0:
|
||||
145,82,72096,6,0,L|218:75,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
336,136,72318,38,0,P|331:213|231:208,1,157.499992690086
|
||||
263,232,72540,6,0,L|278:300,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
183,384,72762,2,0,L|172:307,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
37,140,72985,38,0,B|10:168|10:168|17:204|17:204|54:220|54:220|89:196|89:196|87:157|87:157|57:138,1,225.00000500679
|
||||
275,372,73651,6,0,P|320:352|387:369,1,112.500002503395
|
||||
380,364,74096,2,0,L|436:358,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
495,271,74318,2,0,L|424:282,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
339,270,74540,1,0,0:0:0:0:
|
||||
339,270,74651,1,0,0:0:0:0:
|
||||
339,270,74762,2,0,L|329:196,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
408,46,74985,38,0,L|392:120,1,56.2500012516975
|
||||
220,230,75207,2,0,L|209:156,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
282,7,75429,37,0,0:0:0:0:
|
||||
300,98,75540,1,0,0:0:0:0:
|
||||
197,25,75651,5,0,0:0:0:0:
|
||||
222,103,75762,1,0,0:0:0:0:
|
||||
126,69,75874,5,0,0:0:0:0:
|
||||
153,134,75985,1,0,0:0:0:0:
|
||||
76,145,76096,5,0,0:0:0:0:
|
||||
116,179,76207,1,0,0:0:0:0:
|
||||
70,222,76318,5,0,0:0:0:0:
|
||||
111,222,76429,1,0,0:0:0:0:
|
||||
134,253,76540,6,0,P|135:298|126:314,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
21,384,76762,2,0,P|124:354|260:391,1,224.999996423721,0|0,0:0|0:0,0:0:0:0:
|
||||
384,366,77207,22,0,L|394:268,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
499,62,77429,2,0,L|486:135,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
507,237,77651,2,0,P|450:231|388:184,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
404,203,77874,2,0,L|313:217,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
113,212,78096,6,0,P|128:267|111:328,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
115,319,78318,2,0,L|213:340,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
274,371,78540,38,0,L|257:186,1,179.999991645813,8|0,3:0|0:0,0:0:0:0:
|
||||
128,139,78874,1,0,0:0:0:0:
|
||||
128,139,78985,6,0,L|230:128,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
365,34,79207,37,4,0:0:0:0:
|
||||
430,114,79318,1,0,0:0:0:0:
|
||||
361,184,79429,2,0,P|304:170|277:110,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
278,126,79651,2,0,L|189:133,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
64,263,79874,6,0,B|37:230|37:230|50:143,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
66,119,80096,2,0,L|80:210,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
71,361,80318,38,0,B|135:350|135:350|182:305|182:305|243:297,1,179.999991645813,8|0,3:0|0:0,0:0:0:0:
|
||||
302,247,80651,1,0,0:0:0:0:
|
||||
222,211,80762,1,0,3:0:0:0:
|
||||
478,344,80985,5,4,0:0:0:0:
|
||||
491,309,81096,5,0,0:0:0:0:
|
||||
498,265,81207,5,8,3:0:0:0:
|
||||
485,223,81318,5,0,0:0:0:0:
|
||||
458,179,81429,5,4,0:0:0:0:
|
||||
418,147,81540,5,0,0:0:0:0:
|
||||
352,126,81651,5,0,3:0:0:0:
|
||||
281,149,81762,5,0,0:0:0:0:
|
||||
239,221,81874,5,4,0:0:0:0:
|
||||
159,262,81985,5,0,0:0:0:0:
|
||||
66,234,82096,5,8,3:0:0:0:
|
||||
11,145,82207,5,0,0:0:0:0:
|
||||
55,33,82318,5,4,0:0:0:0:
|
||||
273,44,82540,37,0,3:0:0:0:
|
||||
320,103,82651,1,0,0:0:0:0:
|
||||
394,118,82762,1,4,0:0:0:0:
|
||||
468,100,82874,1,0,0:0:0:0:
|
||||
507,36,82985,1,8,3:0:0:0:
|
||||
495,19,83207,5,4,0:0:0:0:
|
||||
335,83,83318,1,0,0:0:0:0:
|
||||
453,81,83429,1,0,3:0:0:0:
|
||||
283,24,83540,2,0,P|196:37|141:120,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
60,238,83874,1,8,3:0:0:0:
|
||||
21,164,83985,2,0,P|59:149|175:193,1,149.999997615814,0|8,0:0|3:0,0:0:0:0:
|
||||
252,206,84318,38,0,P|271:160|264:125,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
139,257,84540,2,0,P|131:302|149:340,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
240,379,84762,2,0,B|330:360|330:360|298:344,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
312,351,84985,2,0,P|279:321|270:287,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
359,165,85207,6,0,B|389:202|389:202|368:282,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
373,265,85429,2,0,L|454:282,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
498,139,85651,38,0,P|446:120|396:0,1,179.999991645813,8|0,3:0|0:0,0:0:0:0:
|
||||
394,13,85985,1,0,0:0:0:0:
|
||||
301,92,86096,6,0,L|214:83,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
66,66,86318,1,4,0:0:0:0:
|
||||
13,136,86429,1,0,0:0:0:0:
|
||||
72,193,86540,2,0,P|120:210|190:178,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
176,192,86762,2,0,P|154:237|160:288,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
309,370,86985,37,0,3:0:0:0:
|
||||
359,310,87096,1,0,0:0:0:0:
|
||||
283,297,87207,2,0,L|203:318,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
4,203,87429,2,0,B|55:211|55:211|82:255|82:255|134:266,1,149.999997615814,8|0,3:0|0:0,0:0:0:0:
|
||||
238,217,87762,1,0,0:0:0:0:
|
||||
183,120,87874,6,0,L|89:111,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
98,33,88096,2,0,L|23:26,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
306,182,88318,38,0,L|400:173,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
391,95,88540,2,0,L|465:88,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
232,28,88762,2,0,L|220:92,1,37.4999994039535,0|0,3:0|0:0,0:0:0:0:
|
||||
243,39,88874,2,0,L|231:103,1,37.4999994039535,0|0,0:0|0:0,0:0:0:0:
|
||||
256,50,88985,2,0,L|251:87,1,37.4999994039535,4|0,0:0|0:0,0:0:0:0:
|
||||
485,87,89207,6,0,L|493:51,3,37.4999994039535,8|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
396,120,89429,2,0,L|411:197,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
471,317,89651,38,0,P|411:299|320:336,1,149.999997615814,0|4,3:0|0:0,0:0:0:0:
|
||||
61,239,90096,2,0,P|121:221|212:258,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
367,21,90540,6,0,P|336:57|328:104,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
163,96,90762,2,0,P|194:132|202:179,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
190,346,90985,37,8,3:0:0:0:
|
||||
328,272,91096,1,0,0:0:0:0:
|
||||
154,272,91207,5,8,3:0:0:0:
|
||||
365,338,91318,1,0,0:0:0:0:
|
||||
257,382,91429,38,0,B|290:333|224:286|269:219,1,149.999997615814,4|4,3:0|0:0,0:0:0:0:
|
||||
325,196,91762,1,0,0:0:0:0:
|
||||
325,196,91874,2,0,P|365:210|436:184,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
430,190,92096,2,0,B|418:110,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
313,19,92318,2,0,L|190:36,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
201,34,92540,2,0,B|214:117,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
209,252,92762,5,8,3:0:0:0:
|
||||
156,261,92874,1,0,0:0:0:0:
|
||||
112,231,92985,1,4,0:0:0:0:
|
||||
60,222,93096,1,0,0:0:0:0:
|
||||
13,247,93207,38,0,P|4:288|19:328,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
173,186,93429,1,4,0:0:0:0:
|
||||
215,120,93540,1,0,0:0:0:0:
|
||||
162,49,93651,2,0,P|125:39|76:61,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
234,138,93874,2,0,P|273:157|313:148,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
385,39,94096,5,0,3:0:0:0:
|
||||
337,286,94318,2,0,L|322:373,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
409,327,94540,2,0,P|418:277|280:230,1,224.999996423721,8|0,3:0|0:0,0:0:0:0:
|
||||
239,319,94985,2,0,P|218:357|173:373,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
34,344,95207,37,4,0:0:0:0:
|
||||
21,309,95318,5,0,0:0:0:0:
|
||||
14,265,95429,5,8,3:0:0:0:
|
||||
27,223,95540,5,0,0:0:0:0:
|
||||
54,179,95651,5,4,0:0:0:0:
|
||||
94,147,95762,5,0,0:0:0:0:
|
||||
160,126,95873,5,0,3:0:0:0:
|
||||
231,149,95984,5,0,0:0:0:0:
|
||||
273,221,96096,5,4,0:0:0:0:
|
||||
353,262,96207,5,0,0:0:0:0:
|
||||
446,234,96318,5,8,3:0:0:0:
|
||||
501,145,96429,5,0,0:0:0:0:
|
||||
450,36,96540,5,4,0:0:0:0:
|
||||
239,44,96762,5,0,3:0:0:0:
|
||||
192,103,96873,1,0,0:0:0:0:
|
||||
118,118,96984,1,4,0:0:0:0:
|
||||
44,100,97096,1,0,0:0:0:0:
|
||||
5,36,97207,1,8,3:0:0:0:
|
||||
17,19,97429,37,4,0:0:0:0:
|
||||
146,51,97540,1,0,0:0:0:0:
|
||||
29,122,97651,2,0,L|39:193,1,56.2499991059302,0|0,3:0|0:0,0:0:0:0:
|
||||
44,197,97874,6,0,P|100:231|176:201,1,112.500002503395,4|0,0:0|0:0,0:0:0:0:
|
||||
301,160,98096,38,0,P|329:140|382:137,1,84.3750018775463,8|0,3:0|0:0,0:0:0:0:
|
||||
398,147,98318,6,0,B|431:187|431:187|415:279,1,112.500002503395,4|0,0:0|0:0,0:0:0:0:
|
||||
265,371,98540,38,0,L|180:361,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
127,202,98762,2,0,L|141:113,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
193,260,98985,2,0,P|144:291|68:278,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
91,290,99207,2,0,L|79:373,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
20,184,99429,6,0,B|4:141|4:141|27:66,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
23,78,99651,2,0,L|109:91,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
271,74,99874,2,0,P|254:31|222:12,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
186,180,100096,2,0,P|232:175|260:147,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
132,63,100318,37,0,3:0:0:0:
|
||||
253,157,100540,1,4,0:0:0:0:
|
||||
285,167,100651,1,0,0:0:0:0:
|
||||
357,129,100762,5,8,3:0:0:0:
|
||||
389,139,100873,1,0,0:0:0:0:
|
||||
422,148,100985,2,0,P|407:200|416:233,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
459,377,101207,38,0,P|472:333|459:295,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
398,242,101429,2,0,L|314:257,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
165,354,101651,2,0,P|116:332|211:264,1,224.999996423721,8|0,3:0|0:0,0:0:0:0:
|
||||
302,165,102096,6,0,L|292:89,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
392,91,102318,2,0,L|382:14,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
192,229,102540,38,0,L|212:136,1,74.999998807907,8|0,3:0|0:0,0:0:0:0:
|
||||
107,172,102762,2,0,L|127:79,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
314,332,102985,6,0,L|305:278,1,37.4999994039535,0|0,3:0|0:0,0:0:0:0:
|
||||
343,345,103096,2,0,L|334:291,1,37.4999994039535,0|0,0:0|0:0,0:0:0:0:
|
||||
370,358,103207,2,0,L|361:304,1,37.4999994039535,4|0,0:0|0:0,0:0:0:0:
|
||||
380,117,103429,38,0,L|374:75,3,37.4999994039535,8|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
444,166,103651,2,0,P|417:188|346:191,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
392,2,103874,2,0,P|424:14|462:74,1,74.999998807907,4|0,3:0|0:0,0:0:0:0:
|
||||
271,129,104096,2,0,P|265:94|298:31,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
505,113,104318,5,8,3:0:0:0:
|
||||
269,217,104540,38,0,L|216:216,3,37.4999994039535,0|0|0|0,3:0|3:0|3:0|3:0,0:0:0:0:
|
||||
360,220,104762,1,0,3:0:0:0:
|
||||
296,384,104874,1,4,3:0:0:0:
|
||||
102,307,105096,5,0,0:0:0:0:
|
||||
102,307,105207,2,0,B|206:381|258:244|374:330,1,269.999987468719,12|0,3:0|0:0,0:0:0:0:
|
||||
439,319,105651,6,0,P|379:336|396:236,1,168.750003755093,0|0,3:0|0:0,0:0:0:0:
|
||||
373,258,106096,6,0,P|374:315|443:283,1,112.500002503395,8|0,3:0|0:0,0:0:0:0:
|
||||
420,323,106651,37,0,0:0:0:0:
|
||||
469,245,106763,1,4,0:0:0:0:
|
||||
508,322,106874,1,0,0:0:0:0:
|
||||
379,245,106985,1,8,3:0:0:0:
|
||||
483,105,107207,6,0,L|474:40,3,56.2500012516975,4|0|0|0,3:0|0:0|0:0|0:0,0:0:0:0:
|
||||
462,30,107429,38,0,P|401:56|319:25,1,149.999997615814,0|0,3:0|0:0,0:0:0:0:
|
||||
272,120,107874,2,0,P|184:91|118:125,1,149.999997615814,8|4,3:0|0:0,0:0:0:0:
|
||||
103,213,108207,2,0,B|128:232|128:232|269:200,1,149.999997615814,0|0,0:0|0:0,0:0:0:0:
|
||||
393,187,108540,2,0,L|385:286,1,74.999998807907,4|0,0:0|0:0,0:0:0:0:
|
||||
333,338,108763,1,8,3:0:0:0:
|
||||
467,307,108874,6,0,L|509:297,2,37.4999994039535,0|0|4,0:0|0:0|0:0,0:0:0:0:
|
||||
409,380,109096,1,0,0:0:0:0:
|
||||
300,257,109207,38,0,P|279:218|281:171,1,74.999998807907,0|0,3:0|0:0,0:0:0:0:
|
||||
401,118,109429,1,4,0:0:0:0:
|
||||
401,118,109651,6,0,L|315:109,1,74.999998807907,8|4,3:0|0:0,0:0:0:0:
|
||||
256,15,109985,37,0,0:0:0:0:
|
||||
175,121,110096,2,0,P|162:60|109:16,1,112.49999821186,0|0,3:0|0:0,0:0:0:0:
|
||||
128,26,110318,2,0,P|106:86|47:122,1,112.49999821186,0|0,0:0|0:0,0:0:0:0:
|
||||
69,114,110540,2,0,P|135:105|185:131,1,112.49999821186,8|0,3:0|0:0,0:0:0:0:
|
||||
160,223,110762,6,0,B|142:230|142:230|120:228|120:228|95:239|95:239|71:235|71:235|49:244|49:244|22:249,1,112.500002503395,4|0,0:0|0:0,0:0:0:0:
|
||||
193,334,110985,38,0,P|216:310|242:301,1,56.2500012516975,0|0,3:0|0:0,0:0:0:0:
|
||||
335,325,111207,2,0,P|366:353|378:379,1,56.2500012516975,0|0,0:0|0:0,0:0:0:0:
|
||||
273,383,111429,2,0,L|304:213,1,168.750003755093,0|0,0:0|0:0,0:0:0:0:
|
||||
383,255,111874,22,0,B|422:273|422:273|476:273,1,74.999998807907,8|0,3:0|3:0,0:0:0:0:
|
||||
209,219,112096,2,0,B|169:221|169:221|131:206,1,74.999998807907,0|0,0:0|0:0,0:0:0:0:
|
||||
403,147,112318,2,0,B|352:114|352:114|337:43|337:43|295:109|295:109|234:115,1,269.999987468719,8|0,3:0|0:0,0:0:0:0:
|
||||
File diff suppressed because one or more lines are too long
1213
osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/2593923.osu
Normal file
1213
osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/2593923.osu
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
1768
osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/801165.osu
Normal file
1768
osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/801165.osu
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.IO;
|
||||
@@ -19,23 +18,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
public class StackingTest
|
||||
{
|
||||
[Test]
|
||||
public void TestStacking()
|
||||
public void TestStackingEdgeCaseOne()
|
||||
{
|
||||
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(beatmap_data)))
|
||||
using (var reader = new LineBufferedReader(stream))
|
||||
{
|
||||
var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty<Mod>());
|
||||
|
||||
var objects = converted.HitObjects.ToList();
|
||||
|
||||
// The last hitobject triggers the stacking
|
||||
for (int i = 0; i < objects.Count - 1; i++)
|
||||
Assert.AreEqual(0, ((OsuHitObject)objects[i]).StackHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private const string beatmap_data = @"
|
||||
using (var stream = new MemoryStream(@"
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
@@ -62,6 +47,65 @@ SliderTickRate:0.5
|
||||
311,185,218471,2,0,L|325:209,1,25
|
||||
311,185,218671,2,0,L|304:212,1,25
|
||||
311,185,240271,5,0,0:0:0:0:
|
||||
";
|
||||
"u8.ToArray()))
|
||||
using (var reader = new LineBufferedReader(stream))
|
||||
{
|
||||
var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty<Mod>());
|
||||
|
||||
var objects = converted.HitObjects.ToList();
|
||||
|
||||
// The last hitobject triggers the stacking
|
||||
for (int i = 0; i < objects.Count - 1; i++)
|
||||
Assert.AreEqual(0, ((OsuHitObject)objects[i]).StackHeight);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStackingEdgeCaseTwo()
|
||||
{
|
||||
using (var stream = new MemoryStream(@"
|
||||
osu file format v14
|
||||
// extracted from https://osu.ppy.sh/beatmapsets/365006#osu/801165
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.2
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:6
|
||||
CircleSize:4
|
||||
OverallDifficulty:8
|
||||
ApproachRate:9.3
|
||||
SliderMultiplier:2
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
5338,444.444444444444,4,2,0,50,1,0
|
||||
82893,-76.9230769230769,4,2,8,50,0,0
|
||||
85115,-76.9230769230769,4,2,0,50,0,0
|
||||
85337,-100,4,2,8,60,0,0
|
||||
85893,-100,4,2,7,60,0,0
|
||||
86226,-100,4,2,8,60,0,0
|
||||
88893,-58.8235294117647,4,1,8,70,0,1
|
||||
|
||||
[HitObjects]
|
||||
427,124,84226,1,0,3:0:0:0:
|
||||
427,124,84337,1,0,3:0:0:0:
|
||||
427,124,84449,1,8,0:0:0:0:
|
||||
"u8.ToArray()))
|
||||
using (var reader = new LineBufferedReader(stream))
|
||||
{
|
||||
var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
|
||||
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty<Mod>());
|
||||
|
||||
var objects = converted.HitObjects.ToList();
|
||||
|
||||
Assert.That(objects, Has.Count.EqualTo(3));
|
||||
|
||||
// The last hitobject triggers the stacking
|
||||
for (int i = 0; i < objects.Count - 1; i++)
|
||||
Assert.AreEqual(0, ((OsuHitObject)objects[i]).StackHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
continue;
|
||||
|
||||
double endTime = stackBaseObject.GetEndTime();
|
||||
double stackThreshold = objectN.TimePreempt * beatmap.StackLeniency;
|
||||
float stackThreshold = calculateStackThreshold(beatmap, objectN);
|
||||
|
||||
if (objectN.StartTime - endTime > stackThreshold)
|
||||
// We are no longer within stacking range of the next object.
|
||||
@@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
OsuHitObject objectI = hitObjects[i];
|
||||
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
|
||||
|
||||
double stackThreshold = objectI.TimePreempt * beatmap.StackLeniency;
|
||||
float stackThreshold = calculateStackThreshold(beatmap, objectI);
|
||||
|
||||
/* If this object is a hitcircle, then we enter this "special" case.
|
||||
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
|
||||
@@ -151,7 +151,10 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
|
||||
double endTime = objectN.GetEndTime();
|
||||
|
||||
if (objectI.StartTime - endTime > stackThreshold)
|
||||
// truncation to integer is required to match stable
|
||||
// compare https://github.com/peppy/osu-stable-reference/blob/08e3dafd525934cf48880b08e91c24ce4ad8b761/osu!/GameplayElements/HitObjectManager.cs#L1725
|
||||
// - both quantities being subtracted there are integers
|
||||
if ((int)objectI.StartTime - (int)endTime > stackThreshold)
|
||||
// We are no longer within stacking range of the previous object.
|
||||
break;
|
||||
|
||||
@@ -232,7 +235,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
|
||||
for (int j = i + 1; j < hitObjects.Count; j++)
|
||||
{
|
||||
double stackThreshold = hitObjects[i].TimePreempt * beatmap.StackLeniency;
|
||||
float stackThreshold = calculateStackThreshold(beatmap, hitObjects[i]);
|
||||
|
||||
if (hitObjects[j].StartTime - stackThreshold > startTime)
|
||||
break;
|
||||
@@ -264,5 +267,17 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// Truncation of <see cref="OsuHitObject.TimePreempt"/> to <see cref="int"/>, as well as keeping the result as <see cref="float"/>, are both done
|
||||
/// <a href="https://github.com/peppy/osu-stable-reference/blob/08e3dafd525934cf48880b08e91c24ce4ad8b761/osu!/GameplayElements/HitObjectManager.cs#L1652">
|
||||
/// for the purposes of stable compatibility
|
||||
/// </a>.
|
||||
/// Note that for top-level objects <see cref="OsuHitObject.TimePreempt"/> is supposed to be integral anyway;
|
||||
/// see <see cref="OsuHitObject.ApplyDefaultsToSelf"/> using <see cref="IBeatmapDifficultyInfo.DifficultyRangeInt"/> when calculating it.
|
||||
/// Slider ticks and end circles are the exception to that, but they do not matter for stacking.
|
||||
/// </remarks>
|
||||
private static float calculateStackThreshold(IBeatmap beatmap, OsuHitObject hitObject)
|
||||
=> (int)hitObject.TimePreempt * beatmap.StackLeniency;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, PREEMPT_RANGE);
|
||||
TimePreempt = IBeatmapDifficultyInfo.DifficultyRangeInt(difficulty.ApproachRate, PREEMPT_RANGE);
|
||||
|
||||
// Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
|
||||
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above.
|
||||
|
||||
@@ -412,7 +412,8 @@ namespace osu.Game.Rulesets.Osu
|
||||
Description = "Affects how early objects appear on screen relative to their hit time.",
|
||||
AdditionalMetrics =
|
||||
[
|
||||
new RulesetBeatmapAttribute.AdditionalMetric("Approach time", LocalisableString.Interpolate($@"{IBeatmapDifficultyInfo.DifficultyRange(effectiveDifficulty.ApproachRate, OsuHitObject.PREEMPT_RANGE):#,0.##} ms"))
|
||||
new RulesetBeatmapAttribute.AdditionalMetric("Approach time",
|
||||
LocalisableString.Interpolate($@"{IBeatmapDifficultyInfo.DifficultyRangeInt(effectiveDifficulty.ApproachRate, OsuHitObject.PREEMPT_RANGE):#,0.##} ms"))
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
/// </summary>
|
||||
protected bool AllowPartRotation { get; set; }
|
||||
|
||||
private Vector2 cursorScale;
|
||||
private Vector2 cursorScale = Vector2.One;
|
||||
|
||||
public Vector2 CursorScale
|
||||
{
|
||||
@@ -198,7 +198,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
float distance = diff.Length;
|
||||
Vector2 direction = diff / distance;
|
||||
|
||||
float interval = Texture.DisplayWidth / 2.5f * IntervalMultiplier;
|
||||
float interval = Texture.DisplayWidth * CursorScale.X / 2.5f * IntervalMultiplier;
|
||||
float stopAt = distance - (AvoidDrawingNearCursor ? interval : 0);
|
||||
|
||||
for (float d = interval; d < stopAt; d += interval)
|
||||
|
||||
@@ -44,7 +44,13 @@ namespace osu.Game.Tests.Online
|
||||
availableBeatmap = importedSet.Beatmaps[0];
|
||||
unavailableBeatmap = importedSet.Beatmaps[1];
|
||||
|
||||
Realm.Write(r => r.Remove(r.Find<BeatmapInfo>(unavailableBeatmap.ID)!));
|
||||
Realm.Write(r =>
|
||||
{
|
||||
BeatmapInfo available = r.Find<BeatmapInfo>(availableBeatmap.ID)!;
|
||||
available.OnlineMD5Hash = available.MD5Hash;
|
||||
|
||||
r.Remove(r.Find<BeatmapInfo>(unavailableBeatmap.ID)!);
|
||||
});
|
||||
}
|
||||
|
||||
public override void SetUpSteps()
|
||||
|
||||
@@ -27,6 +27,7 @@ using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Tests.Visual;
|
||||
using Realms;
|
||||
|
||||
namespace osu.Game.Tests.Online
|
||||
{
|
||||
@@ -229,6 +230,14 @@ namespace osu.Game.Tests.Online
|
||||
|
||||
return testBeatmapManager.CurrentImport = base.ImportModel(item, archive, parameters, cancellationToken);
|
||||
}
|
||||
|
||||
protected override void PostImport(BeatmapSetInfo model, Realm realm, ImportParameters parameters)
|
||||
{
|
||||
foreach (var beatmap in model.Beatmaps)
|
||||
beatmap.OnlineMD5Hash = beatmap.MD5Hash;
|
||||
|
||||
base.PostImport(model, realm, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
42
osu.Game.Tests/Visual/Editing/TestSceneFormSampleSet.cs
Normal file
42
osu.Game.Tests/Visual/Editing/TestSceneFormSampleSet.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.Graphics;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Components;
|
||||
using osu.Game.Tests.Visual.UserInterface;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public partial class TestSceneFormSampleSet : ThemeComparisonTestScene
|
||||
{
|
||||
public TestSceneFormSampleSet()
|
||||
: base(false)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Drawable CreateContent() => new PopoverContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new OsuContextMenuContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new FormSampleSet
|
||||
{
|
||||
Current =
|
||||
{
|
||||
Value = new EditorBeatmapSkin.SampleSet(3, "Custom set #3")
|
||||
{
|
||||
Filenames = ["normal-hitwhistle3.wav"]
|
||||
}
|
||||
},
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 0.4f,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
271
osu.Game.Tests/Visual/Settings/TestSceneSettingsItemV2.cs
Normal file
271
osu.Game.Tests/Visual/Settings/TestSceneSettingsItemV2.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Tests.Visual.UserInterface;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Settings
|
||||
{
|
||||
public partial class TestSceneSettingsItemV2 : ThemeComparisonTestScene
|
||||
{
|
||||
private readonly Bindable<SettingsNote.Data?> note = new Bindable<SettingsNote.Data?>();
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||
|
||||
private FormSliderBar<float> sliderBar = null!;
|
||||
private FormSliderBar<float> classicSliderBar = null!;
|
||||
|
||||
private SearchContainer searchContainer = null!;
|
||||
|
||||
public TestSceneSettingsItemV2()
|
||||
: base(false)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Drawable CreateContent()
|
||||
{
|
||||
return new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new BackgroundBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new OsuContextMenuContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 400,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Child = new PopoverContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new OsuScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
ScrollbarVisible = false,
|
||||
Child = searchContainer = new SearchContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(7),
|
||||
Padding = new MarginPadding { Vertical = 10 },
|
||||
Children = new[]
|
||||
{
|
||||
new SettingsItemV2(new FormTextBox
|
||||
{
|
||||
Caption = "Artist",
|
||||
HintText = "Poot artist here!",
|
||||
PlaceholderText = "Here is an artist",
|
||||
Current = { Value = string.Empty, Default = string.Empty }
|
||||
}),
|
||||
new SettingsItemV2(new FormTextBox
|
||||
{
|
||||
Caption = "Artist",
|
||||
HintText = "Poot artist here!",
|
||||
PlaceholderText = "Here is an artist",
|
||||
Current = { Value = string.Empty, Default = string.Empty, Disabled = true }
|
||||
}),
|
||||
new SettingsItemV2(new FormNumberBox(allowDecimals: true)
|
||||
{
|
||||
Caption = "Number",
|
||||
HintText = "Insert your favourite number",
|
||||
PlaceholderText = "Mine is 42!",
|
||||
Current = { Value = string.Empty, Default = string.Empty }
|
||||
}),
|
||||
new SettingsItemV2(new FormCheckBox
|
||||
{
|
||||
Caption = EditorSetupStrings.LetterboxDuringBreaks,
|
||||
HintText = EditorSetupStrings.LetterboxDuringBreaksDescription,
|
||||
})
|
||||
{
|
||||
Note = { BindTarget = note },
|
||||
},
|
||||
new SettingsItemV2(new FormCheckBox
|
||||
{
|
||||
Caption = EditorSetupStrings.LetterboxDuringBreaks,
|
||||
HintText = EditorSetupStrings.LetterboxDuringBreaksDescription,
|
||||
Current = { Disabled = true },
|
||||
}),
|
||||
new SettingsItemV2(new FormCheckBox
|
||||
{
|
||||
Caption = EditorSetupStrings.LetterboxDuringBreaks,
|
||||
HintText = EditorSetupStrings.LetterboxDuringBreaksDescription,
|
||||
Current = { Value = true, Disabled = true },
|
||||
}),
|
||||
new SettingsItemV2(new FormEnumDropdown<CountdownType>
|
||||
{
|
||||
Caption = EditorSetupStrings.EnableCountdown,
|
||||
HintText = EditorSetupStrings.CountdownDescription,
|
||||
}),
|
||||
new SettingsItemV2(new FormEnumDropdown<CountdownType>
|
||||
{
|
||||
Caption = EditorSetupStrings.EnableCountdown,
|
||||
HintText = EditorSetupStrings.CountdownDescription,
|
||||
Current = { Disabled = true },
|
||||
}),
|
||||
new SettingsItemV2(new FormEnumDropdown<Language>
|
||||
{
|
||||
Caption = "Dropdown with many items",
|
||||
HintText = EditorSetupStrings.CountdownDescription,
|
||||
})
|
||||
{
|
||||
Note = { BindTarget = note },
|
||||
},
|
||||
new SettingsItemV2(sliderBar = new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider",
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Value = 5,
|
||||
Precision = 0.1f,
|
||||
},
|
||||
}),
|
||||
new SettingsItemV2(new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider",
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Value = 5,
|
||||
Precision = 0.1f,
|
||||
Disabled = true,
|
||||
},
|
||||
TransferValueOnCommit = true,
|
||||
}),
|
||||
new SettingsItemV2(new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider without revert button",
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Value = 5,
|
||||
Precision = 0.1f,
|
||||
},
|
||||
})
|
||||
{
|
||||
ShowRevertToDefaultButton = false
|
||||
},
|
||||
new SettingsItemV2(classicSliderBar = new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider with classic default",
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Value = 5,
|
||||
Precision = 0.1f,
|
||||
},
|
||||
})
|
||||
{
|
||||
ApplyClassicDefault = () => classicSliderBar.Current.Value = 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDisplay()
|
||||
{
|
||||
AddStep("display", () => CreateThemedContent(OverlayColourScheme.Purple));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNote()
|
||||
{
|
||||
AddStep("set informational note", () => note.Value = new SettingsNote.Data(LayoutSettingsStrings.OsuIsRunningExclusiveFullscreen.ToString(), SettingsNote.Type.Informational));
|
||||
AddStep("set warning note",
|
||||
() => note.Value = new SettingsNote.Data(
|
||||
"Using unlimited frame limiter can lead to stutters, bad performance and overheating. It will not improve perceived latency. “2x refresh rate” is recommended.",
|
||||
SettingsNote.Type.Warning));
|
||||
AddStep("set critical note",
|
||||
() => note.Value = new SettingsNote.Data(
|
||||
"You have done something so horrible in the game settings to the point we have invented a new note type for this. Look at it, it's in red. It's worse than yellow.",
|
||||
SettingsNote.Type.Critical));
|
||||
AddStep("clear note", () => note.Value = null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClassicDefault()
|
||||
{
|
||||
AddStep("modify irrelevant setting", () => sliderBar.Current.Value = 4);
|
||||
AddStep("apply classic defaults", () => this.ChildrenOfType<ISettingsItem>().Where(i => i.HasClassicDefault).ForEach(s => s.ApplyClassicDefault()));
|
||||
AddStep("apply regular defaults", () => this.ChildrenOfType<ISettingsItem>().Where(i => i.HasClassicDefault).ForEach(s => s.ApplyDefault()));
|
||||
AddStep("set classic filter", () => searchContainer.SearchTerm = SettingsItemV2.CLASSIC_DEFAULT_SEARCH_TERM);
|
||||
AddStep("apply classic defaults", () => this.ChildrenOfType<ISettingsItem>().Where(i => i.HasClassicDefault).ForEach(s => s.ApplyClassicDefault()));
|
||||
AddStep("apply regular defaults", () => this.ChildrenOfType<ISettingsItem>().Where(i => i.HasClassicDefault).ForEach(s => s.ApplyDefault()));
|
||||
AddStep("set no filter", () => searchContainer.SearchTerm = string.Empty);
|
||||
AddAssert("irrelevant setting left out", () => sliderBar.Current.Value, () => Is.EqualTo(4));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the reset to default button uses the correct implementation of IsDefault to determine whether it should be shown or not.
|
||||
/// Values have been chosen so that after being set, Value != Default (but they are close enough that the difference is negligible compared to Precision).
|
||||
/// </summary>
|
||||
[TestCase(4.2f)]
|
||||
[TestCase(9.9f)]
|
||||
public void TestRestoreDefaultValueButtonPrecision(float initialValue)
|
||||
{
|
||||
BindableFloat current = null!;
|
||||
SettingsRevertToDefaultButton revertToDefaultButton = null!;
|
||||
|
||||
AddStep("set current bindable", () => sliderBar.Current = current = new BindableFloat(initialValue)
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Precision = 0.1f,
|
||||
});
|
||||
|
||||
AddStep("retrieve restore default button", () => revertToDefaultButton = sliderBar.FindClosestParent<SettingsItemV2>().ChildrenOfType<SettingsRevertToDefaultButton>().Single());
|
||||
|
||||
AddAssert("restore button hidden", () => revertToDefaultButton.X == 0);
|
||||
|
||||
AddStep("change value to next closest", () => sliderBar.Current.Value += current.Precision * 0.6f);
|
||||
AddUntilStep("restore button shown", () => revertToDefaultButton.X > 0);
|
||||
|
||||
AddStep("restore default", () => sliderBar.Current.SetDefault());
|
||||
AddUntilStep("restore button hidden", () => revertToDefaultButton.X == 0);
|
||||
}
|
||||
|
||||
private partial class BackgroundBox : Box
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
Colour = colourProvider.Background4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
using osu.Game.Screens.SelectV2;
|
||||
using osuTK.Input;
|
||||
using FilterControl = osu.Game.Screens.SelectV2.FilterControl;
|
||||
using NoResultsPlaceholder = osu.Game.Screens.SelectV2.NoResultsPlaceholder;
|
||||
|
||||
@@ -354,6 +355,30 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
checkMatchedBeatmaps(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScopeToBeatmapWhenDifficultiesSplitApart()
|
||||
{
|
||||
ImportBeatmapForRuleset(0);
|
||||
ImportBeatmapForRuleset(0);
|
||||
|
||||
LoadSongSelect();
|
||||
SortBy(SortMode.Difficulty);
|
||||
checkMatchedBeatmaps(6);
|
||||
|
||||
AddUntilStep("wait for spread indicator", () => this.ChildrenOfType<PanelBeatmapStandalone.SpreadDisplay>().Any(d => d.Enabled.Value));
|
||||
AddStep("click spread indicator", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<PanelBeatmapStandalone.SpreadDisplay>().Single(d => d.Enabled.Value));
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
WaitForFiltering();
|
||||
checkMatchedBeatmaps(3);
|
||||
|
||||
AddStep("press Escape", () => InputManager.Key(Key.Escape));
|
||||
WaitForFiltering();
|
||||
checkMatchedBeatmaps(6);
|
||||
}
|
||||
|
||||
private NoResultsPlaceholder? getPlaceholder() => SongSelect.ChildrenOfType<NoResultsPlaceholder>().FirstOrDefault();
|
||||
|
||||
private void checkMatchedBeatmaps(int expected) => AddUntilStep($"{expected} matching shown", () => Carousel.MatchedBeatmapsCount, () => Is.EqualTo(expected));
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
// 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;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens.Edit.Setup;
|
||||
using osuTK;
|
||||
|
||||
@@ -25,109 +28,199 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
protected override Drawable CreateContent() => new OsuContextMenuContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new PopoverContainer
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new OsuScrollContainer
|
||||
new BackgroundBox
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new FillFlowContainer
|
||||
},
|
||||
new PopoverContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new OsuScrollContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 400,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(5),
|
||||
Padding = new MarginPadding(10),
|
||||
Children = new Drawable[]
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
new FormTextBox
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 400,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(5),
|
||||
Padding = new MarginPadding(10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Caption = "Artist",
|
||||
HintText = "Poot artist here!",
|
||||
PlaceholderText = "Here is an artist",
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
new FormTextBox
|
||||
{
|
||||
Caption = "Artist",
|
||||
HintText = "Poot artist here!",
|
||||
PlaceholderText = "Here is an artist",
|
||||
Current = { Disabled = true },
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
new FormNumberBox(allowDecimals: true)
|
||||
{
|
||||
Caption = "Number",
|
||||
HintText = "Insert your favourite number",
|
||||
PlaceholderText = "Mine is 42!",
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
new FormCheckBox
|
||||
{
|
||||
Caption = EditorSetupStrings.LetterboxDuringBreaks,
|
||||
HintText = EditorSetupStrings.LetterboxDuringBreaksDescription,
|
||||
},
|
||||
new FormCheckBox
|
||||
{
|
||||
Caption = EditorSetupStrings.LetterboxDuringBreaks,
|
||||
HintText = EditorSetupStrings.LetterboxDuringBreaksDescription,
|
||||
Current = { Disabled = true },
|
||||
},
|
||||
new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider",
|
||||
Current = new BindableFloat
|
||||
new FormTextBox
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Value = 5,
|
||||
Precision = 0.1f,
|
||||
Caption = "Artist",
|
||||
HintText = "Poot artist here!",
|
||||
PlaceholderText = "Here is an artist",
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
new FormEnumDropdown<CountdownType>
|
||||
{
|
||||
Caption = EditorSetupStrings.EnableCountdown,
|
||||
HintText = EditorSetupStrings.CountdownDescription,
|
||||
},
|
||||
new FormFileSelector
|
||||
{
|
||||
Caption = "File selector",
|
||||
PlaceholderText = "Select a file",
|
||||
},
|
||||
new FormBeatmapFileSelector(true)
|
||||
{
|
||||
Caption = "File selector with intermediate choice dialog",
|
||||
PlaceholderText = "Select a file",
|
||||
},
|
||||
new FormColourPalette
|
||||
{
|
||||
Caption = "Combo colours",
|
||||
Colours =
|
||||
new FormTextBox
|
||||
{
|
||||
Colour4.Red,
|
||||
Colour4.Green,
|
||||
Colour4.Blue,
|
||||
Colour4.Yellow,
|
||||
}
|
||||
},
|
||||
new FormButton
|
||||
{
|
||||
Caption = "No text in button",
|
||||
Action = () => { },
|
||||
},
|
||||
new FormButton
|
||||
{
|
||||
Caption = "Text in button which is pretty long and is very likely to wrap",
|
||||
ButtonText = "Foo the bar",
|
||||
Action = () => { },
|
||||
Caption = "Artist",
|
||||
HintText = "Poot artist here!",
|
||||
PlaceholderText = "Here is an artist",
|
||||
Current = { Disabled = true },
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
new FormNumberBox(allowDecimals: true)
|
||||
{
|
||||
Caption = "Number",
|
||||
HintText = "Insert your favourite number",
|
||||
PlaceholderText = "Mine is 42!",
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
new FormCheckBox
|
||||
{
|
||||
Caption = EditorSetupStrings.LetterboxDuringBreaks,
|
||||
HintText = EditorSetupStrings.LetterboxDuringBreaksDescription,
|
||||
},
|
||||
new FormCheckBox
|
||||
{
|
||||
Caption = EditorSetupStrings.LetterboxDuringBreaks,
|
||||
HintText = EditorSetupStrings.LetterboxDuringBreaksDescription,
|
||||
Current = { Disabled = true },
|
||||
},
|
||||
new FormCheckBox
|
||||
{
|
||||
Caption = EditorSetupStrings.LetterboxDuringBreaks,
|
||||
HintText = EditorSetupStrings.LetterboxDuringBreaksDescription,
|
||||
Current = { Value = true, Disabled = true },
|
||||
},
|
||||
new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider",
|
||||
HintText = "Slider hint",
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Value = 5,
|
||||
Precision = 0.1f,
|
||||
},
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider",
|
||||
HintText = "Slider hint",
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Value = 5,
|
||||
Precision = 0.1f,
|
||||
Disabled = true,
|
||||
},
|
||||
TransferValueOnCommit = true,
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider (percentage)",
|
||||
HintText = "Percentage slider hint",
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 1,
|
||||
Value = 0.2f,
|
||||
Precision = 0.0001f,
|
||||
},
|
||||
DisplayAsPercentage = true,
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider (custom)",
|
||||
HintText = "Custom slider hint",
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 1,
|
||||
Value = 0.2f,
|
||||
Precision = 0.0001f,
|
||||
},
|
||||
LabelFormat = v => $"{v * 100:0.00} funometer",
|
||||
TooltipFormat = v => $"This setting has the value set to {v * 100:0.00} funometer.",
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider (custom)",
|
||||
HintText = "Custom slider hint",
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 1,
|
||||
Value = 0.2f,
|
||||
Precision = 0.0001f,
|
||||
Disabled = true,
|
||||
},
|
||||
TransferValueOnCommit = true,
|
||||
LabelFormat = v => $"{v * 100:0.00} funometer",
|
||||
TooltipFormat = v => $"This setting has the value set to {v * 100:0.00} funometer.",
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
new FormEnumDropdown<CountdownType>
|
||||
{
|
||||
Caption = EditorSetupStrings.EnableCountdown,
|
||||
HintText = EditorSetupStrings.CountdownDescription,
|
||||
},
|
||||
new FormEnumDropdown<CountdownType>
|
||||
{
|
||||
Caption = EditorSetupStrings.EnableCountdown,
|
||||
HintText = EditorSetupStrings.CountdownDescription,
|
||||
Current = { Disabled = true },
|
||||
},
|
||||
new FormFileSelector
|
||||
{
|
||||
Caption = "File selector",
|
||||
PlaceholderText = "Select a file",
|
||||
},
|
||||
new FormBeatmapFileSelector(true)
|
||||
{
|
||||
Caption = "File selector with intermediate choice dialog",
|
||||
PlaceholderText = "Select a file",
|
||||
},
|
||||
new FormColourPalette
|
||||
{
|
||||
Caption = "Combo colours",
|
||||
Colours =
|
||||
{
|
||||
Colour4.Red,
|
||||
Colour4.Green,
|
||||
Colour4.Blue,
|
||||
Colour4.Yellow,
|
||||
}
|
||||
},
|
||||
new FormButton
|
||||
{
|
||||
Caption = "No text in button",
|
||||
Action = () => { },
|
||||
},
|
||||
new FormButton
|
||||
{
|
||||
Caption = "Text in button which is pretty long and is very likely to wrap",
|
||||
ButtonText = "Foo the bar",
|
||||
Action = () => { },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private partial class BackgroundBox : Box
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
Colour = colourProvider.Background4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,5 +107,105 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
AddAssert("slider is default", () => slider.Current.IsDefault);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDisabled()
|
||||
{
|
||||
FormSliderBar<float> slider = null!;
|
||||
|
||||
AddStep("create content", () =>
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 0.5f,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
slider = new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider",
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Precision = 0.1f,
|
||||
Default = 5f,
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
});
|
||||
AddStep("set slider to 1", () => slider.Current.Value = 1);
|
||||
AddStep("disable slider", () => slider.Current.Disabled = true);
|
||||
|
||||
AddStep("move mouse to nub", () => InputManager.MoveMouseTo(slider.ChildrenOfType<Circle>().Single()));
|
||||
|
||||
AddStep("double click nub", () =>
|
||||
{
|
||||
InputManager.Click(MouseButton.Left);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddAssert("slider is still at 1", () => slider.Current.Value, () => Is.EqualTo(1));
|
||||
|
||||
AddStep("click on textbox part", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(slider.ChildrenOfType<FormTextBox.InnerTextBox>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddAssert("no text selected", () => slider.ChildrenOfType<FormTextBox.InnerTextBox>().Single().SelectedText, () => Is.Empty);
|
||||
AddStep("attempt to input text", () =>
|
||||
{
|
||||
InputManager.Key(Key.Number4);
|
||||
InputManager.Key(Key.Enter);
|
||||
});
|
||||
AddAssert("slider is still at 1", () => slider.Current.Value, () => Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDisabledImmediately()
|
||||
{
|
||||
FormSliderBar<float> slider = null!;
|
||||
|
||||
AddStep("create content", () =>
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 0.5f,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
slider = new FormSliderBar<float>
|
||||
{
|
||||
Caption = "Slider",
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Precision = 0.1f,
|
||||
Default = 5f,
|
||||
Disabled = true,
|
||||
},
|
||||
TransferValueOnCommit = true,
|
||||
},
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("click on textbox part", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(slider.ChildrenOfType<FormTextBox.InnerTextBox>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddAssert("no text selected", () => slider.ChildrenOfType<FormTextBox.InnerTextBox>().Single().SelectedText, () => Is.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,19 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public partial class TestSceneLabelledSwitchButton : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void TestSwitchButton(bool hasDescription) => createSwitchButton(hasDescription);
|
||||
|
||||
@@ -4,15 +4,21 @@
|
||||
#nullable disable
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public partial class TestSceneSwitchButton : OsuManualInputManagerTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);
|
||||
|
||||
private SwitchButton switchButton;
|
||||
|
||||
[SetUp]
|
||||
@@ -42,5 +48,15 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddStep("toggle bindable", () => bindable.Toggle());
|
||||
AddStep("toggle bindable", () => bindable.Toggle());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDisabledState()
|
||||
{
|
||||
AddToggleStep("toggle disabled", v =>
|
||||
{
|
||||
if (switchButton.IsNotNull())
|
||||
switchButton.Current.Disabled = v;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Online.API;
|
||||
@@ -56,7 +55,7 @@ namespace osu.Game.Tournament.Screens.Setup
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(0.2f),
|
||||
Colour = ColourProvider.Background5,
|
||||
},
|
||||
new OsuScrollContainer
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Tournament.Models;
|
||||
|
||||
namespace osu.Game.Tournament.Screens
|
||||
@@ -15,6 +16,9 @@ namespace osu.Game.Tournament.Screens
|
||||
[Resolved]
|
||||
protected LadderInfo LadderInfo { get; private set; } = null!;
|
||||
|
||||
[Cached]
|
||||
protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
protected TournamentScreen()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@@ -328,6 +328,14 @@ namespace osu.Game.Beatmaps
|
||||
.Filter(query, arguments)
|
||||
.FirstOrDefault()?.Detach());
|
||||
|
||||
/// <summary>
|
||||
/// Perform a lookup query on available <see cref="BeatmapInfo"/>s for a specific online ID.
|
||||
/// </summary>
|
||||
/// <returns>A matching local beatmap info if existing and in a valid state.</returns>
|
||||
public BeatmapInfo? QueryOnlineBeatmapId(int id) => Realm.Run(r =>
|
||||
r.All<BeatmapInfo>()
|
||||
.ForOnlineId(id).SingleOrDefault()?.Detach());
|
||||
|
||||
/// <summary>
|
||||
/// A default representation of a WorkingBeatmap to use when no beatmap is available.
|
||||
/// </summary>
|
||||
@@ -476,9 +484,11 @@ namespace osu.Game.Beatmaps
|
||||
public Task<ExternalEditOperation<BeatmapSetInfo>> BeginExternalEditing(BeatmapSetInfo model) =>
|
||||
beatmapImporter.BeginExternalEditing(model);
|
||||
|
||||
public Task Export(BeatmapSetInfo beatmap) => beatmapExporter.ExportAsync(beatmap.ToLive(Realm));
|
||||
public Task Export(BeatmapSetInfo beatmapSet) => beatmapExporter.ExportAsync(beatmapSet.ToLive(Realm));
|
||||
|
||||
public Task ExportLegacy(BeatmapSetInfo beatmap) => legacyBeatmapExporter.ExportAsync(beatmap.ToLive(Realm));
|
||||
public Task ExportLegacy(BeatmapSetInfo beatmapSet) => legacyBeatmapExporter.ExportAsync(beatmapSet.ToLive(Realm));
|
||||
|
||||
public Task ExportLegacy(BeatmapInfo beatmap) => legacyBeatmapExporter.ExportAsync(beatmap.ToLive(Realm));
|
||||
|
||||
private void updateHashAndMarkDirty(BeatmapSetInfo setInfo)
|
||||
{
|
||||
|
||||
@@ -95,6 +95,31 @@ namespace osu.Game.Beatmaps
|
||||
static double DifficultyRange(double difficulty, DifficultyRange range)
|
||||
=> DifficultyRange(difficulty, range.Min, range.Mid, range.Max);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.
|
||||
/// Floors the value to `int`, usually to match osu!stable spec.
|
||||
/// </summary>
|
||||
/// <param name="difficulty">The difficulty value to be mapped.</param>
|
||||
/// <param name="range">The values that define the two linear ranges.
|
||||
/// <list type="table">
|
||||
/// <item>
|
||||
/// <term>od0</term>
|
||||
/// <description>Minimum of the resulting range which will be achieved by a difficulty value of 0.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>od5</term>
|
||||
/// <description>Midpoint of the resulting range which will be achieved by a difficulty value of 5.</description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>od10</term>
|
||||
/// <description>Maximum of the resulting range which will be achieved by a difficulty value of 10.</description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </param>
|
||||
/// <returns>Value to which the difficulty value maps in the specified range.</returns>
|
||||
static int DifficultyRangeInt(double difficulty, DifficultyRange range)
|
||||
=> (int)DifficultyRange(difficulty, range.Min, range.Mid, range.Max);
|
||||
|
||||
/// <summary>
|
||||
/// Inverse function to <see cref="DifficultyRange(double,double,double,double)"/>.
|
||||
/// Maps a value returned by the function above back to the difficulty that produced it.
|
||||
|
||||
@@ -2,17 +2,22 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Database
|
||||
@@ -175,5 +180,54 @@ namespace osu.Game.Database
|
||||
}
|
||||
|
||||
protected override string FileExtension => @".osz";
|
||||
|
||||
public Task ExportAsync(Live<BeatmapInfo> beatmap) => Task.Run(() =>
|
||||
{
|
||||
string itemFilename = Path.GetFileNameWithoutExtension(beatmap.PerformRead(s => s.File!.Filename.GetValidFilename()));
|
||||
const string osu_extension = @".osu";
|
||||
|
||||
if (itemFilename.Length > MAX_FILENAME_LENGTH - osu_extension.Length)
|
||||
itemFilename = itemFilename.Remove(MAX_FILENAME_LENGTH - osu_extension.Length);
|
||||
|
||||
IEnumerable<string> existingExports = ExportStorage
|
||||
.GetFiles(string.Empty, $"{itemFilename}*{osu_extension}")
|
||||
.Concat(ExportStorage.GetDirectories(string.Empty));
|
||||
|
||||
string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{osu_extension}");
|
||||
|
||||
ProgressNotification notification = new ProgressNotification
|
||||
{
|
||||
State = ProgressNotificationState.Active,
|
||||
Text = $"Exporting {itemFilename}...",
|
||||
};
|
||||
|
||||
PostNotification?.Invoke(notification);
|
||||
|
||||
try
|
||||
{
|
||||
beatmap.PerformRead(b =>
|
||||
{
|
||||
using var exportStream = ExportStorage.CreateFileSafely(filename);
|
||||
using var inputFile = GetFileContents(b.BeatmapSet!, b.File!);
|
||||
|
||||
if (inputFile == null)
|
||||
throw new InvalidOperationException($"Beatmap file {b.File!.Filename} could not be opened!");
|
||||
|
||||
inputFile.CopyTo(exportStream);
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
notification.State = ProgressNotificationState.Cancelled;
|
||||
|
||||
// cleanup if export is failed or canceled.
|
||||
ExportStorage.Delete(filename);
|
||||
throw;
|
||||
}
|
||||
|
||||
notification.CompletionText = $"Exported {itemFilename}! Click to view.";
|
||||
notification.CompletionClickAction = () => ExportStorage.PresentFileExternally(filename);
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,13 +41,13 @@ namespace osu.Game.Database
|
||||
protected abstract string FileExtension { get; }
|
||||
|
||||
protected readonly Storage UserFileStorage;
|
||||
private readonly Storage exportStorage;
|
||||
protected readonly Storage ExportStorage;
|
||||
|
||||
public Action<Notification>? PostNotification { get; set; }
|
||||
|
||||
protected LegacyExporter(Storage storage)
|
||||
{
|
||||
exportStorage = (storage as OsuStorage)?.GetExportStorage() ?? storage.GetStorageForDirectory(@"exports");
|
||||
ExportStorage = (storage as OsuStorage)?.GetExportStorage() ?? storage.GetStorageForDirectory(@"exports");
|
||||
UserFileStorage = storage.GetStorageForDirectory(@"files");
|
||||
}
|
||||
|
||||
@@ -74,9 +74,9 @@ namespace osu.Game.Database
|
||||
if (itemFilename.Length > MAX_FILENAME_LENGTH - FileExtension.Length)
|
||||
itemFilename = itemFilename.Remove(MAX_FILENAME_LENGTH - FileExtension.Length);
|
||||
|
||||
IEnumerable<string> existingExports = exportStorage
|
||||
IEnumerable<string> existingExports = ExportStorage
|
||||
.GetFiles(string.Empty, $"{itemFilename}*{FileExtension}")
|
||||
.Concat(exportStorage.GetDirectories(string.Empty));
|
||||
.Concat(ExportStorage.GetDirectories(string.Empty));
|
||||
|
||||
string filename = NamingUtils.GetNextBestFilename(existingExports, $"{itemFilename}{FileExtension}");
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace osu.Game.Database
|
||||
|
||||
try
|
||||
{
|
||||
using (var stream = exportStorage.CreateFileSafely(filename))
|
||||
using (var stream = ExportStorage.CreateFileSafely(filename))
|
||||
{
|
||||
await ExportToStreamAsync(model, stream, notification, linkedSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
@@ -102,12 +102,12 @@ namespace osu.Game.Database
|
||||
notification.State = ProgressNotificationState.Cancelled;
|
||||
|
||||
// cleanup if export is failed or canceled.
|
||||
exportStorage.Delete(filename);
|
||||
ExportStorage.Delete(filename);
|
||||
throw;
|
||||
}
|
||||
|
||||
notification.CompletionText = $"Exported {itemFilename}! Click to view.";
|
||||
notification.CompletionClickAction = () => exportStorage.PresentFileExternally(filename);
|
||||
notification.CompletionClickAction = () => ExportStorage.PresentFileExternally(filename);
|
||||
notification.State = ProgressNotificationState.Completed;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using Realms;
|
||||
|
||||
namespace osu.Game.Database
|
||||
@@ -102,5 +104,13 @@ namespace osu.Game.Database
|
||||
/// Quite often we only care about changes at a collection level. This can be used to guard and early-return when no such changes are in a callback.
|
||||
/// </remarks>
|
||||
public static bool HasCollectionChanges(this ChangeSet changes) => changes.InsertedIndices.Length > 0 || changes.DeletedIndices.Length > 0 || changes.Moves.Length > 0;
|
||||
|
||||
public static IQueryable<BeatmapInfo> NotDeleted(this IQueryable<BeatmapInfo> beatmaps) =>
|
||||
beatmaps.Filter($@"{nameof(BeatmapInfo.BeatmapSet)}.{nameof(BeatmapSetInfo.DeletePending)} == false");
|
||||
|
||||
public static IQueryable<BeatmapInfo> ForOnlineId(this IQueryable<BeatmapInfo> beatmaps, int id) =>
|
||||
beatmaps
|
||||
.NotDeleted()
|
||||
.Filter($@"{nameof(BeatmapInfo.OnlineID)} == $0 AND {nameof(BeatmapInfo.MD5Hash)} == {nameof(BeatmapInfo.OnlineMD5Hash)}", id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace osu.Game.Graphics
|
||||
public static IconUsage EditCircle => get(OsuIconMapping.EditCircle);
|
||||
public static IconUsage LeftCircle => get(OsuIconMapping.LeftCircle);
|
||||
public static IconUsage RightCircle => get(OsuIconMapping.RightCircle);
|
||||
public static IconUsage Undo => get(OsuIconMapping.Undo);
|
||||
|
||||
public static IconUsage Audio => get(OsuIconMapping.Audio);
|
||||
public static IconUsage Beatmap => get(OsuIconMapping.Beatmap);
|
||||
@@ -386,6 +387,9 @@ namespace osu.Game.Graphics
|
||||
[Description(@"twitter")]
|
||||
Twitter,
|
||||
|
||||
[Description(@"undo")]
|
||||
Undo,
|
||||
|
||||
[Description(@"user-interface")]
|
||||
UserInterface,
|
||||
|
||||
|
||||
@@ -25,12 +25,12 @@ namespace osu.Game.Graphics.UserInterface
|
||||
/// </summary>
|
||||
public bool DisplayAsPercentage { get; set; }
|
||||
|
||||
public virtual LocalisableString TooltipText { get; private set; }
|
||||
public virtual LocalisableString TooltipText { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of decimal digits to be displayed in the tooltip.
|
||||
/// </summary>
|
||||
private const int max_decimal_digits = 5;
|
||||
public const int MAX_DECIMAL_DIGITS = 5;
|
||||
|
||||
private Sample sample = null!;
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
CurrentNumber.BindValueChanged(current => TooltipText = GetDisplayableValue(current.NewValue), true);
|
||||
CurrentNumber.BindValueChanged(current => TooltipText = GetTooltipText(current.NewValue), true);
|
||||
}
|
||||
|
||||
protected override void OnUserChange(T value)
|
||||
@@ -55,7 +55,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
|
||||
playSample(value);
|
||||
|
||||
TooltipText = GetDisplayableValue(value);
|
||||
TooltipText = GetTooltipText(value);
|
||||
}
|
||||
|
||||
private void playSample(T value)
|
||||
@@ -83,6 +83,6 @@ namespace osu.Game.Graphics.UserInterface
|
||||
channel.Play();
|
||||
}
|
||||
|
||||
public LocalisableString GetDisplayableValue(T value) => value.ToStandardFormattedString(max_decimal_digits, DisplayAsPercentage);
|
||||
protected virtual LocalisableString GetTooltipText(T value) => value.ToStandardFormattedString(MAX_DECIMAL_DIGITS, DisplayAsPercentage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// 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;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -13,13 +16,12 @@ using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
public partial class FormCheckBox : CompositeDrawable, IHasCurrentValue<bool>
|
||||
public partial class FormCheckBox : CompositeDrawable, IHasCurrentValue<bool>, IFormControl
|
||||
{
|
||||
public Bindable<bool> Current
|
||||
{
|
||||
@@ -42,10 +44,10 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
private Box background = null!;
|
||||
private FormFieldCaption caption = null!;
|
||||
private OsuSpriteText text = null!;
|
||||
private Nub checkbox = null!;
|
||||
|
||||
private Sample? sampleChecked;
|
||||
private Sample? sampleUnchecked;
|
||||
private Sample? sampleDisabled;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
@@ -86,7 +88,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
},
|
||||
checkbox = new Nub
|
||||
new SwitchButton
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
@@ -98,6 +100,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
|
||||
sampleChecked = audio.Samples.Get(@"UI/check-on");
|
||||
sampleUnchecked = audio.Samples.Get(@"UI/check-off");
|
||||
sampleDisabled = audio.Samples.Get(@"UI/default-select-disabled");
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -109,6 +112,8 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
updateState();
|
||||
playSamples();
|
||||
background.FlashColour(ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark2), 800, Easing.OutQuint);
|
||||
|
||||
ValueChanged?.Invoke();
|
||||
});
|
||||
current.BindDisabledChanged(_ => updateState(), true);
|
||||
}
|
||||
@@ -137,25 +142,36 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
if (!Current.Disabled)
|
||||
Current.Value = !Current.Value;
|
||||
else
|
||||
sampleDisabled?.Play();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
background.Colour = Current.Disabled ? colourProvider.Background4 : colourProvider.Background5;
|
||||
caption.Colour = Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content2;
|
||||
checkbox.Colour = Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content1;
|
||||
text.Colour = Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content1;
|
||||
caption.Colour = Current.Disabled ? colourProvider.Background1 : colourProvider.Content2;
|
||||
text.Colour = Current.Disabled ? colourProvider.Background1 : colourProvider.Content1;
|
||||
|
||||
text.Text = Current.Value ? CommonStrings.Enabled : CommonStrings.Disabled;
|
||||
|
||||
if (!Current.Disabled)
|
||||
{
|
||||
BorderThickness = IsHovered ? 2 : 0;
|
||||
// use FadeColour to override any existing colour transform (i.e. FlashColour on click).
|
||||
background.FadeColour(IsHovered
|
||||
? ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark4)
|
||||
: colourProvider.Background5);
|
||||
|
||||
if (IsHovered)
|
||||
BorderColour = colourProvider.Light4;
|
||||
}
|
||||
BorderThickness = IsHovered ? 2 : 0;
|
||||
BorderColour = Current.Disabled ? colourProvider.Dark1 : colourProvider.Light4;
|
||||
}
|
||||
|
||||
public IEnumerable<LocalisableString> FilterTerms => Caption.Yield();
|
||||
|
||||
public event Action? ValueChanged;
|
||||
|
||||
public bool IsDefault => Current.IsDefault;
|
||||
|
||||
public void SetDefault() => Current.SetDefault();
|
||||
|
||||
public bool IsDisabled => Current.Disabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -17,7 +18,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
public partial class FormDropdown<T> : OsuDropdown<T>
|
||||
public partial class FormDropdown<T> : OsuDropdown<T>, IFormControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Caption describing this slider bar, displayed on top of the controls.
|
||||
@@ -29,6 +30,12 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
/// </summary>
|
||||
public LocalisableString HintText { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum height of the dropdown's menu.
|
||||
/// By default, this is set to 200px high. Set to <see cref="float.PositiveInfinity"/> to remove such limit.
|
||||
/// </summary>
|
||||
public float MaxHeight { get; set; } = 200;
|
||||
|
||||
private FormDropdownHeader header = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@@ -40,12 +47,40 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
header.HintText = HintText;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Current.BindValueChanged(_ => ValueChanged?.Invoke());
|
||||
}
|
||||
|
||||
public virtual IEnumerable<LocalisableString> FilterTerms
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return Caption;
|
||||
|
||||
foreach (var item in MenuItems)
|
||||
yield return item.Text.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public event Action? ValueChanged;
|
||||
|
||||
public bool IsDefault => Current.IsDefault;
|
||||
|
||||
public void SetDefault() => Current.SetDefault();
|
||||
|
||||
public bool IsDisabled => Current.Disabled;
|
||||
|
||||
protected override DropdownHeader CreateHeader() => header = new FormDropdownHeader
|
||||
{
|
||||
Dropdown = this,
|
||||
};
|
||||
|
||||
protected override DropdownMenu CreateMenu() => new FormDropdownMenu();
|
||||
protected override DropdownMenu CreateMenu() => new FormDropdownMenu
|
||||
{
|
||||
MaxHeight = MaxHeight,
|
||||
};
|
||||
|
||||
private partial class FormDropdownHeader : DropdownHeader
|
||||
{
|
||||
@@ -136,10 +171,14 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Size = new Vector2(16),
|
||||
Margin = new MarginPadding { Right = 5 },
|
||||
},
|
||||
};
|
||||
|
||||
AddInternal(new HoverClickSounds());
|
||||
AddInternal(new HoverClickSounds
|
||||
{
|
||||
Enabled = { BindTarget = Enabled },
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -175,29 +214,26 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
label.Alpha = string.IsNullOrEmpty(SearchBar.SearchTerm.Value) ? 1 : 0;
|
||||
|
||||
caption.Colour = Dropdown.Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content2;
|
||||
label.Colour = Dropdown.Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content1;
|
||||
chevron.Colour = Dropdown.Current.Disabled ? colourProvider.Foreground1 : colourProvider.Content1;
|
||||
caption.Colour = Dropdown.Current.Disabled ? colourProvider.Background1 : colourProvider.Content2;
|
||||
label.Colour = Dropdown.Current.Disabled ? colourProvider.Background1 : colourProvider.Content1;
|
||||
chevron.Colour = Dropdown.Current.Disabled ? colourProvider.Background1 : colourProvider.Content1;
|
||||
DisabledColour = Colour4.White;
|
||||
|
||||
bool dropdownOpen = Dropdown.Menu.State == MenuState.Open;
|
||||
|
||||
if (!Dropdown.Current.Disabled)
|
||||
{
|
||||
BorderThickness = IsHovered || dropdownOpen ? 2 : 0;
|
||||
BorderThickness = IsHovered || dropdownOpen ? 2 : 0;
|
||||
|
||||
if (Dropdown.Current.Disabled)
|
||||
BorderColour = colourProvider.Dark1;
|
||||
else
|
||||
BorderColour = dropdownOpen ? colourProvider.Highlight1 : colourProvider.Light4;
|
||||
|
||||
if (dropdownOpen)
|
||||
Background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark3);
|
||||
else if (IsHovered)
|
||||
Background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark4);
|
||||
else
|
||||
Background.Colour = colourProvider.Background5;
|
||||
}
|
||||
if (dropdownOpen)
|
||||
Background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark3);
|
||||
else if (IsHovered)
|
||||
Background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark4);
|
||||
else
|
||||
{
|
||||
Background.Colour = colourProvider.Background4;
|
||||
}
|
||||
Background.Colour = colourProvider.Background5;
|
||||
}
|
||||
|
||||
private void updateChevron()
|
||||
|
||||
@@ -252,7 +252,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
return popover;
|
||||
}
|
||||
|
||||
protected partial class FileChooserPopover : OsuPopover
|
||||
public partial class FileChooserPopover : OsuPopover
|
||||
{
|
||||
protected override string PopInSampleName => "UI/overlay-big-pop-in";
|
||||
protected override string PopOutSampleName => "UI/overlay-big-pop-out";
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using osu.Framework.Allocation;
|
||||
@@ -16,13 +17,15 @@ using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
public partial class FormSliderBar<T> : CompositeDrawable, IHasCurrentValue<T>
|
||||
public partial class FormSliderBar<T> : CompositeDrawable, IHasCurrentValue<T>, IFormControl
|
||||
where T : struct, INumber<T>, IMinMaxValue<T>
|
||||
{
|
||||
public Bindable<T> Current
|
||||
@@ -96,9 +99,31 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether to format the tooltip as a percentage or the actual value.
|
||||
/// </summary>
|
||||
public bool DisplayAsPercentage { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether sound effects should play when adjusting this slider.
|
||||
/// </summary>
|
||||
public bool PlaySamplesOnAdjust { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The string formatting function to use for the value label.
|
||||
/// </summary>
|
||||
public Func<T, LocalisableString> LabelFormat { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The string formatting function to use for the slider's tooltip text.
|
||||
/// If not provided, <see cref="LabelFormat"/> is used.
|
||||
/// </summary>
|
||||
public Func<T, LocalisableString> TooltipFormat { get; init; }
|
||||
|
||||
private Box background = null!;
|
||||
private Box flashLayer = null!;
|
||||
private FormTextBox.InnerTextBox textBox = null!;
|
||||
private OsuSpriteText valueLabel = null!;
|
||||
private InnerSlider slider = null!;
|
||||
private FormFieldCaption captionText = null!;
|
||||
private IFocusManager focusManager = null!;
|
||||
@@ -108,6 +133,12 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
|
||||
private readonly Bindable<Language> currentLanguage = new Bindable<Language>();
|
||||
|
||||
public FormSliderBar()
|
||||
{
|
||||
LabelFormat ??= defaultLabelFormat;
|
||||
TooltipFormat ??= v => LabelFormat(v);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, OsuGame? game)
|
||||
{
|
||||
@@ -152,6 +183,10 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.5f,
|
||||
// the textbox is hidden when the control is unfocused,
|
||||
// but clicking on the label should reach the textbox,
|
||||
// therefore make it always present.
|
||||
AlwaysPresent = true,
|
||||
CommitOnFocusLost = true,
|
||||
SelectAllOnFocus = true,
|
||||
OnInputError = () =>
|
||||
@@ -161,6 +196,14 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
},
|
||||
TabbableContentContainer = tabbableContentContainer,
|
||||
},
|
||||
valueLabel = new TruncatingSpriteText
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.5f,
|
||||
Padding = new MarginPadding { Right = 5 },
|
||||
},
|
||||
slider = new InnerSlider
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
@@ -169,6 +212,9 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
Width = 0.5f,
|
||||
Current = currentNumberInstantaneous,
|
||||
OnCommit = () => current.Value = currentNumberInstantaneous.Value,
|
||||
TooltipFormat = TooltipFormat,
|
||||
DisplayAsPercentage = DisplayAsPercentage,
|
||||
PlaySamplesOnAdjust = PlaySamplesOnAdjust,
|
||||
}
|
||||
},
|
||||
},
|
||||
@@ -194,7 +240,12 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
slider.IsDragging.BindValueChanged(_ => updateState());
|
||||
slider.Focused.BindValueChanged(_ => updateState());
|
||||
|
||||
current.ValueChanged += e => currentNumberInstantaneous.Value = e.NewValue;
|
||||
current.ValueChanged += e =>
|
||||
{
|
||||
currentNumberInstantaneous.Value = e.NewValue;
|
||||
ValueChanged?.Invoke();
|
||||
};
|
||||
|
||||
current.MinValueChanged += v => currentNumberInstantaneous.MinValue = v;
|
||||
current.MaxValueChanged += v => currentNumberInstantaneous.MaxValue = v;
|
||||
current.PrecisionChanged += v => currentNumberInstantaneous.Precision = v;
|
||||
@@ -207,10 +258,12 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
}
|
||||
|
||||
currentNumberInstantaneous.Disabled = disabled;
|
||||
updateState();
|
||||
};
|
||||
|
||||
current.CopyTo(currentNumberInstantaneous);
|
||||
currentLanguage.BindValueChanged(_ => Schedule(updateValueDisplay));
|
||||
currentNumberInstantaneous.BindDisabledChanged(_ => updateState());
|
||||
currentNumberInstantaneous.BindValueChanged(e =>
|
||||
{
|
||||
if (!TransferValueOnCommit)
|
||||
@@ -283,7 +336,8 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
focusManager.ChangeFocus(textBox);
|
||||
if (!Current.Disabled)
|
||||
focusManager.ChangeFocus(textBox);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -291,14 +345,20 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
bool childHasFocus = slider.Focused.Value || textBox.Focused.Value;
|
||||
|
||||
textBox.Alpha = 1;
|
||||
textBox.ReadOnly = currentNumberInstantaneous.Disabled;
|
||||
textBox.Alpha = textBox.Focused.Value ? 1 : 0;
|
||||
valueLabel.Alpha = textBox.Focused.Value ? 0 : 1;
|
||||
|
||||
background.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Background4 : colourProvider.Background5;
|
||||
captionText.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Foreground1 : colourProvider.Content2;
|
||||
textBox.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Foreground1 : colourProvider.Content1;
|
||||
captionText.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Background1 : colourProvider.Content2;
|
||||
textBox.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Background1 : colourProvider.Content1;
|
||||
valueLabel.Colour = currentNumberInstantaneous.Disabled ? colourProvider.Background1 : colourProvider.Content1;
|
||||
|
||||
BorderThickness = childHasFocus || IsHovered || slider.IsDragging.Value ? 2 : 0;
|
||||
BorderColour = childHasFocus ? colourProvider.Highlight1 : colourProvider.Light4;
|
||||
|
||||
if (Current.Disabled)
|
||||
BorderColour = colourProvider.Dark1;
|
||||
else
|
||||
BorderColour = childHasFocus ? colourProvider.Highlight1 : colourProvider.Light4;
|
||||
|
||||
if (childHasFocus)
|
||||
background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark3);
|
||||
@@ -312,19 +372,28 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
if (updatingFromTextBox) return;
|
||||
|
||||
textBox.Text = slider.GetDisplayableValue(currentNumberInstantaneous.Value).ToString();
|
||||
textBox.Text = currentNumberInstantaneous.Value.ToStandardFormattedString(OsuSliderBar<T>.MAX_DECIMAL_DIGITS);
|
||||
valueLabel.Text = LabelFormat(currentNumberInstantaneous.Value);
|
||||
}
|
||||
|
||||
private LocalisableString defaultLabelFormat(T value) => currentNumberInstantaneous.Value.ToStandardFormattedString(OsuSliderBar<T>.MAX_DECIMAL_DIGITS, DisplayAsPercentage);
|
||||
|
||||
private partial class InnerSlider : OsuSliderBar<T>
|
||||
{
|
||||
public BindableBool Focused { get; } = new BindableBool();
|
||||
|
||||
public BindableBool IsDragging { get; set; } = new BindableBool();
|
||||
|
||||
public Action? OnCommit { get; set; }
|
||||
|
||||
public sealed override LocalisableString TooltipText => base.TooltipText;
|
||||
|
||||
public required Func<T, LocalisableString> TooltipFormat { get; init; }
|
||||
|
||||
private Box leftBox = null!;
|
||||
private Box rightBox = null!;
|
||||
private InnerSliderNub nub = null!;
|
||||
private HoverClickSounds sounds = null!;
|
||||
public const float NUB_WIDTH = 10;
|
||||
|
||||
[Resolved]
|
||||
@@ -373,14 +442,14 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
}
|
||||
}
|
||||
},
|
||||
new HoverClickSounds()
|
||||
sounds = new HoverClickSounds()
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
updateState();
|
||||
Current.BindDisabledChanged(_ => updateState(), true);
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
@@ -433,9 +502,19 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
sounds.Enabled.Value = !Current.Disabled;
|
||||
rightBox.Colour = colourProvider.Background6;
|
||||
leftBox.Colour = HasFocus || IsHovered || IsDragged ? colourProvider.Highlight1.Opacity(0.5f) : colourProvider.Dark2;
|
||||
nub.Colour = HasFocus || IsHovered || IsDragged ? colourProvider.Highlight1 : colourProvider.Light4;
|
||||
|
||||
if (Current.Disabled)
|
||||
{
|
||||
leftBox.Colour = colourProvider.Dark3;
|
||||
nub.Colour = colourProvider.Dark1;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftBox.Colour = HasFocus || IsHovered || IsDragged ? colourProvider.Highlight1.Opacity(0.5f) : colourProvider.Highlight1.Opacity(0.3f);
|
||||
nub.Colour = HasFocus || IsHovered || IsDragged ? colourProvider.Highlight1 : colourProvider.Light4;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateValue(float value)
|
||||
@@ -452,6 +531,8 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected sealed override LocalisableString GetTooltipText(T value) => TooltipFormat(value);
|
||||
}
|
||||
|
||||
private partial class InnerSliderNub : Circle
|
||||
@@ -475,5 +556,15 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<LocalisableString> FilterTerms => new[] { Caption, HintText };
|
||||
|
||||
public event Action? ValueChanged;
|
||||
|
||||
public bool IsDefault => Current.IsDefault;
|
||||
|
||||
public void SetDefault() => Current.SetDefault();
|
||||
|
||||
public bool IsDisabled => Current.Disabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
@@ -20,7 +22,7 @@ using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
public partial class FormTextBox : CompositeDrawable, IHasCurrentValue<string>
|
||||
public partial class FormTextBox : CompositeDrawable, IHasCurrentValue<string>, IFormControl
|
||||
{
|
||||
public Bindable<string> Current
|
||||
{
|
||||
@@ -157,6 +159,8 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
|
||||
focusManager = GetContainingFocusManager()!;
|
||||
textBox.Focused.BindValueChanged(_ => updateState());
|
||||
|
||||
current.BindValueChanged(_ => ValueChanged?.Invoke());
|
||||
current.BindDisabledChanged(_ => updateState(), true);
|
||||
}
|
||||
|
||||
@@ -185,26 +189,22 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
textBox.ReadOnly = disabled;
|
||||
textBox.Alpha = 1;
|
||||
|
||||
caption.Colour = disabled ? colourProvider.Foreground1 : colourProvider.Content2;
|
||||
caption.Colour = disabled ? colourProvider.Background1 : colourProvider.Content2;
|
||||
textBox.Colour = disabled ? colourProvider.Foreground1 : colourProvider.Content1;
|
||||
|
||||
if (!disabled)
|
||||
{
|
||||
BorderThickness = IsHovered || textBox.Focused.Value ? 2 : 0;
|
||||
BorderThickness = IsHovered || textBox.Focused.Value ? 2 : 0;
|
||||
|
||||
if (disabled)
|
||||
BorderColour = colourProvider.Dark1;
|
||||
else
|
||||
BorderColour = textBox.Focused.Value ? colourProvider.Highlight1 : colourProvider.Light4;
|
||||
|
||||
if (textBox.Focused.Value)
|
||||
background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark3);
|
||||
else if (IsHovered)
|
||||
background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark4);
|
||||
else
|
||||
background.Colour = colourProvider.Background5;
|
||||
}
|
||||
if (textBox.Focused.Value)
|
||||
background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark3);
|
||||
else if (IsHovered)
|
||||
background.Colour = ColourInfo.GradientVertical(colourProvider.Background5, colourProvider.Dark4);
|
||||
else
|
||||
{
|
||||
BorderThickness = 0;
|
||||
background.Colour = colourProvider.Background4;
|
||||
}
|
||||
background.Colour = colourProvider.Background5;
|
||||
}
|
||||
|
||||
internal partial class InnerTextBox : OsuTextBox
|
||||
@@ -247,5 +247,15 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
OnInputError?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public event Action? ValueChanged;
|
||||
|
||||
public bool IsDefault => current.IsDefault;
|
||||
|
||||
public void SetDefault() => current.SetDefault();
|
||||
|
||||
public bool IsDisabled => current.Disabled;
|
||||
|
||||
public IEnumerable<LocalisableString> FilterTerms => Caption.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
35
osu.Game/Graphics/UserInterfaceV2/IFormControl.cs
Normal file
35
osu.Game/Graphics/UserInterfaceV2/IFormControl.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
// 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 osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an interface for all form controls.
|
||||
/// </summary>
|
||||
public interface IFormControl : IDrawable, IHasFilterTerms
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when the value of the control has changed.
|
||||
/// </summary>
|
||||
event Action ValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the value of this control is in a default state.
|
||||
/// </summary>
|
||||
bool IsDefault { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, resets the control to its default state.
|
||||
/// </summary>
|
||||
void SetDefault();
|
||||
|
||||
/// <summary>
|
||||
/// Whether the control is currently disabled.
|
||||
/// </summary>
|
||||
bool IsDisabled { get; }
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
@@ -24,12 +23,12 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
private const float padding = 1.25f;
|
||||
|
||||
private readonly Box fill;
|
||||
private readonly Container switchContainer;
|
||||
private readonly Drawable switchCircle;
|
||||
private readonly CircularBorderContainer circularContainer;
|
||||
private readonly Container nubContainer;
|
||||
private readonly Drawable nub;
|
||||
private readonly CircularContainer content;
|
||||
|
||||
private Color4 enabledColour;
|
||||
private Color4 disabledColour;
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
private Sample? sampleChecked;
|
||||
private Sample? sampleUnchecked;
|
||||
@@ -38,7 +37,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
Size = new Vector2(45, 20);
|
||||
|
||||
InternalChild = circularContainer = new CircularBorderContainer
|
||||
InternalChild = content = new CircularContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BorderColour = Color4.White,
|
||||
@@ -56,15 +55,14 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(border_thickness + padding),
|
||||
Child = switchContainer = new Container
|
||||
Child = nubContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = switchCircle = new CircularContainer
|
||||
Child = nub = new Circle
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fit,
|
||||
Masking = true,
|
||||
Child = new Box { RelativeSizeAxes = Axes.Both }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,14 +71,8 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OverlayColourProvider? colourProvider, OsuColour colours, AudioManager audio)
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
enabledColour = colourProvider?.Highlight1 ?? colours.BlueDark;
|
||||
disabledColour = colourProvider?.Background3 ?? colours.Gray3;
|
||||
|
||||
switchContainer.Colour = enabledColour;
|
||||
fill.Colour = disabledColour;
|
||||
|
||||
sampleChecked = audio.Samples.Get(@"UI/check-on");
|
||||
sampleUnchecked = audio.Samples.Get(@"UI/check-off");
|
||||
}
|
||||
@@ -89,27 +81,29 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Current.BindValueChanged(updateState, true);
|
||||
Current.BindDisabledChanged(_ => updateColours());
|
||||
Current.BindValueChanged(_ => updateState(), true);
|
||||
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private void updateState(ValueChangedEvent<bool> state)
|
||||
private void updateState()
|
||||
{
|
||||
switchCircle.MoveToX(state.NewValue ? switchContainer.DrawWidth - switchCircle.DrawWidth : 0, 200, Easing.OutQuint);
|
||||
fill.FadeTo(state.NewValue ? 1 : 0, 250, Easing.OutQuint);
|
||||
nub.MoveToX(Current.Value ? nubContainer.DrawWidth - nub.DrawWidth : 0, 200, Easing.OutQuint);
|
||||
fill.FadeTo(Current.Value ? 1 : 0, 250, Easing.OutQuint);
|
||||
|
||||
updateBorder();
|
||||
updateColours();
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
updateBorder();
|
||||
updateColours();
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
updateBorder();
|
||||
updateColours();
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
@@ -123,15 +117,35 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
||||
sampleUnchecked?.Play();
|
||||
}
|
||||
|
||||
private void updateBorder()
|
||||
private void updateColours()
|
||||
{
|
||||
circularContainer.TransformBorderTo((Current.Value ? enabledColour : disabledColour).Lighten(IsHovered ? 0.3f : 0));
|
||||
}
|
||||
ColourInfo borderColour;
|
||||
ColourInfo switchColour;
|
||||
|
||||
private partial class CircularBorderContainer : CircularContainer
|
||||
{
|
||||
public void TransformBorderTo(ColourInfo colour)
|
||||
=> this.TransformTo(nameof(BorderColour), colour, 250, Easing.OutQuint);
|
||||
if (Current.Disabled)
|
||||
{
|
||||
borderColour = colourProvider.Dark2;
|
||||
switchColour = colourProvider.Dark1;
|
||||
fill.Colour = colourProvider.Dark5;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hover = IsHovered && !Current.Disabled;
|
||||
|
||||
borderColour = hover ? colourProvider.Highlight1.Opacity(0.5f) : colourProvider.Highlight1.Opacity(0.3f);
|
||||
switchColour = hover ? colourProvider.Highlight1 : colourProvider.Light4;
|
||||
|
||||
if (!Current.Value)
|
||||
{
|
||||
borderColour = borderColour.MultiplyAlpha(0.8f);
|
||||
switchColour = switchColour.MultiplyAlpha(0.8f);
|
||||
}
|
||||
|
||||
fill.Colour = colourProvider.Background6;
|
||||
}
|
||||
|
||||
nubContainer.FadeColour(switchColour, 250, Easing.OutQuint);
|
||||
content.TransformTo(nameof(BorderColour), borderColour, 250, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString ExportForCompatibility => new TranslatableString(getKey(@"export_for_compatibility"), @"For compatibility (.osz)");
|
||||
|
||||
/// <summary>
|
||||
/// "Guest difficulty (.osu)"
|
||||
/// </summary>
|
||||
public static LocalisableString ExportGuestDifficulty => new TranslatableString(getKey(@"export_guest_difficulty"), @"Guest difficulty (.osu)");
|
||||
|
||||
/// <summary>
|
||||
/// "Create new difficulty"
|
||||
/// </summary>
|
||||
|
||||
@@ -259,6 +259,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString NoMatchingBeatmapsDescription => new TranslatableString(getKey(@"no_matching_beatmaps_description"), @"No beatmaps match your filter criteria!");
|
||||
|
||||
/// <summary>
|
||||
/// "Temporarily showing all beatmaps in"
|
||||
/// </summary>
|
||||
public static LocalisableString TemporarilyShowingAllBeatmapsIn => new TranslatableString(getKey(@"temporarily_showing_all_beatmaps_in"), @"Temporarily showing all beatmaps in");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
||||
178
osu.Game/Overlays/Settings/SettingsItemV2.cs
Normal file
178
osu.Game/Overlays/Settings/SettingsItemV2.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
// 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;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
|
||||
namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
public sealed partial class SettingsItemV2 : CompositeDrawable, ISettingsItem, IConditionalFilterable
|
||||
{
|
||||
private readonly IFormControl control;
|
||||
private readonly SettingsRevertToDefaultButton revertButton;
|
||||
|
||||
private readonly BindableBool controlDefault = new BindableBool(true);
|
||||
private readonly BindableBool controlEnabled = new BindableBool(true);
|
||||
|
||||
/// <summary>
|
||||
/// Whether a revert button should be displayed when the control is modified away from default state.
|
||||
/// </summary>
|
||||
public bool ShowRevertToDefaultButton { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// A note to display underneath the setting.
|
||||
/// </summary>
|
||||
public readonly Bindable<SettingsNote.Data?> Note = new Bindable<SettingsNote.Data?>();
|
||||
|
||||
public SettingsItemV2(IFormControl control)
|
||||
{
|
||||
this.control = control;
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS_RIGHT },
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Children = new[]
|
||||
{
|
||||
revertButton = new SettingsRevertToDefaultButton
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Action = ApplyDefault,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Child = (Drawable)control,
|
||||
}
|
||||
}
|
||||
},
|
||||
new SettingsNote
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Current = { BindTarget = Note },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
controlDefault.Value = control.IsDefault;
|
||||
controlEnabled.Value = !control.IsDisabled;
|
||||
|
||||
controlDefault.BindValueChanged(_ => updateDefaultState());
|
||||
controlEnabled.BindValueChanged(_ => updateDefaultState(), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private void updateDefaultState()
|
||||
{
|
||||
bool showRevertButton = !controlDefault.Value && controlEnabled.Value && ShowRevertToDefaultButton;
|
||||
|
||||
if (showRevertButton)
|
||||
revertButton.Show();
|
||||
else
|
||||
revertButton.Hide();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
controlDefault.Value = control.IsDefault;
|
||||
controlEnabled.Value = !control.IsDisabled;
|
||||
}
|
||||
|
||||
#region ISettingsItem
|
||||
|
||||
public bool HasClassicDefault => ApplyClassicDefault != null;
|
||||
|
||||
/// <summary>
|
||||
/// If set, this setting is considered as having a "classic" default value,
|
||||
/// and this is the function for overwriting the control with that value.
|
||||
/// </summary>
|
||||
public Action? ApplyClassicDefault { get; set; }
|
||||
|
||||
void ISettingsItem.ApplyClassicDefault() => ApplyClassicDefault?.Invoke();
|
||||
|
||||
public void ApplyDefault()
|
||||
{
|
||||
if (!control.IsDisabled)
|
||||
control.SetDefault();
|
||||
}
|
||||
|
||||
public event Action SettingChanged
|
||||
{
|
||||
add => control.ValueChanged += value;
|
||||
remove => control.ValueChanged -= value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Filtering
|
||||
|
||||
public const string CLASSIC_DEFAULT_SEARCH_TERM = @"has-classic-default";
|
||||
|
||||
public IEnumerable<string> Keywords { get; init; } = Enumerable.Empty<string>();
|
||||
|
||||
public IEnumerable<LocalisableString> FilterTerms
|
||||
{
|
||||
get
|
||||
{
|
||||
var filterTerms = new List<LocalisableString>(Keywords.Select(k => (LocalisableString)k));
|
||||
filterTerms.AddRange(control.FilterTerms);
|
||||
|
||||
if (HasClassicDefault)
|
||||
filterTerms.Add(CLASSIC_DEFAULT_SEARCH_TERM);
|
||||
|
||||
return filterTerms;
|
||||
}
|
||||
}
|
||||
|
||||
private bool matchingFilter = true;
|
||||
|
||||
public bool MatchingFilter
|
||||
{
|
||||
get => matchingFilter;
|
||||
set
|
||||
{
|
||||
bool wasPresent = IsPresent;
|
||||
|
||||
matchingFilter = value;
|
||||
|
||||
if (IsPresent != wasPresent)
|
||||
Invalidate(Invalidation.Presence);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsPresent => base.IsPresent && MatchingFilter;
|
||||
|
||||
public bool FilteringActive { get; set; }
|
||||
|
||||
public BindableBool CanBeShown { get; } = new BindableBool(true);
|
||||
|
||||
IBindable<bool> IConditionalFilterable.CanBeShown => CanBeShown;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
118
osu.Game/Overlays/Settings/SettingsNote.cs
Normal file
118
osu.Game/Overlays/Settings/SettingsNote.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
// 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;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
public sealed partial class SettingsNote : CompositeDrawable
|
||||
{
|
||||
public readonly Bindable<Data?> Current = new Bindable<Data?>();
|
||||
|
||||
private Box background = null!;
|
||||
private OsuTextFlowContainer text = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AutoSizeDuration = 300;
|
||||
AutoSizeEasing = Easing.OutQuint;
|
||||
|
||||
InternalChild = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Top = 5, Bottom = 5 },
|
||||
Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
CornerRadius = 5,
|
||||
CornerExponent = 2.5f,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
text = new OsuTextFlowContainer(s => s.Font = OsuFont.Style.Caption1.With(weight: FontWeight.SemiBold))
|
||||
{
|
||||
Padding = new MarginPadding(8),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Current.BindValueChanged(_ => updateDisplay(), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private void updateDisplay()
|
||||
{
|
||||
// Explicitly use ClearTransforms to clear any existing auto-size transform before modifying size / flag.
|
||||
ClearTransforms();
|
||||
|
||||
if (Current.Value == null)
|
||||
{
|
||||
AutoSizeAxes = Axes.None;
|
||||
this.ResizeHeightTo(0, 300, Easing.OutQuint);
|
||||
this.FadeOut(250, Easing.OutQuint);
|
||||
return;
|
||||
}
|
||||
|
||||
AutoSizeAxes = Axes.Y;
|
||||
this.FadeIn(250, Easing.OutQuint);
|
||||
|
||||
switch (Current.Value.Type)
|
||||
{
|
||||
case Type.Informational:
|
||||
background.Colour = colourProvider.Dark2;
|
||||
text.Colour = colourProvider.Content2;
|
||||
break;
|
||||
|
||||
case Type.Warning:
|
||||
background.Colour = colours.Orange1;
|
||||
text.Colour = colourProvider.Background5;
|
||||
break;
|
||||
|
||||
case Type.Critical:
|
||||
background.Colour = colours.Red1;
|
||||
text.Colour = colourProvider.Background5;
|
||||
break;
|
||||
}
|
||||
|
||||
text.Text = Current.Value.Text;
|
||||
}
|
||||
|
||||
public record Data(LocalisableString Text, Type Type);
|
||||
|
||||
public enum Type
|
||||
{
|
||||
Informational,
|
||||
Warning,
|
||||
Critical,
|
||||
}
|
||||
}
|
||||
}
|
||||
99
osu.Game/Overlays/Settings/SettingsRevertToDefaultButton.cs
Normal file
99
osu.Game/Overlays/Settings/SettingsRevertToDefaultButton.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Localisation;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
public partial class SettingsRevertToDefaultButton : OsuClickableContainer
|
||||
{
|
||||
public const float WIDTH = 32;
|
||||
|
||||
public float IconSize { get; init; } = 14;
|
||||
|
||||
private Box background = null!;
|
||||
private SpriteIcon spriteIcon = null!;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
// this is done to ensure a click on this button doesn't trigger focus on a parent element which contains the button.
|
||||
public override bool AcceptsFocus => true;
|
||||
|
||||
public SettingsRevertToDefaultButton()
|
||||
{
|
||||
Size = new Vector2(WIDTH, 50);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Masking = true;
|
||||
CornerRadius = 5;
|
||||
CornerExponent = 2.5f;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background3,
|
||||
},
|
||||
spriteIcon = new SpriteIcon
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = colourProvider.Light1,
|
||||
Icon = OsuIcon.Undo,
|
||||
Margin = new MarginPadding { Left = 12, Right = 5 },
|
||||
Size = new Vector2(IconSize),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
Enabled.BindValueChanged(_ => updateDisplay(), true);
|
||||
}
|
||||
|
||||
public override LocalisableString TooltipText => CommonStrings.RevertToDefault;
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
updateDisplay();
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
updateDisplay();
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
public override void Show()
|
||||
{
|
||||
this.FadeIn().MoveToX(WIDTH - 10, 200, Easing.OutElasticQuarter);
|
||||
}
|
||||
|
||||
public override void Hide()
|
||||
{
|
||||
this.MoveToX(0, 120, Easing.OutExpo).Then().FadeOut();
|
||||
}
|
||||
|
||||
private void updateDisplay()
|
||||
{
|
||||
spriteIcon.FadeColour(IsHovered ? colourProvider.Content2 : colourProvider.Light1, 300, Easing.OutQuint);
|
||||
background.FadeColour(IsHovered ? colourProvider.Background2 : colourProvider.Background3, 300, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,9 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
public const float CONTENT_MARGINS = 20;
|
||||
|
||||
// extra margin to give room to the revert-to-default button in settings controls.
|
||||
public const float CONTENT_MARGINS_RIGHT = 30;
|
||||
|
||||
public const float TRANSITION_LENGTH = 600;
|
||||
|
||||
private const float sidebar_width = SettingsSidebar.EXPANDED_WIDTH;
|
||||
|
||||
347
osu.Game/Screens/Edit/Components/FormSampleSet.cs
Normal file
347
osu.Game/Screens/Edit/Components/FormSampleSet.cs
Normal file
@@ -0,0 +1,347 @@
|
||||
// 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;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components
|
||||
{
|
||||
public partial class FormSampleSet : CompositeDrawable, IHasCurrentValue<EditorBeatmapSkin.SampleSet?>
|
||||
{
|
||||
public Bindable<EditorBeatmapSkin.SampleSet?> Current
|
||||
{
|
||||
get => current.Current;
|
||||
set => current.Current = value;
|
||||
}
|
||||
|
||||
public Func<FileInfo, string>? SampleAddRequested { get; init; }
|
||||
public Action<string>? SampleRemoveRequested { get; init; }
|
||||
|
||||
private readonly BindableWithCurrent<EditorBeatmapSkin.SampleSet?> current = new BindableWithCurrent<EditorBeatmapSkin.SampleSet?>();
|
||||
private readonly Dictionary<(string name, string bank), SampleButton> buttons = new Dictionary<(string, string), SampleButton>();
|
||||
|
||||
private Box background = null!;
|
||||
private FormFieldCaption caption = null!;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = 5;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
background = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background5,
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding(9),
|
||||
Spacing = new Vector2(7),
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
caption = new FormFieldCaption(),
|
||||
new GridContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
RowDimensions = Enumerable.Repeat(new Dimension(GridSizeMode.AutoSize), 4).ToArray(),
|
||||
ColumnDimensions = Enumerable.Repeat(new Dimension(GridSizeMode.AutoSize), 5).ToArray(),
|
||||
Content = createTableContent().ToArray(),
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<Drawable[]> createTableContent()
|
||||
{
|
||||
string[] columns = HitSampleInfo.ALL_ADDITIONS.Prepend(HitSampleInfo.HIT_NORMAL).ToArray();
|
||||
string[] rows = HitSampleInfo.ALL_BANKS;
|
||||
|
||||
yield return columns.Select(makeTableHeading).Prepend(Empty()).ToArray();
|
||||
|
||||
foreach (string row in rows)
|
||||
{
|
||||
List<Drawable> drawables = [makeTableHeading(row)];
|
||||
|
||||
foreach (string col in columns)
|
||||
drawables.Add(buttons[(col, row)] = makeButton());
|
||||
|
||||
yield return drawables.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private OsuSpriteText makeTableHeading(string text) => new OsuSpriteText
|
||||
{
|
||||
Text = text,
|
||||
Font = OsuFont.Style.Caption1,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
|
||||
private SampleButton makeButton() => new SampleButton
|
||||
{
|
||||
Width = 60,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Margin = new MarginPadding(5),
|
||||
SampleAddRequested = SampleAddRequested,
|
||||
SampleRemoveRequested = SampleRemoveRequested,
|
||||
};
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
updateState();
|
||||
Current.BindValueChanged(setChanged, true);
|
||||
}
|
||||
|
||||
private void setChanged(ValueChangedEvent<EditorBeatmapSkin.SampleSet?> valueChangedEvent)
|
||||
{
|
||||
var set = valueChangedEvent.NewValue;
|
||||
|
||||
caption.Caption = set?.Name ?? default(LocalisableString);
|
||||
Alpha = set != null && set.SampleSetIndex > 0 ? 1 : 0;
|
||||
|
||||
if (set != null)
|
||||
{
|
||||
foreach (var (sample, button) in buttons)
|
||||
{
|
||||
button.ExpectedFilename.Value = $@"{sample.bank}-{sample.name}{(set.SampleSetIndex > 1 ? set.SampleSetIndex : null)}";
|
||||
button.ActualFilename.Value = set.FindSampleIfExists(sample.name, sample.bank);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
updateState();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
updateState();
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
background.Colour = colourProvider.Background5;
|
||||
caption.Colour = colourProvider.Content2;
|
||||
|
||||
BorderThickness = IsHovered ? 2 : 0;
|
||||
|
||||
if (IsHovered)
|
||||
BorderColour = colourProvider.Light4;
|
||||
}
|
||||
|
||||
public partial class SampleButton : OsuButton, IHasPopover, IHasContextMenu
|
||||
{
|
||||
/// <summary>
|
||||
/// The expected filename for the sample that this button represents.
|
||||
/// Does not contain extension.
|
||||
/// </summary>
|
||||
public Bindable<string> ExpectedFilename { get; } = new Bindable<string>();
|
||||
|
||||
/// <summary>
|
||||
/// The actual chosen filename for the sample that this button represent.
|
||||
/// Can be <see langword="null"/> if the sample is omitted / missing.
|
||||
/// Does contain extension.
|
||||
/// </summary>
|
||||
public Bindable<string?> ActualFilename { get; } = new Bindable<string?>();
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a new sample is selected via this button.
|
||||
/// </summary>
|
||||
public Func<FileInfo, string>? SampleAddRequested { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a sample removal is selected via this button.
|
||||
/// </summary>
|
||||
public Action<string>? SampleRemoveRequested { get; init; }
|
||||
|
||||
private Bindable<FileInfo?> selectedFile { get; } = new Bindable<FileInfo?>();
|
||||
|
||||
private TrianglesV2? triangles { get; set; }
|
||||
|
||||
protected override float HoverLayerFinalAlpha => 0;
|
||||
|
||||
private Color4? triangleGradientSecondColour;
|
||||
private SpriteIcon icon = null!;
|
||||
|
||||
[Resolved]
|
||||
private OverlayColourProvider overlayColourProvider { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private EditorBeatmap? editorBeatmap { get; set; }
|
||||
|
||||
private HoverSounds? hoverSounds;
|
||||
|
||||
private ISample? sample;
|
||||
|
||||
public SampleButton()
|
||||
: base(null)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Add(icon = new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.Solid.Plus,
|
||||
Size = new Vector2(16),
|
||||
Shadow = true,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
|
||||
Action = () =>
|
||||
{
|
||||
if (ActualFilename.Value == null)
|
||||
{
|
||||
selectedFile.Value = null;
|
||||
this.ShowPopover();
|
||||
}
|
||||
else
|
||||
sample?.Play();
|
||||
};
|
||||
|
||||
if (editorBeatmap?.BeatmapSkin != null)
|
||||
editorBeatmap.BeatmapSkin.BeatmapSkinChanged += recycleSamples;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Content.CornerRadius = 4;
|
||||
|
||||
Add(triangles = new TrianglesV2
|
||||
{
|
||||
Thickness = 0.02f,
|
||||
SpawnRatio = 0.6f,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Depth = float.MaxValue,
|
||||
});
|
||||
|
||||
ActualFilename.BindValueChanged(_ => updateState(), true);
|
||||
selectedFile.BindValueChanged(_ => addSample());
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
BackgroundColour = ActualFilename.Value == null ? overlayColourProvider.Background3 : overlayColourProvider.Colour3;
|
||||
triangleGradientSecondColour = BackgroundColour.Lighten(0.2f);
|
||||
icon.Icon = ActualFilename.Value == null ? FontAwesome.Solid.Plus : FontAwesome.Solid.Play;
|
||||
|
||||
recycleSamples();
|
||||
|
||||
if (triangles == null)
|
||||
return;
|
||||
|
||||
triangles.Colour = ColourInfo.GradientVertical(triangleGradientSecondColour.Value, BackgroundColour);
|
||||
}
|
||||
|
||||
private void recycleSamples()
|
||||
{
|
||||
if (hoverSounds?.Parent == this)
|
||||
{
|
||||
RemoveInternal(hoverSounds, true);
|
||||
hoverSounds = null;
|
||||
}
|
||||
|
||||
AddInternal(hoverSounds = (ActualFilename.Value == null ? new HoverClickSounds(HoverSampleSet.Button) : new HoverSounds(HoverSampleSet.Button)));
|
||||
|
||||
sample = ActualFilename.Value == null ? null : editorBeatmap?.BeatmapSkin?.Skin.Samples?.Get(ActualFilename.Value);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
Debug.Assert(triangleGradientSecondColour != null);
|
||||
|
||||
Background.FadeColour(triangleGradientSecondColour.Value, 300, Easing.OutQuint);
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
Background.FadeColour(BackgroundColour, 300, Easing.OutQuint);
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
private void addSample()
|
||||
{
|
||||
if (selectedFile.Value == null)
|
||||
return;
|
||||
|
||||
this.HidePopover();
|
||||
ActualFilename.Value = SampleAddRequested?.Invoke(selectedFile.Value) ?? selectedFile.Value.ToString();
|
||||
}
|
||||
|
||||
private void deleteSample()
|
||||
{
|
||||
if (ActualFilename.Value == null)
|
||||
return;
|
||||
|
||||
SampleRemoveRequested?.Invoke(ActualFilename.Value);
|
||||
ActualFilename.Value = null;
|
||||
}
|
||||
|
||||
public Popover? GetPopover() => ActualFilename.Value == null ? new FormFileSelector.FileChooserPopover(SupportedExtensions.AUDIO_EXTENSIONS, selectedFile, null) : null;
|
||||
|
||||
public MenuItem[]? ContextMenuItems =>
|
||||
ActualFilename.Value != null
|
||||
? [new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, deleteSample)]
|
||||
: null;
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
if (editorBeatmap?.BeatmapSkin != null)
|
||||
editorBeatmap.BeatmapSkin.BeatmapSkinChanged -= recycleSamples;
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,10 +186,22 @@ namespace osu.Game.Screens.Edit.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
// 设置默认的循环范围为当前时间前后2.5秒
|
||||
double currentTime = editorClock.CurrentTime;
|
||||
editorClock.SetLoopStartTime(editorClock.GetSnappedTime(Math.Max(0, currentTime - 2500)));
|
||||
editorClock.SetLoopEndTime(editorClock.GetSnappedTime(Math.Min(editorClock.TrackLength, currentTime + 2500)));
|
||||
// 默认范围:以当前活动光标为 A 起点,向后 8 个 1/4 节拍为 B 终点。
|
||||
// 8 * (1/4 beat) = 2 beats.
|
||||
double currentTime = Math.Clamp(editorClock.CurrentTime, 0, editorClock.TrackLength);
|
||||
|
||||
double startTime = editorClock.GetSnappedTime(currentTime);
|
||||
var timingPoint = editorClock.ControlPointInfo.TimingPointAt(startTime);
|
||||
|
||||
double endTime = startTime + timingPoint.BeatLength * 2;
|
||||
endTime = Math.Min(endTime, editorClock.TrackLength);
|
||||
endTime = editorClock.GetSnappedTime(endTime);
|
||||
|
||||
if (endTime <= startTime)
|
||||
endTime = Math.Min(editorClock.TrackLength, startTime + 1);
|
||||
|
||||
editorClock.SetLoopStartTime(startTime);
|
||||
editorClock.SetLoopEndTime(endTime);
|
||||
}
|
||||
|
||||
editorClock.Seek(editorClock.LoopStartTime.Value); // 跳转到开头
|
||||
|
||||
@@ -1329,8 +1329,9 @@ namespace osu.Game.Screens.Edit
|
||||
{
|
||||
var exportItems = new List<MenuItem>
|
||||
{
|
||||
new EditorMenuItem(EditorStrings.ExportForEditing, MenuItemType.Standard, () => exportBeatmap(false)),
|
||||
new EditorMenuItem(EditorStrings.ExportForCompatibility, MenuItemType.Standard, () => exportBeatmap(true)),
|
||||
new EditorMenuItem(EditorStrings.ExportForEditing, MenuItemType.Standard, () => runExport(manager => manager.Export(Beatmap.Value.BeatmapSetInfo))),
|
||||
new EditorMenuItem(EditorStrings.ExportForCompatibility, MenuItemType.Standard, () => runExport(manager => manager.ExportLegacy(Beatmap.Value.BeatmapSetInfo))),
|
||||
new EditorMenuItem(EditorStrings.ExportGuestDifficulty, MenuItemType.Standard, () => runExport(manager => manager.ExportLegacy(Beatmap.Value.BeatmapInfo))),
|
||||
};
|
||||
|
||||
return new EditorMenuItem(CommonStrings.Export) { Items = exportItems };
|
||||
@@ -1396,7 +1397,7 @@ namespace osu.Game.Screens.Edit
|
||||
void startSubmission() => this.Push(new BeatmapSubmissionScreen());
|
||||
}
|
||||
|
||||
private void exportBeatmap(bool legacy)
|
||||
private void runExport(Func<BeatmapManager, Task> exportAction)
|
||||
{
|
||||
if (HasUnsavedChanges)
|
||||
{
|
||||
@@ -1405,20 +1406,12 @@ namespace osu.Game.Screens.Edit
|
||||
if (!Save())
|
||||
return Task.CompletedTask;
|
||||
|
||||
return runExport();
|
||||
return exportAction.Invoke(beatmapManager);
|
||||
})));
|
||||
}
|
||||
else
|
||||
{
|
||||
attemptAsyncMutationOperation(runExport);
|
||||
}
|
||||
|
||||
Task runExport()
|
||||
{
|
||||
if (legacy)
|
||||
return beatmapManager.ExportLegacy(Beatmap.Value.BeatmapSetInfo);
|
||||
else
|
||||
return beatmapManager.Export(Beatmap.Value.BeatmapSetInfo);
|
||||
attemptAsyncMutationOperation(() => exportAction(beatmapManager));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,14 @@ namespace osu.Game.Screens.Edit
|
||||
}
|
||||
|
||||
public override string ToString() => Name;
|
||||
|
||||
public HashSet<string> Filenames = [];
|
||||
|
||||
public string? FindSampleIfExists(string sampleName, string bankName)
|
||||
=> Filenames.SingleOrDefault(f => f.StartsWith($@"{bankName}-{sampleName}{(SampleSetIndex > 1 ? SampleSetIndex : null)}", StringComparison.Ordinal));
|
||||
|
||||
public virtual bool Equals(SampleSet? other) => SampleSetIndex == other?.SampleSetIndex;
|
||||
public override int GetHashCode() => SampleSetIndex;
|
||||
}
|
||||
|
||||
public IEnumerable<SampleSet> GetAvailableSampleSets()
|
||||
|
||||
@@ -489,7 +489,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||
if (!screen.IsCurrentScreen())
|
||||
return;
|
||||
|
||||
var beatmap = beatmaps.QueryBeatmap($@"{nameof(BeatmapInfo.OnlineID)} == $0 AND {nameof(BeatmapInfo.MD5Hash)} == {nameof(BeatmapInfo.OnlineMD5Hash)}", item.Beatmap.OnlineID);
|
||||
var beatmap = beatmaps.QueryOnlineBeatmapId(item.Beatmap.OnlineID);
|
||||
|
||||
screen.Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); // this will gracefully fall back to dummy beatmap if missing locally.
|
||||
screen.Ruleset.Value = rulesets.GetRuleset(item.RulesetID);
|
||||
|
||||
@@ -246,7 +246,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match
|
||||
|
||||
// Update global gameplay state to correspond to the new selection.
|
||||
// Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
|
||||
var localBeatmap = beatmapManager.QueryBeatmap($@"{nameof(BeatmapInfo.OnlineID)} == $0 AND {nameof(BeatmapInfo.MD5Hash)} == {nameof(BeatmapInfo.OnlineMD5Hash)}", item.BeatmapID);
|
||||
var localBeatmap = beatmapManager.QueryOnlineBeatmapId(item.BeatmapID);
|
||||
|
||||
if (localBeatmap != null)
|
||||
{
|
||||
|
||||
@@ -510,7 +510,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
{
|
||||
MultiplayerPlaylistItem item = client.Room.CurrentPlaylistItem;
|
||||
|
||||
var newBeatmap = beatmapManager.QueryBeatmap($@"{nameof(BeatmapInfo.OnlineID)} == $0 AND {nameof(BeatmapInfo.MD5Hash)} == {nameof(BeatmapInfo.OnlineMD5Hash)}", item.BeatmapID);
|
||||
var newBeatmap = beatmapManager.QueryOnlineBeatmapId(item.BeatmapID);
|
||||
|
||||
if (!Beatmap.Value.BeatmapSetInfo.Equals(newBeatmap?.BeatmapSet))
|
||||
this.MakeCurrent();
|
||||
@@ -652,7 +652,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
// Update global gameplay state to correspond to the new selection.
|
||||
// Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
|
||||
var localBeatmap = beatmapManager.QueryBeatmap($@"{nameof(BeatmapInfo.OnlineID)} == $0 AND {nameof(BeatmapInfo.MD5Hash)} == {nameof(BeatmapInfo.OnlineMD5Hash)}", gameplayBeatmapId);
|
||||
var localBeatmap = beatmapManager.QueryOnlineBeatmapId(gameplayBeatmapId);
|
||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
||||
Ruleset.Value = ruleset;
|
||||
Mods.Value = client.LocalUser.Mods.Concat(item.RequiredMods).Select(m => m.ToMod(rulesetInstance)).ToArray();
|
||||
|
||||
@@ -17,7 +17,6 @@ using osu.Game.Database;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Online.Rooms;
|
||||
using Realms;
|
||||
|
||||
namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
@@ -154,7 +153,8 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
}
|
||||
|
||||
IQueryable<BeatmapInfo> queryBeatmap() =>
|
||||
realm.Realm.All<BeatmapInfo>().Filter("OnlineID == $0 && MD5Hash == $1 && BeatmapSet.DeletePending == false", beatmap.OnlineID, beatmap.MD5Hash);
|
||||
realm.Realm.All<BeatmapInfo>()
|
||||
.ForOnlineId(beatmap.OnlineID);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
||||
@@ -62,8 +62,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
var localBeatmap = beatmapManager.QueryBeatmap($@"{nameof(BeatmapInfo.OnlineID)} == $0 AND {nameof(BeatmapInfo.MD5Hash)} == {nameof(BeatmapInfo.OnlineMD5Hash)}",
|
||||
PlaylistItem.Beatmap.OnlineID);
|
||||
var localBeatmap = beatmapManager.QueryOnlineBeatmapId(PlaylistItem.Beatmap.OnlineID);
|
||||
itemBeatmap = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
||||
|
||||
AddInternal(new Container
|
||||
|
||||
@@ -609,7 +609,7 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
||||
|
||||
// Update global gameplay state to correspond to the new selection.
|
||||
// Retrieve the corresponding local beatmap, since we can't directly use the playlist's beatmap info
|
||||
var localBeatmap = beatmapManager.QueryBeatmap($@"{nameof(BeatmapInfo.OnlineID)} == $0 AND {nameof(BeatmapInfo.MD5Hash)} == {nameof(BeatmapInfo.OnlineMD5Hash)}", gameplayBeatmap.OnlineID);
|
||||
var localBeatmap = beatmapManager.QueryOnlineBeatmapId(gameplayBeatmap.OnlineID);
|
||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
||||
Ruleset.Value = gameplayRuleset;
|
||||
Mods.Value = UserMods.Value.Concat(item.RequiredMods.Select(m => m.ToMod(rulesetInstance))).ToArray();
|
||||
|
||||
@@ -56,10 +56,10 @@ namespace osu.Game.Screens.SelectV2
|
||||
{
|
||||
bool match = criteria.Ruleset == null || beatmap.AllowGameplayWithRuleset(criteria.Ruleset!, criteria.AllowConvertedBeatmaps);
|
||||
|
||||
if (beatmap.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true)
|
||||
if (criteria.SelectedBeatmapSet != null)
|
||||
{
|
||||
// only check ruleset equality or convertability for selected beatmap
|
||||
return match;
|
||||
return beatmap.BeatmapSet?.Equals(criteria.SelectedBeatmapSet) == true && match;
|
||||
}
|
||||
|
||||
if (!match) return false;
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
// 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;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.SelectV2
|
||||
{
|
||||
public partial class FilterControl
|
||||
{
|
||||
public partial class ScopedBeatmapSetDisplay : CompositeDrawable, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
public Bindable<BeatmapSetInfo?> ScopedBeatmapSet
|
||||
{
|
||||
get => scopedBeatmapSet.Current;
|
||||
set => scopedBeatmapSet.Current = value;
|
||||
}
|
||||
|
||||
private readonly BindableWithCurrent<BeatmapSetInfo?> scopedBeatmapSet = new BindableWithCurrent<BeatmapSetInfo?>();
|
||||
private Container content = null!;
|
||||
private OsuTextFlowContainer text = null!;
|
||||
private ShearedButton goBackButton = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
AutoSizeEasing = Easing.OutQuint;
|
||||
AutoSizeDuration = 200;
|
||||
CornerRadius = 8f;
|
||||
Masking = true;
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Highlight1,
|
||||
},
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
BypassAutoSizeAxes = Axes.Y,
|
||||
Shear = -OsuGame.SHEAR,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Horizontal = 6,
|
||||
Vertical = 2,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
text = new OsuTextFlowContainer(t => t.Font = OsuFont.Style.Body)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Colour = colourProvider.Background6,
|
||||
Padding = new MarginPadding { Right = 80, Vertical = 5 }
|
||||
},
|
||||
goBackButton = new ShearedButton(80)
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Text = CommonStrings.Back,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Height = 1,
|
||||
Action = () => scopedBeatmapSet.Value = null,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
scopedBeatmapSet.BindValueChanged(_ => updateState(), true);
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
content.BypassAutoSizeAxes = scopedBeatmapSet.Value != null ? Axes.None : Axes.Y;
|
||||
|
||||
if (scopedBeatmapSet.Value != null)
|
||||
{
|
||||
text.Clear();
|
||||
text.AddText(SongSelectStrings.TemporarilyShowingAllBeatmapsIn);
|
||||
text.AddText(@" ");
|
||||
text.AddText(scopedBeatmapSet.Value.Metadata.GetDisplayTitleRomanisable(), t => t.Font = OsuFont.Style.Body.With(weight: FontWeight.Bold));
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (scopedBeatmapSet.Value != null && e.Action == GlobalAction.Back && !e.Repeat)
|
||||
{
|
||||
goBackButton.TriggerClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Collections;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Database;
|
||||
@@ -38,6 +39,8 @@ namespace osu.Game.Screens.SelectV2
|
||||
|
||||
private const float corner_radius = 10;
|
||||
|
||||
public Bindable<BeatmapSetInfo?> ScopedBeatmapSet { get; } = new Bindable<BeatmapSetInfo?>();
|
||||
|
||||
private SongSelectSearchTextBox searchTextBox = null!;
|
||||
private ShearedToggleButton showConvertedBeatmapsButton = null!;
|
||||
private DifficultyRangeSlider difficultyRangeSlider = null!;
|
||||
@@ -160,6 +163,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
new Dimension(maxSize: 180),
|
||||
new Dimension(GridSizeMode.Absolute, 5),
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
@@ -184,6 +188,11 @@ namespace osu.Game.Screens.SelectV2
|
||||
}
|
||||
}
|
||||
},
|
||||
new ScopedBeatmapSetDisplay
|
||||
{
|
||||
ScopedBeatmapSet = ScopedBeatmapSet,
|
||||
Depth = float.MinValue, // hack to ensure that the scoped display handles `GlobalAction.Back` input before the filter control
|
||||
},
|
||||
csSelector = new EzKeyModeSelector
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Scoring;
|
||||
@@ -43,5 +44,10 @@ namespace osu.Game.Screens.SelectV2
|
||||
/// Gets relevant actionable items for beatmap context menus, based on the type of song select.
|
||||
/// </summary>
|
||||
IEnumerable<OsuMenuItem> GetForwardActions(BeatmapInfo beatmap);
|
||||
|
||||
/// <summary>
|
||||
/// Set this to a non-<see langword="null"/> value in order to temporarily bypass filter and show all difficulties of the given beatmap set.
|
||||
/// </summary>
|
||||
Bindable<BeatmapSetInfo?> ScopedBeatmapSet { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.SelectV2
|
||||
{
|
||||
public partial class PanelBeatmapStandalone
|
||||
{
|
||||
public partial class SpreadDisplay : OsuAnimatedButton
|
||||
{
|
||||
public Bindable<BeatmapInfo?> Beatmap { get; } = new Bindable<BeatmapInfo?>();
|
||||
public Bindable<StarDifficulty> StarDifficulty { get; } = new Bindable<StarDifficulty>();
|
||||
|
||||
private readonly Bindable<BeatmapSetInfo?> scopedBeatmapSet = new Bindable<BeatmapSetInfo?>();
|
||||
private readonly Bindable<bool> showConvertedBeatmaps = new Bindable<bool>();
|
||||
|
||||
private const double transition_duration = 200;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<RulesetInfo> ruleset { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
private FillFlowContainer preceding = null!;
|
||||
public Circle Current { get; private set; } = null!;
|
||||
private FillFlowContainer succeeding = null!;
|
||||
|
||||
private OsuSpriteText countText = null!;
|
||||
private SpriteIcon icon = null!;
|
||||
|
||||
public SpreadDisplay()
|
||||
{
|
||||
AutoSizeAxes = Axes.X;
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Content.CornerRadius = 5;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISongSelect? songSelect, OsuConfigManager configManager)
|
||||
{
|
||||
Add(new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5),
|
||||
Padding = new MarginPadding { Horizontal = 5 },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(2),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
preceding = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(1),
|
||||
Alpha = 0.5f,
|
||||
},
|
||||
Current = new Circle
|
||||
{
|
||||
Size = new Vector2(7, 12),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
},
|
||||
succeeding = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(1),
|
||||
Alpha = 0.5f,
|
||||
}
|
||||
}
|
||||
},
|
||||
countText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Font = OsuFont.Style.Caption2,
|
||||
},
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Size = new Vector2(12),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Icon = FontAwesome.Solid.Eye,
|
||||
Alpha = 0,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (songSelect != null)
|
||||
scopedBeatmapSet.BindTo(songSelect.ScopedBeatmapSet);
|
||||
|
||||
configManager.BindWith(OsuSetting.ShowConvertedBeatmaps, showConvertedBeatmaps);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Beatmap.BindValueChanged(_ => updateBeatmap());
|
||||
StarDifficulty.BindValueChanged(_ => updateBeatmap());
|
||||
showConvertedBeatmaps.BindValueChanged(_ => updateBeatmap());
|
||||
scopedBeatmapSet.BindValueChanged(_ => updateBeatmap(), true);
|
||||
Enabled.BindValueChanged(_ => updateAppearance(), true);
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
private void updateBeatmap()
|
||||
{
|
||||
if (Beatmap.Value == null || scopedBeatmapSet.Value != null)
|
||||
{
|
||||
this.FadeOut(transition_duration, Easing.OutQuint);
|
||||
return;
|
||||
}
|
||||
|
||||
preceding.Clear();
|
||||
succeeding.Clear();
|
||||
|
||||
var otherStarDifficulties = Beatmap.Value.BeatmapSet!.Beatmaps
|
||||
.Except([Beatmap.Value])
|
||||
.Where(b => b.AllowGameplayWithRuleset(ruleset.Value, showConvertedBeatmaps.Value))
|
||||
.OrderBy(b => b.StarRating)
|
||||
.Select(b => b.StarRating)
|
||||
.ToList();
|
||||
this.FadeTo(otherStarDifficulties.Count > 0 ? 1 : 0, transition_duration, Easing.OutQuint);
|
||||
|
||||
if (otherStarDifficulties.Count == 0)
|
||||
return;
|
||||
|
||||
const int max_difficulties_total = 11;
|
||||
|
||||
int startIndex;
|
||||
int endIndex;
|
||||
|
||||
if (otherStarDifficulties.Count <= max_difficulties_total)
|
||||
{
|
||||
startIndex = 0;
|
||||
endIndex = otherStarDifficulties.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
startIndex = otherStarDifficulties.BinarySearch(StarDifficulty.Value.Stars);
|
||||
if (startIndex < 0)
|
||||
startIndex = ~startIndex - 1;
|
||||
|
||||
startIndex = Math.Clamp(startIndex - max_difficulties_total / 2, 0, otherStarDifficulties.Count - 1);
|
||||
endIndex = Math.Clamp(startIndex + max_difficulties_total, 0, otherStarDifficulties.Count - 1);
|
||||
}
|
||||
|
||||
for (int i = startIndex; i <= endIndex; i++)
|
||||
{
|
||||
double otherStarDifficulty = otherStarDifficulties[i];
|
||||
var target = otherStarDifficulty < StarDifficulty.Value.Stars ? preceding : succeeding;
|
||||
|
||||
var circle = new Circle
|
||||
{
|
||||
Size = new Vector2(5, 10),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Colour = colours.ForStarDifficulty(otherStarDifficulty)
|
||||
};
|
||||
target.Add(circle);
|
||||
target.SetLayoutPosition(circle, (float)otherStarDifficulty);
|
||||
}
|
||||
|
||||
int countNotShown = otherStarDifficulties.Count - (preceding.Count + succeeding.Count);
|
||||
countText.Alpha = countNotShown > 0 ? 1 : 0;
|
||||
countText.Text = $@"+{countNotShown}";
|
||||
|
||||
if (startIndex > 0)
|
||||
{
|
||||
for (int i = 0; i < preceding.Count; ++i)
|
||||
{
|
||||
var dot = preceding[i];
|
||||
dot.Alpha = (1 + 4 * (float)(i + 1) / preceding.Count) / 5;
|
||||
}
|
||||
}
|
||||
|
||||
if (endIndex < otherStarDifficulties.Count - 1)
|
||||
{
|
||||
for (int i = 0; i < succeeding.Count; ++i)
|
||||
{
|
||||
var dot = succeeding[i];
|
||||
dot.Alpha = (1 + 4 * (float)(succeeding.Count - i) / succeeding.Count) / 5;
|
||||
}
|
||||
}
|
||||
|
||||
Action = () => scopedBeatmapSet.Value = Beatmap.Value.BeatmapSet!;
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
if (!Enabled.Value)
|
||||
return false;
|
||||
|
||||
base.OnMouseDown(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
if (!Enabled.Value)
|
||||
return false;
|
||||
|
||||
return base.OnClick(e);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
updateAppearance();
|
||||
return base.OnHover(e);
|
||||
}
|
||||
|
||||
protected override void OnHoverLost(HoverLostEvent e)
|
||||
{
|
||||
updateAppearance();
|
||||
base.OnHoverLost(e);
|
||||
}
|
||||
|
||||
private void updateAppearance()
|
||||
{
|
||||
bool isInteractable = Enabled.Value && IsHovered;
|
||||
|
||||
HoverColour = isInteractable ? Colour4.White.Opacity(0.1f) : Colour4.Transparent;
|
||||
preceding.FadeTo(isInteractable ? 1 : 0.5f, transition_duration, Easing.OutQuint);
|
||||
succeeding.FadeTo(isInteractable ? 1 : 0.5f, transition_duration, Easing.OutQuint);
|
||||
icon.FadeTo(isInteractable ? 1 : 0, transition_duration, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
|
||||
private ConstrainedIconContainer difficultyIcon = null!;
|
||||
private StarRatingDisplay starRatingDisplay = null!;
|
||||
private StarCounter starCounter = null!;
|
||||
private SpreadDisplay spreadDisplay = null!;
|
||||
private PanelLocalRankDisplay localRank = null!;
|
||||
private OsuSpriteText keyCountText = null!;
|
||||
private OsuSpriteText difficultyText = null!;
|
||||
@@ -210,11 +210,11 @@ namespace osu.Game.Screens.SelectV2
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Scale = new Vector2(0.875f),
|
||||
},
|
||||
starCounter = new StarCounter
|
||||
spreadDisplay = new SpreadDisplay
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Scale = new Vector2(0.4f)
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Enabled = { BindTarget = Selected }
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
@@ -266,6 +266,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
|
||||
updateCalculationsAsync(beatmap);
|
||||
computeStarRating();
|
||||
spreadDisplay.Beatmap.Value = beatmap;
|
||||
updateKeyCount();
|
||||
}
|
||||
|
||||
@@ -351,6 +352,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
updateButton.BeatmapSet = null;
|
||||
localRank.Beatmap = null;
|
||||
starDifficultyBindable = null;
|
||||
spreadDisplay.Beatmap.Value = null;
|
||||
|
||||
starDifficultyCancellationSource?.Cancel();
|
||||
kpsCalculationCancellationSource?.Cancel();
|
||||
@@ -368,7 +370,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
starDifficultyBindable.BindValueChanged(starDifficulty =>
|
||||
{
|
||||
starRatingDisplay.Current.Value = starDifficulty.NewValue;
|
||||
starCounter.Current = (float)starDifficulty.NewValue.Stars;
|
||||
spreadDisplay.StarDifficulty.Value = starDifficulty.NewValue;
|
||||
}, true);
|
||||
}
|
||||
|
||||
@@ -389,7 +391,7 @@ namespace osu.Game.Screens.SelectV2
|
||||
var diffColour = starRatingDisplay.DisplayedDifficultyColour;
|
||||
|
||||
AccentColour = diffColour;
|
||||
starCounter.Colour = diffColour;
|
||||
spreadDisplay.Current.Colour = diffColour;
|
||||
|
||||
backgroundBorder.Colour = diffColour;
|
||||
difficultyIcon.Colour = starRatingDisplay.DisplayedStars.Value > OsuColour.STAR_DIFFICULTY_DEFINED_COLOUR_CUTOFF ? colours.Orange1 : colourProvider.Background5;
|
||||
|
||||
@@ -1225,6 +1225,8 @@ namespace osu.Game.Screens.SelectV2
|
||||
beatmaps.Restore(b);
|
||||
}
|
||||
|
||||
public Bindable<BeatmapSetInfo?> ScopedBeatmapSet => filterControl.ScopedBeatmapSet;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,15 @@ namespace osu.Game.Utils
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
// aside from opening every zip entry not failing, we also require there to *be* at least one entry.
|
||||
// if there are no entries, the best case is that it's an actual empty zip
|
||||
// and as such probably useless to whatever wants to use it later.
|
||||
// the worst case is that it's actually *not* a zip and instead a stream of binary
|
||||
// which *accidentally* happened to contain the magic sequence of bytes for the zip header (50 4b 05 06),
|
||||
// and if that's the case, then we are *misclassifying* it as a zip by returning `true` unconditionally.
|
||||
return arc.Entries.Count > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -52,9 +58,15 @@ namespace osu.Game.Utils
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
// aside from opening every zip entry not failing, we also require there to *be* at least one entry.
|
||||
// if there are no entries, the best case is that it's an actual empty zip
|
||||
// and as such probably useless to whatever wants to use it later.
|
||||
// the worst case is that it's actually *not* a zip and instead a stream of binary
|
||||
// which *accidentally* happened to contain the magic sequence of bytes for the zip header (50 4b 05 06),
|
||||
// and if that's the case, then we are *misclassifying* it as a zip by returning `true` unconditionally.
|
||||
return arc.Entries.Count > 0;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user