Merge branch 'master' into fix-slider-border-color

This commit is contained in:
Dean Herbert
2019-05-12 17:25:49 +09:00
committed by GitHub
261 changed files with 1466 additions and 289 deletions

View File

@ -1,5 +1,5 @@
#addin "nuget:?package=CodeFileSanity&version=0.0.21" #addin "nuget:?package=CodeFileSanity&version=0.0.21"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.3.4" #addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.1.1"
#tool "nuget:?package=NVika.MSBuild&version=1.0.1" #tool "nuget:?package=NVika.MSBuild&version=1.0.1"
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First(); var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();

View File

@ -77,6 +77,7 @@ namespace osu.Desktop
if (versionManager != null) if (versionManager != null)
versionManager.State = Visibility.Visible; versionManager.State = Visibility.Visible;
break; break;
default: default:
if (versionManager != null) if (versionManager != null)
versionManager.State = Visibility.Hidden; versionManager.State = Visibility.Hidden;
@ -87,6 +88,7 @@ namespace osu.Desktop
public override void SetHost(GameHost host) public override void SetHost(GameHost host)
{ {
base.SetHost(host); base.SetHost(host);
if (host.Window is DesktopGameWindow desktopWindow) if (host.Window is DesktopGameWindow desktopWindow)
{ {
desktopWindow.CursorState |= CursorState.Hidden; desktopWindow.CursorState |= CursorState.Hidden;

View File

@ -95,6 +95,7 @@ namespace osu.Desktop.Overlays
var version = game.Version; var version = game.Version;
var lastVersion = config.Get<string>(OsuSetting.Version); var lastVersion = config.Get<string>(OsuSetting.Version);
if (game.IsDeployedBuild && version != lastVersion) if (game.IsDeployedBuild && version != lastVersion)
{ {
config.Set(OsuSetting.Version, version); config.Set(OsuSetting.Version, version);

View File

@ -31,6 +31,7 @@ namespace osu.Desktop
var importer = new ArchiveImportIPCChannel(host); var importer = new ArchiveImportIPCChannel(host);
// Restore the cwd so relative paths given at the command line work correctly // Restore the cwd so relative paths given at the command line work correctly
Directory.SetCurrentDirectory(cwd); Directory.SetCurrentDirectory(cwd);
foreach (var file in args) foreach (var file in args)
{ {
Console.WriteLine(@"Importing {0}", file); Console.WriteLine(@"Importing {0}", file);

View File

@ -78,6 +78,7 @@ namespace osu.Desktop.Updater
case RuntimeInfo.Platform.Windows: case RuntimeInfo.Platform.Windows:
bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".exe")); bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".exe"));
break; break;
case RuntimeInfo.Platform.MacOsx: case RuntimeInfo.Platform.MacOsx:
bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".app.zip")); bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".app.zip"));
break; break;

View File

@ -175,7 +175,7 @@ namespace osu.Desktop.Updater
public SquirrelLogger() public SquirrelLogger()
{ {
var file = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "SquirrelSetupUpdater.log"); var file = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location ?? Directory.GetCurrentDirectory()), "SquirrelSetupUpdater.log");
if (File.Exists(file)) File.Delete(file); if (File.Exists(file)) File.Delete(file);
path = file; path = file;
} }

View File

@ -26,9 +26,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="4.5.0" /> <PackageReference Include="System.IO.Packaging" Version="4.5.0" />
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.3" /> <PackageReference Include="ppy.squirrel.windows" Version="1.9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.4" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Resources"> <ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" /> <EmbeddedResource Include="lazer.ico" />

View File

@ -36,11 +36,13 @@ namespace osu.Game.Rulesets.Catch.Tests
yield return new ConvertValue((CatchHitObject)nested); yield return new ConvertValue((CatchHitObject)nested);
break; break;
case BananaShower shower: case BananaShower shower:
foreach (var nested in shower.NestedHitObjects) foreach (var nested in shower.NestedHitObjects)
yield return new ConvertValue((CatchHitObject)nested); yield return new ConvertValue((CatchHitObject)nested);
break; break;
default: default:
yield return new ConvertValue((CatchHitObject)hitObject); yield return new ConvertValue((CatchHitObject)hitObject);

View File

@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects); initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects);
int index = 0; int index = 0;
foreach (var obj in Beatmap.HitObjects.OfType<CatchHitObject>()) foreach (var obj in Beatmap.HitObjects.OfType<CatchHitObject>())
{ {
obj.IndexInBeatmap = index++; obj.IndexInBeatmap = index++;
@ -58,6 +59,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
} }
break; break;
case JuiceStream juiceStream: case JuiceStream juiceStream:
foreach (var nested in juiceStream.NestedHitObjects) foreach (var nested in juiceStream.NestedHitObjects)
{ {
@ -103,6 +105,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext); float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext);
if (distanceToHyper < 0) if (distanceToHyper < 0)
{ {
currentObject.HyperDashTarget = nextObject; currentObject.HyperDashTarget = nextObject;

View File

@ -87,6 +87,7 @@ namespace osu.Game.Rulesets.Catch
new CatchModNoFail(), new CatchModNoFail(),
new MultiMod(new CatchModHalfTime(), new CatchModDaycore()) new MultiMod(new CatchModHalfTime(), new CatchModDaycore())
}; };
case ModType.DifficultyIncrease: case ModType.DifficultyIncrease:
return new Mod[] return new Mod[]
{ {
@ -96,17 +97,20 @@ namespace osu.Game.Rulesets.Catch
new CatchModHidden(), new CatchModHidden(),
new CatchModFlashlight(), new CatchModFlashlight(),
}; };
case ModType.Automation: case ModType.Automation:
return new Mod[] return new Mod[]
{ {
new MultiMod(new CatchModAutoplay(), new ModCinema()), new MultiMod(new CatchModAutoplay(), new ModCinema()),
new CatchModRelax(), new CatchModRelax(),
}; };
case ModType.Fun: case ModType.Fun:
return new Mod[] return new Mod[]
{ {
new MultiMod(new ModWindUp<CatchHitObject>(), new ModWindDown<CatchHitObject>()) new MultiMod(new ModWindUp<CatchHitObject>(), new ModWindDown<CatchHitObject>())
}; };
default: default:
return new Mod[] { }; return new Mod[] { };
} }

View File

@ -73,6 +73,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
lastObject = hitObject; lastObject = hitObject;
break; break;
case JuiceStream _: case JuiceStream _:
foreach (var nested in hitObject.NestedHitObjects.OfType<CatchHitObject>().Where(o => !(o is TinyDroplet))) foreach (var nested in hitObject.NestedHitObjects.OfType<CatchHitObject>().Where(o => !(o is TinyDroplet)))
{ {

View File

@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
default: default:
return 0; return 0;
case HitResult.Perfect: case HitResult.Perfect:
return 1100; return 1100;
} }
@ -27,6 +28,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
default: default:
return 0; return 0;
case HitResult.Perfect: case HitResult.Perfect:
return 0.008; return 0.008;
} }

View File

@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
default: default:
return 0; return 0;
case HitResult.Perfect: case HitResult.Perfect:
return 30; return 30;
} }
@ -24,6 +25,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
default: default:
return base.HealthIncreaseFor(result); return base.HealthIncreaseFor(result);
case HitResult.Perfect: case HitResult.Perfect:
return 0.007; return 0.007;
} }

View File

@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
default: default:
return 0; return 0;
case HitResult.Perfect: case HitResult.Perfect:
return 300; return 300;
} }
@ -28,6 +29,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
default: default:
return -0.02; return -0.02;
case HitResult.Perfect: case HitResult.Perfect:
return 0.01; return 0.01;
} }

View File

@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
default: default:
return 0; return 0;
case HitResult.Perfect: case HitResult.Perfect:
return 10; return 10;
} }
@ -26,6 +27,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
{ {
default: default:
return 0; return 0;
case HitResult.Perfect: case HitResult.Perfect:
return 0.004; return 0.004;
} }

View File

@ -84,6 +84,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
case ArmedState.Miss: case ArmedState.Miss:
this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire(); this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out).Expire();
break; break;
case ArmedState.Hit: case ArmedState.Hit:
this.FadeOut().Expire(); this.FadeOut().Expire();
break; break;

View File

@ -106,6 +106,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
default: default:
return new Container(); return new Container();
case FruitVisualRepresentation.Raspberry: case FruitVisualRepresentation.Raspberry:
return new Container return new Container
{ {
@ -144,6 +145,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
}, },
} }
}; };
case FruitVisualRepresentation.Pineapple: case FruitVisualRepresentation.Pineapple:
return new Container return new Container
{ {
@ -182,6 +184,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
}, },
} }
}; };
case FruitVisualRepresentation.Pear: case FruitVisualRepresentation.Pear:
return new Container return new Container
{ {
@ -214,6 +217,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
}, },
} }
}; };
case FruitVisualRepresentation.Grape: case FruitVisualRepresentation.Grape:
return new Container return new Container
{ {
@ -246,6 +250,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
}, },
} }
}; };
case FruitVisualRepresentation.Banana: case FruitVisualRepresentation.Banana:
return new Container return new Container
{ {
@ -283,19 +288,25 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
default: default:
case FruitVisualRepresentation.Pear: case FruitVisualRepresentation.Pear:
return new Color4(17, 136, 170, 255); return new Color4(17, 136, 170, 255);
case FruitVisualRepresentation.Grape: case FruitVisualRepresentation.Grape:
return new Color4(204, 102, 0, 255); return new Color4(204, 102, 0, 255);
case FruitVisualRepresentation.Raspberry: case FruitVisualRepresentation.Raspberry:
return new Color4(121, 9, 13, 255); return new Color4(121, 9, 13, 255);
case FruitVisualRepresentation.Pineapple: case FruitVisualRepresentation.Pineapple:
return new Color4(102, 136, 0, 255); return new Color4(102, 136, 0, 255);
case FruitVisualRepresentation.Banana: case FruitVisualRepresentation.Banana:
switch (RNG.Next(0, 3)) switch (RNG.Next(0, 3))
{ {
default: default:
return new Color4(255, 240, 0, 255); return new Color4(255, 240, 0, 255);
case 1: case 1:
return new Color4(255, 192, 0, 255); return new Color4(255, 192, 0, 255);
case 2: case 2:
return new Color4(214, 221, 28, 255); return new Color4(214, 221, 28, 255);
} }

View File

@ -95,6 +95,7 @@ namespace osu.Game.Rulesets.Catch.Objects
X = X + Path.PositionAt(e.PathProgress).X / CatchPlayfield.BASE_WIDTH, X = X + Path.PositionAt(e.PathProgress).X / CatchPlayfield.BASE_WIDTH,
}); });
break; break;
case SliderEventType.Head: case SliderEventType.Head:
case SliderEventType.Tail: case SliderEventType.Tail:
case SliderEventType.Repeat: case SliderEventType.Repeat:

