mirror of
https://github.com/ppy/osu.git
synced 2025-02-21 03:02:54 +08:00
Merge branch 'master' into slider-late-hit-lenience
This commit is contained in:
commit
c0e96927aa
@ -52,6 +52,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[TestCase("3644427", new[] { typeof(CatchModEasy), typeof(CatchModFlashlight) })]
|
[TestCase("3644427", new[] { typeof(CatchModEasy), typeof(CatchModFlashlight) })]
|
||||||
[TestCase("3689906", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })]
|
[TestCase("3689906", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })]
|
||||||
[TestCase("3949367", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })]
|
[TestCase("3949367", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })]
|
||||||
|
[TestCase("112643")]
|
||||||
public new void Test(string name, params Type[] mods) => base.Test(name, mods);
|
public new void Test(string name, params Type[] mods) => base.Test(name, mods);
|
||||||
|
|
||||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class CatchRateAdjustedDisplayDifficultyTest
|
||||||
|
{
|
||||||
|
private static IEnumerable<float> difficultyValuesToTest()
|
||||||
|
{
|
||||||
|
for (float i = 0; i <= 10; i += 0.5f)
|
||||||
|
yield return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(difficultyValuesToTest))]
|
||||||
|
public void TestApproachRateIsUnchangedWithRateEqualToOne(float originalApproachRate)
|
||||||
|
{
|
||||||
|
var ruleset = new CatchRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty { ApproachRate = originalApproachRate };
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(originalApproachRate));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRateBelowOne()
|
||||||
|
{
|
||||||
|
var ruleset = new CatchRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty();
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 0.75);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(1.67).Within(0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRateAboveOne()
|
||||||
|
{
|
||||||
|
var ruleset = new CatchRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty();
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1.5);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(7.67).Within(0.01));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,582 @@
|
|||||||
|
osu file format v9
|
||||||
|
|
||||||
|
[General]
|
||||||
|
StackLeniency: 0.7
|
||||||
|
Mode: 0
|
||||||
|
|
||||||
|
[Difficulty]
|
||||||
|
HPDrainRate:7
|
||||||
|
CircleSize:5
|
||||||
|
OverallDifficulty:8
|
||||||
|
ApproachRate:8
|
||||||
|
SliderMultiplier:3.2
|
||||||
|
SliderTickRate:2
|
||||||
|
|
||||||
|
[Events]
|
||||||
|
//Background and Video events
|
||||||
|
//Break Periods
|
||||||
|
2,16325,17625
|
||||||
|
2,32325,33875
|
||||||
|
2,66325,67375
|
||||||
|
2,120135,127375
|
||||||
|
//Storyboard Layer 0 (Background)
|
||||||
|
//Storyboard Layer 1 (Fail)
|
||||||
|
//Storyboard Layer 2 (Pass)
|
||||||
|
//Storyboard Layer 3 (Foreground)
|
||||||
|
//Storyboard Sound Samples
|
||||||
|
//Background Colour Transformations
|
||||||
|
3,100,163,162,255
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
125,500,4,1,0,50,1,0
|
||||||
|
36125,-100,4,1,0,50,0,1
|
||||||
|
66125,-100,4,1,0,50,0,0
|
||||||
|
88125,-100,4,1,0,50,0,1
|
||||||
|
120125,-100,4,1,0,50,0,0
|
||||||
|
170125,-100,4,2,0,5,0,0
|
||||||
|
170250,-100,4,1,0,50,0,0
|
||||||
|
172125,-100,4,1,0,50,0,1
|
||||||
|
200125,-100,4,1,0,50,0,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
64,80,2375,5,0
|
||||||
|
172,192,2625,1,2
|
||||||
|
152,36,2875,1,0
|
||||||
|
80,176,3125,1,2
|
||||||
|
224,112,3375,1,0
|
||||||
|
192,256,3625,1,8
|
||||||
|
136,116,3875,1,0
|
||||||
|
272,32,4125,2,2,B|376:0|408:56|412:125|320:144|304:176|328:216|368:272|496:208,1,400,6|0
|
||||||
|
504,216,4875,2,2,B|376:232|288:280|248:384,1,320
|
||||||
|
384,344,5625,1,8
|
||||||
|
272,216,5875,1,0
|
||||||
|
272,216,6000,1,0
|
||||||
|
272,216,6125,1,4
|
||||||
|
92,280,6375,5,0
|
||||||
|
124,108,6625,1,8
|
||||||
|
256,8,6875,1,0
|
||||||
|
388,108,7125,1,2
|
||||||
|
420,280,7375,1,8
|
||||||
|
256,296,7625,1,8
|
||||||
|
256,120,7875,1,0
|
||||||
|
443,152,8125,2,2,B|397:202|305:219|256:192|203:163|114:181|68:231,1,400,2|0
|
||||||
|
24,256,8875,2,2,B|112:227|141:134|122:36|37:1,1,320
|
||||||
|
16,132,9625,1,8
|
||||||
|
136,280,9875,1,0
|
||||||
|
136,280,10000,1,0
|
||||||
|
136,280,10125,1,4
|
||||||
|
256,172,10375,5,0
|
||||||
|
368,56,10625,1,8
|
||||||
|
196,116,10875,1,0
|
||||||
|
316,116,11125,1,2
|
||||||
|
144,56,11375,1,0
|
||||||
|
256,0,11625,1,8
|
||||||
|
112,128,11875,1,0
|
||||||
|
164,280,12125,6,0,B|256:316,1,80,4|2
|
||||||
|
100,348,12500,2,0,B|8:312,1,80,0|2
|
||||||
|
144,212,12875,2,0,B|52:176,1,80,0|2
|
||||||
|
208,144,13250,2,0,B|300:180,1,80,0|2
|
||||||
|
332,324,13625,1,8
|
||||||
|
180,324,13875,1,0
|
||||||
|
256,240,14125,5,4
|
||||||
|
256,240,14250,1,2
|
||||||
|
324,112,14500,1,0
|
||||||
|
324,112,14625,1,2
|
||||||
|
192,56,14875,1,4
|
||||||
|
192,56,15000,1,2
|
||||||
|
256,164,15250,1,0
|
||||||
|
256,164,15375,1,2
|
||||||
|
256,20,15625,1,8
|
||||||
|
120,56,15875,1,0
|
||||||
|
256,92,16125,1,6
|
||||||
|
20,152,18375,5,0
|
||||||
|
180,136,18625,1,8
|
||||||
|
52,228,18875,1,0
|
||||||
|
120,84,19125,1,2
|
||||||
|
128,244,19375,1,0
|
||||||
|
48,84,19625,1,8
|
||||||
|
192,212,19875,1,0
|
||||||
|
300,72,20125,2,4,B|396:36|444:84|396:144|352:184|372:224|416:260|532:224|528:164,1,320,4|0
|
||||||
|
472,40,20875,2,2,B|376:72|304:164|272:260|280:320,1,320
|
||||||
|
404,352,21625,1,8
|
||||||
|
432,196,21875,1,0
|
||||||
|
432,196,22000,1,0
|
||||||
|
432,196,22125,1,4
|
||||||
|
296,100,22375,5,0
|
||||||
|
168,196,22625,2,0,B|32:296,1,160,8|0
|
||||||
|
268,212,23125,2,0,B|168:76,1,160,2|8
|
||||||
|
252,312,23625,2,0,B|388:212,1,160,8|0
|
||||||
|
484,96,24125,2,2,B|412:0|320:36|288:120|240:136|200:132|156:116|132:96|80:44,1,400,2|0
|
||||||
|
72,24,24875,2,2,B|158:66|148:177|67:253|-19:210,1,320
|
||||||
|
56,108,25625,1,8
|
||||||
|
176,200,25875,1,0
|
||||||
|
176,200,26000,1,0
|
||||||
|
176,200,26125,1,4
|
||||||
|
316,92,26375,5,0
|
||||||
|
464,164,26625,2,0,B|394:224|412:336,1,160,2|0
|
||||||
|
232,316,27125,2,0,B|306:256|284:144,1,160,2|8
|
||||||
|
136,88,27625,1,8
|
||||||
|
60,224,27875,1,0
|
||||||
|
212,132,28125,6,0,B|256:32,1,80,4|2
|
||||||
|
340,228,28500,2,0,B|384:128,1,80,0|2
|
||||||
|
256,284,28875,2,0,B|212:184,1,80,4|2
|
||||||
|
128,380,29250,2,0,B|84:280,1,80,0|2
|
||||||
|
238,383,29625,2,0,B|406:379,1,160,8|0
|
||||||
|
512,267,30125,5,4
|
||||||
|
512,267,30250,1,2
|
||||||
|
416,152,30500,1,0
|
||||||
|
416,152,30625,1,2
|
||||||
|
300,264,30875,1,4
|
||||||
|
300,264,31000,1,2
|
||||||
|
236,100,31250,1,0
|
||||||
|
236,100,31375,1,2
|
||||||
|
152,256,31625,1,8
|
||||||
|
300,160,31875,1,0
|
||||||
|
256,332,32125,1,6
|
||||||
|
52,52,34625,5,0
|
||||||
|
152,164,34875,1,0
|
||||||
|
256,56,35125,1,4
|
||||||
|
256,56,35625,1,2
|
||||||
|
256,56,36125,2,4,B|331:63|364:136|320:224,1,160,4|0
|
||||||
|
320,312,36625,1,8
|
||||||
|
204,228,36875,1,0
|
||||||
|
104,328,37125,2,2,B|24:287|44:188,1,160
|
||||||
|
92,60,37625,1,8
|
||||||
|
212,148,37875,1,0
|
||||||
|
268,104,38000,1,0
|
||||||
|
324,60,38125,2,0,B|452:184,1,160,4|0
|
||||||
|
504,300,38625,1,8
|
||||||
|
364,340,38875,1,0
|
||||||
|
232,280,39125,6,2,B|150:282|69:198|105:87|179:53,2,320,2|2|6
|
||||||
|
280,148,40375,1,0
|
||||||
|
400,228,40625,2,0,B|520:368,1,160,8|0
|
||||||
|
480,192,41125,1,2
|
||||||
|
324,220,41375,1,2
|
||||||
|
168,256,41625,1,8
|
||||||
|
72,148,41875,1,2
|
||||||
|
48,84,42000,1,2
|
||||||
|
96,36,42125,2,0,B|164:108|256:44,1,160,6|0
|
||||||
|
400,72,42625,1,2
|
||||||
|
440,236,42875,1,2
|
||||||
|
464,300,43000,1,2
|
||||||
|
416,348,43125,2,0,B|348:276|256:340,1,160,6|0
|
||||||
|
112,312,43625,1,2
|
||||||
|
140,188,43875,1,0
|
||||||
|
52,64,44125,5,6
|
||||||
|
208,48,44375,1,0
|
||||||
|
344,132,44625,1,8
|
||||||
|
448,256,44875,2,2,B|401:321|285:337|217:242|233:163,2,320,2|2|0
|
||||||
|
326,211,46125,2,2,B|279:146|163:130|95:225|111:304,1,320,6|0
|
||||||
|
230,287,46875,2,2,B|277:352|393:368|461:273|445:194,1,320,6|8
|
||||||
|
376,80,47625,1,8
|
||||||
|
376,80,48125,6,0,B|304:128|216:96,1,160,4|0
|
||||||
|
84,56,48625,1,8
|
||||||
|
152,200,48875,1,0
|
||||||
|
44,320,49125,2,0,B|121:364|204:320,1,160,4|0
|
||||||
|
336,240,49625,5,8
|
||||||
|
256,148,49875,1,0
|
||||||
|
176,240,50125,1,0
|
||||||
|
340,144,50625,1,0
|
||||||
|
420,236,50875,1,0
|
||||||
|
500,144,51125,1,2
|
||||||
|
172,144,51625,1,2
|
||||||
|
92,236,51875,1,0
|
||||||
|
12,144,52125,6,0,B|160:48,1,160,4|0
|
||||||
|
304,76,52625,1,8
|
||||||
|
256,228,52875,1,0
|
||||||
|
216,112,53125,2,0,B|364:208,1,160,2|0
|
||||||
|
508,180,53625,1,8
|
||||||
|
460,28,53875,1,0
|
||||||
|
344,96,54125,1,2
|
||||||
|
228,8,54375,1,0
|
||||||
|
153,116,54625,1,2
|
||||||
|
72,220,54875,1,0
|
||||||
|
180,295,55125,1,2
|
||||||
|
284,376,55375,1,0
|
||||||
|
359,268,55625,1,2
|
||||||
|
440,164,55875,1,0
|
||||||
|
352,160,56125,6,0,B|466:294,1,160,4|0
|
||||||
|
312,228,56625,1,8
|
||||||
|
200,300,56875,1,0
|
||||||
|
160,160,57125,2,0,B|46:294,1,160,4|0
|
||||||
|
200,228,57625,1,8
|
||||||
|
312,300,57875,1,0
|
||||||
|
444,208,58125,2,0,B|362:164|380:56,1,160,2|0
|
||||||
|
344,12,58500,1,0
|
||||||
|
272,4,58625,2,0,B|232:88|120:68,1,160,2|0
|
||||||
|
68,176,59125,2,0,B|148:220|132:328,1,160,2|0
|
||||||
|
168,372,59500,1,0
|
||||||
|
240,380,59625,2,0,B|280:296|392:316,1,160,2|0
|
||||||
|
456,176,60125,5,6
|
||||||
|
328,80,60375,1,0
|
||||||
|
216,196,60625,1,8
|
||||||
|
72,136,60875,2,2,B|54:209|91:305|191:336|269:306,2,320,2|2|0
|
||||||
|
200,224,62125,2,2,B|182:150|219:54|319:23|397:53,1,320,2|0
|
||||||
|
480,179,62875,2,2,B|499:252|462:348|362:379|284:349,1,320,2|0
|
||||||
|
136,296,63625,2,0,B|67:220|140:136,1,160,8|0
|
||||||
|
256,56,64125,5,6
|
||||||
|
284,212,64375,1,0
|
||||||
|
440,180,64625,1,8
|
||||||
|
420,24,64875,1,0
|
||||||
|
300,132,65125,1,6
|
||||||
|
272,288,65375,1,0
|
||||||
|
116,256,65625,1,8
|
||||||
|
136,100,65875,1,0
|
||||||
|
256,8,66125,1,4
|
||||||
|
256,56,68125,6,0,B|298:128|244:237|123:241|74:173,1,320
|
||||||
|
132,80,68875,2,2,B|344:328,1,320
|
||||||
|
456,224,69625,1,8
|
||||||
|
340,116,69875,1,0
|
||||||
|
340,116,70000,1,0
|
||||||
|
340,116,70125,1,4
|
||||||
|
228,4,70375,5,0
|
||||||
|
256,160,70625,2,0,B|186:224|88:168,1,160,2|0
|
||||||
|
148,332,71125,2,0,B|216:396|316:340,1,160,2|8
|
||||||
|
424,248,71625,1,8
|
||||||
|
336,112,71875,1,0
|
||||||
|
336,112,72000,1,0
|
||||||
|
336,112,72125,1,4
|
||||||
|
228,208,72375,2,0,B|139:179|144:80,1,160,0|8
|
||||||
|
268,56,72875,2,2,B|272:164|220:272|120:308|72:308,1,320
|
||||||
|
24,192,73625,1,8
|
||||||
|
92,64,73875,1,0
|
||||||
|
92,64,74000,1,0
|
||||||
|
92,64,74125,1,4
|
||||||
|
224,140,74375,5,0
|
||||||
|
340,224,74625,2,0,B|412:211|428:121|363:77,1,160,2|0
|
||||||
|
268,192,75125,2,0,B|196:205|180:295|245:339,1,160,2|0
|
||||||
|
268,192,75625,2,0,B|104:168,1,160,8|0
|
||||||
|
24,52,76125,6,0,B|132:40,1,80
|
||||||
|
176,32,76375,1,2
|
||||||
|
348,60,76625,1,2
|
||||||
|
248,164,76875,1,2
|
||||||
|
264,20,77125,1,2
|
||||||
|
324,140,77375,1,2
|
||||||
|
180,116,77625,1,2
|
||||||
|
240,240,77875,1,0
|
||||||
|
256,92,78125,1,4
|
||||||
|
100,124,78375,5,0
|
||||||
|
8,256,78625,2,0,B|64:332|176:304,1,160,8|0
|
||||||
|
304,260,79125,2,0,B|248:184|136:212,1,160,2|0
|
||||||
|
304,260,79625,1,8
|
||||||
|
460,284,79875,1,2
|
||||||
|
420,128,80125,6,0,B|332:128,1,80,4|0
|
||||||
|
256,124,80375,1,2
|
||||||
|
344,260,80625,1,2
|
||||||
|
168,260,80875,1,2
|
||||||
|
384,192,81125,1,2
|
||||||
|
256,260,81375,1,2
|
||||||
|
168,124,81625,1,2
|
||||||
|
344,124,81875,1,2
|
||||||
|
128,192,82125,1,4
|
||||||
|
48,192,82250,6,0,B|48:84|152:52,1,160,2|0
|
||||||
|
204,44,82625,2,0,B|204:152|308:184,1,160,2|0
|
||||||
|
352,160,83000,2,0,B|244:160|212:264,1,160,2|0
|
||||||
|
192,316,83375,2,0,B|84:316|52:212,1,160,2|2
|
||||||
|
32,88,83875,1,2
|
||||||
|
172,8,84125,1,4
|
||||||
|
256,192,84250,12,6,86125
|
||||||
|
256,192,86250,12,4,87125
|
||||||
|
256,100,88125,6,2,B|308:116|368:104|404:16,1,160,6|0
|
||||||
|
256,100,88625,1,8
|
||||||
|
136,180,88875,1,0
|
||||||
|
8,96,89125,2,0,B|-28:168|16:232|68:256,1,160,2|0
|
||||||
|
164,312,89625,1,8
|
||||||
|
288,236,89875,1,2
|
||||||
|
288,236,90000,1,2
|
||||||
|
288,236,90125,2,2,B|452:164,1,160,6|0
|
||||||
|
476,32,90625,1,8
|
||||||
|
332,104,90875,1,0
|
||||||
|
180,104,91125,5,6
|
||||||
|
36,32,91375,1,8
|
||||||
|
56,164,91625,1,8
|
||||||
|
56,164,92125,2,0,B|260:208,1,160,6|0
|
||||||
|
84,296,92625,1,8
|
||||||
|
220,376,92875,1,0
|
||||||
|
320,268,93125,2,0,B|524:224,1,160,6|0
|
||||||
|
432,80,93625,1,8
|
||||||
|
296,152,93875,1,2
|
||||||
|
296,152,94000,1,2
|
||||||
|
296,152,94125,2,2,B|232:164|176:132|164:52,1,160,6|0
|
||||||
|
216,232,94625,2,2,B|280:220|336:252|348:332,1,160,2|0
|
||||||
|
341,304,95000,1,0
|
||||||
|
341,304,95125,2,0,B|369:84,1,160,2|0
|
||||||
|
171,80,95625,2,0,B|143:300,1,160,2|0
|
||||||
|
43,358,96125,5,6
|
||||||
|
81,219,96375,1,0
|
||||||
|
169,332,96625,1,8
|
||||||
|
304,272,96875,2,2,B|388:252|426:161|418:63|344:19,2,320,2|2|0
|
||||||
|
240,144,98125,2,2,B|219:244|50:229|65:60|168:58,1,320
|
||||||
|
240,144,98875,2,2,B|260:43|429:58|414:227|311:229,1,320,2|0
|
||||||
|
180,292,99625,2,0,B|80:304|36:208,1,160,2|0
|
||||||
|
48,64,100125,6,0,B|224:112,1,160,4|0
|
||||||
|
348,52,100625,2,0,B|524:4,1,160,2|0
|
||||||
|
504,172,101125,2,0,B|328:124,1,160,2|0
|
||||||
|
204,184,101625,2,0,B|28:232,1,160,2|0
|
||||||
|
49,226,102000,1,0
|
||||||
|
49,226,102125,1,2
|
||||||
|
256,324,102625,5,8
|
||||||
|
384,256,102875,1,0
|
||||||
|
256,188,103125,1,6
|
||||||
|
256,188,103625,1,2
|
||||||
|
128,256,103875,1,0
|
||||||
|
256,324,104125,6,0,B|324:252|432:316,1,160,6|0
|
||||||
|
492,168,104625,1,8
|
||||||
|
332,188,104875,1,0
|
||||||
|
256,60,105125,2,0,B|188:132|80:68,1,160,6|0
|
||||||
|
20,216,105625,1,8
|
||||||
|
180,196,105875,1,0
|
||||||
|
368,156,106125,2,0,B|418:184|462:234|408:296,1,160,2|0
|
||||||
|
220,80,106625,2,0,B|248:30|298:-14|360:40,1,160,2|0
|
||||||
|
144,228,107125,2,0,B|94:200|50:150|104:88,1,160,2|0
|
||||||
|
292,304,107625,2,0,B|264:354|214:398|152:344,1,160,2|0
|
||||||
|
44,216,108125,6,0,B|145:221|172:132,1,160,6|0
|
||||||
|
304,224,108625,1,8
|
||||||
|
408,104,108875,1,0
|
||||||
|
468,216,109125,2,0,B|367:221|340:132,1,160,6|0
|
||||||
|
208,224,109625,1,8
|
||||||
|
104,104,109875,1,0
|
||||||
|
256,56,110125,2,0,B|144:180,1,160,2|0
|
||||||
|
256,328,110625,2,0,B|368:204,1,160,2|0
|
||||||
|
208,244,111125,2,0,B|96:368,1,160,2|0
|
||||||
|
304,140,111625,2,0,B|416:16,1,160,2|0
|
||||||
|
252,20,112125,5,6
|
||||||
|
112,60,112375,1,0
|
||||||
|
72,200,112625,1,8
|
||||||
|
158,316,112875,2,2,B|236:321|324:259|326:152|278:89,2,320,2|2|0
|
||||||
|
176,168,114125,2,2,B|214:236|313:276|405:220|431:145,1,320,2|0
|
||||||
|
328,64,114875,2,2,B|259:102|219:201|275:293|350:319,1,320,2|0
|
||||||
|
488,340,115625,2,0,B|456:172,1,160,2|0
|
||||||
|
416,72,116125,5,6
|
||||||
|
288,140,116375,1,0
|
||||||
|
164,68,116625,1,8
|
||||||
|
36,136,116875,1,0
|
||||||
|
104,264,117125,1,6
|
||||||
|
232,332,117375,1,0
|
||||||
|
356,260,117625,1,8
|
||||||
|
484,328,117875,1,0
|
||||||
|
356,384,118125,1,6
|
||||||
|
256,12,128125,5,4
|
||||||
|
256,12,128250,1,2
|
||||||
|
336,128,128500,1,0
|
||||||
|
336,128,128625,1,2
|
||||||
|
400,0,128875,1,0
|
||||||
|
400,0,129000,1,2
|
||||||
|
492,112,129250,1,0
|
||||||
|
492,112,129375,1,2
|
||||||
|
440,248,129625,2,2,B|272:284,1,160
|
||||||
|
256,108,130125,5,4
|
||||||
|
256,108,130250,1,2
|
||||||
|
176,224,130500,1,0
|
||||||
|
176,224,130625,1,2
|
||||||
|
112,96,130875,1,0
|
||||||
|
112,96,131000,1,2
|
||||||
|
20,208,131250,1,0
|
||||||
|
20,208,131375,1,2
|
||||||
|
72,344,131625,2,2,B|240:380,1,160
|
||||||
|
408,376,132125,6,0,B|512:352|584:248|592:-32|416:-48|256:-80|96:-16|56:88|8:224|88:304|144:336|184:368|256:368|256:368|328:368|368:336|424:304|504:224|456:88|416:-16|256:-80|96:-48|-80:-32|-72:248|0:352|104:376,1,2240,6|0
|
||||||
|
256,192,135875,5,2
|
||||||
|
256,192,136000,1,0
|
||||||
|
256,192,136125,1,4
|
||||||
|
136,104,136375,1,0
|
||||||
|
132,240,136625,1,8
|
||||||
|
133,240,136750,1,0
|
||||||
|
256,280,137000,1,0
|
||||||
|
255,280,137125,1,8
|
||||||
|
256,280,137250,1,0
|
||||||
|
256,280,137375,1,0
|
||||||
|
380,240,137625,1,8
|
||||||
|
376,104,137875,1,0
|
||||||
|
256,124,138125,5,4
|
||||||
|
256,124,138375,1,0
|
||||||
|
144,192,138625,1,8
|
||||||
|
144,192,138750,1,0
|
||||||
|
256,260,139000,1,0
|
||||||
|
256,260,139125,1,8
|
||||||
|
256,260,139250,1,0
|
||||||
|
256,260,139375,1,0
|
||||||
|
368,192,139625,1,8
|
||||||
|
256,124,139875,1,0
|
||||||
|
256,124,140000,1,0
|
||||||
|
256,124,140125,2,2,B|188:112|212:76|188:36|256:20,1,160,6|2
|
||||||
|
332,128,140625,5,8
|
||||||
|
332,128,140750,1,0
|
||||||
|
332,256,141000,1,0
|
||||||
|
332,256,141125,1,8
|
||||||
|
332,256,141250,1,0
|
||||||
|
332,256,141375,1,0
|
||||||
|
180,256,141625,1,8
|
||||||
|
180,128,141875,1,0
|
||||||
|
256,56,142125,5,4
|
||||||
|
256,56,142375,1,0
|
||||||
|
256,160,142625,1,8
|
||||||
|
256,160,142750,1,0
|
||||||
|
256,264,143000,1,0
|
||||||
|
256,264,143125,1,8
|
||||||
|
256,264,143250,1,0
|
||||||
|
256,264,143375,1,0
|
||||||
|
188,352,143625,1,8
|
||||||
|
324,352,143875,1,0
|
||||||
|
324,352,144000,1,0
|
||||||
|
324,352,144125,2,0,B|492:352,1,160,6|2
|
||||||
|
392,280,144625,5,8
|
||||||
|
392,280,144750,1,0
|
||||||
|
324,192,145000,1,0
|
||||||
|
324,192,145125,1,8
|
||||||
|
324,192,145250,1,0
|
||||||
|
324,192,145375,1,0
|
||||||
|
188,192,145625,1,8
|
||||||
|
120,280,145875,1,0
|
||||||
|
256,288,146125,5,4
|
||||||
|
256,288,146375,1,0
|
||||||
|
256,176,146625,1,8
|
||||||
|
256,176,146750,1,0
|
||||||
|
176,96,147000,1,0
|
||||||
|
176,96,147125,1,8
|
||||||
|
176,96,147250,1,0
|
||||||
|
176,96,147375,1,0
|
||||||
|
256,16,147625,1,8
|
||||||
|
336,96,147875,1,0
|
||||||
|
336,96,148000,1,0
|
||||||
|
336,96,148125,2,6,B|400:156|388:224|364:248,1,160,6|2
|
||||||
|
256,272,148625,5,8
|
||||||
|
240,264,148750,1,0
|
||||||
|
240,180,149000,1,0
|
||||||
|
256,172,149125,1,8
|
||||||
|
272,164,149250,1,0
|
||||||
|
288,156,149375,1,0
|
||||||
|
256,64,149625,1,8
|
||||||
|
256,64,149875,1,0
|
||||||
|
116,180,150125,5,0
|
||||||
|
120,200,150250,1,0
|
||||||
|
132,224,150375,1,0
|
||||||
|
152,236,150500,1,0
|
||||||
|
176,240,150625,1,8
|
||||||
|
208,240,150750,1,0
|
||||||
|
232,236,150875,1,0
|
||||||
|
248,216,151000,1,0
|
||||||
|
256,192,151125,1,8
|
||||||
|
260,168,151250,1,0
|
||||||
|
272,144,151375,1,8
|
||||||
|
292,132,151500,1,0
|
||||||
|
316,128,151625,1,8
|
||||||
|
348,128,151750,1,8
|
||||||
|
372,132,151875,1,8
|
||||||
|
388,152,152000,1,0
|
||||||
|
404,184,152125,6,0,B|436:250|377:334|292:300,1,160,6|0
|
||||||
|
108,200,152625,2,0,B|76:134|135:50|220:84,1,160,6|0
|
||||||
|
256,192,153125,2,0,B|256:100,1,80,2|0
|
||||||
|
256,192,153375,2,0,B|256:368,2,160,2|8|0
|
||||||
|
360,60,154125,5,0
|
||||||
|
360,60,154250,1,0
|
||||||
|
360,60,154375,1,2
|
||||||
|
256,12,154625,1,0
|
||||||
|
256,12,154750,1,0
|
||||||
|
256,12,154875,1,2
|
||||||
|
154,64,155125,1,0
|
||||||
|
154,64,155250,1,2
|
||||||
|
155,63,155375,2,0,B|87:119|115:191|179:211|227:179,2,160,0|8|0
|
||||||
|
163,74,156000,5,0
|
||||||
|
163,74,156125,1,0
|
||||||
|
163,74,156250,2,2,B|174:151|299:265|445:180|473:106,1,400,2|0
|
||||||
|
320,80,157125,2,2,B|224:88|184:188|224:288|320:295,1,320
|
||||||
|
348,292,157750,1,0
|
||||||
|
380,280,157875,1,0
|
||||||
|
404,260,158000,1,0
|
||||||
|
412,236,158125,1,0
|
||||||
|
412,208,158250,1,0
|
||||||
|
404,180,158375,1,0
|
||||||
|
264,68,158625,2,0,B|184:104,2,80,2|0|2
|
||||||
|
164,216,159125,2,0,B|244:180,2,80,2|0|2
|
||||||
|
56,144,159625,5,8
|
||||||
|
64,276,159875,1,8
|
||||||
|
64,276,160000,1,8
|
||||||
|
64,276,160125,2,0,B|24:352,2,80,2|0|0
|
||||||
|
128,288,160500,2,0,B|136:188,2,80,2|0|0
|
||||||
|
192,300,160875,2,0,B|200:400,2,80,2|0|0
|
||||||
|
240,256,161250,2,0,B|304:176,2,80,2|0|0
|
||||||
|
284,304,161625,2,0,B|356:380,2,80,2|0|0
|
||||||
|
328,256,162000,6,0,B|456:236,2,80,0|2|0
|
||||||
|
308,192,162375,2,0,B|180:172,2,80,0|2|0
|
||||||
|
340,136,162750,2,0,B|468:116,2,80,0|2|0
|
||||||
|
284,100,163125,2,0,B|264:-28,2,80,0|2|0
|
||||||
|
224,128,163500,2,0,B|204:256,2,80,0|2|0
|
||||||
|
180,76,163875,6,0,B|92:52,2,80,2|0|0
|
||||||
|
144,132,164250,2,0,B|72:184,2,80,2|0|0
|
||||||
|
168,196,164625,2,0,B|240:248,2,80,2|0|0
|
||||||
|
136,256,165000,2,0,B|96:340,2,80,2|0|0
|
||||||
|
188,296,165375,2,0,B|228:380,2,80,2|0|0
|
||||||
|
236,252,165750,1,0
|
||||||
|
236,252,165875,1,2
|
||||||
|
364,276,166125,6,2,B|408:176|360:156|320:168|296:176|268:132|264:112|272:76|304:52|328:40,1,240,2|0
|
||||||
|
264,24,166625,2,2,B|308:124|260:144|220:132|196:124|168:168|164:188|172:224|204:248|228:260,1,240,2|0
|
||||||
|
192,280,167125,1,0
|
||||||
|
320,376,167375,1,0
|
||||||
|
192,376,167625,1,0
|
||||||
|
256,328,167750,1,0
|
||||||
|
320,280,167875,1,0
|
||||||
|
256,124,168125,1,6
|
||||||
|
256,192,168250,12,0,170125
|
||||||
|
256,192,171125,12,6,172125
|
||||||
|
48,56,172375,5,0
|
||||||
|
20,184,172625,2,0,B|16:264|92:316|152:304,1,160,8|0
|
||||||
|
240,300,173125,1,2
|
||||||
|
200,176,173375,1,0
|
||||||
|
324,220,173625,2,0,B|360:220|416:258|412:338,1,160,8|0
|
||||||
|
412,334,174000,1,0
|
||||||
|
412,334,174125,2,0,B|456:156,1,160,6|0
|
||||||
|
398,35,174625,2,0,B|220:-8,1,160,2|0
|
||||||
|
245,0,175000,1,0
|
||||||
|
245,0,175125,2,0,B|201:178,1,160,6|0
|
||||||
|
259,299,175625,2,0,B|437:342,1,160,2|0
|
||||||
|
424,176,176125,5,6
|
||||||
|
272,128,176375,1,0
|
||||||
|
116,152,176625,1,8
|
||||||
|
173,253,176875,2,2,B|257:233|295:142|287:44|213:0,2,320,2|2|0
|
||||||
|
28,204,178125,2,2,B|356:316,1,320
|
||||||
|
172,360,178875,2,2,B|500:248,1,320,2|0
|
||||||
|
384,148,179625,2,0,B|292:168|224:96|232:44,1,160,2|0
|
||||||
|
244,93,180000,1,0
|
||||||
|
244,93,180125,6,0,B|64:120,1,160,6|0
|
||||||
|
100,268,180625,2,0,B|256:296,1,160,8|0
|
||||||
|
257,296,181000,1,0
|
||||||
|
256,296,181125,2,0,B|413:267,1,160,6|0
|
||||||
|
426,116,181625,2,0,B|267:93,1,160,8|2
|
||||||
|
267,93,182000,5,2
|
||||||
|
267,93,182125,2,2,B|180:112|168:212,1,160,2|0
|
||||||
|
140,380,182625,2,0,B|227:361|239:261,1,160,8|0
|
||||||
|
62,169,183125,2,2,B|80:256|180:268,1,160,2|0
|
||||||
|
348,296,183625,2,0,B|329:208|229:196,1,160,8|0
|
||||||
|
64,172,184125,1,6
|
||||||
|
256,192,184250,12,2,185625
|
||||||
|
48,188,186125,6,2,B|96:108|256:108|256:192|256:276|416:276|464:196,1,480,2|0
|
||||||
|
328,144,187125,2,0,B|296:316,1,160,2|0
|
||||||
|
184,240,187625,2,0,B|216:68,1,160,2|0
|
||||||
|
256,192,188125,1,6
|
||||||
|
256,192,188250,12,2,189625
|
||||||
|
464,188,190125,6,2,B|416:108|256:108|256:192|256:276|96:276|48:196,1,480,2|0
|
||||||
|
184,144,191125,2,0,B|216:316,1,160,2|0
|
||||||
|
328,240,191625,2,0,B|296:68,1,160,2|0
|
||||||
|
164,32,192125,5,6
|
||||||
|
28,84,192375,1,0
|
||||||
|
28,228,192625,1,8
|
||||||
|
128,332,192875,2,2,B|160:224|300:172|408:244,2,320,2|2|0
|
||||||
|
276,356,194125,2,2,B|384:324|436:184|364:76,1,320
|
||||||
|
236,28,194875,2,2,B|128:60|76:200|148:308,1,320,2|0
|
||||||
|
280,268,195625,2,0,B|232:116,1,160,2|0
|
||||||
|
104,52,196125,5,6
|
||||||
|
136,192,196375,1,0
|
||||||
|
116,344,196625,1,8
|
||||||
|
256,312,196875,1,0
|
||||||
|
332,312,197000,1,0
|
||||||
|
408,332,197125,1,6
|
||||||
|
392,264,197250,1,0
|
||||||
|
376,192,197375,1,0
|
||||||
|
396,40,197625,1,8
|
||||||
|
256,72,197875,5,0
|
||||||
|
256,72,198000,1,0
|
||||||
|
256,72,198125,1,6
|
||||||
|
136,192,198625,1,6
|
||||||
|
256,312,199125,1,6
|
||||||
|
376,192,199625,1,6
|
||||||
|
256,192,200125,1,6
|
@ -15,6 +15,7 @@ using osu.Game.Rulesets.Catch.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Catch.Difficulty;
|
using osu.Game.Rulesets.Catch.Difficulty;
|
||||||
using osu.Game.Rulesets.Catch.Edit;
|
using osu.Game.Rulesets.Catch.Edit;
|
||||||
using osu.Game.Rulesets.Catch.Mods;
|
using osu.Game.Rulesets.Catch.Mods;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Replays;
|
using osu.Game.Rulesets.Catch.Replays;
|
||||||
using osu.Game.Rulesets.Catch.Scoring;
|
using osu.Game.Rulesets.Catch.Scoring;
|
||||||
using osu.Game.Rulesets.Catch.Skinning.Argon;
|
using osu.Game.Rulesets.Catch.Skinning.Argon;
|
||||||
@ -235,5 +236,17 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <seealso cref="CatchHitObject.ApplyDefaultsToSelf"/>
|
||||||
|
public override BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, double rate)
|
||||||
|
{
|
||||||
|
BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty);
|
||||||
|
|
||||||
|
double preempt = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, CatchHitObject.PREEMPT_MAX, CatchHitObject.PREEMPT_MID, CatchHitObject.PREEMPT_MIN);
|
||||||
|
preempt /= rate;
|
||||||
|
adjustedDifficulty.ApproachRate = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, CatchHitObject.PREEMPT_MAX, CatchHitObject.PREEMPT_MID, CatchHitObject.PREEMPT_MIN);
|
||||||
|
|
||||||
|
return adjustedDifficulty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, PREEMPT_MAX, PREEMPT_MID, PREEMPT_MIN);
|
||||||
|
|
||||||
Scale = LegacyRulesetExtensions.CalculateScaleFromCircleSize(difficulty.CircleSize);
|
Scale = LegacyRulesetExtensions.CalculateScaleFromCircleSize(difficulty.CircleSize);
|
||||||
}
|
}
|
||||||
@ -189,6 +189,21 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
// The half of the height of the osu! playfield.
|
// The half of the height of the osu! playfield.
|
||||||
public const float DEFAULT_LEGACY_CONVERT_Y = 192;
|
public const float DEFAULT_LEGACY_CONVERT_Y = 192;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minimum preempt time at AR=10.
|
||||||
|
/// </summary>
|
||||||
|
public const double PREEMPT_MIN = 450;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Median preempt time at AR=5.
|
||||||
|
/// </summary>
|
||||||
|
public const double PREEMPT_MID = 1200;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum preempt time at AR=0.
|
||||||
|
/// </summary>
|
||||||
|
public const double PREEMPT_MAX = 1800;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Y position of the hit object is not used in the normal osu!catch gameplay.
|
/// The Y position of the hit object is not used in the normal osu!catch gameplay.
|
||||||
/// It is preserved to maximize the backward compatibility with the legacy editor, in which the mappers use the Y position to organize the patterns.
|
/// It is preserved to maximize the backward compatibility with the legacy editor, in which the mappers use the Y position to organize the patterns.
|
||||||
|
@ -10,7 +10,6 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
@ -103,8 +102,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
AddNested(new TinyDroplet
|
AddNested(new TinyDroplet
|
||||||
{
|
{
|
||||||
StartTime = t + lastEvent.Value.Time,
|
StartTime = t + lastEvent.Value.Time,
|
||||||
X = ClampToPlayfield(EffectiveX + Path.PositionAt(
|
X = EffectiveX + Path.PositionAt(lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X,
|
||||||
lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,7 +119,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
Samples = dropletSamples,
|
Samples = dropletSamples,
|
||||||
StartTime = e.Time,
|
StartTime = e.Time,
|
||||||
X = ClampToPlayfield(EffectiveX + Path.PositionAt(e.PathProgress).X),
|
X = EffectiveX + Path.PositionAt(e.PathProgress).X,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -132,16 +130,14 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
Samples = this.GetNodeSamples(nodeIndex++),
|
Samples = this.GetNodeSamples(nodeIndex++),
|
||||||
StartTime = e.Time,
|
StartTime = e.Time,
|
||||||
X = ClampToPlayfield(EffectiveX + Path.PositionAt(e.PathProgress).X),
|
X = EffectiveX + Path.PositionAt(e.PathProgress).X,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public float EndX => ClampToPlayfield(EffectiveX + this.CurvePositionAt(1).X);
|
public float EndX => EffectiveX + this.CurvePositionAt(1).X;
|
||||||
|
|
||||||
public float ClampToPlayfield(float value) => Math.Clamp(value, 0, CatchPlayfield.WIDTH);
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public double Duration
|
public double Duration
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -100,16 +99,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
return SkinUtils.As<TValue>(new Bindable<float>(30));
|
return SkinUtils.As<TValue>(new Bindable<float>(30));
|
||||||
|
|
||||||
case LegacyManiaSkinConfigurationLookups.ColumnWidth:
|
case LegacyManiaSkinConfigurationLookups.ColumnWidth:
|
||||||
|
|
||||||
float width;
|
|
||||||
|
|
||||||
bool isSpecialColumn = stage.IsSpecialColumn(columnIndex);
|
bool isSpecialColumn = stage.IsSpecialColumn(columnIndex);
|
||||||
|
|
||||||
// Best effort until we have better mobile support.
|
float width = 60 * (isSpecialColumn ? 2 : 1);
|
||||||
if (RuntimeInfo.IsMobile)
|
|
||||||
width = 170 * Math.Min(1, 7f / beatmap.TotalColumns) * (isSpecialColumn ? 1.8f : 1);
|
|
||||||
else
|
|
||||||
width = 60 * (isSpecialColumn ? 2 : 1);
|
|
||||||
|
|
||||||
return SkinUtils.As<TValue>(new Bindable<float>(width));
|
return SkinUtils.As<TValue>(new Bindable<float>(width));
|
||||||
|
|
||||||
|
@ -34,7 +34,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
FallbackColumnIndex = "S";
|
FallbackColumnIndex = "S";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int distanceToEdge = Math.Min(Column.Index, (stage.Columns - 1) - Column.Index);
|
// Account for cases like dual-stage (assume that all stages have the same column count for now).
|
||||||
|
int columnInStage = Column.Index % stage.Columns;
|
||||||
|
int distanceToEdge = Math.Min(columnInStage, (stage.Columns - 1) - columnInStage);
|
||||||
FallbackColumnIndex = distanceToEdge % 2 == 0 ? "1" : "2";
|
FallbackColumnIndex = distanceToEdge % 2 == 0 ? "1" : "2";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,17 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Skinning;
|
using osu.Game.Rulesets.Mania.Skinning;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
@ -60,6 +63,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
onSkinChanged();
|
onSkinChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
updateMobileSizing();
|
||||||
|
}
|
||||||
|
|
||||||
private void onSkinChanged()
|
private void onSkinChanged()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < stageDefinition.Columns; i++)
|
for (int i = 0; i < stageDefinition.Columns; i++)
|
||||||
@ -77,12 +86,15 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, i))
|
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, i))
|
||||||
?.Value;
|
?.Value;
|
||||||
|
|
||||||
if (width == null)
|
bool isSpecialColumn = stageDefinition.IsSpecialColumn(i);
|
||||||
// only used by default skin (legacy skins get defaults set in LegacyManiaSkinConfiguration)
|
|
||||||
columns[i].Width = stageDefinition.IsSpecialColumn(i) ? Column.SPECIAL_COLUMN_WIDTH : Column.COLUMN_WIDTH;
|
// only used by default skin (legacy skins get defaults set in LegacyManiaSkinConfiguration)
|
||||||
else
|
width ??= isSpecialColumn ? Column.SPECIAL_COLUMN_WIDTH : Column.COLUMN_WIDTH;
|
||||||
columns[i].Width = width.Value;
|
|
||||||
|
columns[i].Width = width.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateMobileSizing();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -92,10 +104,29 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
/// <param name="content">The content.</param>
|
/// <param name="content">The content.</param>
|
||||||
public void SetContentForColumn(int column, TContent content) => columns[column].Child = content;
|
public void SetContentForColumn(int column, TContent content) => columns[column].Child = content;
|
||||||
|
|
||||||
public new MarginPadding Padding
|
private void updateMobileSizing()
|
||||||
{
|
{
|
||||||
get => base.Padding;
|
if (!IsLoaded || !RuntimeInfo.IsMobile)
|
||||||
set => base.Padding = value;
|
return;
|
||||||
|
|
||||||
|
// GridContainer+CellContainer containing this stage (gets split up for dual stages).
|
||||||
|
Vector2? containingCell = this.FindClosestParent<Stage>()?.Parent?.DrawSize;
|
||||||
|
|
||||||
|
// Will be null in tests.
|
||||||
|
if (containingCell == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
float aspectRatio = containingCell.Value.X / containingCell.Value.Y;
|
||||||
|
|
||||||
|
// 2.83 is a mostly arbitrary scale-up (170 / 60, based on original implementation for argon)
|
||||||
|
float mobileAdjust = 2.83f * Math.Min(1, 7f / stageDefinition.Columns);
|
||||||
|
// 1.92 is a "reference" mobile screen aspect ratio for phones.
|
||||||
|
// We should scale it back for cases like tablets which aren't so extreme.
|
||||||
|
mobileAdjust *= aspectRatio / 1.92f;
|
||||||
|
|
||||||
|
// Best effort until we have better mobile support.
|
||||||
|
for (int i = 0; i < stageDefinition.Columns; i++)
|
||||||
|
columns[i].Width *= mobileAdjust;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class OsuRateAdjustedDisplayDifficultyTest
|
||||||
|
{
|
||||||
|
private static IEnumerable<float> difficultyValuesToTest()
|
||||||
|
{
|
||||||
|
for (float i = 0; i <= 10; i += 0.5f)
|
||||||
|
yield return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(difficultyValuesToTest))]
|
||||||
|
public void TestApproachRateIsUnchangedWithRateEqualToOne(float originalApproachRate)
|
||||||
|
{
|
||||||
|
var ruleset = new OsuRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty { ApproachRate = originalApproachRate };
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(originalApproachRate));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(difficultyValuesToTest))]
|
||||||
|
public void TestOverallDifficultyIsUnchangedWithRateEqualToOne(float originalOverallDifficulty)
|
||||||
|
{
|
||||||
|
var ruleset = new OsuRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty { OverallDifficulty = originalOverallDifficulty };
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(originalOverallDifficulty));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRateBelowOne()
|
||||||
|
{
|
||||||
|
var ruleset = new OsuRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty();
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 0.75);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(1.67).Within(0.01));
|
||||||
|
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(2.22).Within(0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRateAboveOne()
|
||||||
|
{
|
||||||
|
var ruleset = new OsuRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty();
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1.5);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(7.67).Within(0.01));
|
||||||
|
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(7.77).Within(0.01));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,16 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const double PREEMPT_MIN = 450;
|
public const double PREEMPT_MIN = 450;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Median preempt time at AR=5.
|
||||||
|
/// </summary>
|
||||||
|
public const double PREEMPT_MID = 1200;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum preempt time at AR=0.
|
||||||
|
/// </summary>
|
||||||
|
public const double PREEMPT_MAX = 1800;
|
||||||
|
|
||||||
public double TimePreempt = 600;
|
public double TimePreempt = 600;
|
||||||
public double TimeFadeIn = 400;
|
public double TimeFadeIn = 400;
|
||||||
|
|
||||||
@ -148,7 +158,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN);
|
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, PREEMPT_MAX, PREEMPT_MID, PREEMPT_MIN);
|
||||||
|
|
||||||
// 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.
|
// 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.
|
// 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.
|
||||||
|
@ -331,5 +331,23 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection();
|
public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection();
|
||||||
|
|
||||||
|
/// <seealso cref="OsuHitObject.ApplyDefaultsToSelf"/>
|
||||||
|
/// <seealso cref="OsuHitWindows"/>
|
||||||
|
public override BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, double rate)
|
||||||
|
{
|
||||||
|
BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty);
|
||||||
|
|
||||||
|
double preempt = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, OsuHitObject.PREEMPT_MAX, OsuHitObject.PREEMPT_MID, OsuHitObject.PREEMPT_MIN);
|
||||||
|
preempt /= rate;
|
||||||
|
adjustedDifficulty.ApproachRate = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, OsuHitObject.PREEMPT_MAX, OsuHitObject.PREEMPT_MID, OsuHitObject.PREEMPT_MIN);
|
||||||
|
|
||||||
|
var greatHitWindowRange = OsuHitWindows.OSU_RANGES.Single(range => range.Result == HitResult.Great);
|
||||||
|
double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max);
|
||||||
|
greatHitWindow /= rate;
|
||||||
|
adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max);
|
||||||
|
|
||||||
|
return adjustedDifficulty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const double MISS_WINDOW = 400;
|
public const double MISS_WINDOW = 400;
|
||||||
|
|
||||||
private static readonly DifficultyRange[] osu_ranges =
|
internal static readonly DifficultyRange[] OSU_RANGES =
|
||||||
{
|
{
|
||||||
new DifficultyRange(HitResult.Great, 80, 50, 20),
|
new DifficultyRange(HitResult.Great, 80, 50, 20),
|
||||||
new DifficultyRange(HitResult.Ok, 140, 100, 60),
|
new DifficultyRange(HitResult.Ok, 140, 100, 60),
|
||||||
@ -34,6 +34,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DifficultyRange[] GetRanges() => osu_ranges;
|
protected override DifficultyRange[] GetRanges() => OSU_RANGES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TaikoRateAdjustedDisplayDifficultyTest
|
||||||
|
{
|
||||||
|
private static IEnumerable<float> difficultyValuesToTest()
|
||||||
|
{
|
||||||
|
for (float i = 0; i <= 10; i += 0.5f)
|
||||||
|
yield return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(difficultyValuesToTest))]
|
||||||
|
public void TestOverallDifficultyIsUnchangedWithRateEqualToOne(float originalOverallDifficulty)
|
||||||
|
{
|
||||||
|
var ruleset = new TaikoRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty { OverallDifficulty = originalOverallDifficulty };
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(originalOverallDifficulty));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRateBelowOne()
|
||||||
|
{
|
||||||
|
var ruleset = new TaikoRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty();
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 0.75);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(1.11).Within(0.01));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRateAboveOne()
|
||||||
|
{
|
||||||
|
var ruleset = new TaikoRuleset();
|
||||||
|
var difficulty = new BeatmapDifficulty();
|
||||||
|
|
||||||
|
var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1.5);
|
||||||
|
|
||||||
|
Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(8.89).Within(0.01));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
|||||||
{
|
{
|
||||||
public class TaikoHitWindows : HitWindows
|
public class TaikoHitWindows : HitWindows
|
||||||
{
|
{
|
||||||
private static readonly DifficultyRange[] taiko_ranges =
|
internal static readonly DifficultyRange[] TAIKO_RANGES =
|
||||||
{
|
{
|
||||||
new DifficultyRange(HitResult.Great, 50, 35, 20),
|
new DifficultyRange(HitResult.Great, 50, 35, 20),
|
||||||
new DifficultyRange(HitResult.Ok, 120, 80, 50),
|
new DifficultyRange(HitResult.Ok, 120, 80, 50),
|
||||||
@ -27,6 +27,6 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DifficultyRange[] GetRanges() => taiko_ranges;
|
protected override DifficultyRange[] GetRanges() => TAIKO_RANGES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,5 +264,18 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
}), true)
|
}), true)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <seealso cref="TaikoHitWindows"/>
|
||||||
|
public override BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, double rate)
|
||||||
|
{
|
||||||
|
BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty);
|
||||||
|
|
||||||
|
var greatHitWindowRange = TaikoHitWindows.TAIKO_RANGES.Single(range => range.Result == HitResult.Great);
|
||||||
|
double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max);
|
||||||
|
greatHitWindow /= rate;
|
||||||
|
adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max);
|
||||||
|
|
||||||
|
return adjustedDifficulty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -283,15 +283,17 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
openSkinEditor();
|
openSkinEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[TestCase(1)]
|
||||||
public void TestOpenSkinEditorGameplaySceneWhenDifferentRulesetActive()
|
[TestCase(2)]
|
||||||
|
[TestCase(3)]
|
||||||
|
public void TestOpenSkinEditorGameplaySceneWhenDifferentRulesetActive(int rulesetId)
|
||||||
{
|
{
|
||||||
BeatmapSetInfo beatmapSet = null!;
|
BeatmapSetInfo beatmapSet = null!;
|
||||||
|
|
||||||
AddStep("import beatmap", () => beatmapSet = BeatmapImportHelper.LoadQuickOszIntoOsu(Game).GetResultSafely());
|
AddStep("import beatmap", () => beatmapSet = BeatmapImportHelper.LoadQuickOszIntoOsu(Game).GetResultSafely());
|
||||||
AddStep("select mania difficulty", () =>
|
AddStep($"select difficulty for ruleset w/ ID {rulesetId}", () =>
|
||||||
{
|
{
|
||||||
var beatmap = beatmapSet.Beatmaps.First(b => b.Ruleset.OnlineID == 3);
|
var beatmap = beatmapSet.Beatmaps.First(b => b.Ruleset.OnlineID == rulesetId);
|
||||||
Game.Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(beatmap);
|
Game.Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(beatmap);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
public void TestSelectedModsDontAffectStatistics()
|
public void TestSelectedModsDontAffectStatistics()
|
||||||
{
|
{
|
||||||
AddStep("show map", () => overlay.ShowBeatmapSet(getBeatmapSet()));
|
AddStep("show map", () => overlay.ShowBeatmapSet(getBeatmapSet()));
|
||||||
AddAssert("AR displayed as 0", () => overlay.ChildrenOfType<AdvancedStats.StatisticRow>().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value == (0, null));
|
AddAssert("AR displayed as 0", () => overlay.ChildrenOfType<AdvancedStats.StatisticRow>().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value, () => Is.EqualTo((0, 0)));
|
||||||
AddStep("set AR10 diff adjust", () => SelectedMods.Value = new[]
|
AddStep("set AR10 diff adjust", () => SelectedMods.Value = new[]
|
||||||
{
|
{
|
||||||
new OsuModDifficultyAdjust
|
new OsuModDifficultyAdjust
|
||||||
@ -228,7 +228,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
ApproachRate = { Value = 10 }
|
ApproachRate = { Value = 10 }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
AddAssert("AR still displayed as 0", () => overlay.ChildrenOfType<AdvancedStats.StatisticRow>().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value == (0, null));
|
AddAssert("AR still displayed as 0", () => overlay.ChildrenOfType<AdvancedStats.StatisticRow>().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value, () => Is.EqualTo((0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -192,7 +192,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
AddStep("select collection", () =>
|
AddStep("select collection", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(getCollectionDropdownItems().ElementAt(1));
|
InputManager.MoveMouseTo(getCollectionDropdownItemAt(1));
|
||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -206,7 +206,8 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
|
|
||||||
AddStep("click manage collections filter", () =>
|
AddStep("click manage collections filter", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(getCollectionDropdownItems().Last());
|
int lastItemIndex = control.ChildrenOfType<CollectionDropdown>().Single().Items.Count() - 1;
|
||||||
|
InputManager.MoveMouseTo(getCollectionDropdownItemAt(lastItemIndex));
|
||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -232,10 +233,10 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
private void assertCollectionDropdownContains(string collectionName, bool shouldContain = true) =>
|
private void assertCollectionDropdownContains(string collectionName, bool shouldContain = true) =>
|
||||||
AddUntilStep($"collection dropdown {(shouldContain ? "contains" : "does not contain")} '{collectionName}'",
|
AddUntilStep($"collection dropdown {(shouldContain ? "contains" : "does not contain")} '{collectionName}'",
|
||||||
// A bit of a roundabout way of going about this, see: https://github.com/ppy/osu-framework/issues/3871 + https://github.com/ppy/osu-framework/issues/3872
|
// A bit of a roundabout way of going about this, see: https://github.com/ppy/osu-framework/issues/3871 + https://github.com/ppy/osu-framework/issues/3872
|
||||||
() => shouldContain == (getCollectionDropdownItems().Any(i => i.ChildrenOfType<CompositeDrawable>().OfType<IHasText>().First().Text == collectionName)));
|
() => shouldContain == control.ChildrenOfType<Menu.DrawableMenuItem>().Any(i => i.ChildrenOfType<CompositeDrawable>().OfType<IHasText>().First().Text == collectionName));
|
||||||
|
|
||||||
private IconButton getAddOrRemoveButton(int index)
|
private IconButton getAddOrRemoveButton(int index)
|
||||||
=> getCollectionDropdownItems().ElementAt(index).ChildrenOfType<IconButton>().Single();
|
=> getCollectionDropdownItemAt(index).ChildrenOfType<IconButton>().Single();
|
||||||
|
|
||||||
private void addExpandHeaderStep() => AddStep("expand header", () =>
|
private void addExpandHeaderStep() => AddStep("expand header", () =>
|
||||||
{
|
{
|
||||||
@ -249,7 +250,11 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
});
|
});
|
||||||
|
|
||||||
private IEnumerable<Dropdown<CollectionFilterMenuItem>.DropdownMenu.DrawableDropdownMenuItem> getCollectionDropdownItems()
|
private Menu.DrawableMenuItem getCollectionDropdownItemAt(int index)
|
||||||
=> control.ChildrenOfType<CollectionDropdown>().Single().ChildrenOfType<Dropdown<CollectionFilterMenuItem>.DropdownMenu.DrawableDropdownMenuItem>();
|
{
|
||||||
|
// todo: we should be able to use Items, but apparently that's not guaranteed to be ordered... see: https://github.com/ppy/osu-framework/pull/6079
|
||||||
|
CollectionFilterMenuItem item = control.ChildrenOfType<CollectionDropdown>().Single().ItemSource.ElementAt(index);
|
||||||
|
return control.ChildrenOfType<Menu.DrawableMenuItem>().Single(i => i.Item.Text.Value == item.CollectionName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
@ -38,6 +39,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
private TestModSelectOverlay modSelectOverlay = null!;
|
private TestModSelectOverlay modSelectOverlay = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuConfigManager configManager { get; set; } = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
@ -566,17 +570,33 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSearchFocusChangeViaKey()
|
public void TestTextSearchActiveByDefault()
|
||||||
{
|
{
|
||||||
|
configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, true);
|
||||||
createScreen();
|
createScreen();
|
||||||
|
|
||||||
const Key focus_switch_key = Key.Tab;
|
AddUntilStep("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus);
|
||||||
|
|
||||||
AddStep("press tab", () => InputManager.Key(focus_switch_key));
|
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||||
AddAssert("focused", () => modSelectOverlay.SearchTextBox.HasFocus);
|
AddAssert("search text box unfocused", () => !modSelectOverlay.SearchTextBox.HasFocus);
|
||||||
|
|
||||||
AddStep("press tab", () => InputManager.Key(focus_switch_key));
|
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||||
AddAssert("lost focus", () => !modSelectOverlay.SearchTextBox.HasFocus);
|
AddAssert("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTextSearchNotActiveByDefault()
|
||||||
|
{
|
||||||
|
configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, false);
|
||||||
|
createScreen();
|
||||||
|
|
||||||
|
AddUntilStep("search text box not focused", () => !modSelectOverlay.SearchTextBox.HasFocus);
|
||||||
|
|
||||||
|
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||||
|
AddAssert("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus);
|
||||||
|
|
||||||
|
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||||
|
AddAssert("search text box unfocused", () => !modSelectOverlay.SearchTextBox.HasFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -787,7 +807,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
InputManager.MoveMouseTo(this.ChildrenOfType<ModPresetPanel>().Single(preset => preset.Preset.Value.Name == "Half Time 0.5x"));
|
InputManager.MoveMouseTo(this.ChildrenOfType<ModPresetPanel>().Single(preset => preset.Preset.Value.Name == "Half Time 0.5x"));
|
||||||
InputManager.Click(MouseButton.Left);
|
InputManager.Click(MouseButton.Left);
|
||||||
});
|
});
|
||||||
AddAssert("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType<ScoreMultiplierDisplay>().Single().Current.Value, () => Is.EqualTo(0.5));
|
AddAssert("difficulty multiplier display shows correct value",
|
||||||
|
() => modSelectOverlay.ChildrenOfType<ScoreMultiplierDisplay>().Single().Current.Value, () => Is.EqualTo(0.1).Within(Precision.DOUBLE_EPSILON));
|
||||||
|
|
||||||
// this is highly unorthodox in a test, but because the `ModSettingChangeTracker` machinery heavily leans on events and object disposal and re-creation,
|
// this is highly unorthodox in a test, but because the `ModSettingChangeTracker` machinery heavily leans on events and object disposal and re-creation,
|
||||||
// it is instrumental in the reproduction of the failure scenario that this test is supposed to cover.
|
// it is instrumental in the reproduction of the failure scenario that this test is supposed to cover.
|
||||||
@ -796,7 +817,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddStep("open customisation area", () => modSelectOverlay.CustomisationButton!.TriggerClick());
|
AddStep("open customisation area", () => modSelectOverlay.CustomisationButton!.TriggerClick());
|
||||||
AddStep("reset half time speed to default", () => modSelectOverlay.ChildrenOfType<ModSettingsArea>().Single()
|
AddStep("reset half time speed to default", () => modSelectOverlay.ChildrenOfType<ModSettingsArea>().Single()
|
||||||
.ChildrenOfType<RevertToDefaultButton<double>>().Single().TriggerClick());
|
.ChildrenOfType<RevertToDefaultButton<double>>().Single().TriggerClick());
|
||||||
AddUntilStep("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType<ScoreMultiplierDisplay>().Single().Current.Value, () => Is.EqualTo(0.7));
|
AddUntilStep("difficulty multiplier display shows correct value",
|
||||||
|
() => modSelectOverlay.ChildrenOfType<ScoreMultiplierDisplay>().Single().Current.Value, () => Is.EqualTo(0.3).Within(Precision.DOUBLE_EPSILON));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitForColumnLoad() => AddUntilStep("all column content loaded", () =>
|
private void waitForColumnLoad() => AddUntilStep("all column content loaded", () =>
|
||||||
|
@ -21,6 +21,10 @@ namespace osu.Game.Tournament.Models
|
|||||||
|
|
||||||
public double StarRating { get; set; }
|
public double StarRating { get; set; }
|
||||||
|
|
||||||
|
public int EndTimeObjectCount { get; set; }
|
||||||
|
|
||||||
|
public int TotalObjectCount { get; set; }
|
||||||
|
|
||||||
public IBeatmapMetadataInfo Metadata { get; set; } = new BeatmapMetadata();
|
public IBeatmapMetadataInfo Metadata { get; set; } = new BeatmapMetadata();
|
||||||
|
|
||||||
public IBeatmapDifficultyInfo Difficulty { get; set; } = new BeatmapDifficulty();
|
public IBeatmapDifficultyInfo Difficulty { get; set; } = new BeatmapDifficulty();
|
||||||
@ -41,6 +45,8 @@ namespace osu.Game.Tournament.Models
|
|||||||
Metadata = beatmap.Metadata;
|
Metadata = beatmap.Metadata;
|
||||||
Difficulty = beatmap.Difficulty;
|
Difficulty = beatmap.Difficulty;
|
||||||
Covers = beatmap.BeatmapSet?.Covers ?? new BeatmapSetOnlineCovers();
|
Covers = beatmap.BeatmapSet?.Covers ?? new BeatmapSetOnlineCovers();
|
||||||
|
EndTimeObjectCount = beatmap.EndTimeObjectCount;
|
||||||
|
TotalObjectCount = beatmap.TotalObjectCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(IBeatmapInfo? other) => other is TournamentBeatmap b && this.MatchesOnlineID(b);
|
public bool Equals(IBeatmapInfo? other) => other is TournamentBeatmap b && this.MatchesOnlineID(b);
|
||||||
|
@ -20,7 +20,6 @@ using osu.Game.Rulesets;
|
|||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Scoring.Legacy;
|
using osu.Game.Scoring.Legacy;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using Realms;
|
|
||||||
|
|
||||||
namespace osu.Game
|
namespace osu.Game
|
||||||
{
|
{
|
||||||
@ -177,9 +176,13 @@ namespace osu.Game
|
|||||||
{
|
{
|
||||||
Logger.Log("Querying for beatmaps with missing hitobject counts to reprocess...");
|
Logger.Log("Querying for beatmaps with missing hitobject counts to reprocess...");
|
||||||
|
|
||||||
HashSet<Guid> beatmapIds = realmAccess.Run(r => new HashSet<Guid>(r.All<BeatmapInfo>()
|
HashSet<Guid> beatmapIds = new HashSet<Guid>();
|
||||||
.Filter($"{nameof(BeatmapInfo.Difficulty)}.{nameof(BeatmapDifficulty.TotalObjectCount)} == 0")
|
|
||||||
.AsEnumerable().Select(b => b.ID)));
|
realmAccess.Run(r =>
|
||||||
|
{
|
||||||
|
foreach (var b in r.All<BeatmapInfo>().Where(b => b.TotalObjectCount == 0))
|
||||||
|
beatmapIds.Add(b.ID);
|
||||||
|
});
|
||||||
|
|
||||||
Logger.Log($"Found {beatmapIds.Count} beatmaps which require reprocessing.");
|
Logger.Log($"Found {beatmapIds.Count} beatmaps which require reprocessing.");
|
||||||
|
|
||||||
|
@ -21,9 +21,6 @@ namespace osu.Game.Beatmaps
|
|||||||
public double SliderMultiplier { get; set; } = 1.4;
|
public double SliderMultiplier { get; set; } = 1.4;
|
||||||
public double SliderTickRate { get; set; } = 1;
|
public double SliderTickRate { get; set; } = 1;
|
||||||
|
|
||||||
public int EndTimeObjectCount { get; set; }
|
|
||||||
public int TotalObjectCount { get; set; }
|
|
||||||
|
|
||||||
public BeatmapDifficulty()
|
public BeatmapDifficulty()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -47,9 +44,6 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
difficulty.SliderMultiplier = SliderMultiplier;
|
difficulty.SliderMultiplier = SliderMultiplier;
|
||||||
difficulty.SliderTickRate = SliderTickRate;
|
difficulty.SliderTickRate = SliderTickRate;
|
||||||
|
|
||||||
difficulty.EndTimeObjectCount = EndTimeObjectCount;
|
|
||||||
difficulty.TotalObjectCount = TotalObjectCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void CopyFrom(IBeatmapDifficultyInfo other)
|
public virtual void CopyFrom(IBeatmapDifficultyInfo other)
|
||||||
@ -61,9 +55,6 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
SliderMultiplier = other.SliderMultiplier;
|
SliderMultiplier = other.SliderMultiplier;
|
||||||
SliderTickRate = other.SliderTickRate;
|
SliderTickRate = other.SliderTickRate;
|
||||||
|
|
||||||
EndTimeObjectCount = other.EndTimeObjectCount;
|
|
||||||
TotalObjectCount = other.TotalObjectCount;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -388,9 +388,7 @@ namespace osu.Game.Beatmaps
|
|||||||
OverallDifficulty = decodedDifficulty.OverallDifficulty,
|
OverallDifficulty = decodedDifficulty.OverallDifficulty,
|
||||||
ApproachRate = decodedDifficulty.ApproachRate,
|
ApproachRate = decodedDifficulty.ApproachRate,
|
||||||
SliderMultiplier = decodedDifficulty.SliderMultiplier,
|
SliderMultiplier = decodedDifficulty.SliderMultiplier,
|
||||||
SliderTickRate = decodedDifficulty.SliderTickRate,
|
SliderTickRate = decodedDifficulty.SliderTickRate
|
||||||
EndTimeObjectCount = decoded.HitObjects.Count(h => h is IHasDuration),
|
|
||||||
TotalObjectCount = decoded.HitObjects.Count
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var metadata = new BeatmapMetadata
|
var metadata = new BeatmapMetadata
|
||||||
@ -428,6 +426,8 @@ namespace osu.Game.Beatmaps
|
|||||||
GridSize = decodedInfo.GridSize,
|
GridSize = decodedInfo.GridSize,
|
||||||
TimelineZoom = decodedInfo.TimelineZoom,
|
TimelineZoom = decodedInfo.TimelineZoom,
|
||||||
MD5Hash = memoryStream.ComputeMD5Hash(),
|
MD5Hash = memoryStream.ComputeMD5Hash(),
|
||||||
|
EndTimeObjectCount = decoded.HitObjects.Count(h => h is IHasDuration),
|
||||||
|
TotalObjectCount = decoded.HitObjects.Count
|
||||||
};
|
};
|
||||||
|
|
||||||
beatmaps.Add(beatmap);
|
beatmaps.Add(beatmap);
|
||||||
|
@ -120,6 +120,10 @@ namespace osu.Game.Beatmaps
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool Hidden { get; set; }
|
public bool Hidden { get; set; }
|
||||||
|
|
||||||
|
public int EndTimeObjectCount { get; set; }
|
||||||
|
|
||||||
|
public int TotalObjectCount { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reset any fetched online linking information (and history).
|
/// Reset any fetched online linking information (and history).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -91,8 +91,8 @@ namespace osu.Game.Beatmaps
|
|||||||
var working = workingBeatmapCache.GetWorkingBeatmap(beatmapInfo);
|
var working = workingBeatmapCache.GetWorkingBeatmap(beatmapInfo);
|
||||||
var beatmap = working.Beatmap;
|
var beatmap = working.Beatmap;
|
||||||
|
|
||||||
beatmapInfo.Difficulty.EndTimeObjectCount = beatmap.HitObjects.Count(h => h is IHasDuration);
|
beatmapInfo.EndTimeObjectCount = beatmap.HitObjects.Count(h => h is IHasDuration);
|
||||||
beatmapInfo.Difficulty.TotalObjectCount = beatmap.HitObjects.Count;
|
beatmapInfo.TotalObjectCount = beatmap.HitObjects.Count;
|
||||||
|
|
||||||
// And invalidate again afterwards as re-fetching the most up-to-date database metadata will be required.
|
// And invalidate again afterwards as re-fetching the most up-to-date database metadata will be required.
|
||||||
workingBeatmapCache.Invalidate(beatmapInfo);
|
workingBeatmapCache.Invalidate(beatmapInfo);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
@ -38,6 +39,8 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
|
|
||||||
private readonly Bindable<double> displayedStars = new BindableDouble();
|
private readonly Bindable<double> displayedStars = new BindableDouble();
|
||||||
|
|
||||||
|
private readonly Container textContainer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently displayed stars of this display wrapped in a bindable.
|
/// The currently displayed stars of this display wrapped in a bindable.
|
||||||
/// This bindable gets transformed on change rather than instantaneous, if animation is enabled.
|
/// This bindable gets transformed on change rather than instantaneous, if animation is enabled.
|
||||||
@ -116,15 +119,19 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
Size = new Vector2(8f),
|
Size = new Vector2(8f),
|
||||||
},
|
},
|
||||||
Empty(),
|
Empty(),
|
||||||
starsText = new OsuSpriteText
|
textContainer = new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
AutoSizeAxes = Axes.Y,
|
||||||
Origin = Anchor.Centre,
|
Child = starsText = new OsuSpriteText
|
||||||
Margin = new MarginPadding { Bottom = 1.5f },
|
{
|
||||||
// todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f
|
Anchor = Anchor.Centre,
|
||||||
// see https://github.com/ppy/osu-framework/issues/3271.
|
Origin = Anchor.Centre,
|
||||||
Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold),
|
Margin = new MarginPadding { Bottom = 1.5f },
|
||||||
Shadow = false,
|
// todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f
|
||||||
|
// see https://github.com/ppy/osu-framework/issues/3271.
|
||||||
|
Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold),
|
||||||
|
Shadow = false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,6 +162,11 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
|
|
||||||
starIcon.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47");
|
starIcon.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47");
|
||||||
starsText.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f);
|
starsText.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f);
|
||||||
|
|
||||||
|
// In order to avoid autosize throwing the width of these displays all over the place,
|
||||||
|
// let's lock in some sane defaults for the text width based on how many digits we're
|
||||||
|
// displaying.
|
||||||
|
textContainer.Width = 24 + Math.Max(starsText.Text.ToString().Length - 4, 0) * 6;
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -44,19 +46,6 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
double SliderTickRate { get; }
|
double SliderTickRate { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of hitobjects in the beatmap with a distinct end time.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Canonically, these are hitobjects are either sliders or spinners.
|
|
||||||
/// </remarks>
|
|
||||||
int EndTimeObjectCount { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The total number of hitobjects in the beatmap.
|
|
||||||
/// </summary>
|
|
||||||
int TotalObjectCount { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.
|
/// Maps a difficulty value [0, 10] to a two-piece linear range of values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -105,5 +94,21 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <returns>Value to which the difficulty value maps in the specified range.</returns>
|
/// <returns>Value to which the difficulty value maps in the specified range.</returns>
|
||||||
static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range)
|
static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range)
|
||||||
=> DifficultyRange(difficulty, range.od0, range.od5, range.od10);
|
=> DifficultyRange(difficulty, range.od0, range.od5, range.od10);
|
||||||
|
|
||||||
|
/// <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.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="difficultyValue">The difficulty-dependent value to be unmapped.</param>
|
||||||
|
/// <param name="diff0">Minimum of the resulting range which will be achieved by a difficulty value of 0.</param>
|
||||||
|
/// <param name="diff5">Midpoint of the resulting range which will be achieved by a difficulty value of 5.</param>
|
||||||
|
/// <param name="diff10">Maximum of the resulting range which will be achieved by a difficulty value of 10.</param>
|
||||||
|
/// <returns>Value to which the difficulty value maps in the specified range.</returns>
|
||||||
|
static double InverseDifficultyRange(double difficultyValue, double diff0, double diff5, double diff10)
|
||||||
|
{
|
||||||
|
return Math.Sign(difficultyValue - diff5) == Math.Sign(diff10 - diff5)
|
||||||
|
? (difficultyValue - diff5) / (diff10 - diff5) * 5 + 5
|
||||||
|
: (difficultyValue - diff5) / (diff5 - diff0) * 5 + 5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,5 +61,18 @@ namespace osu.Game.Beatmaps
|
|||||||
/// The basic star rating for this beatmap (with no mods applied).
|
/// The basic star rating for this beatmap (with no mods applied).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
double StarRating { get; }
|
double StarRating { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of hitobjects in the beatmap with a distinct end time.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Canonically, these are hitobjects are either sliders or spinners.
|
||||||
|
/// </remarks>
|
||||||
|
int EndTimeObjectCount { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total number of hitobjects in the beatmap.
|
||||||
|
/// </summary>
|
||||||
|
int TotalObjectCount { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,11 +43,14 @@ namespace osu.Game.Collections
|
|||||||
|
|
||||||
private IDisposable? realmSubscription;
|
private IDisposable? realmSubscription;
|
||||||
|
|
||||||
|
private readonly CollectionFilterMenuItem allBeatmapsItem = new AllBeatmapsCollectionFilterMenuItem();
|
||||||
|
|
||||||
public CollectionDropdown()
|
public CollectionDropdown()
|
||||||
{
|
{
|
||||||
ItemSource = filters;
|
ItemSource = filters;
|
||||||
|
|
||||||
Current.Value = new AllBeatmapsCollectionFilterMenuItem();
|
Current.Value = allBeatmapsItem;
|
||||||
|
AlwaysShowSearchBar = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -61,37 +64,51 @@ namespace osu.Game.Collections
|
|||||||
|
|
||||||
private void collectionsChanged(IRealmCollection<BeatmapCollection> collections, ChangeSet? changes)
|
private void collectionsChanged(IRealmCollection<BeatmapCollection> collections, ChangeSet? changes)
|
||||||
{
|
{
|
||||||
var selectedItem = SelectedItem?.Value?.Collection;
|
if (changes == null)
|
||||||
|
|
||||||
var allBeatmaps = new AllBeatmapsCollectionFilterMenuItem();
|
|
||||||
|
|
||||||
filters.Clear();
|
|
||||||
filters.Add(allBeatmaps);
|
|
||||||
filters.AddRange(collections.Select(c => new CollectionFilterMenuItem(c.ToLive(realm))));
|
|
||||||
|
|
||||||
if (ShowManageCollectionsItem)
|
|
||||||
filters.Add(new ManageCollectionsFilterMenuItem());
|
|
||||||
|
|
||||||
// This current update and schedule is required to work around dropdown headers not updating text even when the selected item
|
|
||||||
// changes. It's not great but honestly the whole dropdown menu structure isn't great. This needs to be fixed, but I'll issue
|
|
||||||
// a warning that it's going to be a frustrating journey.
|
|
||||||
Current.Value = allBeatmaps;
|
|
||||||
Schedule(() =>
|
|
||||||
{
|
{
|
||||||
// current may have changed before the scheduled call is run.
|
filters.Add(allBeatmapsItem);
|
||||||
if (Current.Value != allBeatmaps)
|
filters.AddRange(collections.Select(c => new CollectionFilterMenuItem(c.ToLive(realm))));
|
||||||
return;
|
if (ShowManageCollectionsItem)
|
||||||
|
filters.Add(new ManageCollectionsFilterMenuItem());
|
||||||
Current.Value = filters.SingleOrDefault(f => f.Collection != null && f.Collection.ID == selectedItem?.ID) ?? filters[0];
|
}
|
||||||
});
|
else
|
||||||
|
|
||||||
// Trigger a re-filter if the current item was in the change set.
|
|
||||||
if (selectedItem != null && changes != null)
|
|
||||||
{
|
{
|
||||||
foreach (int index in changes.ModifiedIndices)
|
foreach (int i in changes.DeletedIndices)
|
||||||
|
filters.RemoveAt(i + 1);
|
||||||
|
|
||||||
|
foreach (int i in changes.InsertedIndices)
|
||||||
|
filters.Insert(i + 1, new CollectionFilterMenuItem(collections[i].ToLive(realm)));
|
||||||
|
|
||||||
|
var selectedItem = SelectedItem?.Value;
|
||||||
|
|
||||||
|
foreach (int i in changes.NewModifiedIndices)
|
||||||
{
|
{
|
||||||
if (collections[index].ID == selectedItem.ID)
|
var updatedItem = collections[i];
|
||||||
|
|
||||||
|
// This is responsible for updating the state of the +/- button and the collection's name.
|
||||||
|
// TODO: we can probably make the menu items update with changes to avoid this.
|
||||||
|
filters.RemoveAt(i + 1);
|
||||||
|
filters.Insert(i + 1, new CollectionFilterMenuItem(updatedItem.ToLive(realm)));
|
||||||
|
|
||||||
|
if (updatedItem.ID == selectedItem?.Collection?.ID)
|
||||||
|
{
|
||||||
|
// This current update and schedule is required to work around dropdown headers not updating text even when the selected item
|
||||||
|
// changes. It's not great but honestly the whole dropdown menu structure isn't great. This needs to be fixed, but I'll issue
|
||||||
|
// a warning that it's going to be a frustrating journey.
|
||||||
|
Current.Value = allBeatmapsItem;
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
// current may have changed before the scheduled call is run.
|
||||||
|
if (Current.Value != allBeatmapsItem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Current.Value = filters.SingleOrDefault(f => f.Collection?.ID == selectedItem.Collection?.ID) ?? filters[0];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger an external re-filter if the current item was in the change set.
|
||||||
RequestFilter?.Invoke();
|
RequestFilter?.Invoke();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
SetDefault(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation);
|
SetDefault(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation);
|
||||||
SetDefault(OsuSetting.ModSelectHotkeyStyle, ModSelectHotkeyStyle.Sequential);
|
SetDefault(OsuSetting.ModSelectHotkeyStyle, ModSelectHotkeyStyle.Sequential);
|
||||||
|
SetDefault(OsuSetting.ModSelectTextSearchStartsActive, true);
|
||||||
|
|
||||||
SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f);
|
SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f);
|
||||||
|
|
||||||
@ -416,5 +417,6 @@ namespace osu.Game.Configuration
|
|||||||
AutomaticallyDownloadMissingBeatmaps,
|
AutomaticallyDownloadMissingBeatmaps,
|
||||||
EditorShowSpeedChanges,
|
EditorShowSpeedChanges,
|
||||||
TouchDisableGameplayTaps,
|
TouchDisableGameplayTaps,
|
||||||
|
ModSelectTextSearchStartsActive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
@ -54,6 +57,49 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
public void UpdateStorage(string stablePath) => cachedStorage = new StableStorage(stablePath, gameHost as DesktopGameHost);
|
public void UpdateStorage(string stablePath) => cachedStorage = new StableStorage(stablePath, gameHost as DesktopGameHost);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether a valid location to run a stable import from can be determined starting from the supplied <paramref name="directory"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="directory">The directory to check for stable import eligibility.</param>
|
||||||
|
/// <param name="stableRoot">
|
||||||
|
/// If the return value is <see langword="true"/>,
|
||||||
|
/// this parameter will contain the <see cref="DirectoryInfo"/> to use as the root directory for importing.
|
||||||
|
/// </param>
|
||||||
|
public bool IsUsableForStableImport(DirectoryInfo? directory, [NotNullWhen(true)] out DirectoryInfo? stableRoot)
|
||||||
|
{
|
||||||
|
if (directory == null)
|
||||||
|
{
|
||||||
|
stableRoot = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A full stable installation will have a configuration file present.
|
||||||
|
// This is the best case scenario, as it may contain a custom beatmap directory we need to traverse to.
|
||||||
|
if (directory.GetFiles(@"osu!.*.cfg").Any())
|
||||||
|
{
|
||||||
|
stableRoot = directory;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The user may only have their songs or skins folders left.
|
||||||
|
// We still want to allow them to import based on this.
|
||||||
|
if (directory.GetDirectories(@"Songs").Any() || directory.GetDirectories(@"Skins").Any())
|
||||||
|
{
|
||||||
|
stableRoot = directory;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The user may have traversed *inside* their songs or skins folders.
|
||||||
|
if (directory.Parent != null && (directory.Name == @"Songs" || directory.Name == @"Skins"))
|
||||||
|
{
|
||||||
|
stableRoot = directory.Parent;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stableRoot = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public bool CheckSongsFolderHardLinkAvailability()
|
public bool CheckSongsFolderHardLinkAvailability()
|
||||||
{
|
{
|
||||||
var stableStorage = GetCurrentStableStorage();
|
var stableStorage = GetCurrentStableStorage();
|
||||||
|
@ -88,9 +88,9 @@ namespace osu.Game.Database
|
|||||||
/// 34 2023-08-21 Add BackgroundReprocessingFailed flag to ScoreInfo to track upgrade failures.
|
/// 34 2023-08-21 Add BackgroundReprocessingFailed flag to ScoreInfo to track upgrade failures.
|
||||||
/// 35 2023-10-16 Clear key combinations of keybindings that are assigned to more than one action in a given settings section.
|
/// 35 2023-10-16 Clear key combinations of keybindings that are assigned to more than one action in a given settings section.
|
||||||
/// 36 2023-10-26 Add LegacyOnlineID to ScoreInfo. Move osu_scores_*_high IDs stored in OnlineID to LegacyOnlineID. Reset anomalous OnlineIDs.
|
/// 36 2023-10-26 Add LegacyOnlineID to ScoreInfo. Move osu_scores_*_high IDs stored in OnlineID to LegacyOnlineID. Reset anomalous OnlineIDs.
|
||||||
/// 37 2023-12-10 Add EndTimeObjectCount and TotalObjectCount to BeatmapDifficulty.
|
/// 38 2023-12-10 Add EndTimeObjectCount and TotalObjectCount to BeatmapInfo.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int schema_version = 37;
|
private const int schema_version = 38;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
|
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
|
||||||
|
@ -49,6 +49,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString OpenOsuFolder => new TranslatableString(getKey(@"open_osu_folder"), @"Open osu! folder");
|
public static LocalisableString OpenOsuFolder => new TranslatableString(getKey(@"open_osu_folder"), @"Open osu! folder");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Export logs"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ExportLogs => new TranslatableString(getKey(@"export_logs"), @"Export logs");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Change folder location..."
|
/// "Change folder location..."
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -104,6 +104,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString ModSelectHotkeyStyle => new TranslatableString(getKey(@"mod_select_hotkey_style"), @"Mod select hotkey style");
|
public static LocalisableString ModSelectHotkeyStyle => new TranslatableString(getKey(@"mod_select_hotkey_style"), @"Mod select hotkey style");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Automatically focus search text box in mod select"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ModSelectTextSearchStartsActive => new TranslatableString(getKey(@"mod_select_text_search_starts_active"), @"Automatically focus search text box in mod select");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "no limit"
|
/// "no limit"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -41,6 +41,10 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
[JsonProperty(@"difficulty_rating")]
|
[JsonProperty(@"difficulty_rating")]
|
||||||
public double StarRating { get; set; }
|
public double StarRating { get; set; }
|
||||||
|
|
||||||
|
public int EndTimeObjectCount => SliderCount + SpinnerCount;
|
||||||
|
|
||||||
|
public int TotalObjectCount => CircleCount + SliderCount + SpinnerCount;
|
||||||
|
|
||||||
[JsonProperty(@"drain")]
|
[JsonProperty(@"drain")]
|
||||||
public float DrainRate { get; set; }
|
public float DrainRate { get; set; }
|
||||||
|
|
||||||
@ -108,9 +112,7 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
DrainRate = DrainRate,
|
DrainRate = DrainRate,
|
||||||
CircleSize = CircleSize,
|
CircleSize = CircleSize,
|
||||||
ApproachRate = ApproachRate,
|
ApproachRate = ApproachRate,
|
||||||
OverallDifficulty = OverallDifficulty,
|
OverallDifficulty = OverallDifficulty
|
||||||
EndTimeObjectCount = SliderCount + SpinnerCount,
|
|
||||||
TotalObjectCount = CircleCount + SliderCount + SpinnerCount
|
|
||||||
};
|
};
|
||||||
|
|
||||||
IBeatmapSetInfo? IBeatmapInfo.BeatmapSet => BeatmapSet;
|
IBeatmapSetInfo? IBeatmapInfo.BeatmapSet => BeatmapSet;
|
||||||
|
@ -244,6 +244,8 @@ namespace osu.Game.Overlays.FirstRunSetup
|
|||||||
[Resolved(canBeNull: true)] // Can't really be null but required to handle potential of disposal before DI completes.
|
[Resolved(canBeNull: true)] // Can't really be null but required to handle potential of disposal before DI completes.
|
||||||
private OsuGameBase? game { get; set; }
|
private OsuGameBase? game { get; set; }
|
||||||
|
|
||||||
|
private bool changingDirectory;
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
@ -259,24 +261,37 @@ namespace osu.Game.Overlays.FirstRunSetup
|
|||||||
|
|
||||||
private void onDirectorySelected(ValueChangedEvent<DirectoryInfo?> directory)
|
private void onDirectorySelected(ValueChangedEvent<DirectoryInfo?> directory)
|
||||||
{
|
{
|
||||||
if (directory.NewValue == null)
|
if (changingDirectory)
|
||||||
{
|
|
||||||
Current.Value = string.Empty;
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
changingDirectory = true;
|
||||||
|
|
||||||
|
if (directory.NewValue == null)
|
||||||
|
{
|
||||||
|
Current.Value = string.Empty;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DirectorySelectors can trigger a noop value changed, but `DirectoryInfo` equality doesn't catch this.
|
||||||
|
if (directory.OldValue?.FullName == directory.NewValue.FullName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (legacyImportManager.IsUsableForStableImport(directory.NewValue, out var stableRoot))
|
||||||
|
{
|
||||||
|
this.HidePopover();
|
||||||
|
|
||||||
|
string path = stableRoot.FullName;
|
||||||
|
|
||||||
|
legacyImportManager.UpdateStorage(path);
|
||||||
|
Current.Value = path;
|
||||||
|
currentDirectory.Value = stableRoot;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
// DirectorySelectors can trigger a noop value changed, but `DirectoryInfo` equality doesn't catch this.
|
|
||||||
if (directory.OldValue?.FullName == directory.NewValue.FullName)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (directory.NewValue?.GetFiles(@"osu!.*.cfg").Any() ?? false)
|
|
||||||
{
|
{
|
||||||
this.HidePopover();
|
changingDirectory = false;
|
||||||
|
|
||||||
string path = directory.NewValue.FullName;
|
|
||||||
|
|
||||||
legacyImportManager.UpdateStorage(path);
|
|
||||||
Current.Value = path;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
142
osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs
Normal file
142
osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Mods
|
||||||
|
{
|
||||||
|
public partial class AdjustedAttributesTooltip : VisibilityContainer, ITooltip<AdjustedAttributesTooltip.Data?>
|
||||||
|
{
|
||||||
|
private FillFlowContainer attributesFillFlow = null!;
|
||||||
|
|
||||||
|
private Container content = null!;
|
||||||
|
|
||||||
|
private Data? data;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; } = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 5;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
content = new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colours.Gray3,
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Vertical = 10, Horizontal = 15 },
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = "One or more values are being adjusted by mods that change speed.",
|
||||||
|
},
|
||||||
|
attributesFillFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
AutoSizeAxes = Axes.Both
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
updateDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDisplay()
|
||||||
|
{
|
||||||
|
attributesFillFlow.Clear();
|
||||||
|
|
||||||
|
if (data != null)
|
||||||
|
{
|
||||||
|
attemptAdd("CS", bd => bd.CircleSize);
|
||||||
|
attemptAdd("HP", bd => bd.DrainRate);
|
||||||
|
attemptAdd("OD", bd => bd.OverallDifficulty);
|
||||||
|
attemptAdd("AR", bd => bd.ApproachRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributesFillFlow.Any())
|
||||||
|
content.Show();
|
||||||
|
else
|
||||||
|
content.Hide();
|
||||||
|
|
||||||
|
void attemptAdd(string name, Func<BeatmapDifficulty, double> lookup)
|
||||||
|
{
|
||||||
|
double originalValue = lookup(data.OriginalDifficulty);
|
||||||
|
double adjustedValue = lookup(data.AdjustedDifficulty);
|
||||||
|
|
||||||
|
if (!Precision.AlmostEquals(originalValue, adjustedValue))
|
||||||
|
attributesFillFlow.Add(new AttributeDisplay(name, originalValue, adjustedValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetContent(Data? data)
|
||||||
|
{
|
||||||
|
if (this.data == data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
updateDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn() => this.FadeIn(200, Easing.OutQuint);
|
||||||
|
protected override void PopOut() => this.FadeOut(200, Easing.OutQuint);
|
||||||
|
|
||||||
|
public void Move(Vector2 pos) => Position = pos;
|
||||||
|
|
||||||
|
public class Data
|
||||||
|
{
|
||||||
|
public BeatmapDifficulty OriginalDifficulty { get; }
|
||||||
|
public BeatmapDifficulty AdjustedDifficulty { get; }
|
||||||
|
|
||||||
|
public Data(BeatmapDifficulty originalDifficulty, BeatmapDifficulty adjustedDifficulty)
|
||||||
|
{
|
||||||
|
OriginalDifficulty = originalDifficulty;
|
||||||
|
AdjustedDifficulty = adjustedDifficulty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class AttributeDisplay : CompositeDrawable
|
||||||
|
{
|
||||||
|
public AttributeDisplay(string name, double original, double adjusted)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.Default.With(weight: FontWeight.Bold),
|
||||||
|
Text = $"{name}: {original:0.0#} → {adjusted:0.0#}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,21 +3,23 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.LocalisationExtensions;
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Drawables;
|
using osu.Game.Beatmaps.Drawables;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using System.Threading;
|
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Game.Configuration;
|
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Mods
|
namespace osu.Game.Overlays.Mods
|
||||||
{
|
{
|
||||||
@ -25,7 +27,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
/// On the mod select overlay, this provides a local updating view of BPM, star rating and other
|
/// On the mod select overlay, this provides a local updating view of BPM, star rating and other
|
||||||
/// difficulty attributes so the user can have a better insight into what mods are changing.
|
/// difficulty attributes so the user can have a better insight into what mods are changing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class BeatmapAttributesDisplay : ModFooterInformationDisplay
|
public partial class BeatmapAttributesDisplay : ModFooterInformationDisplay, IHasCustomTooltip<AdjustedAttributesTooltip.Data?>
|
||||||
{
|
{
|
||||||
private StarRatingDisplay starRatingDisplay = null!;
|
private StarRatingDisplay starRatingDisplay = null!;
|
||||||
private BPMDisplay bpmDisplay = null!;
|
private BPMDisplay bpmDisplay = null!;
|
||||||
@ -47,9 +49,18 @@ namespace osu.Game.Overlays.Mods
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private BeatmapDifficultyCache difficultyCache { get; set; } = null!;
|
private BeatmapDifficultyCache difficultyCache { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuGameBase game { get; set; } = null!;
|
||||||
|
|
||||||
|
private IBindable<RulesetInfo> gameRuleset = null!;
|
||||||
|
|
||||||
private CancellationTokenSource? cancellationSource;
|
private CancellationTokenSource? cancellationSource;
|
||||||
private IBindable<StarDifficulty?> starDifficulty = null!;
|
private IBindable<StarDifficulty?> starDifficulty = null!;
|
||||||
|
|
||||||
|
public ITooltip<AdjustedAttributesTooltip.Data?> GetCustomTooltip() => new AdjustedAttributesTooltip();
|
||||||
|
|
||||||
|
public AdjustedAttributesTooltip.Data? TooltipContent { get; private set; }
|
||||||
|
|
||||||
private const float transition_duration = 250;
|
private const float transition_duration = 250;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -80,8 +91,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
circleSizeDisplay = new VerticalAttributeDisplay("CS") { Shear = new Vector2(-shear, 0), },
|
circleSizeDisplay = new VerticalAttributeDisplay("CS") { Shear = new Vector2(-shear, 0), },
|
||||||
drainRateDisplay = new VerticalAttributeDisplay("HP") { Shear = new Vector2(-shear, 0), },
|
drainRateDisplay = new VerticalAttributeDisplay("HP") { Shear = new Vector2(-shear, 0), },
|
||||||
approachRateDisplay = new VerticalAttributeDisplay("AR") { Shear = new Vector2(-shear, 0), },
|
|
||||||
overallDifficultyDisplay = new VerticalAttributeDisplay("OD") { Shear = new Vector2(-shear, 0), },
|
overallDifficultyDisplay = new VerticalAttributeDisplay("OD") { Shear = new Vector2(-shear, 0), },
|
||||||
|
approachRateDisplay = new VerticalAttributeDisplay("AR") { Shear = new Vector2(-shear, 0), },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +103,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
mods.BindValueChanged(_ =>
|
mods.BindValueChanged(_ =>
|
||||||
{
|
{
|
||||||
modSettingChangeTracker?.Dispose();
|
modSettingChangeTracker?.Dispose();
|
||||||
|
|
||||||
modSettingChangeTracker = new ModSettingChangeTracker(mods.Value);
|
modSettingChangeTracker = new ModSettingChangeTracker(mods.Value);
|
||||||
modSettingChangeTracker.SettingChanged += _ => updateValues();
|
modSettingChangeTracker.SettingChanged += _ => updateValues();
|
||||||
updateValues();
|
updateValues();
|
||||||
@ -107,6 +117,11 @@ namespace osu.Game.Overlays.Mods
|
|||||||
updateCollapsedState();
|
updateCollapsedState();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gameRuleset = game.Ruleset.GetBoundCopy();
|
||||||
|
gameRuleset.BindValueChanged(_ => updateValues());
|
||||||
|
|
||||||
|
BeatmapInfo.BindValueChanged(_ => updateValues(), true);
|
||||||
|
|
||||||
updateCollapsedState();
|
updateCollapsedState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,13 +144,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private void startAnimating()
|
private void startAnimating()
|
||||||
{
|
{
|
||||||
Content.AutoSizeEasing = Easing.OutQuint;
|
LeftContent.AutoSizeEasing = Content.AutoSizeEasing = Easing.OutQuint;
|
||||||
Content.AutoSizeDuration = transition_duration;
|
LeftContent.AutoSizeDuration = Content.AutoSizeDuration = transition_duration;
|
||||||
}
|
|
||||||
|
|
||||||
private void updateCollapsedState()
|
|
||||||
{
|
|
||||||
RightContent.FadeTo(Collapsed.Value && !IsHovered ? 0 : 1, transition_duration, Easing.OutQuint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateValues() => Scheduler.AddOnce(() =>
|
private void updateValues() => Scheduler.AddOnce(() =>
|
||||||
@ -160,9 +170,18 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
bpmDisplay.Current.Value = BeatmapInfo.Value.BPM * rate;
|
bpmDisplay.Current.Value = BeatmapInfo.Value.BPM * rate;
|
||||||
|
|
||||||
BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty);
|
BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty);
|
||||||
|
|
||||||
foreach (var mod in mods.Value.OfType<IApplicableToDifficulty>())
|
foreach (var mod in mods.Value.OfType<IApplicableToDifficulty>())
|
||||||
mod.ApplyToDifficulty(adjustedDifficulty);
|
mod.ApplyToDifficulty(originalDifficulty);
|
||||||
|
|
||||||
|
Ruleset ruleset = gameRuleset.Value.CreateInstance();
|
||||||
|
BeatmapDifficulty adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate);
|
||||||
|
|
||||||
|
TooltipContent = new AdjustedAttributesTooltip.Data(originalDifficulty, adjustedDifficulty);
|
||||||
|
|
||||||
|
approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate);
|
||||||
|
overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty);
|
||||||
|
|
||||||
circleSizeDisplay.Current.Value = adjustedDifficulty.CircleSize;
|
circleSizeDisplay.Current.Value = adjustedDifficulty.CircleSize;
|
||||||
drainRateDisplay.Current.Value = adjustedDifficulty.DrainRate;
|
drainRateDisplay.Current.Value = adjustedDifficulty.DrainRate;
|
||||||
@ -170,6 +189,11 @@ namespace osu.Game.Overlays.Mods
|
|||||||
overallDifficultyDisplay.Current.Value = adjustedDifficulty.OverallDifficulty;
|
overallDifficultyDisplay.Current.Value = adjustedDifficulty.OverallDifficulty;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
private void updateCollapsedState()
|
||||||
|
{
|
||||||
|
RightContent.FadeTo(Collapsed.Value && !IsHovered ? 0 : 1, transition_duration, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
private partial class BPMDisplay : RollingCounter<double>
|
private partial class BPMDisplay : RollingCounter<double>
|
||||||
{
|
{
|
||||||
protected override double RollingDuration => 500;
|
protected override double RollingDuration => 500;
|
||||||
|
@ -115,6 +115,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
public IEnumerable<ModState> AllAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value);
|
public IEnumerable<ModState> AllAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value);
|
||||||
|
|
||||||
private readonly BindableBool customisationVisible = new BindableBool();
|
private readonly BindableBool customisationVisible = new BindableBool();
|
||||||
|
private Bindable<bool> textSearchStartsActive = null!;
|
||||||
|
|
||||||
private ModSettingsArea modSettingsArea = null!;
|
private ModSettingsArea modSettingsArea = null!;
|
||||||
private ColumnScrollContainer columnScroll = null!;
|
private ColumnScrollContainer columnScroll = null!;
|
||||||
@ -154,7 +155,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuGameBase game, OsuColour colours, AudioManager audio)
|
private void load(OsuGameBase game, OsuColour colours, AudioManager audio, OsuConfigManager configManager)
|
||||||
{
|
{
|
||||||
Header.Title = ModSelectOverlayStrings.ModSelectTitle;
|
Header.Title = ModSelectOverlayStrings.ModSelectTitle;
|
||||||
Header.Description = ModSelectOverlayStrings.ModSelectDescription;
|
Header.Description = ModSelectOverlayStrings.ModSelectDescription;
|
||||||
@ -282,6 +283,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
globalAvailableMods.BindTo(game.AvailableMods);
|
globalAvailableMods.BindTo(game.AvailableMods);
|
||||||
|
|
||||||
|
textSearchStartsActive = configManager.GetBindable<bool>(OsuSetting.ModSelectTextSearchStartsActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Hide()
|
public override void Hide()
|
||||||
@ -617,6 +620,9 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
nonFilteredColumnCount += 1;
|
nonFilteredColumnCount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (textSearchStartsActive.Value)
|
||||||
|
SearchTextBox.TakeFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopOut()
|
protected override void PopOut()
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.LocalisationExtensions;
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Mods
|
namespace osu.Game.Overlays.Mods
|
||||||
{
|
{
|
||||||
@ -23,11 +28,45 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private readonly BindableWithCurrent<double> current = new BindableWithCurrent<double>();
|
private readonly BindableWithCurrent<double> current = new BindableWithCurrent<double>();
|
||||||
|
|
||||||
|
public Bindable<ModEffect> AdjustType = new Bindable<ModEffect>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Text to display in the top area of the display.
|
/// Text to display in the top area of the display.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LocalisableString Label { get; protected set; }
|
public LocalisableString Label { get; protected set; }
|
||||||
|
|
||||||
|
private readonly EffectCounter counter;
|
||||||
|
private readonly OsuSpriteText text;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; } = null!;
|
||||||
|
|
||||||
|
private void updateTextColor()
|
||||||
|
{
|
||||||
|
Color4 newColor;
|
||||||
|
|
||||||
|
switch (AdjustType.Value)
|
||||||
|
{
|
||||||
|
case ModEffect.NotChanged:
|
||||||
|
newColor = Color4.White;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ModEffect.DifficultyReduction:
|
||||||
|
newColor = colours.ForModType(ModType.DifficultyReduction);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ModEffect.DifficultyIncrease:
|
||||||
|
newColor = colours.ForModType(ModType.DifficultyIncrease);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(AdjustType.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
text.Colour = newColor;
|
||||||
|
counter.Colour = newColor;
|
||||||
|
}
|
||||||
|
|
||||||
public VerticalAttributeDisplay(LocalisableString label)
|
public VerticalAttributeDisplay(LocalisableString label)
|
||||||
{
|
{
|
||||||
Label = label;
|
Label = label;
|
||||||
@ -37,15 +76,18 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Origin = Anchor.CentreLeft;
|
Origin = Anchor.CentreLeft;
|
||||||
Anchor = Anchor.CentreLeft;
|
Anchor = Anchor.CentreLeft;
|
||||||
|
|
||||||
|
AdjustType.BindValueChanged(_ => updateTextColor());
|
||||||
|
|
||||||
InternalChild = new FillFlowContainer
|
InternalChild = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Width = 50,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new OsuSpriteText
|
text = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -53,7 +95,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
Margin = new MarginPadding { Horizontal = 15 }, // to reserve space for 0.XX value
|
Margin = new MarginPadding { Horizontal = 15 }, // to reserve space for 0.XX value
|
||||||
Font = OsuFont.Default.With(size: 20, weight: FontWeight.Bold)
|
Font = OsuFont.Default.With(size: 20, weight: FontWeight.Bold)
|
||||||
},
|
},
|
||||||
new EffectCounter
|
counter = new EffectCounter
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -63,11 +105,28 @@ namespace osu.Game.Overlays.Mods
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ModEffect CalculateEffect(double oldValue, double newValue)
|
||||||
|
{
|
||||||
|
if (Precision.AlmostEquals(newValue, oldValue, 0.01))
|
||||||
|
return ModEffect.NotChanged;
|
||||||
|
if (newValue < oldValue)
|
||||||
|
return ModEffect.DifficultyReduction;
|
||||||
|
|
||||||
|
return ModEffect.DifficultyIncrease;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ModEffect
|
||||||
|
{
|
||||||
|
NotChanged,
|
||||||
|
DifficultyReduction,
|
||||||
|
DifficultyIncrease,
|
||||||
|
}
|
||||||
|
|
||||||
private partial class EffectCounter : RollingCounter<double>
|
private partial class EffectCounter : RollingCounter<double>
|
||||||
{
|
{
|
||||||
protected override double RollingDuration => 500;
|
protected override double RollingDuration => 500;
|
||||||
|
|
||||||
protected override LocalisableString FormatCount(double count) => count.ToLocalisableString("0.0");
|
protected override LocalisableString FormatCount(double count) => count.ToLocalisableString("0.0#");
|
||||||
|
|
||||||
protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText
|
protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
{
|
{
|
||||||
LabelText = GeneralSettingsStrings.LanguageDropdown,
|
LabelText = GeneralSettingsStrings.LanguageDropdown,
|
||||||
Current = game.CurrentLanguage,
|
Current = game.CurrentLanguage,
|
||||||
|
AlwaysShowSearchBar = true,
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
@ -16,23 +15,27 @@ using osu.Game.Localisation;
|
|||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Overlays.Settings.Sections.Maintenance;
|
using osu.Game.Overlays.Settings.Sections.Maintenance;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
|
using SharpCompress.Archives.Zip;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings.Sections.General
|
namespace osu.Game.Overlays.Settings.Sections.General
|
||||||
{
|
{
|
||||||
public partial class UpdateSettings : SettingsSubsection
|
public partial class UpdateSettings : SettingsSubsection
|
||||||
{
|
{
|
||||||
[Resolved(CanBeNull = true)]
|
|
||||||
private UpdateManager updateManager { get; set; }
|
|
||||||
|
|
||||||
protected override LocalisableString Header => GeneralSettingsStrings.UpdateHeader;
|
protected override LocalisableString Header => GeneralSettingsStrings.UpdateHeader;
|
||||||
|
|
||||||
private SettingsButton checkForUpdatesButton;
|
private SettingsButton checkForUpdatesButton = null!;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved]
|
||||||
private INotificationOverlay notifications { get; set; }
|
private UpdateManager? updateManager { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[Resolved]
|
||||||
private void load(Storage storage, OsuConfigManager config, OsuGame game)
|
private INotificationOverlay? notifications { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private Storage storage { get; set; } = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuConfigManager config, OsuGame? game)
|
||||||
{
|
{
|
||||||
Add(new SettingsEnumDropdown<ReleaseStream>
|
Add(new SettingsEnumDropdown<ReleaseStream>
|
||||||
{
|
{
|
||||||
@ -54,7 +57,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
{
|
{
|
||||||
notifications?.Post(new SimpleNotification
|
notifications?.Post(new SimpleNotification
|
||||||
{
|
{
|
||||||
Text = GeneralSettingsStrings.RunningLatestRelease(game.Version),
|
Text = GeneralSettingsStrings.RunningLatestRelease(game!.Version),
|
||||||
Icon = FontAwesome.Solid.CheckCircle,
|
Icon = FontAwesome.Solid.CheckCircle,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -74,6 +77,13 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
Action = () => storage.PresentExternally(),
|
Action = () => storage.PresentExternally(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Add(new SettingsButton
|
||||||
|
{
|
||||||
|
Text = GeneralSettingsStrings.ExportLogs,
|
||||||
|
Keywords = new[] { @"bug", "report", "logs", "files" },
|
||||||
|
Action = () => Task.Run(exportLogs),
|
||||||
|
});
|
||||||
|
|
||||||
Add(new SettingsButton
|
Add(new SettingsButton
|
||||||
{
|
{
|
||||||
Text = GeneralSettingsStrings.ChangeFolderLocation,
|
Text = GeneralSettingsStrings.ChangeFolderLocation,
|
||||||
@ -81,5 +91,44 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void exportLogs()
|
||||||
|
{
|
||||||
|
ProgressNotification notification = new ProgressNotification
|
||||||
|
{
|
||||||
|
State = ProgressNotificationState.Active,
|
||||||
|
Text = "Exporting logs...",
|
||||||
|
};
|
||||||
|
|
||||||
|
notifications?.Post(notification);
|
||||||
|
|
||||||
|
const string archive_filename = "exports/compressed-logs.zip";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var logStorage = Logger.Storage;
|
||||||
|
|
||||||
|
using (var outStream = storage.CreateFileSafely(archive_filename))
|
||||||
|
using (var zip = ZipArchive.Create())
|
||||||
|
{
|
||||||
|
foreach (string? f in logStorage.GetFiles(string.Empty, "*.log")) zip.AddEntry(f, logStorage.GetStream(f), true);
|
||||||
|
|
||||||
|
zip.SaveTo(outStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
notification.State = ProgressNotificationState.Cancelled;
|
||||||
|
|
||||||
|
// cleanup if export is failed or canceled.
|
||||||
|
storage.Delete(archive_filename);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.CompletionText = "Exported logs! Click to view.";
|
||||||
|
notification.CompletionClickAction = () => storage.PresentFileExternally(archive_filename);
|
||||||
|
|
||||||
|
notification.State = ProgressNotificationState.Completed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Database;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
||||||
{
|
{
|
||||||
@ -13,9 +15,12 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
{
|
{
|
||||||
private readonly TaskCompletionSource<string> taskCompletionSource;
|
private readonly TaskCompletionSource<string> taskCompletionSource;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private LegacyImportManager legacyImportManager { get; set; } = null!;
|
||||||
|
|
||||||
protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
|
protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
|
||||||
|
|
||||||
protected override bool IsValidDirectory(DirectoryInfo? info) => info?.GetFiles("osu!.*.cfg").Any() ?? false;
|
protected override bool IsValidDirectory(DirectoryInfo? info) => legacyImportManager.IsUsableForStableImport(info, out _);
|
||||||
|
|
||||||
public override LocalisableString HeaderText => "Please select your osu!stable install location";
|
public override LocalisableString HeaderText => "Please select your osu!stable install location";
|
||||||
|
|
||||||
@ -26,7 +31,10 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance
|
|||||||
|
|
||||||
protected override void OnSelection(DirectoryInfo directory)
|
protected override void OnSelection(DirectoryInfo directory)
|
||||||
{
|
{
|
||||||
taskCompletionSource.TrySetResult(directory.FullName);
|
if (!legacyImportManager.IsUsableForStableImport(directory, out var stableRoot))
|
||||||
|
throw new InvalidOperationException($@"{nameof(OnSelection)} was called on an invalid directory. This should never happen.");
|
||||||
|
|
||||||
|
taskCompletionSource.TrySetResult(stableRoot.FullName);
|
||||||
this.Exit();
|
this.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,12 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
|||||||
ClassicDefault = ModSelectHotkeyStyle.Classic
|
ClassicDefault = ModSelectHotkeyStyle.Classic
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = UserInterfaceStrings.ModSelectTextSearchStartsActive,
|
||||||
|
Current = config.GetBindable<bool>(OsuSetting.ModSelectTextSearchStartsActive),
|
||||||
|
ClassicDefault = false
|
||||||
|
},
|
||||||
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = GameplaySettingsStrings.BackgroundBlur,
|
LabelText = GameplaySettingsStrings.BackgroundBlur,
|
||||||
Current = config.GetBindable<bool>(OsuSetting.SongSelectBackgroundBlur),
|
Current = config.GetBindable<bool>(OsuSetting.SongSelectBackgroundBlur),
|
||||||
|
@ -17,6 +17,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
@ -58,6 +59,9 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private Bindable<IReadOnlyList<Mod>> mods { get; set; } = null!;
|
private Bindable<IReadOnlyList<Mod>> mods { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private Bindable<RulesetInfo> ruleset { get; set; } = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
|
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
|
||||||
|
|
||||||
@ -153,7 +157,11 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
if (screen is Player)
|
if (screen is Player)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var replayGeneratingMod = beatmap.Value.BeatmapInfo.Ruleset.CreateInstance().GetAutoplayMod();
|
// the validity of the current game-wide beatmap + ruleset combination is enforced by song select.
|
||||||
|
// if we're anywhere else, the state is unknown and may not make sense, so forcibly set something that does.
|
||||||
|
if (screen is not PlaySongSelect)
|
||||||
|
ruleset.Value = beatmap.Value.BeatmapInfo.Ruleset;
|
||||||
|
var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod();
|
||||||
|
|
||||||
IReadOnlyList<Mod> usableMods = mods.Value;
|
IReadOnlyList<Mod> usableMods = mods.Value;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
|
|
||||||
public override string Acronym => "CL";
|
public override string Acronym => "CL";
|
||||||
|
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 0.5;
|
||||||
|
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.History;
|
public override IconUsage? Icon => FontAwesome.Solid.History;
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override string Name => "Synesthesia";
|
public override string Name => "Synesthesia";
|
||||||
public override string Acronym => "SY";
|
public override string Acronym => "SY";
|
||||||
public override LocalisableString Description => "Colours hit objects based on the rhythm.";
|
public override LocalisableString Description => "Colours hit objects based on the rhythm.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 0.8;
|
||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,9 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
value -= 1;
|
value -= 1;
|
||||||
|
|
||||||
if (SpeedChange.Value >= 1)
|
if (SpeedChange.Value >= 1)
|
||||||
value /= 5;
|
return 1 + value / 5;
|
||||||
|
else
|
||||||
return 1 + value;
|
return 0.6 + value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,6 +377,17 @@ namespace osu.Game.Rulesets
|
|||||||
/// <returns>The display name.</returns>
|
/// <returns>The display name.</returns>
|
||||||
public virtual LocalisableString GetDisplayNameForHitResult(HitResult result) => result.GetLocalisableDescription();
|
public virtual LocalisableString GetDisplayNameForHitResult(HitResult result) => result.GetLocalisableDescription();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies changes to difficulty attributes for presenting to a user a rough estimate of how rate adjust mods affect difficulty.
|
||||||
|
/// Importantly, this should NOT BE USED FOR ANY CALCULATIONS.
|
||||||
|
///
|
||||||
|
/// It is also not always correct, and arguably is never correct depending on your frame of mind.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="difficulty">>The <see cref="IBeatmapDifficultyInfo"/> that will be adjusted.</param>
|
||||||
|
/// <param name="rate">The rate adjustment multiplier from mods. For example 1.5 for DT.</param>
|
||||||
|
/// <returns>The adjusted difficulty attributes.</returns>
|
||||||
|
public virtual BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, double rate) => new BeatmapDifficulty(difficulty);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates ruleset-specific beatmap filter criteria to be used on the song select screen.
|
/// Creates ruleset-specific beatmap filter criteria to be used on the song select screen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -139,9 +139,12 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A special result used as a padding value for legacy rulesets. It is a hit type and affects combo, but does not affect the base score (does not affect accuracy).
|
/// A special result used as a padding value for legacy rulesets. It is a hit type and affects combo, but does not affect the base score (does not affect accuracy).
|
||||||
|
///
|
||||||
|
/// DO NOT USE FOR ANYTHING EVER.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// DO NOT USE.
|
/// This is used when dealing with legacy scores, which historically only have counts stored for 300/100/50/miss.
|
||||||
|
/// For these scores, we pad the hit statistics with `LegacyComboIncrease` to meet the correct max combo for the score.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[EnumMember(Value = "legacy_combo_increase")]
|
[EnumMember(Value = "legacy_combo_increase")]
|
||||||
[Order(99)]
|
[Order(99)]
|
||||||
|
@ -62,8 +62,8 @@ namespace osu.Game.Rulesets.Scoring.Legacy
|
|||||||
SourceRuleset = beatmapInfo.Ruleset,
|
SourceRuleset = beatmapInfo.Ruleset,
|
||||||
CircleSize = beatmapInfo.Difficulty.CircleSize,
|
CircleSize = beatmapInfo.Difficulty.CircleSize,
|
||||||
OverallDifficulty = beatmapInfo.Difficulty.OverallDifficulty,
|
OverallDifficulty = beatmapInfo.Difficulty.OverallDifficulty,
|
||||||
EndTimeObjectCount = beatmapInfo.Difficulty.EndTimeObjectCount,
|
EndTimeObjectCount = beatmapInfo.EndTimeObjectCount,
|
||||||
TotalObjectCount = beatmapInfo.Difficulty.TotalObjectCount
|
TotalObjectCount = beatmapInfo.TotalObjectCount
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ namespace osu.Game.Screens.Play
|
|||||||
/// Encapsulates gameplay timing logic and provides a <see cref="IGameplayClock"/> via DI for gameplay components to use.
|
/// Encapsulates gameplay timing logic and provides a <see cref="IGameplayClock"/> via DI for gameplay components to use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Cached(typeof(IGameplayClock))]
|
[Cached(typeof(IGameplayClock))]
|
||||||
|
[Cached(typeof(GameplayClockContainer))]
|
||||||
public partial class GameplayClockContainer : Container, IAdjustableClock, IGameplayClock
|
public partial class GameplayClockContainer : Container, IAdjustableClock, IGameplayClock
|
||||||
{
|
{
|
||||||
public IBindable<bool> IsPaused => isPaused;
|
public IBindable<bool> IsPaused => isPaused;
|
||||||
|
@ -212,6 +212,13 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, GlowBarValue > 0 ? 1 : 0, 40, Time.Elapsed);
|
glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, GlowBarValue > 0 ? 1 : 0, 40, Time.Elapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void FinishInitialAnimation(double value)
|
||||||
|
{
|
||||||
|
base.FinishInitialAnimation(value);
|
||||||
|
this.TransformTo(nameof(HealthBarValue), value, 500, Easing.OutQuint);
|
||||||
|
this.TransformTo(nameof(GlowBarValue), value, 250, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Flash()
|
protected override void Flash()
|
||||||
{
|
{
|
||||||
base.Flash();
|
base.Flash();
|
||||||
|
@ -58,7 +58,9 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
health = HealthProcessor.Health.GetBoundCopy();
|
health = HealthProcessor.Health.GetBoundCopy();
|
||||||
health.BindValueChanged(h =>
|
health.BindValueChanged(h =>
|
||||||
{
|
{
|
||||||
finishInitialAnimation();
|
if (initialIncrease != null)
|
||||||
|
FinishInitialAnimation(h.OldValue);
|
||||||
|
|
||||||
Current.Value = h.NewValue;
|
Current.Value = h.NewValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -90,16 +92,16 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
Scheduler.AddOnce(Flash);
|
Scheduler.AddOnce(Flash);
|
||||||
|
|
||||||
if (newValue >= health.Value)
|
if (newValue >= health.Value)
|
||||||
finishInitialAnimation();
|
FinishInitialAnimation(health.Value);
|
||||||
}, increase_delay, true);
|
}, increase_delay, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void finishInitialAnimation()
|
protected virtual void FinishInitialAnimation(double value)
|
||||||
{
|
{
|
||||||
if (initialIncrease == null)
|
if (initialIncrease == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
initialIncrease?.Cancel();
|
initialIncrease.Cancel();
|
||||||
initialIncrease = null;
|
initialIncrease = null;
|
||||||
|
|
||||||
// aside from the repeating `initialIncrease` scheduled task,
|
// aside from the repeating `initialIncrease` scheduled task,
|
||||||
|
@ -485,7 +485,14 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Clear() => judgementsContainer.Clear();
|
public override void Clear()
|
||||||
|
{
|
||||||
|
foreach (var j in judgementsContainer)
|
||||||
|
{
|
||||||
|
j.ClearTransforms();
|
||||||
|
j.Expire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum CentreMarkerStyles
|
public enum CentreMarkerStyles
|
||||||
{
|
{
|
||||||
|
@ -63,7 +63,14 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters
|
|||||||
judgementsFlow.Push(GetColourForHitResult(judgement.Type));
|
judgementsFlow.Push(GetColourForHitResult(judgement.Type));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Clear() => judgementsFlow.Clear();
|
public override void Clear()
|
||||||
|
{
|
||||||
|
foreach (var j in judgementsFlow)
|
||||||
|
{
|
||||||
|
j.ClearTransforms();
|
||||||
|
j.Expire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private partial class JudgementFlow : FillFlowContainer<HitErrorShape>
|
private partial class JudgementFlow : FillFlowContainer<HitErrorShape>
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private readonly Score score;
|
private readonly Score score;
|
||||||
|
|
||||||
|
public override bool AllowBackButton => true;
|
||||||
|
|
||||||
protected override bool CheckModsAllowFailure()
|
protected override bool CheckModsAllowFailure()
|
||||||
{
|
{
|
||||||
if (!allowFail)
|
if (!allowFail)
|
||||||
|
@ -245,6 +245,9 @@ namespace osu.Game.Screens.Select.Carousel
|
|||||||
|
|
||||||
private void updateKeyCount()
|
private void updateKeyCount()
|
||||||
{
|
{
|
||||||
|
if (Item?.State.Value == CarouselItemState.Collapsed)
|
||||||
|
return;
|
||||||
|
|
||||||
if (ruleset.Value.OnlineID == 3)
|
if (ruleset.Value.OnlineID == 3)
|
||||||
{
|
{
|
||||||
// Account for mania differences locally for now.
|
// Account for mania differences locally for now.
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -25,10 +26,11 @@ using osu.Framework.Utils;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Resources.Localisation.Web;
|
using osu.Game.Resources.Localisation.Web;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select.Details
|
namespace osu.Game.Screens.Select.Details
|
||||||
{
|
{
|
||||||
public partial class AdvancedStats : Container
|
public partial class AdvancedStats : Container, IHasCustomTooltip<AdjustedAttributesTooltip.Data>
|
||||||
{
|
{
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private BeatmapDifficultyCache difficultyCache { get; set; }
|
private BeatmapDifficultyCache difficultyCache { get; set; }
|
||||||
@ -44,6 +46,9 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate;
|
protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate;
|
||||||
private readonly StatisticRow starDifficulty;
|
private readonly StatisticRow starDifficulty;
|
||||||
|
|
||||||
|
public ITooltip<AdjustedAttributesTooltip.Data> GetCustomTooltip() => new AdjustedAttributesTooltip();
|
||||||
|
public AdjustedAttributesTooltip.Data TooltipContent { get; private set; }
|
||||||
|
|
||||||
private IBeatmapInfo beatmapInfo;
|
private IBeatmapInfo beatmapInfo;
|
||||||
|
|
||||||
public IBeatmapInfo BeatmapInfo
|
public IBeatmapInfo BeatmapInfo
|
||||||
@ -118,15 +123,28 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty;
|
IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty;
|
||||||
BeatmapDifficulty adjustedDifficulty = null;
|
BeatmapDifficulty adjustedDifficulty = null;
|
||||||
|
|
||||||
if (baseDifficulty != null && mods.Value.Any(m => m is IApplicableToDifficulty))
|
IRulesetInfo ruleset = gameRuleset?.Value ?? beatmapInfo.Ruleset;
|
||||||
|
|
||||||
|
if (baseDifficulty != null)
|
||||||
{
|
{
|
||||||
adjustedDifficulty = new BeatmapDifficulty(baseDifficulty);
|
BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(baseDifficulty);
|
||||||
|
|
||||||
foreach (var mod in mods.Value.OfType<IApplicableToDifficulty>())
|
foreach (var mod in mods.Value.OfType<IApplicableToDifficulty>())
|
||||||
mod.ApplyToDifficulty(adjustedDifficulty);
|
mod.ApplyToDifficulty(originalDifficulty);
|
||||||
}
|
|
||||||
|
|
||||||
IRulesetInfo ruleset = gameRuleset?.Value ?? beatmapInfo.Ruleset;
|
adjustedDifficulty = originalDifficulty;
|
||||||
|
|
||||||
|
if (gameRuleset != null)
|
||||||
|
{
|
||||||
|
double rate = 1;
|
||||||
|
foreach (var mod in mods.Value.OfType<IApplicableToRate>())
|
||||||
|
rate = mod.ApplyToRate(0, rate);
|
||||||
|
|
||||||
|
adjustedDifficulty = ruleset.CreateInstance().GetRateAdjustedDisplayDifficulty(originalDifficulty, rate);
|
||||||
|
|
||||||
|
TooltipContent = new AdjustedAttributesTooltip.Data(originalDifficulty, adjustedDifficulty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (ruleset.OnlineID)
|
switch (ruleset.OnlineID)
|
||||||
{
|
{
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="11.5.0" />
|
<PackageReference Include="Realm" Version="11.5.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2023.1213.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2023.1213.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.1127.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.1215.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.40.0" />
|
<PackageReference Include="Sentry" Version="3.40.0" />
|
||||||
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
||||||
<PackageReference Include="SharpCompress" Version="0.33.0" />
|
<PackageReference Include="SharpCompress" Version="0.33.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user