mirror of
https://github.com/osukey/osukey.git
synced 2025-05-29 17:37:23 +09:00
Merge branch 'legacy-skin-default-fallback' into fix-skin-sample-lookup
This commit is contained in:
commit
d26c9a66c2
@ -10,14 +10,6 @@ trim_trailing_whitespace = true
|
|||||||
|
|
||||||
#Roslyn naming styles
|
#Roslyn naming styles
|
||||||
|
|
||||||
#PascalCase for public and protected members
|
|
||||||
dotnet_naming_style.pascalcase.capitalization = pascal_case
|
|
||||||
dotnet_naming_symbols.public_members.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
|
||||||
dotnet_naming_symbols.public_members.applicable_kinds = property,method,field,event
|
|
||||||
dotnet_naming_rule.public_members_pascalcase.severity = error
|
|
||||||
dotnet_naming_rule.public_members_pascalcase.symbols = public_members
|
|
||||||
dotnet_naming_rule.public_members_pascalcase.style = pascalcase
|
|
||||||
|
|
||||||
#camelCase for private members
|
#camelCase for private members
|
||||||
dotnet_naming_style.camelcase.capitalization = camel_case
|
dotnet_naming_style.camelcase.capitalization = camel_case
|
||||||
|
|
||||||
@ -197,4 +189,4 @@ dotnet_diagnostic.IDE0069.severity = none
|
|||||||
dotnet_diagnostic.CA2225.severity = none
|
dotnet_diagnostic.CA2225.severity = none
|
||||||
|
|
||||||
# Banned APIs
|
# Banned APIs
|
||||||
dotnet_diagnostic.RS0030.severity = error
|
dotnet_diagnostic.RS0030.severity = error
|
||||||
|
@ -97,7 +97,7 @@ Before committing your code, please run a code formatter. This can be achieved b
|
|||||||
|
|
||||||
We have adopted some cross-platform, compiler integrated analyzers. They can provide warnings when you are editing, building inside IDE or from command line, as-if they are provided by the compiler itself.
|
We have adopted some cross-platform, compiler integrated analyzers. They can provide warnings when you are editing, building inside IDE or from command line, as-if they are provided by the compiler itself.
|
||||||
|
|
||||||
JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`, which is [only supported on Windows](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
|
JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`. Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.EmptyFreeform
|
|||||||
|
|
||||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.EmptyFreeform.Objects;
|
|
||||||
using osu.Game.Rulesets.EmptyFreeform.Replays;
|
using osu.Game.Rulesets.EmptyFreeform.Replays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -11,7 +10,7 @@ using osu.Game.Users;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.EmptyFreeform.Mods
|
namespace osu.Game.Rulesets.EmptyFreeform.Mods
|
||||||
{
|
{
|
||||||
public class EmptyFreeformModAutoplay : ModAutoplay<EmptyFreeformHitObject>
|
public class EmptyFreeformModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -4,14 +4,13 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Pippidon.Objects;
|
|
||||||
using osu.Game.Rulesets.Pippidon.Replays;
|
using osu.Game.Rulesets.Pippidon.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Pippidon.Mods
|
namespace osu.Game.Rulesets.Pippidon.Mods
|
||||||
{
|
{
|
||||||
public class PippidonModAutoplay : ModAutoplay<PippidonHitObject>
|
public class PippidonModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon
|
|||||||
|
|
||||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.EmptyScrolling
|
|||||||
|
|
||||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.EmptyScrolling.Objects;
|
|
||||||
using osu.Game.Rulesets.EmptyScrolling.Replays;
|
using osu.Game.Rulesets.EmptyScrolling.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -11,7 +10,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.EmptyScrolling.Mods
|
namespace osu.Game.Rulesets.EmptyScrolling.Mods
|
||||||
{
|
{
|
||||||
public class EmptyScrollingModAutoplay : ModAutoplay<EmptyScrollingHitObject>
|
public class EmptyScrollingModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -4,14 +4,13 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Pippidon.Objects;
|
|
||||||
using osu.Game.Rulesets.Pippidon.Replays;
|
using osu.Game.Rulesets.Pippidon.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Pippidon.Mods
|
namespace osu.Game.Rulesets.Pippidon.Mods
|
||||||
{
|
{
|
||||||
public class PippidonModAutoplay : ModAutoplay<PippidonHitObject>
|
public class PippidonModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon
|
|||||||
|
|
||||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.528.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.601.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -57,7 +57,7 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
private string getStableInstallPath()
|
private string getStableInstallPath()
|
||||||
{
|
{
|
||||||
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
|
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")) || File.Exists(Path.Combine(p, "osu!.cfg"));
|
||||||
|
|
||||||
string stableInstallPath;
|
string stableInstallPath;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
|
||||||
<PackageReference Include="nunit" Version="3.13.2" />
|
<PackageReference Include="nunit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
CircleSize = circleSize
|
CircleSize = circleSize
|
||||||
};
|
};
|
||||||
|
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
var droppedObjectContainer = new Container<CaughtObject>
|
var droppedObjectContainer = new Container<CaughtObject>
|
||||||
{
|
{
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
scoreProcessor = new ScoreProcessor();
|
scoreProcessor = new ScoreProcessor();
|
||||||
|
|
||||||
SetContents(() => new CatchComboDisplay
|
SetContents(_ => new CatchComboDisplay
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -19,22 +19,22 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
AddStep("show pear", () => SetContents(() => createDrawableFruit(0)));
|
AddStep("show pear", () => SetContents(_ => createDrawableFruit(0)));
|
||||||
AddStep("show grape", () => SetContents(() => createDrawableFruit(1)));
|
AddStep("show grape", () => SetContents(_ => createDrawableFruit(1)));
|
||||||
AddStep("show pineapple / apple", () => SetContents(() => createDrawableFruit(2)));
|
AddStep("show pineapple / apple", () => SetContents(_ => createDrawableFruit(2)));
|
||||||
AddStep("show raspberry / orange", () => SetContents(() => createDrawableFruit(3)));
|
AddStep("show raspberry / orange", () => SetContents(_ => createDrawableFruit(3)));
|
||||||
|
|
||||||
AddStep("show banana", () => SetContents(createDrawableBanana));
|
AddStep("show banana", () => SetContents(_ => createDrawableBanana()));
|
||||||
|
|
||||||
AddStep("show droplet", () => SetContents(() => createDrawableDroplet()));
|
AddStep("show droplet", () => SetContents(_ => createDrawableDroplet()));
|
||||||
AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet));
|
AddStep("show tiny droplet", () => SetContents(_ => createDrawableTinyDroplet()));
|
||||||
|
|
||||||
AddStep("show hyperdash pear", () => SetContents(() => createDrawableFruit(0, true)));
|
AddStep("show hyperdash pear", () => SetContents(_ => createDrawableFruit(0, true)));
|
||||||
AddStep("show hyperdash grape", () => SetContents(() => createDrawableFruit(1, true)));
|
AddStep("show hyperdash grape", () => SetContents(_ => createDrawableFruit(1, true)));
|
||||||
AddStep("show hyperdash pineapple / apple", () => SetContents(() => createDrawableFruit(2, true)));
|
AddStep("show hyperdash pineapple / apple", () => SetContents(_ => createDrawableFruit(2, true)));
|
||||||
AddStep("show hyperdash raspberry / orange", () => SetContents(() => createDrawableFruit(3, true)));
|
AddStep("show hyperdash raspberry / orange", () => SetContents(_ => createDrawableFruit(3, true)));
|
||||||
|
|
||||||
AddStep("show hyperdash droplet", () => SetContents(() => createDrawableDroplet(true)));
|
AddStep("show hyperdash droplet", () => SetContents(_ => createDrawableDroplet(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) =>
|
private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) =>
|
||||||
|
@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
AddStep("fruit changes visual and hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
|
AddStep("fruit changes visual and hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
|
||||||
{
|
{
|
||||||
IndexInBeatmapBindable = { BindTarget = indexInBeatmap },
|
IndexInBeatmapBindable = { BindTarget = indexInBeatmap },
|
||||||
HyperDashBindable = { BindTarget = hyperDash },
|
HyperDashBindable = { BindTarget = hyperDash },
|
||||||
}))));
|
}))));
|
||||||
|
|
||||||
AddStep("droplet changes hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
|
AddStep("droplet changes hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
|
||||||
{
|
{
|
||||||
HyperDashBindable = { BindTarget = hyperDash },
|
HyperDashBindable = { BindTarget = hyperDash },
|
||||||
}))));
|
}))));
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods)
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||||
{
|
{
|
||||||
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
|
|
||||||
return new Skill[]
|
return new Skill[]
|
||||||
{
|
{
|
||||||
new Movement(mods, halfCatcherWidth),
|
new Movement(mods, halfCatcherWidth, clockRate),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly double StrainTime;
|
public readonly double StrainTime;
|
||||||
|
|
||||||
public readonly double ClockRate;
|
|
||||||
|
|
||||||
public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
|
public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
|
||||||
: base(hitObject, lastObject, clockRate)
|
: base(hitObject, lastObject, clockRate)
|
||||||
{
|
{
|
||||||
@ -37,7 +35,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
|||||||
|
|
||||||
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
|
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
|
||||||
StrainTime = Math.Max(40, DeltaTime);
|
StrainTime = Math.Max(40, DeltaTime);
|
||||||
ClockRate = clockRate;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,21 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
private float lastDistanceMoved;
|
private float lastDistanceMoved;
|
||||||
private double lastStrainTime;
|
private double lastStrainTime;
|
||||||
|
|
||||||
public Movement(Mod[] mods, float halfCatcherWidth)
|
/// <summary>
|
||||||
|
/// The speed multiplier applied to the player's catcher.
|
||||||
|
/// </summary>
|
||||||
|
private readonly double catcherSpeedMultiplier;
|
||||||
|
|
||||||
|
public Movement(Mod[] mods, float halfCatcherWidth, double clockRate)
|
||||||
: base(mods)
|
: base(mods)
|
||||||
{
|
{
|
||||||
HalfCatcherWidth = halfCatcherWidth;
|
HalfCatcherWidth = halfCatcherWidth;
|
||||||
|
|
||||||
|
// In catch, clockrate adjustments do not only affect the timings of hitobjects,
|
||||||
|
// but also the speed of the player's catcher, which has an impact on difficulty
|
||||||
|
// TODO: Support variable clockrates caused by mods such as ModTimeRamp
|
||||||
|
// (perhaps by using IApplicableToRate within the CatchDifficultyHitObject constructor to set a catcher speed for each object before processing)
|
||||||
|
catcherSpeedMultiplier = clockRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override double StrainValueOf(DifficultyHitObject current)
|
protected override double StrainValueOf(DifficultyHitObject current)
|
||||||
@ -48,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
|
|
||||||
float distanceMoved = playerPosition - lastPlayerPosition.Value;
|
float distanceMoved = playerPosition - lastPlayerPosition.Value;
|
||||||
|
|
||||||
double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catchCurrent.ClockRate);
|
double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catcherSpeedMultiplier);
|
||||||
|
|
||||||
double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510);
|
double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510);
|
||||||
double sqrtStrain = Math.Sqrt(weightedStrainTime);
|
double sqrtStrain = Math.Sqrt(weightedStrainTime);
|
||||||
@ -81,7 +92,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
playerPosition = catchCurrent.NormalizedPosition;
|
playerPosition = catchCurrent.NormalizedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
|
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catcherSpeedMultiplier, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
|
||||||
}
|
}
|
||||||
|
|
||||||
lastPlayerPosition = playerPosition;
|
lastPlayerPosition = playerPosition;
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
|
||||||
using osu.Game.Rulesets.Catch.Replays;
|
using osu.Game.Rulesets.Catch.Replays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -11,7 +10,7 @@ using osu.Game.Users;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModAutoplay : ModAutoplay<CatchHitObject>
|
public class CatchModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
SetContents(() => new FillFlowContainer
|
SetContents(_ => new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new FillFlowContainer
|
SetContents(_ => new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new FillFlowContainer
|
SetContents(_ => new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
if (hitWindows.IsHitResultAllowed(result))
|
if (hitWindows.IsHitResultAllowed(result))
|
||||||
{
|
{
|
||||||
AddStep("Show " + result.GetDescription(), () => SetContents(() =>
|
AddStep("Show " + result.GetDescription(), () => SetContents(_ =>
|
||||||
new DrawableManiaJudgement(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())
|
new DrawableManiaJudgement(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())
|
||||||
{
|
{
|
||||||
Type = result
|
Type = result
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
var pool = new DrawablePool<PoolableHitExplosion>(5);
|
var pool = new DrawablePool<PoolableHitExplosion>(5);
|
||||||
hitExplosionPools.Add(pool);
|
hitExplosionPools.Add(pool);
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new FillFlowContainer
|
SetContents(_ => new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
new StageDefinition { Columns = 2 }
|
new StageDefinition { Columns = 2 }
|
||||||
};
|
};
|
||||||
|
|
||||||
SetContents(() => new ManiaPlayfield(stageDefinitions));
|
SetContents(_ => new ManiaPlayfield(stageDefinitions));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
new StageDefinition { Columns = 2 }
|
new StageDefinition { Columns = 2 }
|
||||||
};
|
};
|
||||||
|
|
||||||
SetContents(() => new ManiaPlayfield(stageDefinitions));
|
SetContents(_ => new ManiaPlayfield(stageDefinitions));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
ManiaAction normalAction = ManiaAction.Key1;
|
ManiaAction normalAction = ManiaAction.Key1;
|
||||||
ManiaAction specialAction = ManiaAction.Special1;
|
ManiaAction specialAction = ManiaAction.Special1;
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }),
|
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }),
|
||||||
_ => new DefaultStageBackground())
|
_ => new DefaultStageBackground())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null)
|
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
// Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required.
|
// Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required.
|
||||||
protected override IEnumerable<DifficultyHitObject> SortObjects(IEnumerable<DifficultyHitObject> input) => input;
|
protected override IEnumerable<DifficultyHitObject> SortObjects(IEnumerable<DifficultyHitObject> input) => input;
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||||
{
|
{
|
||||||
new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns)
|
new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns)
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
|
||||||
using osu.Game.Rulesets.Mania.Replays;
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -12,7 +11,7 @@ using osu.Game.Users;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
|
public class ManiaModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -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.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
@ -17,8 +18,11 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
|
|
||||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
|
Seed.Value ??= RNG.Next();
|
||||||
|
var rng = new Random((int)Seed.Value);
|
||||||
|
|
||||||
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||||
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
|
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => rng.Next()).ToList();
|
||||||
|
|
||||||
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
|
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Mode: 0
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
0,300,4,1,2,100,1,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
444,320,1000,5,0,0:0:0:0:
|
@ -39,18 +39,28 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestLegacySmoothCursorTrail()
|
public void TestLegacySmoothCursorTrail()
|
||||||
{
|
{
|
||||||
createTest(() => new LegacySkinContainer(false)
|
createTest(() =>
|
||||||
{
|
{
|
||||||
Child = new LegacyCursorTrail()
|
var skinContainer = new LegacySkinContainer(false);
|
||||||
|
var legacyCursorTrail = new LegacyCursorTrail(skinContainer);
|
||||||
|
|
||||||
|
skinContainer.Child = legacyCursorTrail;
|
||||||
|
|
||||||
|
return skinContainer;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestLegacyDisjointCursorTrail()
|
public void TestLegacyDisjointCursorTrail()
|
||||||
{
|
{
|
||||||
createTest(() => new LegacySkinContainer(true)
|
createTest(() =>
|
||||||
{
|
{
|
||||||
Child = new LegacyCursorTrail()
|
var skinContainer = new LegacySkinContainer(true);
|
||||||
|
var legacyCursorTrail = new LegacyCursorTrail(skinContainer);
|
||||||
|
|
||||||
|
skinContainer.Child = legacyCursorTrail;
|
||||||
|
|
||||||
|
return skinContainer;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
int poolIndex = 0;
|
int poolIndex = 0;
|
||||||
|
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
DrawablePool<TestDrawableOsuJudgement> pool;
|
DrawablePool<TestDrawableOsuJudgement> pool;
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private void loadContent(bool automated = true, Func<SkinProvidingContainer> skinProvider = null)
|
private void loadContent(bool automated = true, Func<SkinProvidingContainer> skinProvider = null)
|
||||||
{
|
{
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
var inputManager = automated ? (InputManager)new MovingCursorInputManager() : new OsuInputManager(new OsuRuleset().RulesetInfo);
|
var inputManager = automated ? (InputManager)new MovingCursorInputManager() : new OsuInputManager(new OsuRuleset().RulesetInfo);
|
||||||
var skinContainer = skinProvider?.Invoke() ?? new SkinProvidingContainer(null);
|
var skinContainer = skinProvider?.Invoke() ?? new SkinProvidingContainer(null);
|
||||||
|
@ -23,18 +23,18 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestVariousHitCircles()
|
public void TestVariousHitCircles()
|
||||||
{
|
{
|
||||||
AddStep("Miss Big Single", () => SetContents(() => testSingle(2)));
|
AddStep("Miss Big Single", () => SetContents(_ => testSingle(2)));
|
||||||
AddStep("Miss Medium Single", () => SetContents(() => testSingle(5)));
|
AddStep("Miss Medium Single", () => SetContents(_ => testSingle(5)));
|
||||||
AddStep("Miss Small Single", () => SetContents(() => testSingle(7)));
|
AddStep("Miss Small Single", () => SetContents(_ => testSingle(7)));
|
||||||
AddStep("Hit Big Single", () => SetContents(() => testSingle(2, true)));
|
AddStep("Hit Big Single", () => SetContents(_ => testSingle(2, true)));
|
||||||
AddStep("Hit Medium Single", () => SetContents(() => testSingle(5, true)));
|
AddStep("Hit Medium Single", () => SetContents(_ => testSingle(5, true)));
|
||||||
AddStep("Hit Small Single", () => SetContents(() => testSingle(7, true)));
|
AddStep("Hit Small Single", () => SetContents(_ => testSingle(7, true)));
|
||||||
AddStep("Miss Big Stream", () => SetContents(() => testStream(2)));
|
AddStep("Miss Big Stream", () => SetContents(_ => testStream(2)));
|
||||||
AddStep("Miss Medium Stream", () => SetContents(() => testStream(5)));
|
AddStep("Miss Medium Stream", () => SetContents(_ => testStream(5)));
|
||||||
AddStep("Miss Small Stream", () => SetContents(() => testStream(7)));
|
AddStep("Miss Small Stream", () => SetContents(_ => testStream(7)));
|
||||||
AddStep("Hit Big Stream", () => SetContents(() => testStream(2, true)));
|
AddStep("Hit Big Stream", () => SetContents(_ => testStream(2, true)));
|
||||||
AddStep("Hit Medium Stream", () => SetContents(() => testStream(5, true)));
|
AddStep("Hit Medium Stream", () => SetContents(_ => testStream(5, true)));
|
||||||
AddStep("Hit Small Stream", () => SetContents(() => testStream(7, true)));
|
AddStep("Hit Small Stream", () => SetContents(_ => testStream(7, true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
||||||
|
30
osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleKiai.cs
Normal file
30
osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleKiai.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneHitCircleKiai : TestSceneHitCircle
|
||||||
|
{
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
var controlPointInfo = new ControlPointInfo();
|
||||||
|
|
||||||
|
controlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true });
|
||||||
|
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPointInfo
|
||||||
|
});
|
||||||
|
|
||||||
|
// track needs to be playing for BeatSyncedContainer to work.
|
||||||
|
Beatmap.Value.Track.Start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
49
osu.Game.Rulesets.Osu.Tests/TestSceneOsuHitObjectSamples.cs
Normal file
49
osu.Game.Rulesets.Osu.Tests/TestSceneOsuHitObjectSamples.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Reflection;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.IO.Stores;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneOsuHitObjectSamples : HitObjectSampleTest
|
||||||
|
{
|
||||||
|
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||||
|
|
||||||
|
protected override IResourceStore<byte[]> RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneOsuHitObjectSamples)));
|
||||||
|
|
||||||
|
[TestCase("normal-hitnormal")]
|
||||||
|
[TestCase("hitnormal")]
|
||||||
|
public void TestDefaultCustomSampleFromBeatmap(string expectedSample)
|
||||||
|
{
|
||||||
|
SetupSkins(expectedSample, expectedSample);
|
||||||
|
|
||||||
|
CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu");
|
||||||
|
|
||||||
|
AssertBeatmapLookup(expectedSample);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("normal-hitnormal")]
|
||||||
|
[TestCase("hitnormal")]
|
||||||
|
public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample)
|
||||||
|
{
|
||||||
|
SetupSkins(string.Empty, expectedSample);
|
||||||
|
|
||||||
|
CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu");
|
||||||
|
|
||||||
|
AssertUserLookup(expectedSample);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("normal-hitnormal2")]
|
||||||
|
public void TestUserSkinLookupIgnoresSampleBank(string unwantedSample)
|
||||||
|
{
|
||||||
|
SetupSkins(string.Empty, unwantedSample);
|
||||||
|
|
||||||
|
CreateTestWithBeatmap("osu-hitobject-beatmap-custom-sample-bank.osu");
|
||||||
|
|
||||||
|
AssertNoLookup(unwantedSample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,54 +30,54 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestVariousSliders()
|
public void TestVariousSliders()
|
||||||
{
|
{
|
||||||
AddStep("Big Single", () => SetContents(() => testSimpleBig()));
|
AddStep("Big Single", () => SetContents(_ => testSimpleBig()));
|
||||||
AddStep("Medium Single", () => SetContents(() => testSimpleMedium()));
|
AddStep("Medium Single", () => SetContents(_ => testSimpleMedium()));
|
||||||
AddStep("Small Single", () => SetContents(() => testSimpleSmall()));
|
AddStep("Small Single", () => SetContents(_ => testSimpleSmall()));
|
||||||
AddStep("Big 1 Repeat", () => SetContents(() => testSimpleBig(1)));
|
AddStep("Big 1 Repeat", () => SetContents(_ => testSimpleBig(1)));
|
||||||
AddStep("Medium 1 Repeat", () => SetContents(() => testSimpleMedium(1)));
|
AddStep("Medium 1 Repeat", () => SetContents(_ => testSimpleMedium(1)));
|
||||||
AddStep("Small 1 Repeat", () => SetContents(() => testSimpleSmall(1)));
|
AddStep("Small 1 Repeat", () => SetContents(_ => testSimpleSmall(1)));
|
||||||
AddStep("Big 2 Repeats", () => SetContents(() => testSimpleBig(2)));
|
AddStep("Big 2 Repeats", () => SetContents(_ => testSimpleBig(2)));
|
||||||
AddStep("Medium 2 Repeats", () => SetContents(() => testSimpleMedium(2)));
|
AddStep("Medium 2 Repeats", () => SetContents(_ => testSimpleMedium(2)));
|
||||||
AddStep("Small 2 Repeats", () => SetContents(() => testSimpleSmall(2)));
|
AddStep("Small 2 Repeats", () => SetContents(_ => testSimpleSmall(2)));
|
||||||
|
|
||||||
AddStep("Slow Slider", () => SetContents(testSlowSpeed)); // slow long sliders take ages already so no repeat steps
|
AddStep("Slow Slider", () => SetContents(_ => testSlowSpeed())); // slow long sliders take ages already so no repeat steps
|
||||||
AddStep("Slow Short Slider", () => SetContents(() => testShortSlowSpeed()));
|
AddStep("Slow Short Slider", () => SetContents(_ => testShortSlowSpeed()));
|
||||||
AddStep("Slow Short Slider 1 Repeats", () => SetContents(() => testShortSlowSpeed(1)));
|
AddStep("Slow Short Slider 1 Repeats", () => SetContents(_ => testShortSlowSpeed(1)));
|
||||||
AddStep("Slow Short Slider 2 Repeats", () => SetContents(() => testShortSlowSpeed(2)));
|
AddStep("Slow Short Slider 2 Repeats", () => SetContents(_ => testShortSlowSpeed(2)));
|
||||||
|
|
||||||
AddStep("Fast Slider", () => SetContents(() => testHighSpeed()));
|
AddStep("Fast Slider", () => SetContents(_ => testHighSpeed()));
|
||||||
AddStep("Fast Slider 1 Repeat", () => SetContents(() => testHighSpeed(1)));
|
AddStep("Fast Slider 1 Repeat", () => SetContents(_ => testHighSpeed(1)));
|
||||||
AddStep("Fast Slider 2 Repeats", () => SetContents(() => testHighSpeed(2)));
|
AddStep("Fast Slider 2 Repeats", () => SetContents(_ => testHighSpeed(2)));
|
||||||
AddStep("Fast Short Slider", () => SetContents(() => testShortHighSpeed()));
|
AddStep("Fast Short Slider", () => SetContents(_ => testShortHighSpeed()));
|
||||||
AddStep("Fast Short Slider 1 Repeat", () => SetContents(() => testShortHighSpeed(1)));
|
AddStep("Fast Short Slider 1 Repeat", () => SetContents(_ => testShortHighSpeed(1)));
|
||||||
AddStep("Fast Short Slider 2 Repeats", () => SetContents(() => testShortHighSpeed(2)));
|
AddStep("Fast Short Slider 2 Repeats", () => SetContents(_ => testShortHighSpeed(2)));
|
||||||
AddStep("Fast Short Slider 6 Repeats", () => SetContents(() => testShortHighSpeed(6)));
|
AddStep("Fast Short Slider 6 Repeats", () => SetContents(_ => testShortHighSpeed(6)));
|
||||||
|
|
||||||
AddStep("Perfect Curve", () => SetContents(() => testPerfect()));
|
AddStep("Perfect Curve", () => SetContents(_ => testPerfect()));
|
||||||
AddStep("Perfect Curve 1 Repeat", () => SetContents(() => testPerfect(1)));
|
AddStep("Perfect Curve 1 Repeat", () => SetContents(_ => testPerfect(1)));
|
||||||
AddStep("Perfect Curve 2 Repeats", () => SetContents(() => testPerfect(2)));
|
AddStep("Perfect Curve 2 Repeats", () => SetContents(_ => testPerfect(2)));
|
||||||
|
|
||||||
AddStep("Linear Slider", () => SetContents(() => testLinear()));
|
AddStep("Linear Slider", () => SetContents(_ => testLinear()));
|
||||||
AddStep("Linear Slider 1 Repeat", () => SetContents(() => testLinear(1)));
|
AddStep("Linear Slider 1 Repeat", () => SetContents(_ => testLinear(1)));
|
||||||
AddStep("Linear Slider 2 Repeats", () => SetContents(() => testLinear(2)));
|
AddStep("Linear Slider 2 Repeats", () => SetContents(_ => testLinear(2)));
|
||||||
|
|
||||||
AddStep("Bezier Slider", () => SetContents(() => testBezier()));
|
AddStep("Bezier Slider", () => SetContents(_ => testBezier()));
|
||||||
AddStep("Bezier Slider 1 Repeat", () => SetContents(() => testBezier(1)));
|
AddStep("Bezier Slider 1 Repeat", () => SetContents(_ => testBezier(1)));
|
||||||
AddStep("Bezier Slider 2 Repeats", () => SetContents(() => testBezier(2)));
|
AddStep("Bezier Slider 2 Repeats", () => SetContents(_ => testBezier(2)));
|
||||||
|
|
||||||
AddStep("Linear Overlapping", () => SetContents(() => testLinearOverlapping()));
|
AddStep("Linear Overlapping", () => SetContents(_ => testLinearOverlapping()));
|
||||||
AddStep("Linear Overlapping 1 Repeat", () => SetContents(() => testLinearOverlapping(1)));
|
AddStep("Linear Overlapping 1 Repeat", () => SetContents(_ => testLinearOverlapping(1)));
|
||||||
AddStep("Linear Overlapping 2 Repeats", () => SetContents(() => testLinearOverlapping(2)));
|
AddStep("Linear Overlapping 2 Repeats", () => SetContents(_ => testLinearOverlapping(2)));
|
||||||
|
|
||||||
AddStep("Catmull Slider", () => SetContents(() => testCatmull()));
|
AddStep("Catmull Slider", () => SetContents(_ => testCatmull()));
|
||||||
AddStep("Catmull Slider 1 Repeat", () => SetContents(() => testCatmull(1)));
|
AddStep("Catmull Slider 1 Repeat", () => SetContents(_ => testCatmull(1)));
|
||||||
AddStep("Catmull Slider 2 Repeats", () => SetContents(() => testCatmull(2)));
|
AddStep("Catmull Slider 2 Repeats", () => SetContents(_ => testCatmull(2)));
|
||||||
|
|
||||||
AddStep("Big Single, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset()));
|
AddStep("Big Single, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset()));
|
||||||
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset(1)));
|
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset(1)));
|
||||||
|
|
||||||
AddStep("Distance Overflow", () => SetContents(() => testDistanceOverflow()));
|
AddStep("Distance Overflow", () => SetContents(_ => testDistanceOverflow()));
|
||||||
AddStep("Distance Overflow 1 Repeat", () => SetContents(() => testDistanceOverflow(1)));
|
AddStep("Distance Overflow 1 Repeat", () => SetContents(_ => testDistanceOverflow(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -29,15 +29,15 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
public void TestVariousSpinners(bool autoplay)
|
public void TestVariousSpinners(bool autoplay)
|
||||||
{
|
{
|
||||||
string term = autoplay ? "Hit" : "Miss";
|
string term = autoplay ? "Hit" : "Miss";
|
||||||
AddStep($"{term} Big", () => SetContents(() => testSingle(2, autoplay)));
|
AddStep($"{term} Big", () => SetContents(_ => testSingle(2, autoplay)));
|
||||||
AddStep($"{term} Medium", () => SetContents(() => testSingle(5, autoplay)));
|
AddStep($"{term} Medium", () => SetContents(_ => testSingle(5, autoplay)));
|
||||||
AddStep($"{term} Small", () => SetContents(() => testSingle(7, autoplay)));
|
AddStep($"{term} Small", () => SetContents(_ => testSingle(7, autoplay)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSpinningSamplePitchShift()
|
public void TestSpinningSamplePitchShift()
|
||||||
{
|
{
|
||||||
AddStep("Add spinner", () => SetContents(() => testSingle(5, true, 4000)));
|
AddStep("Add spinner", () => SetContents(_ => testSingle(5, true, 4000)));
|
||||||
AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8);
|
AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8);
|
||||||
AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8);
|
AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8);
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
public void TestLongSpinner(bool autoplay)
|
public void TestLongSpinner(bool autoplay)
|
||||||
{
|
{
|
||||||
AddStep("Very long spinner", () => SetContents(() => testSingle(5, autoplay, 4000)));
|
AddStep("Very long spinner", () => SetContents(_ => testSingle(5, autoplay, 4000)));
|
||||||
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
|
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
|
||||||
AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0));
|
AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0));
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
public void TestSuperShortSpinner(bool autoplay)
|
public void TestSuperShortSpinner(bool autoplay)
|
||||||
{
|
{
|
||||||
AddStep("Very short spinner", () => SetContents(() => testSingle(5, autoplay, 200)));
|
AddStep("Very short spinner", () => SetContents(_ => testSingle(5, autoplay, 200)));
|
||||||
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
|
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
|
||||||
AddUntilStep("Short spinner implicitly completes", () => drawableSpinner.Progress == 1);
|
AddUntilStep("Short spinner implicitly completes", () => drawableSpinner.Progress == 1);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||||
{
|
{
|
||||||
new Aim(mods),
|
new Aim(mods),
|
||||||
new Speed(mods)
|
new Speed(mods)
|
||||||
|
@ -6,14 +6,13 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
|
||||||
using osu.Game.Rulesets.Osu.Replays;
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModAutoplay : ModAutoplay<OsuHitObject>
|
public class OsuModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray();
|
||||||
|
|
||||||
|
280
osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
Normal file
280
osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Mod that randomises the positions of the <see cref="HitObject"/>s
|
||||||
|
/// </summary>
|
||||||
|
public class OsuModRandom : ModRandom, IApplicableToBeatmap
|
||||||
|
{
|
||||||
|
public override string Description => "It never gets boring!";
|
||||||
|
public override bool Ranked => false;
|
||||||
|
|
||||||
|
// The relative distance to the edge of the playfield before objects' positions should start to "turn around" and curve towards the middle.
|
||||||
|
// The closer the hit objects draw to the border, the sharper the turn
|
||||||
|
private const float playfield_edge_ratio = 0.375f;
|
||||||
|
|
||||||
|
private static readonly float border_distance_x = OsuPlayfield.BASE_SIZE.X * playfield_edge_ratio;
|
||||||
|
private static readonly float border_distance_y = OsuPlayfield.BASE_SIZE.Y * playfield_edge_ratio;
|
||||||
|
|
||||||
|
private static readonly Vector2 playfield_middle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2);
|
||||||
|
|
||||||
|
private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast;
|
||||||
|
|
||||||
|
private Random rng;
|
||||||
|
|
||||||
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
if (!(beatmap is OsuBeatmap osuBeatmap))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var hitObjects = osuBeatmap.HitObjects;
|
||||||
|
|
||||||
|
Seed.Value ??= RNG.Next();
|
||||||
|
|
||||||
|
rng = new Random((int)Seed.Value);
|
||||||
|
|
||||||
|
RandomObjectInfo previous = null;
|
||||||
|
|
||||||
|
float rateOfChangeMultiplier = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < hitObjects.Count; i++)
|
||||||
|
{
|
||||||
|
var hitObject = hitObjects[i];
|
||||||
|
|
||||||
|
var current = new RandomObjectInfo(hitObject);
|
||||||
|
|
||||||
|
// rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams
|
||||||
|
if (i % 3 == 0)
|
||||||
|
rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1;
|
||||||
|
|
||||||
|
if (hitObject is Spinner)
|
||||||
|
{
|
||||||
|
previous = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyRandomisation(rateOfChangeMultiplier, previous, current);
|
||||||
|
|
||||||
|
hitObject.Position = current.PositionRandomised;
|
||||||
|
|
||||||
|
// update end position as it may have changed as a result of the position update.
|
||||||
|
current.EndPositionRandomised = current.PositionRandomised;
|
||||||
|
|
||||||
|
switch (hitObject)
|
||||||
|
{
|
||||||
|
case Slider slider:
|
||||||
|
// Shift nested objects the same distance as the slider got shifted in the randomisation process
|
||||||
|
// so that moveSliderIntoPlayfield() can determine their relative distances to slider.Position and thus minMargin
|
||||||
|
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal));
|
||||||
|
|
||||||
|
var oldPos = new Vector2(slider.Position.X, slider.Position.Y);
|
||||||
|
|
||||||
|
moveSliderIntoPlayfield(slider, current);
|
||||||
|
|
||||||
|
// Shift them again to move them to their final position after the slider got moved into the playfield
|
||||||
|
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, oldPos));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the final position of the hit object
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Final position of the hit object</returns>
|
||||||
|
private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo previous, RandomObjectInfo current)
|
||||||
|
{
|
||||||
|
if (previous == null)
|
||||||
|
{
|
||||||
|
var playfieldSize = OsuPlayfield.BASE_SIZE;
|
||||||
|
|
||||||
|
current.AngleRad = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI);
|
||||||
|
current.PositionRandomised = new Vector2((float)rng.NextDouble() * playfieldSize.X, (float)rng.NextDouble() * playfieldSize.Y);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float distanceToPrev = Vector2.Distance(previous.EndPositionOriginal, current.PositionOriginal);
|
||||||
|
|
||||||
|
// The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object)
|
||||||
|
// is proportional to the distance between the last and the current hit object
|
||||||
|
// to allow jumps and prevent too sharp turns during streams.
|
||||||
|
var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / playfield_diagonal;
|
||||||
|
|
||||||
|
current.AngleRad = (float)randomAngleRad + previous.AngleRad;
|
||||||
|
if (current.AngleRad < 0)
|
||||||
|
current.AngleRad += 2 * (float)Math.PI;
|
||||||
|
|
||||||
|
var posRelativeToPrev = new Vector2(
|
||||||
|
distanceToPrev * (float)Math.Cos(current.AngleRad),
|
||||||
|
distanceToPrev * (float)Math.Sin(current.AngleRad)
|
||||||
|
);
|
||||||
|
|
||||||
|
posRelativeToPrev = getRotatedVector(previous.EndPositionRandomised, posRelativeToPrev);
|
||||||
|
|
||||||
|
current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X);
|
||||||
|
|
||||||
|
var position = Vector2.Add(previous.EndPositionRandomised, posRelativeToPrev);
|
||||||
|
|
||||||
|
// Move hit objects back into the playfield if they are outside of it,
|
||||||
|
// which would sometimes happen during big jumps otherwise.
|
||||||
|
position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X);
|
||||||
|
position.Y = MathHelper.Clamp(position.Y, 0, OsuPlayfield.BASE_SIZE.Y);
|
||||||
|
|
||||||
|
current.PositionRandomised = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves the <see cref="Slider"/> and all necessary nested <see cref="OsuHitObject"/>s into the <see cref="OsuPlayfield"/> if they aren't already.
|
||||||
|
/// </summary>
|
||||||
|
private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo)
|
||||||
|
{
|
||||||
|
// Min. distances from the slider's position to the playfield border
|
||||||
|
var minMargin = new MarginPadding();
|
||||||
|
|
||||||
|
foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderEndCircle))
|
||||||
|
{
|
||||||
|
if (!(hitObject is OsuHitObject osuHitObject))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var relativePos = Vector2.Subtract(osuHitObject.Position, slider.Position);
|
||||||
|
|
||||||
|
minMargin.Left = Math.Max(minMargin.Left, -relativePos.X);
|
||||||
|
minMargin.Right = Math.Max(minMargin.Right, relativePos.X);
|
||||||
|
minMargin.Top = Math.Max(minMargin.Top, -relativePos.Y);
|
||||||
|
minMargin.Bottom = Math.Max(minMargin.Bottom, relativePos.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slider.Position.X < minMargin.Left)
|
||||||
|
slider.Position = new Vector2(minMargin.Left, slider.Position.Y);
|
||||||
|
else if (slider.Position.X + minMargin.Right > OsuPlayfield.BASE_SIZE.X)
|
||||||
|
slider.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - minMargin.Right, slider.Position.Y);
|
||||||
|
|
||||||
|
if (slider.Position.Y < minMargin.Top)
|
||||||
|
slider.Position = new Vector2(slider.Position.X, minMargin.Top);
|
||||||
|
else if (slider.Position.Y + minMargin.Bottom > OsuPlayfield.BASE_SIZE.Y)
|
||||||
|
slider.Position = new Vector2(slider.Position.X, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom);
|
||||||
|
|
||||||
|
currentObjectInfo.PositionRandomised = slider.Position;
|
||||||
|
currentObjectInfo.EndPositionRandomised = slider.EndPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shifts all nested <see cref="SliderTick"/>s and <see cref="SliderRepeat"/>s by the specified shift.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slider"><see cref="Slider"/> whose nested <see cref="SliderTick"/>s and <see cref="SliderRepeat"/>s should be shifted</param>
|
||||||
|
/// <param name="shift">The <see cref="Vector2"/> the <see cref="Slider"/>'s nested <see cref="SliderTick"/>s and <see cref="SliderRepeat"/>s should be shifted by</param>
|
||||||
|
private void shiftNestedObjects(Slider slider, Vector2 shift)
|
||||||
|
{
|
||||||
|
foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderRepeat))
|
||||||
|
{
|
||||||
|
if (!(hitObject is OsuHitObject osuHitObject))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
osuHitObject.Position = Vector2.Add(osuHitObject.Position, shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines the position of the current hit object relative to the previous one.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The position of the current hit object relative to the previous one</returns>
|
||||||
|
private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev)
|
||||||
|
{
|
||||||
|
var relativeRotationDistance = 0f;
|
||||||
|
|
||||||
|
if (prevPosChanged.X < playfield_middle.X)
|
||||||
|
{
|
||||||
|
relativeRotationDistance = Math.Max(
|
||||||
|
(border_distance_x - prevPosChanged.X) / border_distance_x,
|
||||||
|
relativeRotationDistance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
relativeRotationDistance = Math.Max(
|
||||||
|
(prevPosChanged.X - (OsuPlayfield.BASE_SIZE.X - border_distance_x)) / border_distance_x,
|
||||||
|
relativeRotationDistance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevPosChanged.Y < playfield_middle.Y)
|
||||||
|
{
|
||||||
|
relativeRotationDistance = Math.Max(
|
||||||
|
(border_distance_y - prevPosChanged.Y) / border_distance_y,
|
||||||
|
relativeRotationDistance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
relativeRotationDistance = Math.Max(
|
||||||
|
(prevPosChanged.Y - (OsuPlayfield.BASE_SIZE.Y - border_distance_y)) / border_distance_y,
|
||||||
|
relativeRotationDistance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rotateVectorTowardsVector(posRelativeToPrev, playfield_middle - prevPosChanged, relativeRotationDistance / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotates vector "initial" towards vector "destinantion"
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="initial">Vector to rotate to "destination"</param>
|
||||||
|
/// <param name="destination">Vector "initial" should be rotated to</param>
|
||||||
|
/// <param name="relativeDistance">The angle the vector should be rotated relative to the difference between the angles of the the two vectors.</param>
|
||||||
|
/// <returns>Resulting vector</returns>
|
||||||
|
private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance)
|
||||||
|
{
|
||||||
|
var initialAngleRad = Math.Atan2(initial.Y, initial.X);
|
||||||
|
var destAngleRad = Math.Atan2(destination.Y, destination.X);
|
||||||
|
|
||||||
|
var diff = destAngleRad - initialAngleRad;
|
||||||
|
|
||||||
|
while (diff < -Math.PI) diff += 2 * Math.PI;
|
||||||
|
|
||||||
|
while (diff > Math.PI) diff -= 2 * Math.PI;
|
||||||
|
|
||||||
|
var finalAngleRad = initialAngleRad + relativeDistance * diff;
|
||||||
|
|
||||||
|
return new Vector2(
|
||||||
|
initial.Length * (float)Math.Cos(finalAngleRad),
|
||||||
|
initial.Length * (float)Math.Sin(finalAngleRad)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RandomObjectInfo
|
||||||
|
{
|
||||||
|
public float AngleRad { get; set; }
|
||||||
|
|
||||||
|
public Vector2 PositionOriginal { get; }
|
||||||
|
public Vector2 PositionRandomised { get; set; }
|
||||||
|
|
||||||
|
public Vector2 EndPositionOriginal { get; }
|
||||||
|
public Vector2 EndPositionRandomised { get; set; }
|
||||||
|
|
||||||
|
public RandomObjectInfo(OsuHitObject hitObject)
|
||||||
|
{
|
||||||
|
PositionRandomised = PositionOriginal = hitObject.Position;
|
||||||
|
EndPositionRandomised = EndPositionOriginal = hitObject.EndPosition;
|
||||||
|
AngleRad = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -182,8 +182,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
// todo: temporary / arbitrary, used for lifetime optimisation.
|
// todo: temporary / arbitrary, used for lifetime optimisation.
|
||||||
this.Delay(800).FadeOut();
|
this.Delay(800).FadeOut();
|
||||||
|
|
||||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Idle:
|
case ArmedState.Idle:
|
||||||
|
@ -97,8 +97,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.UpdateHitStateTransforms(state);
|
base.UpdateHitStateTransforms(state);
|
||||||
|
|
||||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Idle:
|
case ArmedState.Idle:
|
||||||
|
@ -87,8 +87,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
Debug.Assert(HitObject.HitWindows != null);
|
Debug.Assert(HitObject.HitWindows != null);
|
||||||
|
|
||||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Idle:
|
case ArmedState.Idle:
|
||||||
|
@ -164,7 +164,8 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
{
|
{
|
||||||
new OsuModTarget(),
|
new OsuModTarget(),
|
||||||
new OsuModDifficultyAdjust(),
|
new OsuModDifficultyAdjust(),
|
||||||
new OsuModClassic()
|
new OsuModClassic(),
|
||||||
|
new OsuModRandom(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
|
@ -42,6 +42,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Texture = textures.Get(@"Gameplay/osu/disc"),
|
Texture = textures.Get(@"Gameplay/osu/disc"),
|
||||||
},
|
},
|
||||||
|
new KiaiFlash
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
triangles = new TrianglesPiece
|
triangles = new TrianglesPiece
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
// 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.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|
||||||
{
|
|
||||||
public interface IMainCirclePiece
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Begins animating this <see cref="IMainCirclePiece"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="state">The <see cref="ArmedState"/> of the related <see cref="DrawableHitCircle"/>.</param>
|
|
||||||
void Animate(ArmedState state);
|
|
||||||
}
|
|
||||||
}
|
|
43
osu.Game.Rulesets.Osu/Skinning/Default/KiaiFlash.cs
Normal file
43
osu.Game.Rulesets.Osu/Skinning/Default/KiaiFlash.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||||
|
{
|
||||||
|
public class KiaiFlash : BeatSyncedContainer
|
||||||
|
{
|
||||||
|
private const double fade_length = 80;
|
||||||
|
|
||||||
|
private const float flash_opacity = 0.25f;
|
||||||
|
|
||||||
|
public KiaiFlash()
|
||||||
|
{
|
||||||
|
EarlyActivationMilliseconds = 80;
|
||||||
|
Blending = BlendingParameters.Additive;
|
||||||
|
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.White,
|
||||||
|
Alpha = 0f,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||||
|
{
|
||||||
|
if (!effectPoint.KiaiMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Child
|
||||||
|
.FadeTo(flash_opacity, EarlyActivationMilliseconds, Easing.OutQuint)
|
||||||
|
.Then()
|
||||||
|
.FadeOut(timingPoint.BeatLength - fade_length, Easing.OutSine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||||
{
|
{
|
||||||
public class MainCirclePiece : CompositeDrawable, IMainCirclePiece
|
public class MainCirclePiece : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly CirclePiece circle;
|
private readonly CirclePiece circle;
|
||||||
private readonly RingPiece ring;
|
private readonly RingPiece ring;
|
||||||
@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||||
|
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private DrawableHitObject drawableObject { get; set; }
|
private DrawableHitObject drawableObject { get; set; }
|
||||||
@ -53,6 +54,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
accentColour.BindTo(drawableObject.AccentColour);
|
accentColour.BindTo(drawableObject.AccentColour);
|
||||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||||
|
armedState.BindTo(drawableObject.State);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -67,16 +69,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
|
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
|
||||||
|
|
||||||
|
armedState.BindValueChanged(animate, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Animate(ArmedState state)
|
private void animate(ValueChangedEvent<ArmedState> state)
|
||||||
{
|
{
|
||||||
|
ClearTransforms(true);
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
|
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
|
||||||
glow.FadeOut(400);
|
glow.FadeOut(400);
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state.NewValue)
|
||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
const double flash_in = 40;
|
const double flash_in = 40;
|
||||||
@ -89,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
explode.FadeIn(flash_in);
|
explode.FadeIn(flash_in);
|
||||||
this.ScaleTo(1.5f, 400, Easing.OutQuad);
|
this.ScaleTo(1.5f, 400, Easing.OutQuad);
|
||||||
|
|
||||||
using (BeginDelayedSequence(flash_in, true))
|
using (BeginDelayedSequence(flash_in))
|
||||||
{
|
{
|
||||||
// after the flash, we can hide some elements that were behind it
|
// after the flash, we can hide some elements that were behind it
|
||||||
ring.FadeOut();
|
ring.FadeOut();
|
||||||
|
61
osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingSprite.cs
Normal file
61
osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingSprite.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||||
|
{
|
||||||
|
internal class KiaiFlashingSprite : BeatSyncedContainer
|
||||||
|
{
|
||||||
|
private readonly Sprite mainSprite;
|
||||||
|
private readonly Sprite flashingSprite;
|
||||||
|
|
||||||
|
public Texture Texture
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
mainSprite.Texture = value;
|
||||||
|
flashingSprite.Texture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const float flash_opacity = 0.3f;
|
||||||
|
|
||||||
|
public KiaiFlashingSprite()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
mainSprite = new Sprite
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
},
|
||||||
|
flashingSprite = new Sprite
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Alpha = 0,
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||||
|
{
|
||||||
|
if (!effectPoint.KiaiMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
flashingSprite
|
||||||
|
.FadeTo(flash_opacity)
|
||||||
|
.Then()
|
||||||
|
.FadeOut(timingPoint.BeatLength * 0.75f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,10 +11,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
public class LegacyCursor : OsuCursorSprite
|
public class LegacyCursor : OsuCursorSprite
|
||||||
{
|
{
|
||||||
|
private readonly ISkin skin;
|
||||||
private bool spin;
|
private bool spin;
|
||||||
|
|
||||||
public LegacyCursor()
|
public LegacyCursor(ISkin skin)
|
||||||
{
|
{
|
||||||
|
this.skin = skin;
|
||||||
Size = new Vector2(50);
|
Size = new Vector2(50);
|
||||||
|
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
@ -22,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin)
|
private void load()
|
||||||
{
|
{
|
||||||
bool centre = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorCentre)?.Value ?? true;
|
bool centre = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorCentre)?.Value ?? true;
|
||||||
spin = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorRotate)?.Value ?? true;
|
spin = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorRotate)?.Value ?? true;
|
||||||
|
@ -14,14 +14,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
public class LegacyCursorTrail : CursorTrail
|
public class LegacyCursorTrail : CursorTrail
|
||||||
{
|
{
|
||||||
|
private readonly ISkin skin;
|
||||||
private const double disjoint_trail_time_separation = 1000 / 60.0;
|
private const double disjoint_trail_time_separation = 1000 / 60.0;
|
||||||
|
|
||||||
private bool disjointTrail;
|
private bool disjointTrail;
|
||||||
private double lastTrailTime;
|
private double lastTrailTime;
|
||||||
private IBindable<float> cursorSize;
|
private IBindable<float> cursorSize;
|
||||||
|
|
||||||
|
public LegacyCursorTrail(ISkin skin)
|
||||||
|
{
|
||||||
|
this.skin = skin;
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin, OsuConfigManager config)
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
Texture = skin.GetTexture("cursortrail");
|
Texture = skin.GetTexture("cursortrail");
|
||||||
disjointTrail = skin.GetTexture("cursormiddle") == null;
|
disjointTrail = skin.GetTexture("cursormiddle") == null;
|
||||||
|
@ -5,14 +5,12 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
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 osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -20,7 +18,7 @@ using static osu.Game.Skinning.LegacySkinConfiguration;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||||
{
|
{
|
||||||
public class LegacyMainCirclePiece : CompositeDrawable, IMainCirclePiece
|
public class LegacyMainCirclePiece : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly string priorityLookup;
|
private readonly string priorityLookup;
|
||||||
private readonly bool hasNumber;
|
private readonly bool hasNumber;
|
||||||
@ -33,14 +31,15 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Container<Sprite> circleSprites;
|
private Container circleSprites;
|
||||||
private Sprite hitCircleSprite;
|
private Drawable hitCircleSprite;
|
||||||
private Sprite hitCircleOverlay;
|
private Drawable hitCircleOverlay;
|
||||||
|
|
||||||
private SkinnableSpriteText hitCircleText;
|
private SkinnableSpriteText hitCircleText;
|
||||||
|
|
||||||
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||||
|
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private DrawableHitObject drawableObject { get; set; }
|
private DrawableHitObject drawableObject { get; set; }
|
||||||
@ -72,20 +71,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
circleSprites = new Container<Sprite>
|
circleSprites = new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
hitCircleSprite = new Sprite
|
hitCircleSprite = new KiaiFlashingSprite
|
||||||
{
|
{
|
||||||
Texture = baseTexture,
|
Texture = baseTexture,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
hitCircleOverlay = new Sprite
|
hitCircleOverlay = new KiaiFlashingSprite
|
||||||
{
|
{
|
||||||
Texture = overlayTexture,
|
Texture = overlayTexture,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -115,6 +114,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
accentColour.BindTo(drawableObject.AccentColour);
|
accentColour.BindTo(drawableObject.AccentColour);
|
||||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||||
|
armedState.BindTo(drawableObject.State);
|
||||||
|
|
||||||
Texture getTextureWithFallback(string name)
|
Texture getTextureWithFallback(string name)
|
||||||
{
|
{
|
||||||
@ -139,15 +139,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||||
if (hasNumber)
|
if (hasNumber)
|
||||||
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
||||||
|
|
||||||
|
armedState.BindValueChanged(animate, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Animate(ArmedState state)
|
private void animate(ValueChangedEvent<ArmedState> state)
|
||||||
{
|
{
|
||||||
const double legacy_fade_duration = 240;
|
const double legacy_fade_duration = 240;
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
|
ClearTransforms(true);
|
||||||
|
|
||||||
|
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state.NewValue)
|
||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
||||||
|
@ -84,14 +84,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
case OsuSkinComponents.Cursor:
|
case OsuSkinComponents.Cursor:
|
||||||
if (Source.GetTexture("cursor") != null)
|
var cursorProvider = Source.FindProvider(s => s.GetTexture("cursor") != null);
|
||||||
return new LegacyCursor();
|
|
||||||
|
if (cursorProvider != null)
|
||||||
|
return new LegacyCursor(cursorProvider);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case OsuSkinComponents.CursorTrail:
|
case OsuSkinComponents.CursorTrail:
|
||||||
if (Source.GetTexture("cursortrail") != null)
|
var trailProvider = Source.FindProvider(s => s.GetTexture("cursortrail") != null);
|
||||||
return new LegacyCursorTrail();
|
|
||||||
|
if (trailProvider != null)
|
||||||
|
return new LegacyCursorTrail(trailProvider);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
AddStep("Bar line", () => SetContents(() =>
|
AddStep("Bar line", () => SetContents(_ =>
|
||||||
{
|
{
|
||||||
ScrollingHitObjectContainer hoc;
|
ScrollingHitObjectContainer hoc;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
return cont;
|
return cont;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Bar line (major)", () => SetContents(() =>
|
AddStep("Bar line (major)", () => SetContents(_ =>
|
||||||
{
|
{
|
||||||
ScrollingHitObjectContainer hoc;
|
ScrollingHitObjectContainer hoc;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
AddStep("Drum roll", () => SetContents(() =>
|
AddStep("Drum roll", () => SetContents(_ =>
|
||||||
{
|
{
|
||||||
var hoc = new ScrollingHitObjectContainer();
|
var hoc = new ScrollingHitObjectContainer();
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
return hoc;
|
return hoc;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Drum roll (strong)", () => SetContents(() =>
|
AddStep("Drum roll (strong)", () => SetContents(_ =>
|
||||||
{
|
{
|
||||||
var hoc = new ScrollingHitObjectContainer();
|
var hoc = new ScrollingHitObjectContainer();
|
||||||
|
|
||||||
|
@ -17,25 +17,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
AddStep("Centre hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
|
AddStep("Centre hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Centre hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true))
|
AddStep("Centre hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true))
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Rim hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
|
AddStep("Rim hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Rim hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true))
|
AddStep("Rim hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true))
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -54,16 +54,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
{
|
{
|
||||||
AddStep("set beatmap", () => setBeatmap());
|
AddStep("set beatmap", () => setBeatmap());
|
||||||
|
|
||||||
AddStep("clear state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear)));
|
AddStep("clear state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear)));
|
||||||
AddStep("idle state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle)));
|
AddStep("idle state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle)));
|
||||||
AddStep("kiai state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai)));
|
AddStep("kiai state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai)));
|
||||||
AddStep("fail state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail)));
|
AddStep("fail state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestInitialState()
|
public void TestInitialState()
|
||||||
{
|
{
|
||||||
AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
||||||
|
|
||||||
AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle));
|
AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle));
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
{
|
{
|
||||||
AddStep("set beatmap", () => setBeatmap());
|
AddStep("set beatmap", () => setBeatmap());
|
||||||
|
|
||||||
AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
||||||
|
|
||||||
AddStep("set clear state", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear));
|
AddStep("set clear state", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear));
|
||||||
AddStep("miss", () => mascots.ForEach(mascot => mascot.LastResult.Value = new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }));
|
AddStep("miss", () => mascots.ForEach(mascot => mascot.LastResult.Value = new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }));
|
||||||
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
{
|
{
|
||||||
Beatmap.Value.Track.Start();
|
Beatmap.Value.Track.Start();
|
||||||
|
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
var ruleset = new TaikoRuleset();
|
var ruleset = new TaikoRuleset();
|
||||||
return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo));
|
return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo));
|
||||||
|
@ -22,16 +22,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestNormalHit()
|
public void TestNormalHit()
|
||||||
{
|
{
|
||||||
AddStep("Great", () => SetContents(() => getContentFor(createHit(HitResult.Great))));
|
AddStep("Great", () => SetContents(_ => getContentFor(createHit(HitResult.Great))));
|
||||||
AddStep("Ok", () => SetContents(() => getContentFor(createHit(HitResult.Ok))));
|
AddStep("Ok", () => SetContents(_ => getContentFor(createHit(HitResult.Ok))));
|
||||||
AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss))));
|
AddStep("Miss", () => SetContents(_ => getContentFor(createHit(HitResult.Miss))));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(HitResult.Great)]
|
[TestCase(HitResult.Great)]
|
||||||
[TestCase(HitResult.Ok)]
|
[TestCase(HitResult.Ok)]
|
||||||
public void TestStrongHit(HitResult type)
|
public void TestStrongHit(HitResult type)
|
||||||
{
|
{
|
||||||
AddStep("create hit", () => SetContents(() => getContentFor(createStrongHit(type))));
|
AddStep("create hit", () => SetContents(_ => getContentFor(createStrongHit(type))));
|
||||||
AddStep("visualise second hit",
|
AddStep("visualise second hit",
|
||||||
() => this.ChildrenOfType<HitExplosion>()
|
() => this.ChildrenOfType<HitExplosion>()
|
||||||
.ForEach(e => e.VisualiseSecondHit(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement()))));
|
.ForEach(e => e.VisualiseSecondHit(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement()))));
|
||||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new TaikoInputManager(new TaikoRuleset().RulesetInfo)
|
SetContents(_ => new TaikoInputManager(new TaikoRuleset().RulesetInfo)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = new Container
|
Child = new Container
|
||||||
|
@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestKiaiHits()
|
public void TestKiaiHits()
|
||||||
{
|
{
|
||||||
AddStep("rim hit", () => SetContents(() => getContentFor(createHit(HitType.Rim))));
|
AddStep("rim hit", () => SetContents(_ => getContentFor(createHit(HitType.Rim))));
|
||||||
AddStep("centre hit", () => SetContents(() => getContentFor(createHit(HitType.Centre))));
|
AddStep("centre hit", () => SetContents(_ => getContentFor(createHit(HitType.Centre))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable getContentFor(DrawableTestHit hit)
|
private Drawable getContentFor(DrawableTestHit hit)
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
Beatmap.Value.Track.Start();
|
Beatmap.Value.Track.Start();
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo())
|
AddStep("Load playfield", () => SetContents(_ => new TaikoPlayfield(new ControlPointInfo())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
|
|
||||||
public TestSceneTaikoScroller()
|
public TestSceneTaikoScroller()
|
||||||
{
|
{
|
||||||
AddStep("Load scroller", () => SetContents(() =>
|
AddStep("Load scroller", () => SetContents(_ =>
|
||||||
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty())
|
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty())
|
||||||
{
|
{
|
||||||
Clock = new FramedClock(clock),
|
Clock = new FramedClock(clock),
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
[TestCase("taiko-normal-hitnormal")]
|
[TestCase("taiko-normal-hitnormal")]
|
||||||
[TestCase("normal-hitnormal")]
|
[TestCase("normal-hitnormal")]
|
||||||
// [TestCase("hitnormal")] intentionally broken (will play classic default instead).
|
[TestCase("hitnormal")]
|
||||||
public void TestDefaultCustomSampleFromBeatmap(string expectedSample)
|
public void TestDefaultCustomSampleFromBeatmap(string expectedSample)
|
||||||
{
|
{
|
||||||
SetupSkins(expectedSample, expectedSample);
|
SetupSkins(expectedSample, expectedSample);
|
||||||
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
[TestCase("taiko-normal-hitnormal")]
|
[TestCase("taiko-normal-hitnormal")]
|
||||||
[TestCase("normal-hitnormal")]
|
[TestCase("normal-hitnormal")]
|
||||||
// [TestCase("hitnormal")] intentionally broken (will play classic default instead).
|
[TestCase("hitnormal")]
|
||||||
public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample)
|
public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample)
|
||||||
{
|
{
|
||||||
SetupSkins(string.Empty, expectedSample);
|
SetupSkins(string.Empty, expectedSample);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||||
{
|
{
|
||||||
new Colour(mods),
|
new Colour(mods),
|
||||||
new Rhythm(mods),
|
new Rhythm(mods),
|
||||||
|
@ -4,14 +4,13 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
|
||||||
using osu.Game.Rulesets.Taiko.Replays;
|
using osu.Game.Rulesets.Taiko.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Mods
|
namespace osu.Game.Rulesets.Taiko.Mods
|
||||||
{
|
{
|
||||||
public class TaikoModAutoplay : ModAutoplay<TaikoHitObject>
|
public class TaikoModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -20,10 +20,13 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
var taikoBeatmap = (TaikoBeatmap)beatmap;
|
var taikoBeatmap = (TaikoBeatmap)beatmap;
|
||||||
|
|
||||||
|
Seed.Value ??= RNG.Next();
|
||||||
|
var rng = new Random((int)Seed.Value);
|
||||||
|
|
||||||
foreach (var obj in taikoBeatmap.HitObjects)
|
foreach (var obj in taikoBeatmap.HitObjects)
|
||||||
{
|
{
|
||||||
if (obj is Hit hit)
|
if (obj is Hit hit)
|
||||||
hit.Type = RNG.Next(2) == 0 ? HitType.Centre : HitType.Rim;
|
hit.Type = rng.Next(2) == 0 ? HitType.Centre : HitType.Rim;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,32 +152,35 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}");
|
throw new ArgumentOutOfRangeException(nameof(component), $"Invalid component type: {component}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ISample GetSample(ISampleInfo sampleInfo) => Source.GetSample(new LegacyTaikoSampleInfo(sampleInfo));
|
public override ISample GetSample(ISampleInfo sampleInfo)
|
||||||
|
{
|
||||||
|
if (sampleInfo is HitSampleInfo hitSampleInfo)
|
||||||
|
return Source.GetSample(new LegacyTaikoSampleInfo(hitSampleInfo));
|
||||||
|
|
||||||
|
return base.GetSample(sampleInfo);
|
||||||
|
}
|
||||||
|
|
||||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => Source.GetConfig<TLookup, TValue>(lookup);
|
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => Source.GetConfig<TLookup, TValue>(lookup);
|
||||||
|
|
||||||
private class LegacyTaikoSampleInfo : ISampleInfo
|
private class LegacyTaikoSampleInfo : HitSampleInfo
|
||||||
{
|
{
|
||||||
private readonly ISampleInfo source;
|
public LegacyTaikoSampleInfo(HitSampleInfo sampleInfo)
|
||||||
|
: base(sampleInfo.Name, sampleInfo.Bank, sampleInfo.Suffix, sampleInfo.Volume)
|
||||||
|
|
||||||
public LegacyTaikoSampleInfo(ISampleInfo source)
|
|
||||||
{
|
{
|
||||||
this.source = source;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> LookupNames
|
public override IEnumerable<string> LookupNames
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
foreach (var name in source.LookupNames)
|
foreach (var name in base.LookupNames)
|
||||||
yield return name.Insert(name.LastIndexOf('/') + 1, "taiko-");
|
yield return name.Insert(name.LastIndexOf('/') + 1, "taiko-");
|
||||||
|
|
||||||
foreach (var name in source.LookupNames)
|
foreach (var name in base.LookupNames)
|
||||||
yield return name;
|
yield return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Volume => source.Volume;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -489,5 +489,23 @@ namespace osu.Game.Tests.Chat
|
|||||||
Assert.AreEqual(result.Links[2].Url, "\uD83D\uDE00");
|
Assert.AreEqual(result.Links[2].Url, "\uD83D\uDE00");
|
||||||
Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20");
|
Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAbsoluteExternalLinks()
|
||||||
|
{
|
||||||
|
LinkDetails result = MessageFormatter.GetLinkDetails("https://google.com");
|
||||||
|
|
||||||
|
Assert.AreEqual(LinkAction.External, result.Action);
|
||||||
|
Assert.AreEqual("https://google.com", result.Argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRelativeExternalLinks()
|
||||||
|
{
|
||||||
|
LinkDetails result = MessageFormatter.GetLinkDetails("/relative");
|
||||||
|
|
||||||
|
Assert.AreEqual(LinkAction.External, result.Action);
|
||||||
|
Assert.AreEqual("/relative", result.Argument);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,41 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
AddAssert("Lifetime is correct", () => dho.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY && entry.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY);
|
AddAssert("Lifetime is correct", () => dho.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY && entry.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDrawableLifetimeUpdateOnEntryLifetimeChange()
|
||||||
|
{
|
||||||
|
TestDrawableHitObject dho = null;
|
||||||
|
TestLifetimeEntry entry = null;
|
||||||
|
AddStep("Create DHO", () =>
|
||||||
|
{
|
||||||
|
dho = new TestDrawableHitObject(null);
|
||||||
|
dho.Apply(entry = new TestLifetimeEntry(new HitObject()));
|
||||||
|
Child = dho;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("Set entry lifetime", () =>
|
||||||
|
{
|
||||||
|
entry.LifetimeStart = 777;
|
||||||
|
entry.LifetimeEnd = 888;
|
||||||
|
});
|
||||||
|
AddAssert("Drawable lifetime is updated", () => dho.LifetimeStart == 777 && dho.LifetimeEnd == 888);
|
||||||
|
|
||||||
|
AddStep("KeepAlive = true", () => entry.KeepAlive = true);
|
||||||
|
AddAssert("Drawable lifetime is updated", () => dho.LifetimeStart == double.MinValue && dho.LifetimeEnd == double.MaxValue);
|
||||||
|
|
||||||
|
AddStep("Modify start time", () => entry.HitObject.StartTime = 100);
|
||||||
|
AddAssert("Drawable lifetime is correct", () => dho.LifetimeStart == double.MinValue);
|
||||||
|
|
||||||
|
AddStep("Set LifetimeStart", () => dho.LifetimeStart = 666);
|
||||||
|
AddAssert("Lifetime change is blocked", () => dho.LifetimeStart == double.MinValue);
|
||||||
|
|
||||||
|
AddStep("Set LifetimeEnd", () => dho.LifetimeEnd = 999);
|
||||||
|
AddAssert("Lifetime change is blocked", () => dho.LifetimeEnd == double.MaxValue);
|
||||||
|
|
||||||
|
AddStep("KeepAlive = false", () => entry.KeepAlive = false);
|
||||||
|
AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 666 && dho.LifetimeEnd == 999);
|
||||||
|
}
|
||||||
|
|
||||||
private class TestDrawableHitObject : DrawableHitObject
|
private class TestDrawableHitObject : DrawableHitObject
|
||||||
{
|
{
|
||||||
public const double INITIAL_LIFETIME_OFFSET = 100;
|
public const double INITIAL_LIFETIME_OFFSET = 100;
|
||||||
|
@ -217,7 +217,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods)
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public abstract class SkinnableHUDComponentTestScene : SkinnableTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
SetContents(skin =>
|
||||||
|
{
|
||||||
|
var implementation = skin != null
|
||||||
|
? CreateLegacyImplementation()
|
||||||
|
: CreateDefaultImplementation();
|
||||||
|
|
||||||
|
implementation.Anchor = Anchor.Centre;
|
||||||
|
implementation.Origin = Anchor.Centre;
|
||||||
|
return implementation;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
protected abstract Drawable CreateDefaultImplementation();
|
||||||
|
protected abstract Drawable CreateLegacyImplementation();
|
||||||
|
}
|
||||||
|
}
|
@ -18,12 +18,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Description("Player instantiated with an autoplay mod.")]
|
[Description("Player instantiated with an autoplay mod.")]
|
||||||
public class TestSceneAutoplay : TestSceneAllRulesetPlayers
|
public class TestSceneAutoplay : TestSceneAllRulesetPlayers
|
||||||
{
|
{
|
||||||
protected new TestPlayer Player => (TestPlayer)base.Player;
|
protected new TestReplayPlayer Player => (TestReplayPlayer)base.Player;
|
||||||
|
|
||||||
protected override Player CreatePlayer(Ruleset ruleset)
|
protected override Player CreatePlayer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
SelectedMods.Value = new[] { ruleset.GetAutoplayMod() };
|
SelectedMods.Value = new[] { ruleset.GetAutoplayMod() };
|
||||||
return new TestPlayer(false);
|
return new TestReplayPlayer(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddCheckSteps()
|
protected override void AddCheckSteps()
|
||||||
|
@ -90,6 +90,20 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
assertChildPosition(5);
|
assertChildPosition(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase("pooled")]
|
||||||
|
[TestCase("non-pooled")]
|
||||||
|
public void TestLifetimeRecomputedWhenTimeRangeChanges(string pooled)
|
||||||
|
{
|
||||||
|
var beatmap = createBeatmap(_ => pooled == "pooled" ? new TestPooledHitObject() : new TestHitObject());
|
||||||
|
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range });
|
||||||
|
createTest(beatmap);
|
||||||
|
|
||||||
|
assertDead(3);
|
||||||
|
|
||||||
|
AddStep("increase time range", () => drawableRuleset.TimeRange.Value = 3 * time_range);
|
||||||
|
assertPosition(3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestRelativeBeatLengthScaleSingleTimingPoint()
|
public void TestRelativeBeatLengthScaleSingleTimingPoint()
|
||||||
{
|
{
|
||||||
|
@ -29,8 +29,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddStep("allow skin lookup", () => storyboard.UseSkinSprites = false);
|
AddStep("allow skin lookup", () => storyboard.UseSkinSprites = false);
|
||||||
|
|
||||||
AddStep("create sprites", () => SetContents(
|
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||||
() => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
|
||||||
|
|
||||||
assertSpritesFromSkin(false);
|
assertSpritesFromSkin(false);
|
||||||
}
|
}
|
||||||
@ -42,8 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddStep("allow skin lookup", () => storyboard.UseSkinSprites = true);
|
AddStep("allow skin lookup", () => storyboard.UseSkinSprites = true);
|
||||||
|
|
||||||
AddStep("create sprites", () => SetContents(
|
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.Centre, Vector2.Zero)));
|
||||||
() => createSprite(lookup_name, Anchor.Centre, Vector2.Zero)));
|
|
||||||
|
|
||||||
assertSpritesFromSkin(true);
|
assertSpritesFromSkin(true);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestToggleEditor()
|
public void TestToggleEditor()
|
||||||
{
|
{
|
||||||
AddStep("show available components", () => SetContents(() => new SkinComponentToolbox(300)
|
AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox(300)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
AddStep("create editor overlay", () =>
|
AddStep("create editor overlay", () =>
|
||||||
{
|
{
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
var ruleset = new OsuRuleset();
|
var ruleset = new OsuRuleset();
|
||||||
var mods = new[] { ruleset.GetAutoplayMod() };
|
var mods = new[] { ruleset.GetAutoplayMod() };
|
||||||
|
@ -3,26 +3,26 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
public class TestSceneSkinnableAccuracyCounter : SkinnableTestScene
|
public class TestSceneSkinnableAccuracyCounter : SkinnableHUDComponentTestScene
|
||||||
{
|
{
|
||||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||||
|
|
||||||
|
protected override Drawable CreateDefaultImplementation() => new DefaultAccuracyCounter();
|
||||||
|
protected override Drawable CreateLegacyImplementation() => new LegacyAccuracyCounter();
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public void SetUpSteps()
|
public void SetUpSteps()
|
||||||
{
|
{
|
||||||
AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1);
|
AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1);
|
||||||
AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -3,26 +3,19 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
public class TestSceneComboCounter : SkinnableTestScene
|
public class TestSceneSkinnableComboCounter : SkinnableHUDComponentTestScene
|
||||||
{
|
{
|
||||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||||
|
|
||||||
[SetUpSteps]
|
protected override Drawable CreateDefaultImplementation() => new DefaultComboCounter();
|
||||||
public void SetUpSteps()
|
protected override Drawable CreateLegacyImplementation() => new LegacyComboCounter();
|
||||||
{
|
|
||||||
AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter))));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestComboCounterIncrementing()
|
public void TestComboCounterIncrementing()
|
@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
AddStep("create overlay", () =>
|
AddStep("create overlay", () =>
|
||||||
{
|
{
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
hudOverlay = new HUDOverlay(null, Array.Empty<Mod>());
|
hudOverlay = new HUDOverlay(null, Array.Empty<Mod>());
|
||||||
|
|
||||||
|
@ -3,28 +3,28 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Osu;
|
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
public class TestSceneSkinnableHealthDisplay : SkinnableTestScene
|
public class TestSceneSkinnableHealthDisplay : SkinnableHUDComponentTestScene
|
||||||
{
|
{
|
||||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
|
||||||
|
|
||||||
[Cached(typeof(HealthProcessor))]
|
[Cached(typeof(HealthProcessor))]
|
||||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||||
|
|
||||||
|
protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay();
|
||||||
|
protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay();
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public void SetUpSteps()
|
public void SetUpSteps()
|
||||||
{
|
{
|
||||||
AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay))));
|
|
||||||
AddStep(@"Reset all", delegate
|
AddStep(@"Reset all", delegate
|
||||||
{
|
{
|
||||||
healthProcessor.Health.Value = 1;
|
healthProcessor.Health.Value = 1;
|
||||||
|
@ -3,26 +3,20 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Rulesets.Osu;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
public class TestSceneSkinnableScoreCounter : SkinnableTestScene
|
public class TestSceneSkinnableScoreCounter : SkinnableHUDComponentTestScene
|
||||||
{
|
{
|
||||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
|
||||||
|
|
||||||
[Cached]
|
[Cached]
|
||||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||||
|
|
||||||
[SetUpSteps]
|
protected override Drawable CreateDefaultImplementation() => new DefaultScoreCounter();
|
||||||
public void SetUpSteps()
|
protected override Drawable CreateLegacyImplementation() => new LegacyScoreCounter();
|
||||||
{
|
|
||||||
AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter))));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestScoreCounterIncrementing()
|
public void TestScoreCounterIncrementing()
|
||||||
|
@ -12,6 +12,7 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Overlays.Toolbar;
|
using osu.Game.Overlays.Toolbar;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Screens.Ranking;
|
using osu.Game.Screens.Ranking;
|
||||||
@ -95,11 +96,12 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
|
|
||||||
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
||||||
|
|
||||||
AddStep("set autoplay", () => Game.SelectedMods.Value = new[] { new OsuModAutoplay() });
|
AddStep("set mods", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModDoubleTime { SpeedChange = { Value = 2 } } });
|
||||||
|
|
||||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||||
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
|
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
|
||||||
AddStep("seek to end", () => player.ChildrenOfType<GameplayClockContainer>().First().Seek(beatmap().Track.Length));
|
AddUntilStep("wait for track playing", () => beatmap().Track.IsRunning);
|
||||||
|
AddStep("seek to near end", () => player.ChildrenOfType<GameplayClockContainer>().First().Seek(beatmap().Beatmap.HitObjects[^1].StartTime - 1000));
|
||||||
AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded);
|
AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded);
|
||||||
AddStep("attempt to retry", () => results.ChildrenOfType<HotkeyRetryOverlay>().First().Action());
|
AddStep("attempt to retry", () => results.ChildrenOfType<HotkeyRetryOverlay>().First().Action());
|
||||||
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != player && Game.ScreenStack.CurrentScreen is Player);
|
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != player && Game.ScreenStack.CurrentScreen is Player);
|
||||||
|
@ -9,6 +9,8 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -36,6 +38,10 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
private Channel previousChannel => joinedChannels.ElementAt(joinedChannels.ToList().IndexOf(currentChannel) - 1);
|
private Channel previousChannel => joinedChannels.ElementAt(joinedChannels.ToList().IndexOf(currentChannel) - 1);
|
||||||
private Channel channel1 => channels[0];
|
private Channel channel1 => channels[0];
|
||||||
private Channel channel2 => channels[1];
|
private Channel channel2 => channels[1];
|
||||||
|
private Channel channel3 => channels[2];
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private GameHost host { get; set; }
|
||||||
|
|
||||||
public TestSceneChatOverlay()
|
public TestSceneChatOverlay()
|
||||||
{
|
{
|
||||||
@ -44,7 +50,8 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
Name = $"Channel no. {index}",
|
Name = $"Channel no. {index}",
|
||||||
Topic = index == 3 ? null : $"We talk about the number {index} here",
|
Topic = index == 3 ? null : $"We talk about the number {index} here",
|
||||||
Type = index % 2 == 0 ? ChannelType.PM : ChannelType.Temporary
|
Type = index % 2 == 0 ? ChannelType.PM : ChannelType.Temporary,
|
||||||
|
Id = index
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
@ -228,6 +235,92 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any());
|
AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCloseTabShortcut()
|
||||||
|
{
|
||||||
|
AddStep("Join 2 channels", () =>
|
||||||
|
{
|
||||||
|
channelManager.JoinChannel(channel1);
|
||||||
|
channelManager.JoinChannel(channel2);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Want to close channel 2
|
||||||
|
AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2]));
|
||||||
|
AddStep("Close tab via shortcut", pressCloseDocumentKeys);
|
||||||
|
|
||||||
|
// Channel 2 should be closed
|
||||||
|
AddAssert("Channel 1 open", () => channelManager.JoinedChannels.Contains(channel1));
|
||||||
|
AddAssert("Channel 2 closed", () => !channelManager.JoinedChannels.Contains(channel2));
|
||||||
|
|
||||||
|
// Want to close channel 1
|
||||||
|
AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
|
||||||
|
|
||||||
|
AddStep("Close tab via shortcut", pressCloseDocumentKeys);
|
||||||
|
// Channel 1 and channel 2 should be closed
|
||||||
|
AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNewTabShortcut()
|
||||||
|
{
|
||||||
|
AddStep("Join 2 channels", () =>
|
||||||
|
{
|
||||||
|
channelManager.JoinChannel(channel1);
|
||||||
|
channelManager.JoinChannel(channel2);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Want to join another channel
|
||||||
|
AddStep("Press new tab shortcut", pressNewTabKeys);
|
||||||
|
|
||||||
|
// Selector should be visible
|
||||||
|
AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRestoreTabShortcut()
|
||||||
|
{
|
||||||
|
AddStep("Join 3 channels", () =>
|
||||||
|
{
|
||||||
|
channelManager.JoinChannel(channel1);
|
||||||
|
channelManager.JoinChannel(channel2);
|
||||||
|
channelManager.JoinChannel(channel3);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should do nothing
|
||||||
|
AddStep("Restore tab via shortcut", pressRestoreTabKeys);
|
||||||
|
AddAssert("All channels still open", () => channelManager.JoinedChannels.Count == 3);
|
||||||
|
|
||||||
|
// Close channel 1
|
||||||
|
AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
|
||||||
|
AddStep("Click normal close button", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child));
|
||||||
|
AddAssert("Channel 1 closed", () => !channelManager.JoinedChannels.Contains(channel1));
|
||||||
|
AddAssert("Other channels still open", () => channelManager.JoinedChannels.Count == 2);
|
||||||
|
|
||||||
|
// Reopen channel 1
|
||||||
|
AddStep("Restore tab via shortcut", pressRestoreTabKeys);
|
||||||
|
AddAssert("All channels now open", () => channelManager.JoinedChannels.Count == 3);
|
||||||
|
AddAssert("Current channel is channel 1", () => currentChannel == channel1);
|
||||||
|
|
||||||
|
// Close two channels
|
||||||
|
AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
|
||||||
|
AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child));
|
||||||
|
AddStep("Select channel 2", () => clickDrawable(chatOverlay.TabMap[channel2]));
|
||||||
|
AddStep("Close channel 2", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child));
|
||||||
|
AddAssert("Only one channel open", () => channelManager.JoinedChannels.Count == 1);
|
||||||
|
AddAssert("Current channel is channel 3", () => currentChannel == channel3);
|
||||||
|
|
||||||
|
// Should first re-open channel 2
|
||||||
|
AddStep("Restore tab via shortcut", pressRestoreTabKeys);
|
||||||
|
AddAssert("Channel 1 still closed", () => !channelManager.JoinedChannels.Contains(channel1));
|
||||||
|
AddAssert("Channel 2 now open", () => channelManager.JoinedChannels.Contains(channel2));
|
||||||
|
AddAssert("Current channel is channel 2", () => currentChannel == channel2);
|
||||||
|
|
||||||
|
// Should then re-open channel 1
|
||||||
|
AddStep("Restore tab via shortcut", pressRestoreTabKeys);
|
||||||
|
AddAssert("All channels now open", () => channelManager.JoinedChannels.Count == 3);
|
||||||
|
AddAssert("Current channel is channel 1", () => currentChannel == channel1);
|
||||||
|
}
|
||||||
|
|
||||||
private void pressChannelHotkey(int number)
|
private void pressChannelHotkey(int number)
|
||||||
{
|
{
|
||||||
var channelKey = Key.Number0 + number;
|
var channelKey = Key.Number0 + number;
|
||||||
@ -236,6 +329,23 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
InputManager.ReleaseKey(Key.AltLeft);
|
InputManager.ReleaseKey(Key.AltLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void pressCloseDocumentKeys() => pressKeysFor(PlatformActionType.DocumentClose);
|
||||||
|
|
||||||
|
private void pressNewTabKeys() => pressKeysFor(PlatformActionType.TabNew);
|
||||||
|
|
||||||
|
private void pressRestoreTabKeys() => pressKeysFor(PlatformActionType.TabRestore);
|
||||||
|
|
||||||
|
private void pressKeysFor(PlatformActionType type)
|
||||||
|
{
|
||||||
|
var binding = host.PlatformKeyBindings.First(b => ((PlatformAction)b.Action).ActionType == type);
|
||||||
|
|
||||||
|
foreach (var k in binding.KeyCombination.Keys)
|
||||||
|
InputManager.PressKey((Key)k);
|
||||||
|
|
||||||
|
foreach (var k in binding.KeyCombination.Keys)
|
||||||
|
InputManager.ReleaseKey((Key)k);
|
||||||
|
}
|
||||||
|
|
||||||
private void clickDrawable(Drawable d)
|
private void clickDrawable(Drawable d)
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(d);
|
InputManager.MoveMouseTo(d);
|
||||||
|
@ -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.Net;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.API.Requests;
|
using osu.Game.Online.API.Requests;
|
||||||
@ -32,7 +33,14 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddStep("Show Article Page", () => wiki.ShowPage("Interface"));
|
AddStep("Show Article Page", () => wiki.ShowPage("Interface"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpWikiResponse(APIWikiPage r)
|
[Test]
|
||||||
|
public void TestErrorPage()
|
||||||
|
{
|
||||||
|
setUpWikiResponse(null, true);
|
||||||
|
AddStep("Show Error Page", () => wiki.ShowPage("Error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpWikiResponse(APIWikiPage r, bool isFailed = false)
|
||||||
=> AddStep("set up response", () =>
|
=> AddStep("set up response", () =>
|
||||||
{
|
{
|
||||||
dummyAPI.HandleRequest = request =>
|
dummyAPI.HandleRequest = request =>
|
||||||
@ -40,7 +48,11 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
if (!(request is GetWikiRequest getWikiRequest))
|
if (!(request is GetWikiRequest getWikiRequest))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
getWikiRequest.TriggerSuccess(r);
|
if (isFailed)
|
||||||
|
getWikiRequest.TriggerFailure(new WebException());
|
||||||
|
else
|
||||||
|
getWikiRequest.TriggerSuccess(r);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="DeepEqual" Version="2.0.0" />
|
<PackageReference Include="DeepEqual" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user