View File

@ -32,6 +32,7 @@ namespace osu.Game.Rulesets.Catch.Scoring
{ {
case HitResult.Miss: case HitResult.Miss:
return hpDrainRate; return hpDrainRate;
default: default:
return 10.2 - hpDrainRate; // Award less HP as drain rate is increased return 10.2 - hpDrainRate; // Award less HP as drain rate is increased
} }

View File

@ -292,6 +292,7 @@ namespace osu.Game.Rulesets.Catch.UI
const float hyper_dash_transition_length = 180; const float hyper_dash_transition_length = 180;
bool previouslyHyperDashing = HyperDashing; bool previouslyHyperDashing = HyperDashing;
if (modifier <= 1 || X == targetPosition) if (modifier <= 1 || X == targetPosition)
{ {
hyperDashModifier = 1; hyperDashModifier = 1;
@ -325,9 +326,11 @@ namespace osu.Game.Rulesets.Catch.UI
case CatchAction.MoveLeft: case CatchAction.MoveLeft:
currentDirection--; currentDirection--;
return true; return true;
case CatchAction.MoveRight: case CatchAction.MoveRight:
currentDirection++; currentDirection++;
return true; return true;
case CatchAction.Dash: case CatchAction.Dash:
Dashing = true; Dashing = true;
return true; return true;
@ -343,9 +346,11 @@ namespace osu.Game.Rulesets.Catch.UI
case CatchAction.MoveLeft: case CatchAction.MoveLeft:
currentDirection++; currentDirection++;
return true; return true;
case CatchAction.MoveRight: case CatchAction.MoveRight:
currentDirection--; currentDirection--;
return true; return true;
case CatchAction.Dash: case CatchAction.Dash:
Dashing = false; Dashing = false;
return true; return true;

View File

@ -48,14 +48,19 @@ namespace osu.Game.Rulesets.Catch.UI
{ {
case Banana banana: case Banana banana:
return new DrawableBanana(banana); return new DrawableBanana(banana);
case Fruit fruit: case Fruit fruit:
return new DrawableFruit(fruit); return new DrawableFruit(fruit);
case JuiceStream stream: case JuiceStream stream:
return new DrawableJuiceStream(stream, CreateDrawableRepresentation); return new DrawableJuiceStream(stream, CreateDrawableRepresentation);
case BananaShower shower: case BananaShower shower:
return new DrawableBananaShower(shower, CreateDrawableRepresentation); return new DrawableBananaShower(shower, CreateDrawableRepresentation);
case TinyDroplet tiny: case TinyDroplet tiny:
return new DrawableTinyDroplet(tiny); return new DrawableTinyDroplet(tiny);
case Droplet droplet: case Droplet droplet:
return new DrawableDroplet(droplet); return new DrawableDroplet(droplet);
} }

View File

@ -168,11 +168,13 @@ namespace osu.Game.Rulesets.Mania.Tests
foreach (var nested in obj.NestedHitObjects) foreach (var nested in obj.NestedHitObjects)
{ {
double finalPosition = (nested.HitObject.StartTime - obj.HitObject.StartTime) / endTime.Duration; double finalPosition = (nested.HitObject.StartTime - obj.HitObject.StartTime) / endTime.Duration;
switch (direction) switch (direction)
{ {
case ScrollingDirection.Up: case ScrollingDirection.Up:
nested.Y = (float)(finalPosition * content.DrawHeight); nested.Y = (float)(finalPosition * content.DrawHeight);
break; break;
case ScrollingDirection.Down: case ScrollingDirection.Down:
nested.Y = (float)(-finalPosition * content.DrawHeight); nested.Y = (float)(-finalPosition * content.DrawHeight);
break; break;

View File

@ -48,6 +48,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
if (IsForCurrentRuleset) if (IsForCurrentRuleset)
{ {
TargetColumns = (int)Math.Max(1, roundedCircleSize); TargetColumns = (int)Math.Max(1, roundedCircleSize);
if (TargetColumns >= 10) if (TargetColumns >= 10)
{ {
TargetColumns = TargetColumns / 2; TargetColumns = TargetColumns / 2;

View File

@ -179,6 +179,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects; int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects;
int nextColumn = GetRandomColumn(); int nextColumn = GetRandomColumn();
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++) for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
{ {
// Find available column // Find available column
@ -217,6 +218,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern); nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
int lastColumn = nextColumn; int lastColumn = nextColumn;
for (int i = 0; i < noteCount; i++) for (int i = 0; i < noteCount; i++)
{ {
addToPattern(pattern, nextColumn, startTime, startTime); addToPattern(pattern, nextColumn, startTime, startTime);
@ -299,6 +301,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0)); int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0));
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i <= spanCount; i++) for (int i = 0; i <= spanCount; i++)
{ {
addToPattern(pattern, nextColumn, startTime, startTime); addToPattern(pattern, nextColumn, startTime, startTime);
@ -341,16 +344,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p3 = 0; p3 = 0;
p4 = 0; p4 = 0;
break; break;
case 3: case 3:
p2 = Math.Min(p2, 0.1); p2 = Math.Min(p2, 0.1);
p3 = 0; p3 = 0;
p4 = 0; p4 = 0;
break; break;
case 4: case 4:
p2 = Math.Min(p2, 0.3); p2 = Math.Min(p2, 0.3);
p3 = Math.Min(p3, 0.04); p3 = Math.Min(p3, 0.04);
p4 = 0; p4 = 0;
break; break;
case 5: case 5:
p2 = Math.Min(p2, 0.34); p2 = Math.Min(p2, 0.34);
p3 = Math.Min(p3, 0.1); p3 = Math.Min(p3, 0.1);
@ -440,6 +446,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP); bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP);
var rowPattern = new Pattern(); var rowPattern = new Pattern();
for (int i = 0; i <= spanCount; i++) for (int i = 0; i <= spanCount; i++)
{ {
if (!(ignoreHead && startTime == HitObject.StartTime)) if (!(ignoreHead && startTime == HitObject.StartTime))

View File

@ -38,9 +38,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
case 8 when HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000: case 8 when HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000:
addToPattern(pattern, 0, generateHold); addToPattern(pattern, 0, generateHold);
break; break;
case 8: case 8:
addToPattern(pattern, FindAvailableColumn(GetRandomColumn(), PreviousPattern), generateHold); addToPattern(pattern, FindAvailableColumn(GetRandomColumn(), PreviousPattern), generateHold);
break; break;
default: default:
if (TotalColumns > 0) if (TotalColumns > 0)
addToPattern(pattern, GetRandomColumn(), generateHold); addToPattern(pattern, GetRandomColumn(), generateHold);

View File

@ -233,6 +233,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects); noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i < noteCount; i++) for (int i = 0; i < noteCount; i++)
{ {
nextColumn = allowStacking nextColumn = allowStacking
@ -303,6 +304,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2; int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
int nextColumn = GetRandomColumn(upperBound: columnLimit); int nextColumn = GetRandomColumn(upperBound: columnLimit);
for (int i = 0; i < noteCount; i++) for (int i = 0; i < noteCount; i++)
{ {
nextColumn = FindAvailableColumn(nextColumn, upperBound: columnLimit, patterns: pattern); nextColumn = FindAvailableColumn(nextColumn, upperBound: columnLimit, patterns: pattern);
@ -340,18 +342,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p4 = 0; p4 = 0;
p5 = 0; p5 = 0;
break; break;
case 3: case 3:
p2 = Math.Min(p2, 0.1); p2 = Math.Min(p2, 0.1);
p3 = 0; p3 = 0;
p4 = 0; p4 = 0;
p5 = 0; p5 = 0;
break; break;
case 4: case 4:
p2 = Math.Min(p2, 0.23); p2 = Math.Min(p2, 0.23);
p3 = Math.Min(p3, 0.04); p3 = Math.Min(p3, 0.04);
p4 = 0; p4 = 0;
p5 = 0; p5 = 0;
break; break;
case 5: case 5:
p3 = Math.Min(p3, 0.15); p3 = Math.Min(p3, 0.15);
p4 = Math.Min(p4, 0.03); p4 = Math.Min(p4, 0.03);
@ -384,20 +389,24 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
p2 = 0; p2 = 0;
p3 = 0; p3 = 0;
break; break;
case 3: case 3:
centreProbability = Math.Min(centreProbability, 0.03); centreProbability = Math.Min(centreProbability, 0.03);
p2 = 0; p2 = 0;
p3 = 0; p3 = 0;
break; break;
case 4: case 4:
centreProbability = 0; centreProbability = 0;
p2 = Math.Min(p2 * 2, 0.2); p2 = Math.Min(p2 * 2, 0.2);
p3 = 0; p3 = 0;
break; break;
case 5: case 5:
centreProbability = Math.Min(centreProbability, 0.03); centreProbability = Math.Min(centreProbability, 0.03);
p3 = 0; p3 = 0;
break; break;
case 6: case 6:
centreProbability = 0; centreProbability = 0;
p2 = Math.Min(p2 * 2, 0.5); p2 = Math.Min(p2 * 2, 0.5);

View File

@ -158,6 +158,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
// Ensure that we have at least one free column, so that an endless loop is avoided // Ensure that we have at least one free column, so that an endless loop is avoided
bool hasValidColumns = false; bool hasValidColumns = false;
for (int i = lowerBound.Value; i < upperBound.Value; i++) for (int i = lowerBound.Value; i < upperBound.Value; i++)
{ {
hasValidColumns = isValid(i); hasValidColumns = isValid(i);

View File

@ -66,6 +66,7 @@ namespace osu.Game.Rulesets.Mania.Edit
{ {
case DrawableNote note: case DrawableNote note:
return new NoteSelectionBlueprint(note); return new NoteSelectionBlueprint(note);
case DrawableHoldNote holdNote: case DrawableHoldNote holdNote:
return new HoldNoteSelectionBlueprint(holdNote); return new HoldNoteSelectionBlueprint(holdNote);
} }

View File

@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Mania.Judgements
{ {
case HitResult.Miss: case HitResult.Miss:
return 0; return 0;
default: default:
return 0.040; return 0.040;
} }

View File

@ -14,12 +14,16 @@ namespace osu.Game.Rulesets.Mania.Judgements
{ {
default: default:
return 0; return 0;
case HitResult.Meh: case HitResult.Meh:
return 50; return 50;
case HitResult.Ok: case HitResult.Ok:
return 100; return 100;
case HitResult.Good: case HitResult.Good:
return 200; return 200;
case HitResult.Great: case HitResult.Great:
case HitResult.Perfect: case HitResult.Perfect:
return 300; return 300;
@ -32,16 +36,22 @@ namespace osu.Game.Rulesets.Mania.Judgements
{ {
case HitResult.Miss: case HitResult.Miss:
return -0.125; return -0.125;
case HitResult.Meh: case HitResult.Meh:
return 0.005; return 0.005;
case HitResult.Ok: case HitResult.Ok:
return 0.010; return 0.010;
case HitResult.Good: case HitResult.Good:
return 0.035; return 0.035;
case HitResult.Great: case HitResult.Great:
return 0.055; return 0.055;
case HitResult.Perfect: case HitResult.Perfect:
return 0.065; return 0.065;
default: default:
return 0; return 0;
} }

View File

@ -117,6 +117,7 @@ namespace osu.Game.Rulesets.Mania
new ManiaModNoFail(), new ManiaModNoFail(),
new MultiMod(new ManiaModHalfTime(), new ManiaModDaycore()), new MultiMod(new ManiaModHalfTime(), new ManiaModDaycore()),
}; };
case ModType.DifficultyIncrease: case ModType.DifficultyIncrease:
return new Mod[] return new Mod[]
{ {
@ -126,6 +127,7 @@ namespace osu.Game.Rulesets.Mania
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()), new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()),
new ManiaModFlashlight(), new ManiaModFlashlight(),
}; };
case ModType.Conversion: case ModType.Conversion:
return new Mod[] return new Mod[]
{ {
@ -142,16 +144,19 @@ namespace osu.Game.Rulesets.Mania
new ManiaModDualStages(), new ManiaModDualStages(),
new ManiaModMirror(), new ManiaModMirror(),
}; };
case ModType.Automation: case ModType.Automation:
return new Mod[] return new Mod[]
{ {
new MultiMod(new ManiaModAutoplay(), new ModCinema()), new MultiMod(new ManiaModAutoplay(), new ModCinema()),
}; };
case ModType.Fun: case ModType.Fun:
return new Mod[] return new Mod[]
{ {
new MultiMod(new ModWindUp<ManiaHitObject>(), new ModWindDown<ManiaHitObject>()) new MultiMod(new ModWindUp<ManiaHitObject>(), new ModWindDown<ManiaHitObject>())
}; };
default: default:
return new Mod[] { }; return new Mod[] { };
} }
@ -214,6 +219,7 @@ namespace osu.Game.Rulesets.Mania
SpecialAction = ManiaAction.Special1, SpecialAction = ManiaAction.Special1,
NormalActionStart = ManiaAction.Key1, NormalActionStart = ManiaAction.Key1,
}.GenerateKeyBindingsFor(variant, out _); }.GenerateKeyBindingsFor(variant, out _);
case PlayfieldType.Dual: case PlayfieldType.Dual:
int keys = getDualStageKeyCount(variant); int keys = getDualStageKeyCount(variant);
@ -271,6 +277,7 @@ namespace osu.Game.Rulesets.Mania
{ {
default: default:
return $"{variant}K"; return $"{variant}K";
case PlayfieldType.Dual: case PlayfieldType.Dual:
{ {
var keys = getDualStageKeyCount(variant); var keys = getDualStageKeyCount(variant);

View File

@ -65,6 +65,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
case ArmedState.Miss: case ArmedState.Miss:
this.FadeOut(150, Easing.In).Expire(); this.FadeOut(150, Easing.In).Expire();
break; break;
case ArmedState.Hit: case ArmedState.Hit:
this.FadeOut(150, Easing.OutQuint).Expire(); this.FadeOut(150, Easing.OutQuint).Expire();
break; break;

View File

@ -145,6 +145,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
const float animation_length = 50; const float animation_length = 50;
Foreground.ClearTransforms(false, nameof(Foreground.Colour)); Foreground.ClearTransforms(false, nameof(Foreground.Colour));
if (hitting) if (hitting)
{ {
// wait for the next sync point // wait for the next sync point

View File

@ -28,6 +28,7 @@ namespace osu.Game.Rulesets.Mania.Replays
var normalAction = ManiaAction.Key1; var normalAction = ManiaAction.Key1;
var specialAction = ManiaAction.Special1; var specialAction = ManiaAction.Special1;
int totalCounter = 0; int totalCounter = 0;
foreach (var stage in Beatmap.Stages) foreach (var stage in Beatmap.Stages)
{ {
for (int i = 0; i < stage.Columns; i++) for (int i = 0; i < stage.Columns; i++)
@ -51,6 +52,7 @@ namespace osu.Game.Rulesets.Mania.Replays
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
var actions = new List<ManiaAction>(); var actions = new List<ManiaAction>();
foreach (var group in pointGroups) foreach (var group in pointGroups)
{ {
foreach (var point in group) foreach (var point in group)
@ -60,6 +62,7 @@ namespace osu.Game.Rulesets.Mania.Replays
case HitPoint _: case HitPoint _:
actions.Add(columnActions[point.Column]); actions.Add(columnActions[point.Column]);
break; break;
case ReleasePoint _: case ReleasePoint _:
actions.Remove(columnActions[point.Column]); actions.Remove(columnActions[point.Column]);
break; break;

View File

@ -39,6 +39,7 @@ namespace osu.Game.Rulesets.Mania.Replays
int activeColumns = (int)(legacyFrame.MouseX ?? 0); int activeColumns = (int)(legacyFrame.MouseX ?? 0);
int counter = 0; int counter = 0;
while (activeColumns > 0) while (activeColumns > 0)
{ {
var isSpecial = stage.IsSpecialColumn(counter); var isSpecial = stage.IsSpecialColumn(counter);

View File

@ -57,6 +57,7 @@ namespace osu.Game.Rulesets.Mania.UI
double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature; double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - point.BeatLength : lastObjectTime + point.BeatLength * (int)point.TimeSignature;
int index = 0; int index = 0;
for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++) for (double t = timingPoints[i].Time; Precision.DefinitelyBigger(endTime, t); t += point.BeatLength, index++)
{ {
barLines.Add(new BarLine barLines.Add(new BarLine
@ -105,8 +106,10 @@ namespace osu.Game.Rulesets.Mania.UI
{ {
case HoldNote holdNote: case HoldNote holdNote:
return new DrawableHoldNote(holdNote); return new DrawableHoldNote(holdNote);
case Note note: case Note note:
return new DrawableNote(note); return new DrawableNote(note);
default: default:
return null; return null;
} }

View File

@ -38,6 +38,7 @@ namespace osu.Game.Rulesets.Mania.UI
var normalColumnAction = ManiaAction.Key1; var normalColumnAction = ManiaAction.Key1;
var specialColumnAction = ManiaAction.Special1; var specialColumnAction = ManiaAction.Special1;
int firstColumnIndex = 0; int firstColumnIndex = 0;
for (int i = 0; i < stageDefinitions.Count; i++) for (int i = 0; i < stageDefinitions.Count; i++)
{ {
var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction); var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
@ -92,6 +93,7 @@ namespace osu.Game.Rulesets.Mania.UI
private ManiaStage getStageByColumn(int column) private ManiaStage getStageByColumn(int column)
{ {
int sum = 0; int sum = 0;
foreach (var stage in stages) foreach (var stage in stages)
{ {
sum = sum + stage.Columns.Count; sum = sum + stage.Columns.Count;

View File

@ -35,6 +35,7 @@ namespace osu.Game.Rulesets.Osu.Tests
yield return createConvertValue(nested); yield return createConvertValue(nested);
break; break;
default: default:
yield return createConvertValue(hitObject); yield return createConvertValue(hitObject);

View File

@ -0,0 +1,19 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Screens.Play;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestCaseOsuFlashlight : TestCaseOsuPlayer
{
protected override Player CreatePlayer(Ruleset ruleset)
{
Mods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), };
return base.CreatePlayer(ruleset);
}
}
}

View File

@ -353,6 +353,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
protected override bool PauseOnFocusLost => false;
public ScoreAccessibleReplayPlayer(Score score) public ScoreAccessibleReplayPlayer(Score score)
: base(score, false, false) : base(score, false, false)
{ {

View File

@ -44,12 +44,14 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
if (endIndex < 0) throw new ArgumentOutOfRangeException(nameof(endIndex), $"{nameof(endIndex)} cannot be less than 0."); if (endIndex < 0) throw new ArgumentOutOfRangeException(nameof(endIndex), $"{nameof(endIndex)} cannot be less than 0.");
int extendedEndIndex = endIndex; int extendedEndIndex = endIndex;
if (endIndex < beatmap.HitObjects.Count - 1) if (endIndex < beatmap.HitObjects.Count - 1)
{ {
// Extend the end index to include objects they are stacked on // Extend the end index to include objects they are stacked on
for (int i = endIndex; i >= startIndex; i--) for (int i = endIndex; i >= startIndex; i--)
{ {
int stackBaseIndex = i; int stackBaseIndex = i;
for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++) for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++)
{ {
OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex]; OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex];
@ -87,6 +89,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
//Reverse pass for stack calculation. //Reverse pass for stack calculation.
int extendedStartIndex = startIndex; int extendedStartIndex = startIndex;
for (int i = extendedEndIndex; i > startIndex; i--) for (int i = extendedEndIndex; i > startIndex; i--)
{ {
int n = i; int n = i;
@ -138,6 +141,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
if (objectN is Slider && Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance) if (objectN is Slider && Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
{ {
int offset = objectI.StackHeight - objectN.StackHeight + 1; int offset = objectI.StackHeight - objectN.StackHeight + 1;
for (int j = n + 1; j <= i; j++) for (int j = n + 1; j <= i; j++)
{ {
//For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above). //For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above).

View File

@ -109,6 +109,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
double approachRateFactor = 1.0f; double approachRateFactor = 1.0f;
if (Attributes.ApproachRate > 10.33f) if (Attributes.ApproachRate > 10.33f)
approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f); approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
else if (Attributes.ApproachRate < 8.0f) else if (Attributes.ApproachRate < 8.0f)

View File

@ -56,6 +56,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
{ {
// We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps. // We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps.
float scalingFactor = normalized_radius / (float)BaseObject.Radius; float scalingFactor = normalized_radius / (float)BaseObject.Radius;
if (BaseObject.Radius < 30) if (BaseObject.Radius < 30)
{ {
float smallCircleBonus = Math.Min(30 - (float)BaseObject.Radius, 5) / 50; float smallCircleBonus = Math.Min(30 - (float)BaseObject.Radius, 5) / 50;

View File

@ -42,9 +42,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2); speedBonus = 1 + Math.Pow((min_speed_bonus - deltaTime) / speed_balancing_factor, 2);
double angleBonus = 1.0; double angleBonus = 1.0;
if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin) if (osuCurrent.Angle != null && osuCurrent.Angle.Value < angle_bonus_begin)
{ {
angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - osuCurrent.Angle.Value)), 2) / 3.57; angleBonus = 1 + Math.Pow(Math.Sin(1.5 * (angle_bonus_begin - osuCurrent.Angle.Value)), 2) / 3.57;
if (osuCurrent.Angle.Value < pi_over_2) if (osuCurrent.Angle.Value < pi_over_2)
{ {
angleBonus = 1.28; angleBonus = 1.28;

View File

@ -37,6 +37,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
case SliderPosition.Start: case SliderPosition.Start:
Position = slider.StackedPosition + slider.Path.PositionAt(0); Position = slider.StackedPosition + slider.Path.PositionAt(0);
break; break;
case SliderPosition.End: case SliderPosition.End:
Position = slider.StackedPosition + slider.Path.PositionAt(1); Position = slider.StackedPosition + slider.Path.PositionAt(1);
break; break;

View File

@ -62,6 +62,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
case PlacementState.Initial: case PlacementState.Initial:
HitObject.Position = e.MousePosition; HitObject.Position = e.MousePosition;
return true; return true;
case PlacementState.Body: case PlacementState.Body:
cursor = e.MousePosition - HitObject.Position; cursor = e.MousePosition - HitObject.Position;
return true; return true;
@ -77,6 +78,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
case PlacementState.Initial: case PlacementState.Initial:
beginCurve(); beginCurve();
break; break;
case PlacementState.Body: case PlacementState.Body:
switch (e.Button) switch (e.Button)
{ {

View File

@ -42,8 +42,10 @@ namespace osu.Game.Rulesets.Osu.Edit
{ {
case DrawableHitCircle circle: case DrawableHitCircle circle:
return new HitCircleSelectionBlueprint(circle); return new HitCircleSelectionBlueprint(circle);
case DrawableSlider slider: case DrawableSlider slider:
return new SliderSelectionBlueprint(slider); return new SliderSelectionBlueprint(slider);
case DrawableSpinner spinner: case DrawableSpinner spinner:
return new SpinnerSelectionBlueprint(spinner); return new SpinnerSelectionBlueprint(spinner);
} }

View File

@ -16,10 +16,13 @@ namespace osu.Game.Rulesets.Osu.Judgements
{ {
default: default:
return 0; return 0;
case HitResult.Meh: case HitResult.Meh:
return 50; return 50;
case HitResult.Good: case HitResult.Good:
return 100; return 100;
case HitResult.Great: case HitResult.Great:
return 300; return 300;
} }
@ -31,10 +34,12 @@ namespace osu.Game.Rulesets.Osu.Judgements
{ {
case HitResult.Miss: case HitResult.Miss:
return -0.02; return -0.02;
case HitResult.Meh: case HitResult.Meh:
case HitResult.Good: case HitResult.Good:
case HitResult.Great: case HitResult.Great:
return 0.01; return 0.01;
default: default:
return 0; return 0;
} }

View File

@ -1,23 +1,37 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Mods namespace osu.Game.Rulesets.Osu.Mods
{ {
public class OsuModFlashlight : ModFlashlight<OsuHitObject> public class OsuModFlashlight : ModFlashlight<OsuHitObject>, IApplicableToDrawableHitObjects
{ {
public override double ScoreMultiplier => 1.12; public override double ScoreMultiplier => 1.12;
private const float default_flashlight_size = 180; private const float default_flashlight_size = 180;
public override Flashlight CreateFlashlight() => new OsuFlashlight(); private OsuFlashlight flashlight;
public override Flashlight CreateFlashlight() => flashlight = new OsuFlashlight();
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
foreach (var s in drawables.OfType<DrawableSlider>())
{
s.Tracking.ValueChanged += flashlight.OnSliderTrackingChange;
}
}
private class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition private class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition
{ {
@ -26,6 +40,12 @@ namespace osu.Game.Rulesets.Osu.Mods
FlashlightSize = new Vector2(0, getSizeFor(0)); FlashlightSize = new Vector2(0, getSizeFor(0));
} }
public void OnSliderTrackingChange(ValueChangedEvent<bool> e)
{
// If a slider is in a tracking state, a further dim should be applied to the (remaining) visible portion of the playfield over a brief duration.
this.TransformTo(nameof(FlashlightDim), e.NewValue ? 0.8f : 0.0f, 50);
}
protected override bool OnMouseMove(MouseMoveEvent e) protected override bool OnMouseMove(MouseMoveEvent e)
{ {
FlashlightPosition = e.MousePosition; FlashlightPosition = e.MousePosition;

View File

@ -33,6 +33,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
case DrawableSpinner _: case DrawableSpinner _:
continue; continue;
default: default:
drawable.ApplyCustomUpdateState += ApplyCustomState; drawable.ApplyCustomUpdateState += ApplyCustomState;
break; break;
@ -51,6 +52,7 @@ namespace osu.Game.Rulesets.Osu.Mods
case DrawableSliderTail _: case DrawableSliderTail _:
// special cases we should *not* be scaling. // special cases we should *not* be scaling.
break; break;
case DrawableSlider _: case DrawableSlider _:
case DrawableHitCircle _: case DrawableHitCircle _:
{ {

View File

@ -59,11 +59,13 @@ namespace osu.Game.Rulesets.Osu.Mods
circle.FadeOut(fadeOutDuration); circle.FadeOut(fadeOutDuration);
break; break;
case DrawableSlider slider: case DrawableSlider slider:
using (slider.BeginAbsoluteSequence(fadeOutStartTime, true)) using (slider.BeginAbsoluteSequence(fadeOutStartTime, true))
slider.Body.FadeOut(longFadeDuration, Easing.Out); slider.Body.FadeOut(longFadeDuration, Easing.Out);
break; break;
case DrawableSliderTick sliderTick: case DrawableSliderTick sliderTick:
// slider ticks fade out over up to one second // slider ticks fade out over up to one second
var tickFadeOutDuration = Math.Min(sliderTick.HitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000); var tickFadeOutDuration = Math.Min(sliderTick.HitObject.TimePreempt - DrawableSliderTick.ANIM_DURATION, 1000);
@ -72,6 +74,7 @@ namespace osu.Game.Rulesets.Osu.Mods
sliderTick.FadeOut(tickFadeOutDuration); sliderTick.FadeOut(tickFadeOutDuration);
break; break;
case DrawableSpinner spinner: case DrawableSpinner spinner:
// hide elements we don't care about. // hide elements we don't care about.
spinner.Disc.Hide(); spinner.Disc.Hide();

View File

@ -67,6 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
return; return;
OsuHitObject prevHitObject = null; OsuHitObject prevHitObject = null;
foreach (var currHitObject in hitObjects) foreach (var currHitObject in hitObjects)
{ {
if (prevHitObject != null && !currHitObject.NewCombo && !(prevHitObject is Spinner) && !(currHitObject is Spinner)) if (prevHitObject != null && !currHitObject.NewCombo && !(prevHitObject is Spinner) && !(currHitObject is Spinner))

View File

@ -124,6 +124,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
} }
var result = HitObject.HitWindows.ResultFor(timeOffset); var result = HitObject.HitWindows.ResultFor(timeOffset);
if (result == HitResult.None) if (result == HitResult.None)
{ {
Shake(Math.Abs(timeOffset) - HitObject.HitWindows.HalfWindowFor(HitResult.Miss)); Shake(Math.Abs(timeOffset) - HitObject.HitWindows.HalfWindowFor(HitResult.Miss));
@ -158,11 +159,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early. // override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early.
LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss); LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss);
break; break;
case ArmedState.Miss: case ArmedState.Miss:
ApproachCircle.FadeOut(50); ApproachCircle.FadeOut(50);
this.FadeOut(100); this.FadeOut(100);
Expire(); Expire();
break; break;
case ArmedState.Hit: case ArmedState.Hit:
ApproachCircle.FadeOut(50); ApproachCircle.FadeOut(50);

View File

@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (repeatPoint.StartTime <= Time.Current) if (repeatPoint.StartTime <= Time.Current)
ApplyResult(r => r.Type = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss); ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? HitResult.Great : HitResult.Miss);
} }
protected override void UpdatePreemptState() protected override void UpdatePreemptState()
@ -64,9 +64,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
case ArmedState.Idle: case ArmedState.Idle:
this.Delay(HitObject.TimePreempt).FadeOut(); this.Delay(HitObject.TimePreempt).FadeOut();
break; break;
case ArmedState.Miss: case ArmedState.Miss:
this.FadeOut(animDuration); this.FadeOut(animDuration);
break; break;
case ArmedState.Hit: case ArmedState.Hit:
this.FadeOut(animDuration, Easing.OutQuint) this.FadeOut(animDuration, Easing.OutQuint)
.ScaleTo(Scale * 1.5f, animDuration, Easing.Out); .ScaleTo(Scale * 1.5f, animDuration, Easing.Out);

View File

@ -130,13 +130,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
} }
} }
public bool Tracking; public readonly Bindable<bool> Tracking = new Bindable<bool>();
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
Tracking = Ball.Tracking; Tracking.Value = Ball.Tracking;
double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);

View File

@ -67,10 +67,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
case ArmedState.Idle: case ArmedState.Idle:
this.Delay(HitObject.TimePreempt).FadeOut(); this.Delay(HitObject.TimePreempt).FadeOut();
break; break;
case ArmedState.Miss: case ArmedState.Miss:
this.FadeOut(ANIM_DURATION); this.FadeOut(ANIM_DURATION);
this.FadeColour(Color4.Red, ANIM_DURATION / 2); this.FadeColour(Color4.Red, ANIM_DURATION / 2);
break; break;
case ArmedState.Hit: case ArmedState.Hit:
this.FadeOut(ANIM_DURATION, Easing.OutQuint); this.FadeOut(ANIM_DURATION, Easing.OutQuint);
this.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out); this.ScaleTo(Scale * 1.5f, ANIM_DURATION, Easing.Out);

View File

@ -222,9 +222,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
case ArmedState.Idle: case ArmedState.Idle:
Expire(true); Expire(true);
break; break;
case ArmedState.Hit: case ArmedState.Hit:
sequence.ScaleTo(Scale * 1.2f, 320, Easing.Out); sequence.ScaleTo(Scale * 1.2f, 320, Easing.Out);
break; break;
case ArmedState.Miss: case ArmedState.Miss:
sequence.ScaleTo(Scale * 0.8f, 320, Easing.In); sequence.ScaleTo(Scale * 0.8f, 320, Easing.In);
break; break;

View File

@ -183,6 +183,7 @@ namespace osu.Game.Rulesets.Osu.Objects
Samples = sampleList Samples = sampleList
}); });
break; break;
case SliderEventType.Head: case SliderEventType.Head:
AddNested(HeadCircle = new SliderCircle AddNested(HeadCircle = new SliderCircle
{ {
@ -194,6 +195,7 @@ namespace osu.Game.Rulesets.Osu.Objects
ComboIndex = ComboIndex, ComboIndex = ComboIndex,
}); });
break; break;
case SliderEventType.LegacyLastTick: case SliderEventType.LegacyLastTick:
// we need to use the LegacyLastTick here for compatibility reasons (difficulty). // we need to use the LegacyLastTick here for compatibility reasons (difficulty).
// it is *okay* to use this because the TailCircle is not used for any meaningful purpose in gameplay. // it is *okay* to use this because the TailCircle is not used for any meaningful purpose in gameplay.
@ -206,6 +208,7 @@ namespace osu.Game.Rulesets.Osu.Objects
ComboIndex = ComboIndex, ComboIndex = ComboIndex,
}); });
break; break;
case SliderEventType.Repeat: case SliderEventType.Repeat:
AddNested(new RepeatPoint AddNested(new RepeatPoint
{ {

View File

@ -104,6 +104,7 @@ namespace osu.Game.Rulesets.Osu
new MultiMod(new OsuModHalfTime(), new OsuModDaycore()), new MultiMod(new OsuModHalfTime(), new OsuModDaycore()),
new OsuModSpunOut(), new OsuModSpunOut(),
}; };
case ModType.DifficultyIncrease: case ModType.DifficultyIncrease:
return new Mod[] return new Mod[]
{ {
@ -113,11 +114,13 @@ namespace osu.Game.Rulesets.Osu
new OsuModHidden(), new OsuModHidden(),
new MultiMod(new OsuModFlashlight(), new OsuModBlinds()), new MultiMod(new OsuModFlashlight(), new OsuModBlinds()),
}; };
case ModType.Conversion: case ModType.Conversion:
return new Mod[] return new Mod[]
{ {
new OsuModTarget(), new OsuModTarget(),
}; };
case ModType.Automation: case ModType.Automation:
return new Mod[] return new Mod[]
{ {
@ -125,6 +128,7 @@ namespace osu.Game.Rulesets.Osu
new OsuModRelax(), new OsuModRelax(),
new OsuModAutopilot(), new OsuModAutopilot(),
}; };
case ModType.Fun: case ModType.Fun:
return new Mod[] return new Mod[]
{ {
@ -133,6 +137,7 @@ namespace osu.Game.Rulesets.Osu
new OsuModGrow(), new OsuModGrow(),
new MultiMod(new ModWindUp<OsuHitObject>(), new ModWindDown<OsuHitObject>()), new MultiMod(new ModWindUp<OsuHitObject>(), new ModWindDown<OsuHitObject>()),
}; };
default: default:
return new Mod[] { }; return new Mod[] { };
} }

View File

@ -199,6 +199,7 @@ namespace osu.Game.Rulesets.Osu.Replays
// Wait until Auto could "see and react" to the next note. // Wait until Auto could "see and react" to the next note.
double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime); double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime);
if (waitTime > lastFrame.Time) if (waitTime > lastFrame.Time)
{ {
lastFrame = new OsuReplayFrame(waitTime, lastFrame.Position) { Actions = lastFrame.Actions }; lastFrame = new OsuReplayFrame(waitTime, lastFrame.Position) { Actions = lastFrame.Actions };
@ -314,6 +315,7 @@ namespace osu.Game.Rulesets.Osu.Replays
endFrame.Position = endPosition; endFrame.Position = endPosition;
break; break;
case Slider slider: case Slider slider:
for (double j = FrameDelay; j < slider.Duration; j += FrameDelay) for (double j = FrameDelay; j < slider.Duration; j += FrameDelay)
{ {

View File

@ -195,6 +195,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time); shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time);
int updateStart = -1, updateEnd = 0; int updateStart = -1, updateEnd = 0;
for (int i = 0; i < parts.Length; ++i) for (int i = 0; i < parts.Length; ++i)
{ {
if (parts[i].WasUpdated) if (parts[i].WasUpdated)

View File

@ -46,8 +46,10 @@ namespace osu.Game.Rulesets.Osu.UI
{ {
case HitCircle circle: case HitCircle circle:
return new DrawableHitCircle(circle); return new DrawableHitCircle(circle);
case Slider slider: case Slider slider:
return new DrawableSlider(slider); return new DrawableSlider(slider);
case Spinner spinner: case Spinner spinner:
return new DrawableSpinner(spinner); return new DrawableSpinner(spinner);
} }

View File

@ -101,15 +101,19 @@ namespace osu.Game.Rulesets.Taiko.Tests
case 1: case 1:
addCentreHit(false); addCentreHit(false);
break; break;
case 2: case 2:
addCentreHit(true); addCentreHit(true);
break; break;
case 3: case 3:
addDrumRoll(false); addDrumRoll(false);
break; break;
case 4: case 4:
addDrumRoll(true); addDrumRoll(true);
break; break;
case 5: case 5:
addSwell(); addSwell();
delay = scroll_time - 100; delay = scroll_time - 100;
@ -122,6 +126,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
default: default:
playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, rng.Next(25, 400)), 500); playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, rng.Next(25, 400)), 500);
break; break;
case 6: case 6:
playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_HEIGHT), 500); playfieldContainer.Delay(delay).ResizeTo(new Vector2(1, TaikoPlayfield.DEFAULT_HEIGHT), 500);
break; break;

View File

@ -120,6 +120,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
List<List<SampleInfo>> allSamples = curveData != null ? curveData.NodeSamples : new List<List<SampleInfo>>(new[] { samples }); List<List<SampleInfo>> allSamples = curveData != null ? curveData.NodeSamples : new List<List<SampleInfo>>(new[] { samples });
int i = 0; int i = 0;
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing) for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
{ {
List<SampleInfo> currentSamples = allSamples[i]; List<SampleInfo> currentSamples = allSamples[i];

View File

@ -16,6 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{ {
case HitResult.Miss: case HitResult.Miss:
return 0; return 0;
default: default:
return base.HealthIncreaseFor(result); return base.HealthIncreaseFor(result);
} }

View File

@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{ {
case HitResult.Great: case HitResult.Great:
return 200; return 200;
default: default:
return 0; return 0;
} }
@ -26,6 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{ {
case HitResult.Great: case HitResult.Great:
return 0.15; return 0.15;
default: default:
return 0; return 0;
} }

View File

@ -16,8 +16,10 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{ {
case HitResult.Good: case HitResult.Good:
return 100; return 100;
case HitResult.Great: case HitResult.Great:
return 300; return 300;
default: default:
return 0; return 0;
} }
@ -29,10 +31,13 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{ {
case HitResult.Miss: case HitResult.Miss:
return -1.0; return -1.0;
case HitResult.Good: case HitResult.Good:
return 1.1; return 1.1;
case HitResult.Great: case HitResult.Great:
return 3.0; return 3.0;
default: default:
return 0; return 0;
} }

View File

@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{ {
case HitResult.Miss: case HitResult.Miss:
return -0.65; return -0.65;
default: default:
return 0; return 0;
} }

View File

@ -98,6 +98,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
circlePiece?.FlashBox.FinishTransforms(); circlePiece?.FlashBox.FinishTransforms();
var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime; var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime;
using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true)) using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true))
{ {
switch (State.Value) switch (State.Value)
@ -108,15 +109,18 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
UnproxyContent(); UnproxyContent();
this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire(); this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire();
break; break;
case ArmedState.Miss: case ArmedState.Miss:
this.FadeOut(100) this.FadeOut(100)
.Expire(); .Expire();
break; break;
case ArmedState.Hit: case ArmedState.Hit:
// If we're far enough away from the left stage, we should bring outselves in front of it // If we're far enough away from the left stage, we should bring outselves in front of it
ProxyContent(); ProxyContent();
var flash = circlePiece?.FlashBox; var flash = circlePiece?.FlashBox;
if (flash != null) if (flash != null)
{ {
flash.FadeTo(0.9f); flash.FadeTo(0.9f);

View File

@ -192,6 +192,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
using (BeginAbsoluteSequence(HitObject.StartTime - preempt, true)) using (BeginAbsoluteSequence(HitObject.StartTime - preempt, true))
targetRing.ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint); targetRing.ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint);
break; break;
case ArmedState.Miss: case ArmedState.Miss:
case ArmedState.Hit: case ArmedState.Hit:
this.FadeOut(out_transition_time, Easing.Out); this.FadeOut(out_transition_time, Easing.Out);

View File

@ -111,6 +111,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
MainPiece.KiaiMode = HitObject.Kiai; MainPiece.KiaiMode = HitObject.Kiai;
var strongObject = HitObject.NestedHitObjects.OfType<StrongHitObject>().FirstOrDefault(); var strongObject = HitObject.NestedHitObjects.OfType<StrongHitObject>().FirstOrDefault();
if (strongObject != null) if (strongObject != null)
{ {
var strongHit = CreateStrongHit(strongObject); var strongHit = CreateStrongHit(strongObject);

View File

@ -70,6 +70,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
return; return;
bool first = true; bool first = true;
for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing) for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing)
{ {
AddNested(new DrumRollTick AddNested(new DrumRollTick

View File

@ -25,6 +25,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
case HitResult.Good: case HitResult.Good:
case HitResult.Miss: case HitResult.Miss:
return true; return true;
default: default:
return false; return false;
} }

View File

@ -52,6 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
int count = 0; int count = 0;
int req = swell.RequiredHits; int req = swell.RequiredHits;
double hitRate = Math.Min(swell_hit_speed, swell.Duration / req); double hitRate = Math.Min(swell_hit_speed, swell.Duration / req);
for (double j = h.StartTime; j < endTime; j += hitRate) for (double j = h.StartTime; j < endTime; j += hitRate)
{ {
TaikoAction action; TaikoAction action;
@ -62,12 +63,15 @@ namespace osu.Game.Rulesets.Taiko.Replays
case 0: case 0:
action = TaikoAction.LeftCentre; action = TaikoAction.LeftCentre;
break; break;
case 1: case 1:
action = TaikoAction.LeftRim; action = TaikoAction.LeftRim;
break; break;
case 2: case 2:
action = TaikoAction.RightCentre; action = TaikoAction.RightCentre;
break; break;
case 3: case 3:
action = TaikoAction.RightRim; action = TaikoAction.RightRim;
break; break;

View File

@ -86,6 +86,7 @@ namespace osu.Game.Rulesets.Taiko
new TaikoModNoFail(), new TaikoModNoFail(),
new MultiMod(new TaikoModHalfTime(), new TaikoModDaycore()), new MultiMod(new TaikoModHalfTime(), new TaikoModDaycore()),
}; };
case ModType.DifficultyIncrease: case ModType.DifficultyIncrease:
return new Mod[] return new Mod[]
{ {
@ -95,17 +96,20 @@ namespace osu.Game.Rulesets.Taiko
new TaikoModHidden(), new TaikoModHidden(),
new TaikoModFlashlight(), new TaikoModFlashlight(),
}; };
case ModType.Automation: case ModType.Automation:
return new Mod[] return new Mod[]
{ {
new MultiMod(new TaikoModAutoplay(), new ModCinema()), new MultiMod(new TaikoModAutoplay(), new ModCinema()),
new TaikoModRelax(), new TaikoModRelax(),
}; };
case ModType.Fun: case ModType.Fun:
return new Mod[] return new Mod[]
{ {
new MultiMod(new ModWindUp<TaikoHitObject>(), new ModWindDown<TaikoHitObject>()) new MultiMod(new ModWindUp<TaikoHitObject>(), new ModWindDown<TaikoHitObject>())
}; };
default: default:
return new Mod[] { }; return new Mod[] { };
} }

View File

@ -33,6 +33,7 @@ namespace osu.Game.Rulesets.Taiko.UI
case HitResult.Good: case HitResult.Good:
JudgementBody.Colour = colours.GreenLight; JudgementBody.Colour = colours.GreenLight;
break; break;
case HitResult.Great: case HitResult.Great:
JudgementBody.Colour = colours.BlueLight; JudgementBody.Colour = colours.BlueLight;
break; break;

View File

@ -54,9 +54,11 @@ namespace osu.Game.Rulesets.Taiko.UI
int currentIndex = 0; int currentIndex = 0;
int currentBeat = 0; int currentBeat = 0;
double time = timingPoints[currentIndex].Time; double time = timingPoints[currentIndex].Time;
while (time <= lastHitTime) while (time <= lastHitTime)
{ {
int nextIndex = currentIndex + 1; int nextIndex = currentIndex + 1;
if (nextIndex < timingPoints.Count && time > timingPoints[nextIndex].Time) if (nextIndex < timingPoints.Count && time > timingPoints[nextIndex].Time)
{ {
currentIndex = nextIndex; currentIndex = nextIndex;
@ -95,10 +97,13 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
case CentreHit centreHit: case CentreHit centreHit:
return new DrawableCentreHit(centreHit); return new DrawableCentreHit(centreHit);
case RimHit rimHit: case RimHit rimHit:
return new DrawableRimHit(rimHit); return new DrawableRimHit(rimHit);
case DrumRoll drumRoll: case DrumRoll drumRoll:
return new DrawableDrumRoll(drumRoll); return new DrawableDrumRoll(drumRoll);
case Swell swell: case Swell swell:
return new DrawableSwell(swell); return new DrawableSwell(swell);
} }

View File

@ -212,6 +212,7 @@ namespace osu.Game.Rulesets.Taiko.UI
case DrawableBarLine barline: case DrawableBarLine barline:
barlineContainer.Add(barline.CreateProxy()); barlineContainer.Add(barline.CreateProxy());
break; break;
case DrawableTaikoHitObject taikoObject: case DrawableTaikoHitObject taikoObject:
topLevelHitContainer.Add(taikoObject.CreateProxiedContent()); topLevelHitContainer.Add(taikoObject.CreateProxiedContent());
break; break;
@ -232,6 +233,7 @@ namespace osu.Game.Rulesets.Taiko.UI
if (result.IsHit) if (result.IsHit)
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit(); hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit();
break; break;
default: default:
judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject) judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject)
{ {

View File

@ -49,6 +49,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapGeneral() public void TestDecodeBeatmapGeneral()
{ {
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -72,6 +73,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapEditor() public void TestDecodeBeatmapEditor()
{ {
var decoder = new LegacyBeatmapDecoder(); var decoder = new LegacyBeatmapDecoder();
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -97,6 +99,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapMetadata() public void TestDecodeBeatmapMetadata()
{ {
var decoder = new LegacyBeatmapDecoder(); var decoder = new LegacyBeatmapDecoder();
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -121,6 +124,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapDifficulty() public void TestDecodeBeatmapDifficulty()
{ {
var decoder = new LegacyBeatmapDecoder(); var decoder = new LegacyBeatmapDecoder();
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -139,6 +143,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapEvents() public void TestDecodeBeatmapEvents()
{ {
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -157,6 +162,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapTimingPoints() public void TestDecodeBeatmapTimingPoints()
{ {
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -192,6 +198,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapColours() public void TestDecodeBeatmapColours()
{ {
var decoder = new LegacySkinDecoder(); var decoder = new LegacySkinDecoder();
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -217,6 +224,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapComboOffsetsOsu() public void TestDecodeBeatmapComboOffsetsOsu()
{ {
var decoder = new LegacyBeatmapDecoder(); var decoder = new LegacyBeatmapDecoder();
using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -239,6 +247,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapComboOffsetsCatch() public void TestDecodeBeatmapComboOffsetsCatch()
{ {
var decoder = new LegacyBeatmapDecoder(); var decoder = new LegacyBeatmapDecoder();
using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu")) using (var resStream = TestResources.OpenResource("hitobject-combo-offset.osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -261,6 +270,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeBeatmapHitObjects() public void TestDecodeBeatmapHitObjects()
{ {
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var resStream = TestResources.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -288,6 +298,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeControlPointCustomSampleBank() public void TestDecodeControlPointCustomSampleBank()
{ {
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("controlpoint-custom-samplebank.osu")) using (var resStream = TestResources.OpenResource("controlpoint-custom-samplebank.osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -309,6 +320,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeHitObjectCustomSampleBank() public void TestDecodeHitObjectCustomSampleBank()
{ {
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("hitobject-custom-samplebank.osu")) using (var resStream = TestResources.OpenResource("hitobject-custom-samplebank.osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -326,6 +338,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeHitObjectFileSamples() public void TestDecodeHitObjectFileSamples()
{ {
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("hitobject-file-samples.osu")) using (var resStream = TestResources.OpenResource("hitobject-file-samples.osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -345,6 +358,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeSliderSamples() public void TestDecodeSliderSamples()
{ {
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("slider-samples.osu")) using (var resStream = TestResources.OpenResource("slider-samples.osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -388,6 +402,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeHitObjectNullAdditionBank() public void TestDecodeHitObjectNullAdditionBank()
{ {
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("hitobject-no-addition-bank.osu")) using (var resStream = TestResources.OpenResource("hitobject-no-addition-bank.osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {

View File

@ -19,6 +19,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeStoryboardEvents() public void TestDecodeStoryboardEvents()
{ {
var decoder = new LegacyStoryboardDecoder(); var decoder = new LegacyStoryboardDecoder();
using (var resStream = TestResources.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu")) using (var resStream = TestResources.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {
@ -91,6 +92,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
public void TestDecodeVariableWithSuffix() public void TestDecodeVariableWithSuffix()
{ {
var decoder = new LegacyStoryboardDecoder(); var decoder = new LegacyStoryboardDecoder();
using (var resStream = TestResources.OpenResource("variable-with-suffix.osb")) using (var resStream = TestResources.OpenResource("variable-with-suffix.osb"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {

View File

@ -151,6 +151,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var sr = new StreamReader(stream)) using (var sr = new StreamReader(stream))
{ {
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms)) using (var sw = new StreamWriter(ms))
using (var sr2 = new StreamReader(ms)) using (var sr2 = new StreamReader(ms))

View File

@ -75,6 +75,7 @@ namespace osu.Game.Tests.Beatmaps.IO
using (var osz = TestResources.GetTestBeatmapStream()) using (var osz = TestResources.GetTestBeatmapStream())
{ {
var reader = new ZipArchiveReader(osz); var reader = new ZipArchiveReader(osz);
using (var stream = new StreamReader( using (var stream = new StreamReader(
reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
{ {

View File

@ -18,6 +18,7 @@ namespace osu.Game.Tests.Skins
public void TestDecodeSkinColours(bool hasColours) public void TestDecodeSkinColours(bool hasColours)
{ {
var decoder = new LegacySkinDecoder(); var decoder = new LegacySkinDecoder();
using (var resStream = TestResources.OpenResource(hasColours ? "skin.ini" : "skin-empty.ini")) using (var resStream = TestResources.OpenResource(hasColours ? "skin.ini" : "skin-empty.ini"))
using (var stream = new StreamReader(resStream)) using (var stream = new StreamReader(resStream))
{ {

View File

@ -328,7 +328,7 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR); public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
} }
private class TestPlayer : Player private class TestPlayer : Visual.TestPlayer
{ {
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Framework.Testing;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Screens.Edit.Compose.Components.Timeline; using osu.Game.Screens.Edit.Compose.Components.Timeline;
@ -18,38 +19,49 @@ namespace osu.Game.Tests.Visual.Editor
{ {
public class TestCaseZoomableScrollContainer : ManualInputManagerTestCase public class TestCaseZoomableScrollContainer : ManualInputManagerTestCase
{ {
private readonly ZoomableScrollContainer scrollContainer; private ZoomableScrollContainer scrollContainer;
private readonly Drawable innerBox; private Drawable innerBox;
public TestCaseZoomableScrollContainer() [SetUpSteps]
public void SetUpSteps()
{ {
Children = new Drawable[] AddStep("Add new scroll container", () =>
{ {
new Container Children = new Drawable[]
{ {
Anchor = Anchor.Centre, new Container
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = 250,
Width = 0.75f,
Children = new Drawable[]
{ {
new Box Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = 250,
Width = 0.75f,
Children = new Drawable[]
{ {
RelativeSizeAxes = Axes.Both, new Box
Colour = OsuColour.Gray(30) {
}, RelativeSizeAxes = Axes.Both,
scrollContainer = new ZoomableScrollContainer { RelativeSizeAxes = Axes.Both } Colour = OsuColour.Gray(30)
} },
}, scrollContainer = new ZoomableScrollContainer { RelativeSizeAxes = Axes.Both }
new MenuCursor() }
}; },
new MenuCursor()
};
scrollContainer.Add(innerBox = new Box scrollContainer.Add(innerBox = new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(new Color4(0.8f, 0.6f, 0.4f, 1f), new Color4(0.4f, 0.6f, 0.8f, 1f)) Colour = ColourInfo.GradientHorizontal(new Color4(0.8f, 0.6f, 0.4f, 1f), new Color4(0.4f, 0.6f, 0.8f, 1f))
});
}); });
AddUntilStep("Scroll container is loaded", () => scrollContainer.LoadState >= LoadState.Loaded);
}
[Test]
public void TestWidthInitialization()
{
AddAssert("Inner container width was initialized", () => innerBox.DrawWidth > 0);
} }
[Test] [Test]

View File

@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0)); AddUntilStep("key counter counted keys", () => ((ScoreAccessiblePlayer)Player).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0));
} }
private class ScoreAccessiblePlayer : Player private class ScoreAccessiblePlayer : TestPlayer
{ {
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay; public new HUDOverlay HUDOverlay => base.HUDOverlay;

View File

@ -73,6 +73,26 @@ namespace osu.Game.Tests.Visual.Gameplay
checkFrameCount(2); checkFrameCount(2);
} }
[Test]
public void TestInitialSeekWithGameplayStart()
{
seekManualTo(1000);
createStabilityContainer(30000);
confirmSeek(1000);
checkFrameCount(0);
seekManualTo(10000);
confirmSeek(10000);
checkFrameCount(1);
seekManualTo(130000);
confirmSeek(130000);
checkFrameCount(6002);
}
[Test] [Test]
public void TestInitialSeek() public void TestInitialSeek()
{ {
@ -83,7 +103,11 @@ namespace osu.Game.Tests.Visual.Gameplay
checkFrameCount(0); checkFrameCount(0);
} }
private void createStabilityContainer() => AddStep("create container", () => mainContainer.Child = new FrameStabilityContainer().WithChild(consumer = new ClockConsumingChild())); private const int max_frames_catchup = 50;
private void createStabilityContainer(double gameplayStartTime = double.MinValue) => AddStep("create container", () =>
mainContainer.Child = new FrameStabilityContainer(gameplayStartTime) { MaxCatchUpFrames = max_frames_catchup }
.WithChild(consumer = new ClockConsumingChild()));
private void seekManualTo(double time) => AddStep($"seek manual clock to {time}", () => manualClock.CurrentTime = time); private void seekManualTo(double time) => AddStep($"seek manual clock to {time}", () => manualClock.CurrentTime = time);

View File

@ -6,6 +6,7 @@ using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -31,6 +32,14 @@ namespace osu.Game.Tests.Visual.Gameplay
base.Content.Add(content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }); base.Content.Add(content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both });
} }
[SetUpSteps]
public override void SetUpSteps()
{
base.SetUpSteps();
AddStep("resume player", () => Player.GameplayClockContainer.Start());
confirmClockRunning(true);
}
[Test] [Test]
public void TestPauseResume() public void TestPauseResume()
{ {
@ -157,6 +166,8 @@ namespace osu.Game.Tests.Visual.Gameplay
private void confirmPaused() private void confirmPaused()
{ {
confirmClockRunning(false); confirmClockRunning(false);
AddAssert("player not exited", () => Player.IsCurrentScreen());
AddAssert("player not failed", () => !Player.HasFailed);
AddAssert("pause overlay shown", () => Player.PauseOverlayVisible); AddAssert("pause overlay shown", () => Player.PauseOverlayVisible);
} }
@ -184,7 +195,7 @@ namespace osu.Game.Tests.Visual.Gameplay
protected override Player CreatePlayer(Ruleset ruleset) => new PausePlayer(); protected override Player CreatePlayer(Ruleset ruleset) => new PausePlayer();
protected class PausePlayer : Player protected class PausePlayer : TestPlayer
{ {
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
@ -196,9 +207,10 @@ namespace osu.Game.Tests.Visual.Gameplay
public bool PauseOverlayVisible => PauseOverlay.State == Visibility.Visible; public bool PauseOverlayVisible => PauseOverlay.State == Visibility.Visible;
public PausePlayer() public override void OnEntering(IScreen last)
{ {
PauseOnFocusLost = false; base.OnEntering(last);
GameplayClockContainer.Stop();
} }
} }
} }

View File

@ -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 System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -34,20 +35,20 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestLoadContinuation() public void TestLoadContinuation()
{ {
AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player(false, false)))); Player player = null;
SlowLoadPlayer slowPlayer = null;
AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => player = new TestPlayer(false, false))));
AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddUntilStep("wait for current", () => loader.IsCurrentScreen());
AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen()); AddUntilStep("wait for player to be current", () => player.IsCurrentScreen());
AddStep("load slow dummy beatmap", () => AddStep("load slow dummy beatmap", () =>
{ {
SlowLoadPlayer slow = null; stack.Push(loader = new PlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false)));
Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000);
stack.Push(loader = new PlayerLoader(() => slow = new SlowLoadPlayer(false, false)));
Scheduler.AddDelayed(() => slow.Ready = true, 5000);
}); });
AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen()); AddUntilStep("wait for player to be current", () => slowPlayer.IsCurrentScreen());
} }
[Test] [Test]
@ -101,19 +102,19 @@ namespace osu.Game.Tests.Visual.Gameplay
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
} }
private class TestPlayer : Player private class TestPlayer : Visual.TestPlayer
{ {
public new Bindable<IReadOnlyList<Mod>> Mods => base.Mods; public new Bindable<IReadOnlyList<Mod>> Mods => base.Mods;
public TestPlayer() public TestPlayer(bool allowPause = true, bool showResults = true)
: base(false, false) : base(allowPause, showResults)
{ {
} }
} }
protected class SlowLoadPlayer : Player protected class SlowLoadPlayer : Visual.TestPlayer
{ {
public bool Ready; public readonly ManualResetEventSlim AllowLoad = new ManualResetEventSlim(false);
public SlowLoadPlayer(bool allowPause = true, bool showResults = true) public SlowLoadPlayer(bool allowPause = true, bool showResults = true)
: base(allowPause, showResults) : base(allowPause, showResults)
@ -123,8 +124,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
while (!Ready) if (!AllowLoad.Wait(TimeSpan.FromSeconds(10)))
Thread.Sleep(1); throw new TimeoutException();
} }
} }
} }

View File

@ -33,6 +33,8 @@ namespace osu.Game.Tests.Visual.Gameplay
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay; public new HUDOverlay HUDOverlay => base.HUDOverlay;
protected override bool PauseOnFocusLost => false;
public ScoreAccessibleReplayPlayer(Score score) public ScoreAccessibleReplayPlayer(Score score)
: base(score) : base(score)
{ {

View File

@ -129,12 +129,15 @@ namespace osu.Game.Tests.Visual.Gameplay
case ScrollingDirection.Up: case ScrollingDirection.Up:
obj.Anchor = Anchor.TopCentre; obj.Anchor = Anchor.TopCentre;
break; break;
case ScrollingDirection.Down: case ScrollingDirection.Down:
obj.Anchor = Anchor.BottomCentre; obj.Anchor = Anchor.BottomCentre;
break; break;
case ScrollingDirection.Left: case ScrollingDirection.Left:
obj.Anchor = Anchor.CentreLeft; obj.Anchor = Anchor.CentreLeft;
break; break;
case ScrollingDirection.Right: case ScrollingDirection.Right:
obj.Anchor = Anchor.CentreRight; obj.Anchor = Anchor.CentreRight;
break; break;
@ -189,6 +192,7 @@ namespace osu.Game.Tests.Visual.Gameplay
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = 2; Height = 2;
break; break;
case ScrollingDirection.Left: case ScrollingDirection.Left:
case ScrollingDirection.Right: case ScrollingDirection.Right:
RelativeSizeAxes = Axes.Y; RelativeSizeAxes = Axes.Y;

View File

@ -508,6 +508,7 @@ namespace osu.Game.Tests.Visual.SongSelect
}, },
Beatmaps = new List<BeatmapInfo>(), Beatmaps = new List<BeatmapInfo>(),
}; };
for (int b = 1; b < 101; b++) for (int b = 1; b < 101; b++)
{ {
toReturn.Beatmaps.Add(new BeatmapInfo toReturn.Beatmaps.Add(new BeatmapInfo

View File

@ -83,15 +83,19 @@ namespace osu.Game.Tests.Visual.SongSelect
case OsuRuleset _: case OsuRuleset _:
testInfoLabels(5); testInfoLabels(5);
break; break;
case TaikoRuleset _: case TaikoRuleset _:
testInfoLabels(5); testInfoLabels(5);
break; break;
case CatchRuleset _: case CatchRuleset _:
testInfoLabels(5); testInfoLabels(5);
break; break;
case ManiaRuleset _: case ManiaRuleset _:
testInfoLabels(4); testInfoLabels(4);
break; break;
default: default:
testInfoLabels(2); testInfoLabels(2);
break; break;

View File

@ -0,0 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Graphics.Sprites;
namespace osu.Game.Tests.Visual
{
public class TestCaseCharLookup : OsuTestCase
{
public TestCaseCharLookup()
{
AddStep("null", () => { });
AddStep("display acharacter", () => Add(new OsuSpriteText { Text = "振込申請" }));
}
}
}

View File

@ -86,12 +86,15 @@ namespace osu.Game.Tests.Visual.UserInterface
case 0: case 0:
sendHelloNotification(); sendHelloNotification();
break; break;
case 1: case 1:
sendAmazingNotification(); sendAmazingNotification();
break; break;
case 2: case 2:
sendUploadProgress(); sendUploadProgress();
break; break;
case 3: case 3:
sendDownloadProgress(); sendDownloadProgress();
break; break;

View File

@ -1,22 +1,27 @@
// 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.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Tests.Beatmaps.IO; using osu.Game.Tests.Beatmaps.IO;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestCaseUpdateableBeatmapBackgroundSprite : OsuTestCase public class TestCaseUpdateableBeatmapBackgroundSprite : OsuTestCase
{ {
private TestUpdateableBeatmapBackgroundSprite backgroundSprite; private BeatmapSetInfo testBeatmap;
private IAPIProvider api;
private RulesetStore rulesets;
[Resolved] [Resolved]
private BeatmapManager beatmaps { get; set; } private BeatmapManager beatmaps { get; set; }
@ -24,40 +29,119 @@ namespace osu.Game.Tests.Visual.UserInterface
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuGameBase osu, IAPIProvider api, RulesetStore rulesets) private void load(OsuGameBase osu, IAPIProvider api, RulesetStore rulesets)
{ {
Bindable<BeatmapInfo> beatmapBindable = new Bindable<BeatmapInfo>(); this.api = api;
this.rulesets = rulesets;
var imported = ImportBeatmapTest.LoadOszIntoOsu(osu); testBeatmap = ImportBeatmapTest.LoadOszIntoOsu(osu);
}
Child = backgroundSprite = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }; [Test]
public void TestNullBeatmap()
{
TestUpdateableBeatmapBackgroundSprite background = null;
backgroundSprite.Beatmap.BindTo(beatmapBindable); AddStep("load null beatmap", () => Child = background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both });
AddUntilStep("wait for load", () => background.ContentLoaded);
}
var req = new GetBeatmapSetRequest(1); [Test]
api.Queue(req); public void TestLocalBeatmap()
{
TestUpdateableBeatmapBackgroundSprite background = null;
AddStep("load null beatmap", () => beatmapBindable.Value = null); AddStep("load local beatmap", () =>
AddUntilStep("wait for cleanup...", () => backgroundSprite.ChildCount == 1); {
AddStep("load imported beatmap", () => beatmapBindable.Value = imported.Beatmaps.First()); Child = background = new TestUpdateableBeatmapBackgroundSprite
AddUntilStep("wait for cleanup...", () => backgroundSprite.ChildCount == 1); {
RelativeSizeAxes = Axes.Both,
Beatmap = { Value = testBeatmap.Beatmaps.First() }
};
});
AddUntilStep("wait for load", () => background.ContentLoaded);
}
[Test]
public void TestOnlineBeatmap()
{
if (api.IsLoggedIn) if (api.IsLoggedIn)
{ {
var req = new GetBeatmapSetRequest(1);
api.Queue(req);
AddUntilStep("wait for api response", () => req.Result != null); AddUntilStep("wait for api response", () => req.Result != null);
AddStep("load online beatmap", () => beatmapBindable.Value = new BeatmapInfo
TestUpdateableBeatmapBackgroundSprite background = null;
AddStep("load online beatmap", () =>
{ {
BeatmapSet = req.Result?.ToBeatmapSet(rulesets) Child = background = new TestUpdateableBeatmapBackgroundSprite
{
RelativeSizeAxes = Axes.Both,
Beatmap = { Value = new BeatmapInfo { BeatmapSet = req.Result?.ToBeatmapSet(rulesets) } }
};
}); });
AddUntilStep("wait for cleanup...", () => backgroundSprite.ChildCount == 1);
AddUntilStep("wait for load", () => background.ContentLoaded);
} }
else else
{
AddStep("online (login first)", () => { }); AddStep("online (login first)", () => { });
} }
[Test]
public void TestUnloadAndReload()
{
var backgrounds = new List<TestUpdateableBeatmapBackgroundSprite>();
ScrollContainer scrollContainer = null;
AddStep("create backgrounds hierarchy", () =>
{
FillFlowContainer backgroundFlow;
Child = scrollContainer = new ScrollContainer
{
Size = new Vector2(500),
Child = backgroundFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(10),
Padding = new MarginPadding { Bottom = 550 }
}
};
for (int i = 0; i < 25; i++)
{
var background = new TestUpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both };
if (i % 2 == 0)
background.Beatmap.Value = testBeatmap.Beatmaps.First();
backgroundFlow.Add(new Container
{
RelativeSizeAxes = Axes.X,
Height = 100,
Masking = true,
Child = background
});
backgrounds.Add(background);
}
});
var loadedBackgrounds = backgrounds.Where(b => b.ContentLoaded);
AddUntilStep("some loaded", () => loadedBackgrounds.Any());
AddStep("scroll to bottom", () => scrollContainer.ScrollToEnd());
AddUntilStep("all unloaded", () => !loadedBackgrounds.Any());
} }
private class TestUpdateableBeatmapBackgroundSprite : UpdateableBeatmapBackgroundSprite private class TestUpdateableBeatmapBackgroundSprite : UpdateableBeatmapBackgroundSprite
{ {
public int ChildCount => InternalChildren.Count; protected override double UnloadDelay => 2000;
public bool ContentLoaded => ((DelayedLoadUnloadWrapper)InternalChildren.LastOrDefault())?.Content?.IsLoaded ?? false;
} }
} }
} }

View File

@ -117,6 +117,7 @@ namespace osu.Game.Beatmaps
if (beatmapSet.OnlineBeatmapSetID != null) if (beatmapSet.OnlineBeatmapSetID != null)
{ {
var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID); var existingOnlineId = beatmaps.ConsumableItems.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID);
if (existingOnlineId != null) if (existingOnlineId != null)
{ {
Delete(existingOnlineId); Delete(existingOnlineId);
@ -325,6 +326,7 @@ namespace osu.Game.Beatmaps
{ {
// let's make sure there are actually .osu files to import. // let's make sure there are actually .osu files to import.
string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu")); string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu"));
if (string.IsNullOrEmpty(mapName)) if (string.IsNullOrEmpty(mapName))
{ {
Logger.Log($"No beatmap files found in the beatmap archive ({reader.Name}).", LoggingTarget.Database); Logger.Log($"No beatmap files found in the beatmap archive ({reader.Name}).", LoggingTarget.Database);

View File

@ -101,6 +101,7 @@ namespace osu.Game.Beatmaps
protected override Storyboard GetStoryboard() protected override Storyboard GetStoryboard()
{ {
Storyboard storyboard; Storyboard storyboard;
try try
{ {
using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path))))
@ -131,6 +132,7 @@ namespace osu.Game.Beatmaps
protected override Skin GetSkin() protected override Skin GetSkin()
{ {
Skin skin; Skin skin;
try try
{ {
skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager); skin = new LegacyBeatmapSkin(BeatmapInfo, store, audioManager);

View File

@ -65,7 +65,6 @@ namespace osu.Game.Beatmaps
protected override IQueryable<BeatmapSetInfo> AddIncludesForDeletion(IQueryable<BeatmapSetInfo> query) => protected override IQueryable<BeatmapSetInfo> AddIncludesForDeletion(IQueryable<BeatmapSetInfo> query) =>
base.AddIncludesForDeletion(query) base.AddIncludesForDeletion(query)
.Include(s => s.Metadata) .Include(s => s.Metadata)
.Include(s => s.Beatmaps).ThenInclude(b => b.Scores)
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata); .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata);

View File

@ -32,9 +32,11 @@ namespace osu.Game.Beatmaps.Drawables
case BeatmapSetCoverType.Cover: case BeatmapSetCoverType.Cover:
resource = set.OnlineInfo.Covers.Cover; resource = set.OnlineInfo.Covers.Cover;
break; break;
case BeatmapSetCoverType.Card: case BeatmapSetCoverType.Card:
resource = set.OnlineInfo.Covers.Card; resource = set.OnlineInfo.Covers.Card;
break; break;
case BeatmapSetCoverType.List: case BeatmapSetCoverType.List:
resource = set.OnlineInfo.Covers.List; resource = set.OnlineInfo.Covers.List;
break; break;

Some files were not shown because too many files have changed in this diff Show More