mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 03:15:45 +08: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
|
||||
|
||||
#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
|
||||
dotnet_naming_style.camelcase.capitalization = camel_case
|
||||
|
||||
@ -197,4 +189,4 @@ dotnet_diagnostic.IDE0069.severity = none
|
||||
dotnet_diagnostic.CA2225.severity = none
|
||||
|
||||
# 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.
|
||||
|
||||
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
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<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 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 osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.EmptyFreeform.Objects;
|
||||
using osu.Game.Rulesets.EmptyFreeform.Replays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
@ -11,7 +10,7 @@ using osu.Game.Users;
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
@ -4,14 +4,13 @@
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Pippidon.Objects;
|
||||
using osu.Game.Rulesets.Pippidon.Replays;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon
|
||||
|
||||
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>
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<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 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.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.EmptyScrolling.Objects;
|
||||
using osu.Game.Rulesets.EmptyScrolling.Replays;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
@ -11,7 +10,7 @@ using System.Collections.Generic;
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
@ -4,14 +4,13 @@
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Pippidon.Objects;
|
||||
using osu.Game.Rulesets.Pippidon.Replays;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon
|
||||
|
||||
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>
|
||||
<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>
|
||||
</Project>
|
||||
|
@ -57,7 +57,7 @@ namespace osu.Desktop
|
||||
|
||||
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;
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
</ItemGroup>
|
||||
|
@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
CircleSize = circleSize
|
||||
};
|
||||
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
var droppedObjectContainer = new Container<CaughtObject>
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
scoreProcessor = new ScoreProcessor();
|
||||
|
||||
SetContents(() => new CatchComboDisplay
|
||||
SetContents(_ => new CatchComboDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -19,22 +19,22 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
AddStep("show pear", () => SetContents(() => createDrawableFruit(0)));
|
||||
AddStep("show grape", () => SetContents(() => createDrawableFruit(1)));
|
||||
AddStep("show pineapple / apple", () => SetContents(() => createDrawableFruit(2)));
|
||||
AddStep("show raspberry / orange", () => SetContents(() => createDrawableFruit(3)));
|
||||
AddStep("show pear", () => SetContents(_ => createDrawableFruit(0)));
|
||||
AddStep("show grape", () => SetContents(_ => createDrawableFruit(1)));
|
||||
AddStep("show pineapple / apple", () => SetContents(_ => createDrawableFruit(2)));
|
||||
AddStep("show raspberry / orange", () => SetContents(_ => createDrawableFruit(3)));
|
||||
|
||||
AddStep("show banana", () => SetContents(createDrawableBanana));
|
||||
AddStep("show banana", () => SetContents(_ => createDrawableBanana()));
|
||||
|
||||
AddStep("show droplet", () => SetContents(() => createDrawableDroplet()));
|
||||
AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet));
|
||||
AddStep("show droplet", () => SetContents(_ => createDrawableDroplet()));
|
||||
AddStep("show tiny droplet", () => SetContents(_ => createDrawableTinyDroplet()));
|
||||
|
||||
AddStep("show hyperdash pear", () => SetContents(() => createDrawableFruit(0, true)));
|
||||
AddStep("show hyperdash grape", () => SetContents(() => createDrawableFruit(1, true)));
|
||||
AddStep("show hyperdash pineapple / apple", () => SetContents(() => createDrawableFruit(2, true)));
|
||||
AddStep("show hyperdash raspberry / orange", () => SetContents(() => createDrawableFruit(3, true)));
|
||||
AddStep("show hyperdash pear", () => SetContents(_ => createDrawableFruit(0, true)));
|
||||
AddStep("show hyperdash grape", () => SetContents(_ => createDrawableFruit(1, true)));
|
||||
AddStep("show hyperdash pineapple / apple", () => SetContents(_ => createDrawableFruit(2, 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) =>
|
||||
|
@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
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 },
|
||||
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 },
|
||||
}))));
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<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;
|
||||
|
||||
@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
|
||||
return new Skill[]
|
||||
{
|
||||
new Movement(mods, halfCatcherWidth),
|
||||
new Movement(mods, halfCatcherWidth, clockRate),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
||||
/// </summary>
|
||||
public readonly double StrainTime;
|
||||
|
||||
public readonly double ClockRate;
|
||||
|
||||
public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
|
||||
: 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
|
||||
StrainTime = Math.Max(40, DeltaTime);
|
||||
ClockRate = clockRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +28,21 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
private float lastDistanceMoved;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@ -48,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
|
||||
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 sqrtStrain = Math.Sqrt(weightedStrainTime);
|
||||
@ -81,7 +92,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
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;
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
@ -11,7 +10,7 @@ using osu.Game.Users;
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
SetContents(() => new FillFlowContainer
|
||||
SetContents(_ => new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() => new FillFlowContainer
|
||||
SetContents(_ => new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() => new FillFlowContainer
|
||||
SetContents(_ => new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
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())
|
||||
{
|
||||
Type = result
|
||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
var pool = new DrawablePool<PoolableHitExplosion>(5);
|
||||
hitExplosionPools.Add(pool);
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() => new FillFlowContainer
|
||||
SetContents(_ => new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
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 }
|
||||
};
|
||||
|
||||
SetContents(() => new ManiaPlayfield(stageDefinitions));
|
||||
SetContents(_ => new ManiaPlayfield(stageDefinitions));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
ManiaAction normalAction = ManiaAction.Key1;
|
||||
ManiaAction specialAction = ManiaAction.Special1;
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
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())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
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,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<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.
|
||||
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)
|
||||
};
|
||||
|
@ -4,7 +4,6 @@
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
@ -12,7 +11,7 @@ using osu.Game.Users;
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Utils;
|
||||
@ -17,8 +18,11 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
Seed.Value ??= RNG.Next();
|
||||
var rng = new Random((int)Seed.Value);
|
||||
|
||||
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]);
|
||||
}
|
||||
|
@ -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]
|
||||
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]
|
||||
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;
|
||||
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
DrawablePool<TestDrawableOsuJudgement> pool;
|
||||
|
||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private void loadContent(bool automated = true, Func<SkinProvidingContainer> skinProvider = null)
|
||||
{
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
var inputManager = automated ? (InputManager)new MovingCursorInputManager() : new OsuInputManager(new OsuRuleset().RulesetInfo);
|
||||
var skinContainer = skinProvider?.Invoke() ?? new SkinProvidingContainer(null);
|
||||
|
@ -23,18 +23,18 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[Test]
|
||||
public void TestVariousHitCircles()
|
||||
{
|
||||
AddStep("Miss Big Single", () => SetContents(() => testSingle(2)));
|
||||
AddStep("Miss Medium Single", () => SetContents(() => testSingle(5)));
|
||||
AddStep("Miss Small Single", () => SetContents(() => testSingle(7)));
|
||||
AddStep("Hit Big Single", () => SetContents(() => testSingle(2, true)));
|
||||
AddStep("Hit Medium Single", () => SetContents(() => testSingle(5, true)));
|
||||
AddStep("Hit Small Single", () => SetContents(() => testSingle(7, true)));
|
||||
AddStep("Miss Big Stream", () => SetContents(() => testStream(2)));
|
||||
AddStep("Miss Medium Stream", () => SetContents(() => testStream(5)));
|
||||
AddStep("Miss Small Stream", () => SetContents(() => testStream(7)));
|
||||
AddStep("Hit Big Stream", () => SetContents(() => testStream(2, true)));
|
||||
AddStep("Hit Medium Stream", () => SetContents(() => testStream(5, true)));
|
||||
AddStep("Hit Small Stream", () => SetContents(() => testStream(7, true)));
|
||||
AddStep("Miss Big Single", () => SetContents(_ => testSingle(2)));
|
||||
AddStep("Miss Medium Single", () => SetContents(_ => testSingle(5)));
|
||||
AddStep("Miss Small Single", () => SetContents(_ => testSingle(7)));
|
||||
AddStep("Hit Big Single", () => SetContents(_ => testSingle(2, true)));
|
||||
AddStep("Hit Medium Single", () => SetContents(_ => testSingle(5, true)));
|
||||
AddStep("Hit Small Single", () => SetContents(_ => testSingle(7, true)));
|
||||
AddStep("Miss Big Stream", () => SetContents(_ => testStream(2)));
|
||||
AddStep("Miss Medium Stream", () => SetContents(_ => testStream(5)));
|
||||
AddStep("Miss Small Stream", () => SetContents(_ => testStream(7)));
|
||||
AddStep("Hit Big Stream", () => SetContents(_ => testStream(2, true)));
|
||||
AddStep("Hit Medium Stream", () => SetContents(_ => testStream(5, true)));
|
||||
AddStep("Hit Small Stream", () => SetContents(_ => testStream(7, true)));
|
||||
}
|
||||
|
||||
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]
|
||||
public void TestVariousSliders()
|
||||
{
|
||||
AddStep("Big Single", () => SetContents(() => testSimpleBig()));
|
||||
AddStep("Medium Single", () => SetContents(() => testSimpleMedium()));
|
||||
AddStep("Small Single", () => SetContents(() => testSimpleSmall()));
|
||||
AddStep("Big 1 Repeat", () => SetContents(() => testSimpleBig(1)));
|
||||
AddStep("Medium 1 Repeat", () => SetContents(() => testSimpleMedium(1)));
|
||||
AddStep("Small 1 Repeat", () => SetContents(() => testSimpleSmall(1)));
|
||||
AddStep("Big 2 Repeats", () => SetContents(() => testSimpleBig(2)));
|
||||
AddStep("Medium 2 Repeats", () => SetContents(() => testSimpleMedium(2)));
|
||||
AddStep("Small 2 Repeats", () => SetContents(() => testSimpleSmall(2)));
|
||||
AddStep("Big Single", () => SetContents(_ => testSimpleBig()));
|
||||
AddStep("Medium Single", () => SetContents(_ => testSimpleMedium()));
|
||||
AddStep("Small Single", () => SetContents(_ => testSimpleSmall()));
|
||||
AddStep("Big 1 Repeat", () => SetContents(_ => testSimpleBig(1)));
|
||||
AddStep("Medium 1 Repeat", () => SetContents(_ => testSimpleMedium(1)));
|
||||
AddStep("Small 1 Repeat", () => SetContents(_ => testSimpleSmall(1)));
|
||||
AddStep("Big 2 Repeats", () => SetContents(_ => testSimpleBig(2)));
|
||||
AddStep("Medium 2 Repeats", () => SetContents(_ => testSimpleMedium(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 Short Slider", () => SetContents(() => testShortSlowSpeed()));
|
||||
AddStep("Slow Short Slider 1 Repeats", () => SetContents(() => testShortSlowSpeed(1)));
|
||||
AddStep("Slow Short Slider 2 Repeats", () => SetContents(() => testShortSlowSpeed(2)));
|
||||
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 1 Repeats", () => SetContents(_ => testShortSlowSpeed(1)));
|
||||
AddStep("Slow Short Slider 2 Repeats", () => SetContents(_ => testShortSlowSpeed(2)));
|
||||
|
||||
AddStep("Fast Slider", () => SetContents(() => testHighSpeed()));
|
||||
AddStep("Fast Slider 1 Repeat", () => SetContents(() => testHighSpeed(1)));
|
||||
AddStep("Fast Slider 2 Repeats", () => SetContents(() => testHighSpeed(2)));
|
||||
AddStep("Fast Short Slider", () => SetContents(() => testShortHighSpeed()));
|
||||
AddStep("Fast Short Slider 1 Repeat", () => SetContents(() => testShortHighSpeed(1)));
|
||||
AddStep("Fast Short Slider 2 Repeats", () => SetContents(() => testShortHighSpeed(2)));
|
||||
AddStep("Fast Short Slider 6 Repeats", () => SetContents(() => testShortHighSpeed(6)));
|
||||
AddStep("Fast Slider", () => SetContents(_ => testHighSpeed()));
|
||||
AddStep("Fast Slider 1 Repeat", () => SetContents(_ => testHighSpeed(1)));
|
||||
AddStep("Fast Slider 2 Repeats", () => SetContents(_ => testHighSpeed(2)));
|
||||
AddStep("Fast Short Slider", () => SetContents(_ => testShortHighSpeed()));
|
||||
AddStep("Fast Short Slider 1 Repeat", () => SetContents(_ => testShortHighSpeed(1)));
|
||||
AddStep("Fast Short Slider 2 Repeats", () => SetContents(_ => testShortHighSpeed(2)));
|
||||
AddStep("Fast Short Slider 6 Repeats", () => SetContents(_ => testShortHighSpeed(6)));
|
||||
|
||||
AddStep("Perfect Curve", () => SetContents(() => testPerfect()));
|
||||
AddStep("Perfect Curve 1 Repeat", () => SetContents(() => testPerfect(1)));
|
||||
AddStep("Perfect Curve 2 Repeats", () => SetContents(() => testPerfect(2)));
|
||||
AddStep("Perfect Curve", () => SetContents(_ => testPerfect()));
|
||||
AddStep("Perfect Curve 1 Repeat", () => SetContents(_ => testPerfect(1)));
|
||||
AddStep("Perfect Curve 2 Repeats", () => SetContents(_ => testPerfect(2)));
|
||||
|
||||
AddStep("Linear Slider", () => SetContents(() => testLinear()));
|
||||
AddStep("Linear Slider 1 Repeat", () => SetContents(() => testLinear(1)));
|
||||
AddStep("Linear Slider 2 Repeats", () => SetContents(() => testLinear(2)));
|
||||
AddStep("Linear Slider", () => SetContents(_ => testLinear()));
|
||||
AddStep("Linear Slider 1 Repeat", () => SetContents(_ => testLinear(1)));
|
||||
AddStep("Linear Slider 2 Repeats", () => SetContents(_ => testLinear(2)));
|
||||
|
||||
AddStep("Bezier Slider", () => SetContents(() => testBezier()));
|
||||
AddStep("Bezier Slider 1 Repeat", () => SetContents(() => testBezier(1)));
|
||||
AddStep("Bezier Slider 2 Repeats", () => SetContents(() => testBezier(2)));
|
||||
AddStep("Bezier Slider", () => SetContents(_ => testBezier()));
|
||||
AddStep("Bezier Slider 1 Repeat", () => SetContents(_ => testBezier(1)));
|
||||
AddStep("Bezier Slider 2 Repeats", () => SetContents(_ => testBezier(2)));
|
||||
|
||||
AddStep("Linear Overlapping", () => SetContents(() => testLinearOverlapping()));
|
||||
AddStep("Linear Overlapping 1 Repeat", () => SetContents(() => testLinearOverlapping(1)));
|
||||
AddStep("Linear Overlapping 2 Repeats", () => SetContents(() => testLinearOverlapping(2)));
|
||||
AddStep("Linear Overlapping", () => SetContents(_ => testLinearOverlapping()));
|
||||
AddStep("Linear Overlapping 1 Repeat", () => SetContents(_ => testLinearOverlapping(1)));
|
||||
AddStep("Linear Overlapping 2 Repeats", () => SetContents(_ => testLinearOverlapping(2)));
|
||||
|
||||
AddStep("Catmull Slider", () => SetContents(() => testCatmull()));
|
||||
AddStep("Catmull Slider 1 Repeat", () => SetContents(() => testCatmull(1)));
|
||||
AddStep("Catmull Slider 2 Repeats", () => SetContents(() => testCatmull(2)));
|
||||
AddStep("Catmull Slider", () => SetContents(_ => testCatmull()));
|
||||
AddStep("Catmull Slider 1 Repeat", () => SetContents(_ => testCatmull(1)));
|
||||
AddStep("Catmull Slider 2 Repeats", () => SetContents(_ => testCatmull(2)));
|
||||
|
||||
AddStep("Big Single, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset()));
|
||||
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset(1)));
|
||||
AddStep("Big Single, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset()));
|
||||
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset(1)));
|
||||
|
||||
AddStep("Distance Overflow", () => SetContents(() => testDistanceOverflow()));
|
||||
AddStep("Distance Overflow 1 Repeat", () => SetContents(() => testDistanceOverflow(1)));
|
||||
AddStep("Distance Overflow", () => SetContents(_ => testDistanceOverflow()));
|
||||
AddStep("Distance Overflow 1 Repeat", () => SetContents(_ => testDistanceOverflow(1)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -29,15 +29,15 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
public void TestVariousSpinners(bool autoplay)
|
||||
{
|
||||
string term = autoplay ? "Hit" : "Miss";
|
||||
AddStep($"{term} Big", () => SetContents(() => testSingle(2, autoplay)));
|
||||
AddStep($"{term} Medium", () => SetContents(() => testSingle(5, autoplay)));
|
||||
AddStep($"{term} Small", () => SetContents(() => testSingle(7, autoplay)));
|
||||
AddStep($"{term} Big", () => SetContents(_ => testSingle(2, autoplay)));
|
||||
AddStep($"{term} Medium", () => SetContents(_ => testSingle(5, autoplay)));
|
||||
AddStep($"{term} Small", () => SetContents(_ => testSingle(7, autoplay)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
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 increases", () => getSpinningSample().Frequency.Value > 0.8);
|
||||
|
||||
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase(true)]
|
||||
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("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0));
|
||||
}
|
||||
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase(true)]
|
||||
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("Short spinner implicitly completes", () => drawableSpinner.Progress == 1);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<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 Speed(mods)
|
||||
|
@ -6,14 +6,13 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
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();
|
||||
|
||||
|
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.
|
||||
this.Delay(800).FadeOut();
|
||||
|
||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
|
@ -97,8 +97,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
base.UpdateHitStateTransforms(state);
|
||||
|
||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
|
@ -87,8 +87,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
Debug.Assert(HitObject.HitWindows != null);
|
||||
|
||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
|
@ -164,7 +164,8 @@ namespace osu.Game.Rulesets.Osu
|
||||
{
|
||||
new OsuModTarget(),
|
||||
new OsuModDifficultyAdjust(),
|
||||
new OsuModClassic()
|
||||
new OsuModClassic(),
|
||||
new OsuModRandom(),
|
||||
};
|
||||
|
||||
case ModType.Automation:
|
||||
|
@ -42,6 +42,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
Origin = Anchor.Centre,
|
||||
Texture = textures.Get(@"Gameplay/osu/disc"),
|
||||
},
|
||||
new KiaiFlash
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
triangles = new TrianglesPiece
|
||||
{
|
||||
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
|
||||
{
|
||||
public class MainCirclePiece : CompositeDrawable, IMainCirclePiece
|
||||
public class MainCirclePiece : CompositeDrawable
|
||||
{
|
||||
private readonly CirclePiece circle;
|
||||
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<int> indexInCurrentCombo = new Bindable<int>();
|
||||
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
||||
|
||||
[Resolved]
|
||||
private DrawableHitObject drawableObject { get; set; }
|
||||
@ -53,6 +54,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||
armedState.BindTo(drawableObject.State);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -67,16 +69,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
}, 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))
|
||||
glow.FadeOut(400);
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
{
|
||||
switch (state)
|
||||
switch (state.NewValue)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
const double flash_in = 40;
|
||||
@ -89,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
explode.FadeIn(flash_in);
|
||||
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
|
||||
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
|
||||
{
|
||||
private readonly ISkin skin;
|
||||
private bool spin;
|
||||
|
||||
public LegacyCursor()
|
||||
public LegacyCursor(ISkin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
Size = new Vector2(50);
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
@ -22,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
private void load()
|
||||
{
|
||||
bool centre = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorCentre)?.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
|
||||
{
|
||||
private readonly ISkin skin;
|
||||
private const double disjoint_trail_time_separation = 1000 / 60.0;
|
||||
|
||||
private bool disjointTrail;
|
||||
private double lastTrailTime;
|
||||
private IBindable<float> cursorSize;
|
||||
|
||||
public LegacyCursorTrail(ISkin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin, OsuConfigManager config)
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
Texture = skin.GetTexture("cursortrail");
|
||||
disjointTrail = skin.GetTexture("cursormiddle") == null;
|
||||
|
@ -5,14 +5,12 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@ -20,7 +18,7 @@ using static osu.Game.Skinning.LegacySkinConfiguration;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyMainCirclePiece : CompositeDrawable, IMainCirclePiece
|
||||
public class LegacyMainCirclePiece : CompositeDrawable
|
||||
{
|
||||
private readonly string priorityLookup;
|
||||
private readonly bool hasNumber;
|
||||
@ -33,14 +31,15 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||
}
|
||||
|
||||
private Container<Sprite> circleSprites;
|
||||
private Sprite hitCircleSprite;
|
||||
private Sprite hitCircleOverlay;
|
||||
private Container circleSprites;
|
||||
private Drawable hitCircleSprite;
|
||||
private Drawable hitCircleOverlay;
|
||||
|
||||
private SkinnableSpriteText hitCircleText;
|
||||
|
||||
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
||||
|
||||
[Resolved]
|
||||
private DrawableHitObject drawableObject { get; set; }
|
||||
@ -72,20 +71,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
circleSprites = new Container<Sprite>
|
||||
circleSprites = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new[]
|
||||
{
|
||||
hitCircleSprite = new Sprite
|
||||
hitCircleSprite = new KiaiFlashingSprite
|
||||
{
|
||||
Texture = baseTexture,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
hitCircleOverlay = new Sprite
|
||||
hitCircleOverlay = new KiaiFlashingSprite
|
||||
{
|
||||
Texture = overlayTexture,
|
||||
Anchor = Anchor.Centre,
|
||||
@ -115,6 +114,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||
armedState.BindTo(drawableObject.State);
|
||||
|
||||
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);
|
||||
if (hasNumber)
|
||||
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;
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
|
||||
ClearTransforms(true);
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
{
|
||||
switch (state)
|
||||
switch (state.NewValue)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
|
@ -84,14 +84,18 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.Cursor:
|
||||
if (Source.GetTexture("cursor") != null)
|
||||
return new LegacyCursor();
|
||||
var cursorProvider = Source.FindProvider(s => s.GetTexture("cursor") != null);
|
||||
|
||||
if (cursorProvider != null)
|
||||
return new LegacyCursor(cursorProvider);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.CursorTrail:
|
||||
if (Source.GetTexture("cursortrail") != null)
|
||||
return new LegacyCursorTrail();
|
||||
var trailProvider = Source.FindProvider(s => s.GetTexture("cursortrail") != null);
|
||||
|
||||
if (trailProvider != null)
|
||||
return new LegacyCursorTrail(trailProvider);
|
||||
|
||||
return null;
|
||||
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddStep("Bar line", () => SetContents(() =>
|
||||
AddStep("Bar line", () => SetContents(_ =>
|
||||
{
|
||||
ScrollingHitObjectContainer hoc;
|
||||
|
||||
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
return cont;
|
||||
}));
|
||||
|
||||
AddStep("Bar line (major)", () => SetContents(() =>
|
||||
AddStep("Bar line (major)", () => SetContents(_ =>
|
||||
{
|
||||
ScrollingHitObjectContainer hoc;
|
||||
|
||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddStep("Drum roll", () => SetContents(() =>
|
||||
AddStep("Drum roll", () => SetContents(_ =>
|
||||
{
|
||||
var hoc = new ScrollingHitObjectContainer();
|
||||
|
||||
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
return hoc;
|
||||
}));
|
||||
|
||||
AddStep("Drum roll (strong)", () => SetContents(() =>
|
||||
AddStep("Drum roll (strong)", () => SetContents(_ =>
|
||||
{
|
||||
var hoc = new ScrollingHitObjectContainer();
|
||||
|
||||
|
@ -17,25 +17,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddStep("Centre hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
|
||||
AddStep("Centre hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime())
|
||||
{
|
||||
Anchor = 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,
|
||||
Origin = Anchor.Centre,
|
||||
}));
|
||||
|
||||
AddStep("Rim hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
|
||||
AddStep("Rim hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime())
|
||||
{
|
||||
Anchor = 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,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -54,16 +54,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
{
|
||||
AddStep("set beatmap", () => setBeatmap());
|
||||
|
||||
AddStep("clear state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear)));
|
||||
AddStep("idle state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle)));
|
||||
AddStep("kiai state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai)));
|
||||
AddStep("fail state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail)));
|
||||
AddStep("clear state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear)));
|
||||
AddStep("idle state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle)));
|
||||
AddStep("kiai state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai)));
|
||||
AddStep("fail state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
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));
|
||||
}
|
||||
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
{
|
||||
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("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();
|
||||
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
var ruleset = new TaikoRuleset();
|
||||
return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo));
|
||||
|
@ -22,16 +22,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[Test]
|
||||
public void TestNormalHit()
|
||||
{
|
||||
AddStep("Great", () => SetContents(() => getContentFor(createHit(HitResult.Great))));
|
||||
AddStep("Ok", () => SetContents(() => getContentFor(createHit(HitResult.Ok))));
|
||||
AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss))));
|
||||
AddStep("Great", () => SetContents(_ => getContentFor(createHit(HitResult.Great))));
|
||||
AddStep("Ok", () => SetContents(_ => getContentFor(createHit(HitResult.Ok))));
|
||||
AddStep("Miss", () => SetContents(_ => getContentFor(createHit(HitResult.Miss))));
|
||||
}
|
||||
|
||||
[TestCase(HitResult.Great)]
|
||||
[TestCase(HitResult.Ok)]
|
||||
public void TestStrongHit(HitResult type)
|
||||
{
|
||||
AddStep("create hit", () => SetContents(() => getContentFor(createStrongHit(type))));
|
||||
AddStep("create hit", () => SetContents(_ => getContentFor(createStrongHit(type))));
|
||||
AddStep("visualise second hit",
|
||||
() => this.ChildrenOfType<HitExplosion>()
|
||||
.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]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() => new TaikoInputManager(new TaikoRuleset().RulesetInfo)
|
||||
SetContents(_ => new TaikoInputManager(new TaikoRuleset().RulesetInfo)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new Container
|
||||
|
@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[Test]
|
||||
public void TestKiaiHits()
|
||||
{
|
||||
AddStep("rim hit", () => SetContents(() => getContentFor(createHit(HitType.Rim))));
|
||||
AddStep("centre hit", () => SetContents(() => getContentFor(createHit(HitType.Centre))));
|
||||
AddStep("rim hit", () => SetContents(_ => getContentFor(createHit(HitType.Rim))));
|
||||
AddStep("centre hit", () => SetContents(_ => getContentFor(createHit(HitType.Centre))));
|
||||
}
|
||||
|
||||
private Drawable getContentFor(DrawableTestHit hit)
|
||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
Beatmap.Value.Track.Start();
|
||||
});
|
||||
|
||||
AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo())
|
||||
AddStep("Load playfield", () => SetContents(_ => new TaikoPlayfield(new ControlPointInfo())
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
|
||||
public TestSceneTaikoScroller()
|
||||
{
|
||||
AddStep("Load scroller", () => SetContents(() =>
|
||||
AddStep("Load scroller", () => SetContents(_ =>
|
||||
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty())
|
||||
{
|
||||
Clock = new FramedClock(clock),
|
||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
|
||||
[TestCase("taiko-normal-hitnormal")]
|
||||
[TestCase("normal-hitnormal")]
|
||||
// [TestCase("hitnormal")] intentionally broken (will play classic default instead).
|
||||
[TestCase("hitnormal")]
|
||||
public void TestDefaultCustomSampleFromBeatmap(string expectedSample)
|
||||
{
|
||||
SetupSkins(expectedSample, expectedSample);
|
||||
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
|
||||
[TestCase("taiko-normal-hitnormal")]
|
||||
[TestCase("normal-hitnormal")]
|
||||
// [TestCase("hitnormal")] intentionally broken (will play classic default instead).
|
||||
[TestCase("hitnormal")]
|
||||
public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample)
|
||||
{
|
||||
SetupSkins(string.Empty, expectedSample);
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<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 Rhythm(mods),
|
||||
|
@ -4,14 +4,13 @@
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Replays;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -20,10 +20,13 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
var taikoBeatmap = (TaikoBeatmap)beatmap;
|
||||
|
||||
Seed.Value ??= RNG.Next();
|
||||
var rng = new Random((int)Seed.Value);
|
||||
|
||||
foreach (var obj in taikoBeatmap.HitObjects)
|
||||
{
|
||||
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}");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
{
|
||||
foreach (var name in source.LookupNames)
|
||||
foreach (var name in base.LookupNames)
|
||||
yield return name.Insert(name.LastIndexOf('/') + 1, "taiko-");
|
||||
|
||||
foreach (var name in source.LookupNames)
|
||||
foreach (var name in base.LookupNames)
|
||||
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[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);
|
||||
}
|
||||
|
||||
[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
|
||||
{
|
||||
public const double INITIAL_LIFETIME_OFFSET = 100;
|
||||
|
@ -217,7 +217,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods)
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||
{
|
||||
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.")]
|
||||
public class TestSceneAutoplay : TestSceneAllRulesetPlayers
|
||||
{
|
||||
protected new TestPlayer Player => (TestPlayer)base.Player;
|
||||
protected new TestReplayPlayer Player => (TestReplayPlayer)base.Player;
|
||||
|
||||
protected override Player CreatePlayer(Ruleset ruleset)
|
||||
{
|
||||
SelectedMods.Value = new[] { ruleset.GetAutoplayMod() };
|
||||
return new TestPlayer(false);
|
||||
return new TestReplayPlayer(false);
|
||||
}
|
||||
|
||||
protected override void AddCheckSteps()
|
||||
|
@ -90,6 +90,20 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
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]
|
||||
public void TestRelativeBeatLengthScaleSingleTimingPoint()
|
||||
{
|
||||
|
@ -29,8 +29,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("allow skin lookup", () => storyboard.UseSkinSprites = false);
|
||||
|
||||
AddStep("create sprites", () => SetContents(
|
||||
() => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.TopLeft, Vector2.Zero)));
|
||||
|
||||
assertSpritesFromSkin(false);
|
||||
}
|
||||
@ -42,8 +41,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("allow skin lookup", () => storyboard.UseSkinSprites = true);
|
||||
|
||||
AddStep("create sprites", () => SetContents(
|
||||
() => createSprite(lookup_name, Anchor.Centre, Vector2.Zero)));
|
||||
AddStep("create sprites", () => SetContents(_ => createSprite(lookup_name, Anchor.Centre, Vector2.Zero)));
|
||||
|
||||
assertSpritesFromSkin(true);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestToggleEditor()
|
||||
{
|
||||
AddStep("show available components", () => SetContents(() => new SkinComponentToolbox(300)
|
||||
AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox(300)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddStep("create editor overlay", () =>
|
||||
{
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
var ruleset = new OsuRuleset();
|
||||
var mods = new[] { ruleset.GetAutoplayMod() };
|
||||
|
@ -3,26 +3,26 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinnableAccuracyCounter : SkinnableTestScene
|
||||
public class TestSceneSkinnableAccuracyCounter : SkinnableHUDComponentTestScene
|
||||
{
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||
|
||||
protected override Drawable CreateDefaultImplementation() => new DefaultAccuracyCounter();
|
||||
protected override Drawable CreateLegacyImplementation() => new LegacyAccuracyCounter();
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1);
|
||||
AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter))));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -3,26 +3,19 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneComboCounter : SkinnableTestScene
|
||||
public class TestSceneSkinnableComboCounter : SkinnableHUDComponentTestScene
|
||||
{
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter))));
|
||||
}
|
||||
protected override Drawable CreateDefaultImplementation() => new DefaultComboCounter();
|
||||
protected override Drawable CreateLegacyImplementation() => new LegacyComboCounter();
|
||||
|
||||
[Test]
|
||||
public void TestComboCounterIncrementing()
|
@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddStep("create overlay", () =>
|
||||
{
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
hudOverlay = new HUDOverlay(null, Array.Empty<Mod>());
|
||||
|
||||
|
@ -3,28 +3,28 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinnableHealthDisplay : SkinnableTestScene
|
||||
public class TestSceneSkinnableHealthDisplay : SkinnableHUDComponentTestScene
|
||||
{
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
|
||||
[Cached(typeof(HealthProcessor))]
|
||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||
|
||||
protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay();
|
||||
protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay();
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay))));
|
||||
AddStep(@"Reset all", delegate
|
||||
{
|
||||
healthProcessor.Health.Value = 1;
|
||||
|
@ -3,26 +3,20 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinnableScoreCounter : SkinnableTestScene
|
||||
public class TestSceneSkinnableScoreCounter : SkinnableHUDComponentTestScene
|
||||
{
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter))));
|
||||
}
|
||||
protected override Drawable CreateDefaultImplementation() => new DefaultScoreCounter();
|
||||
protected override Drawable CreateLegacyImplementation() => new LegacyScoreCounter();
|
||||
|
||||
[Test]
|
||||
public void TestScoreCounterIncrementing()
|
||||
|
@ -12,6 +12,7 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Overlays.Toolbar;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
@ -95,11 +96,12 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
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));
|
||||
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);
|
||||
AddStep("attempt to retry", () => results.ChildrenOfType<HotkeyRetryOverlay>().First().Action());
|
||||
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.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
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 channel1 => channels[0];
|
||||
private Channel channel2 => channels[1];
|
||||
private Channel channel3 => channels[2];
|
||||
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
|
||||
public TestSceneChatOverlay()
|
||||
{
|
||||
@ -44,7 +50,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
Name = $"Channel no. {index}",
|
||||
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();
|
||||
}
|
||||
@ -228,6 +235,92 @@ namespace osu.Game.Tests.Visual.Online
|
||||
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)
|
||||
{
|
||||
var channelKey = Key.Number0 + number;
|
||||
@ -236,6 +329,23 @@ namespace osu.Game.Tests.Visual.Online
|
||||
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)
|
||||
{
|
||||
InputManager.MoveMouseTo(d);
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Net;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
@ -32,7 +33,14 @@ namespace osu.Game.Tests.Visual.Online
|
||||
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", () =>
|
||||
{
|
||||
dummyAPI.HandleRequest = request =>
|
||||
@ -40,7 +48,11 @@ namespace osu.Game.Tests.Visual.Online
|
||||
if (!(request is GetWikiRequest getWikiRequest))
|
||||
return false;
|
||||
|
||||
getWikiRequest.TriggerSuccess(r);
|
||||
if (isFailed)
|
||||
getWikiRequest.TriggerFailure(new WebException());
|
||||
else
|
||||
getWikiRequest.TriggerSuccess(r);
|
||||
|
||||
return true;
|
||||
};
|
||||
});
|
||||
|
@ -3,7 +3,7 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" 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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
@ -5,7 +5,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<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="NUnit3TestAdapter" Version="3.17.0" />
|
||||
</ItemGroup>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user