mirror of
https://github.com/ppy/osu.git
synced 2025-02-11 00:43:12 +08:00
Merge remote-tracking branch 'upstream/master' into chat-mention
This commit is contained in:
commit
39c3b08fc7
@ -10,14 +10,6 @@ trim_trailing_whitespace = true
|
|||||||
|
|
||||||
#Roslyn naming styles
|
#Roslyn naming styles
|
||||||
|
|
||||||
#PascalCase for public and protected members
|
|
||||||
dotnet_naming_style.pascalcase.capitalization = pascal_case
|
|
||||||
dotnet_naming_symbols.public_members.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
|
||||||
dotnet_naming_symbols.public_members.applicable_kinds = property,method,field,event
|
|
||||||
dotnet_naming_rule.public_members_pascalcase.severity = error
|
|
||||||
dotnet_naming_rule.public_members_pascalcase.symbols = public_members
|
|
||||||
dotnet_naming_rule.public_members_pascalcase.style = pascalcase
|
|
||||||
|
|
||||||
#camelCase for private members
|
#camelCase for private members
|
||||||
dotnet_naming_style.camelcase.capitalization = camel_case
|
dotnet_naming_style.camelcase.capitalization = camel_case
|
||||||
|
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature Request
|
|
||||||
about: Propose a feature you would like to see in the game!
|
|
||||||
---
|
|
||||||
**Describe the new feature:**
|
|
||||||
|
|
||||||
**Proposal designs of the feature:**
|
|
9
.github/ISSUE_TEMPLATE/config.yml
vendored
9
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,12 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
|
- name: Suggestions or feature request
|
||||||
|
url: https://github.com/ppy/osu/discussions/categories/ideas
|
||||||
|
about: Got something you think should change or be added? Search for or start a new discussion!
|
||||||
|
- name: Help
|
||||||
|
url: https://github.com/ppy/osu/discussions/categories/q-a
|
||||||
|
about: osu! not working as you'd expect? Not sure it's a bug? Check the Q&A section!
|
||||||
- name: osu!stable issues
|
- name: osu!stable issues
|
||||||
url: https://github.com/ppy/osu-stable-issues
|
url: https://github.com/ppy/osu-stable-issues
|
||||||
about: For issues regarding osu!stable (not osu!lazer), open them here.
|
about: For osu!stable bugs (not osu!lazer), check out the dedicated repository. Note that we only accept serious bug reports.
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ Before committing your code, please run a code formatter. This can be achieved b
|
|||||||
|
|
||||||
We have adopted some cross-platform, compiler integrated analyzers. They can provide warnings when you are editing, building inside IDE or from command line, as-if they are provided by the compiler itself.
|
We have adopted some cross-platform, compiler integrated analyzers. They can provide warnings when you are editing, building inside IDE or from command line, as-if they are provided by the compiler itself.
|
||||||
|
|
||||||
JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`, which is [only supported on Windows](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
|
JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`. Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.EmptyFreeform
|
|||||||
|
|
||||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.EmptyFreeform.Objects;
|
|
||||||
using osu.Game.Rulesets.EmptyFreeform.Replays;
|
using osu.Game.Rulesets.EmptyFreeform.Replays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -11,7 +10,7 @@ using osu.Game.Users;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.EmptyFreeform.Mods
|
namespace osu.Game.Rulesets.EmptyFreeform.Mods
|
||||||
{
|
{
|
||||||
public class EmptyFreeformModAutoplay : ModAutoplay<EmptyFreeformHitObject>
|
public class EmptyFreeformModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -4,14 +4,13 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Pippidon.Objects;
|
|
||||||
using osu.Game.Rulesets.Pippidon.Replays;
|
using osu.Game.Rulesets.Pippidon.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Pippidon.Mods
|
namespace osu.Game.Rulesets.Pippidon.Mods
|
||||||
{
|
{
|
||||||
public class PippidonModAutoplay : ModAutoplay<PippidonHitObject>
|
public class PippidonModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon
|
|||||||
|
|
||||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.EmptyScrolling
|
|||||||
|
|
||||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.EmptyScrolling.Objects;
|
|
||||||
using osu.Game.Rulesets.EmptyScrolling.Replays;
|
using osu.Game.Rulesets.EmptyScrolling.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -11,7 +10,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.EmptyScrolling.Mods
|
namespace osu.Game.Rulesets.EmptyScrolling.Mods
|
||||||
{
|
{
|
||||||
public class EmptyScrollingModAutoplay : ModAutoplay<EmptyScrollingHitObject>
|
public class EmptyScrollingModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -4,14 +4,13 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Pippidon.Objects;
|
|
||||||
using osu.Game.Rulesets.Pippidon.Replays;
|
using osu.Game.Rulesets.Pippidon.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Pippidon.Mods
|
namespace osu.Game.Rulesets.Pippidon.Mods
|
||||||
{
|
{
|
||||||
public class PippidonModAutoplay : ModAutoplay<PippidonHitObject>
|
public class PippidonModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon
|
|||||||
|
|
||||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.422.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.525.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.524.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.601.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -57,7 +57,7 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
private string getStableInstallPath()
|
private string getStableInstallPath()
|
||||||
{
|
{
|
||||||
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
|
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")) || File.Exists(Path.Combine(p, "osu!.cfg"));
|
||||||
|
|
||||||
string stableInstallPath;
|
string stableInstallPath;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
|
||||||
<PackageReference Include="nunit" Version="3.13.2" />
|
<PackageReference Include="nunit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
@ -10,5 +18,22 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
public class TestSceneCatchPlayerLegacySkin : LegacySkinPlayerTestScene
|
public class TestSceneCatchPlayerLegacySkin : LegacySkinPlayerTestScene
|
||||||
{
|
{
|
||||||
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLegacyHUDComboCounterHidden([Values] bool withModifiedSkin)
|
||||||
|
{
|
||||||
|
if (withModifiedSkin)
|
||||||
|
{
|
||||||
|
AddStep("change component scale", () => Player.ChildrenOfType<LegacyScoreCounter>().First().Scale = new Vector2(2f));
|
||||||
|
AddStep("update target", () => Player.ChildrenOfType<SkinnableTargetContainer>().ForEach(LegacySkin.UpdateDrawableTarget));
|
||||||
|
AddStep("exit player", () => Player.Exit());
|
||||||
|
CreateTest(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddAssert("legacy HUD combo counter hidden", () =>
|
||||||
|
{
|
||||||
|
return Player.ChildrenOfType<LegacyComboCounter>().All(c => c.ChildrenOfType<Container>().Single().Alpha == 0f);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
AddStep("enable hit lighting", () => config.SetValue(OsuSetting.HitLighting, true));
|
AddStep("enable hit lighting", () => config.SetValue(OsuSetting.HitLighting, true));
|
||||||
AddStep("catch fruit", () => attemptCatch(new Fruit()));
|
AddStep("catch fruit", () => attemptCatch(new Fruit()));
|
||||||
AddAssert("correct hit lighting colour", () =>
|
AddAssert("correct hit lighting colour", () =>
|
||||||
catcher.ChildrenOfType<HitExplosion>().First()?.ObjectColour == fruitColour);
|
catcher.ChildrenOfType<HitExplosion>().First()?.Entry?.ObjectColour == fruitColour);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
CircleSize = circleSize
|
CircleSize = circleSize
|
||||||
};
|
};
|
||||||
|
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
var droppedObjectContainer = new Container<CaughtObject>
|
var droppedObjectContainer = new Container<CaughtObject>
|
||||||
{
|
{
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
scoreProcessor = new ScoreProcessor();
|
scoreProcessor = new ScoreProcessor();
|
||||||
|
|
||||||
SetContents(() => new CatchComboDisplay
|
SetContents(_ => new CatchComboDisplay
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -19,22 +19,22 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
AddStep("show pear", () => SetContents(() => createDrawableFruit(0)));
|
AddStep("show pear", () => SetContents(_ => createDrawableFruit(0)));
|
||||||
AddStep("show grape", () => SetContents(() => createDrawableFruit(1)));
|
AddStep("show grape", () => SetContents(_ => createDrawableFruit(1)));
|
||||||
AddStep("show pineapple / apple", () => SetContents(() => createDrawableFruit(2)));
|
AddStep("show pineapple / apple", () => SetContents(_ => createDrawableFruit(2)));
|
||||||
AddStep("show raspberry / orange", () => SetContents(() => createDrawableFruit(3)));
|
AddStep("show raspberry / orange", () => SetContents(_ => createDrawableFruit(3)));
|
||||||
|
|
||||||
AddStep("show banana", () => SetContents(createDrawableBanana));
|
AddStep("show banana", () => SetContents(_ => createDrawableBanana()));
|
||||||
|
|
||||||
AddStep("show droplet", () => SetContents(() => createDrawableDroplet()));
|
AddStep("show droplet", () => SetContents(_ => createDrawableDroplet()));
|
||||||
AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet));
|
AddStep("show tiny droplet", () => SetContents(_ => createDrawableTinyDroplet()));
|
||||||
|
|
||||||
AddStep("show hyperdash pear", () => SetContents(() => createDrawableFruit(0, true)));
|
AddStep("show hyperdash pear", () => SetContents(_ => createDrawableFruit(0, true)));
|
||||||
AddStep("show hyperdash grape", () => SetContents(() => createDrawableFruit(1, true)));
|
AddStep("show hyperdash grape", () => SetContents(_ => createDrawableFruit(1, true)));
|
||||||
AddStep("show hyperdash pineapple / apple", () => SetContents(() => createDrawableFruit(2, true)));
|
AddStep("show hyperdash pineapple / apple", () => SetContents(_ => createDrawableFruit(2, true)));
|
||||||
AddStep("show hyperdash raspberry / orange", () => SetContents(() => createDrawableFruit(3, true)));
|
AddStep("show hyperdash raspberry / orange", () => SetContents(_ => createDrawableFruit(3, true)));
|
||||||
|
|
||||||
AddStep("show hyperdash droplet", () => SetContents(() => createDrawableDroplet(true)));
|
AddStep("show hyperdash droplet", () => SetContents(_ => createDrawableDroplet(true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) =>
|
private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) =>
|
||||||
|
@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
AddStep("fruit changes visual and hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
|
AddStep("fruit changes visual and hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
|
||||||
{
|
{
|
||||||
IndexInBeatmapBindable = { BindTarget = indexInBeatmap },
|
IndexInBeatmapBindable = { BindTarget = indexInBeatmap },
|
||||||
HyperDashBindable = { BindTarget = hyperDash },
|
HyperDashBindable = { BindTarget = hyperDash },
|
||||||
}))));
|
}))));
|
||||||
|
|
||||||
AddStep("droplet changes hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
|
AddStep("droplet changes hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
|
||||||
{
|
{
|
||||||
HyperDashBindable = { BindTarget = hyperDash },
|
HyperDashBindable = { BindTarget = hyperDash },
|
||||||
}))));
|
}))));
|
||||||
|
@ -32,28 +32,28 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[TestCase(true, false)]
|
[TestCase(true, false)]
|
||||||
[TestCase(false, true)]
|
[TestCase(false, true)]
|
||||||
[TestCase(false, false)]
|
[TestCase(false, false)]
|
||||||
public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin)
|
public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin)
|
||||||
{
|
{
|
||||||
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
|
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true));
|
||||||
base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin);
|
ConfigureTest(useBeatmapSkin, true, userHasCustomColours);
|
||||||
AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
|
AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin)
|
public void TestBeatmapComboColoursOverride(bool useBeatmapSkin)
|
||||||
{
|
{
|
||||||
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
|
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true));
|
||||||
base.TestBeatmapComboColoursOverride(useBeatmapSkin);
|
ConfigureTest(useBeatmapSkin, false, true);
|
||||||
AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
|
AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin)
|
public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin)
|
||||||
{
|
{
|
||||||
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
|
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true));
|
||||||
base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin);
|
ConfigureTest(useBeatmapSkin, false, false);
|
||||||
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
|
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,10 +61,10 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[TestCase(false, true)]
|
[TestCase(false, true)]
|
||||||
[TestCase(true, false)]
|
[TestCase(true, false)]
|
||||||
[TestCase(false, false)]
|
[TestCase(false, false)]
|
||||||
public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour)
|
public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour)
|
||||||
{
|
{
|
||||||
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false);
|
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false));
|
||||||
base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour);
|
ConfigureTest(useBeatmapSkin, useBeatmapColour, false);
|
||||||
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
|
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,10 +72,10 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[TestCase(false, true)]
|
[TestCase(false, true)]
|
||||||
[TestCase(true, false)]
|
[TestCase(true, false)]
|
||||||
[TestCase(false, false)]
|
[TestCase(false, false)]
|
||||||
public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour)
|
public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour)
|
||||||
{
|
{
|
||||||
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, false);
|
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, false));
|
||||||
base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour);
|
ConfigureTest(useBeatmapSkin, useBeatmapColour, true);
|
||||||
AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
|
AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public void TestBeatmapHyperDashColours(bool useBeatmapSkin)
|
public void TestBeatmapHyperDashColours(bool useBeatmapSkin)
|
||||||
{
|
{
|
||||||
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
|
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true));
|
||||||
ConfigureTest(useBeatmapSkin, true, true);
|
ConfigureTest(useBeatmapSkin, true, true);
|
||||||
AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestBeatmapSkin.HYPER_DASH_COLOUR);
|
AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestBeatmapSkin.HYPER_DASH_COLOUR);
|
||||||
AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestBeatmapSkin.HYPER_DASH_AFTER_IMAGE_COLOUR);
|
AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestBeatmapSkin.HYPER_DASH_AFTER_IMAGE_COLOUR);
|
||||||
@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public void TestBeatmapHyperDashColoursOverride(bool useBeatmapSkin)
|
public void TestBeatmapHyperDashColoursOverride(bool useBeatmapSkin)
|
||||||
{
|
{
|
||||||
TestBeatmap = new CatchCustomSkinWorkingBeatmap(audio, true);
|
PrepareBeatmap(() => new CatchCustomSkinWorkingBeatmap(audio, true));
|
||||||
ConfigureTest(useBeatmapSkin, false, true);
|
ConfigureTest(useBeatmapSkin, false, true);
|
||||||
AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestSkin.HYPER_DASH_COLOUR);
|
AddAssert("is custom hyper dash colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashColour == TestSkin.HYPER_DASH_COLOUR);
|
||||||
AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestSkin.HYPER_DASH_AFTER_IMAGE_COLOUR);
|
AddAssert("is custom hyper dash after image colours", () => ((CatchExposedPlayer)TestPlayer).UsableHyperDashAfterImageColour == TestSkin.HYPER_DASH_AFTER_IMAGE_COLOUR);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -161,13 +161,13 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
case HitResult.LargeTickHit:
|
case HitResult.LargeTickHit:
|
||||||
return "large droplet";
|
return "Large droplet";
|
||||||
|
|
||||||
case HitResult.SmallTickHit:
|
case HitResult.SmallTickHit:
|
||||||
return "small droplet";
|
return "Small droplet";
|
||||||
|
|
||||||
case HitResult.LargeBonus:
|
case HitResult.LargeBonus:
|
||||||
return "banana";
|
return "Banana";
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.GetDisplayNameForHitResult(result);
|
return base.GetDisplayNameForHitResult(result);
|
||||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods)
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||||
{
|
{
|
||||||
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
|
|
||||||
return new Skill[]
|
return new Skill[]
|
||||||
{
|
{
|
||||||
new Movement(mods, halfCatcherWidth),
|
new Movement(mods, halfCatcherWidth, clockRate),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly double StrainTime;
|
public readonly double StrainTime;
|
||||||
|
|
||||||
public readonly double ClockRate;
|
|
||||||
|
|
||||||
public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
|
public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
|
||||||
: base(hitObject, lastObject, clockRate)
|
: base(hitObject, lastObject, clockRate)
|
||||||
{
|
{
|
||||||
@ -37,7 +35,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
|||||||
|
|
||||||
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
|
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
|
||||||
StrainTime = Math.Max(40, DeltaTime);
|
StrainTime = Math.Max(40, DeltaTime);
|
||||||
ClockRate = clockRate;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,10 +28,21 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
private float lastDistanceMoved;
|
private float lastDistanceMoved;
|
||||||
private double lastStrainTime;
|
private double lastStrainTime;
|
||||||
|
|
||||||
public Movement(Mod[] mods, float halfCatcherWidth)
|
/// <summary>
|
||||||
|
/// The speed multiplier applied to the player's catcher.
|
||||||
|
/// </summary>
|
||||||
|
private readonly double catcherSpeedMultiplier;
|
||||||
|
|
||||||
|
public Movement(Mod[] mods, float halfCatcherWidth, double clockRate)
|
||||||
: base(mods)
|
: base(mods)
|
||||||
{
|
{
|
||||||
HalfCatcherWidth = halfCatcherWidth;
|
HalfCatcherWidth = halfCatcherWidth;
|
||||||
|
|
||||||
|
// In catch, clockrate adjustments do not only affect the timings of hitobjects,
|
||||||
|
// but also the speed of the player's catcher, which has an impact on difficulty
|
||||||
|
// TODO: Support variable clockrates caused by mods such as ModTimeRamp
|
||||||
|
// (perhaps by using IApplicableToRate within the CatchDifficultyHitObject constructor to set a catcher speed for each object before processing)
|
||||||
|
catcherSpeedMultiplier = clockRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override double StrainValueOf(DifficultyHitObject current)
|
protected override double StrainValueOf(DifficultyHitObject current)
|
||||||
@ -48,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
|
|
||||||
float distanceMoved = playerPosition - lastPlayerPosition.Value;
|
float distanceMoved = playerPosition - lastPlayerPosition.Value;
|
||||||
|
|
||||||
double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catchCurrent.ClockRate);
|
double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catcherSpeedMultiplier);
|
||||||
|
|
||||||
double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510);
|
double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510);
|
||||||
double sqrtStrain = Math.Sqrt(weightedStrainTime);
|
double sqrtStrain = Math.Sqrt(weightedStrainTime);
|
||||||
@ -81,7 +92,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
playerPosition = catchCurrent.NormalizedPosition;
|
playerPosition = catchCurrent.NormalizedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
|
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catcherSpeedMultiplier, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
|
||||||
}
|
}
|
||||||
|
|
||||||
lastPlayerPosition = playerPosition;
|
lastPlayerPosition = playerPosition;
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
|
||||||
using osu.Game.Rulesets.Catch.Replays;
|
using osu.Game.Rulesets.Catch.Replays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -11,7 +10,7 @@ using osu.Game.Users;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModAutoplay : ModAutoplay<CatchHitObject>
|
public class CatchModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -22,59 +24,68 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
|||||||
|
|
||||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||||
{
|
{
|
||||||
if (component is HUDSkinComponent hudComponent)
|
if (component is SkinnableTargetComponent targetComponent)
|
||||||
{
|
{
|
||||||
switch (hudComponent.Component)
|
switch (targetComponent.Target)
|
||||||
{
|
{
|
||||||
case HUDSkinComponents.ComboCounter:
|
case SkinnableTarget.MainHUDComponents:
|
||||||
// catch may provide its own combo counter; hide the default.
|
var components = Source.GetDrawableComponent(component) as SkinnableTargetComponentsContainer;
|
||||||
return providesComboCounter ? Drawable.Empty() : null;
|
|
||||||
|
if (providesComboCounter && components != null)
|
||||||
|
{
|
||||||
|
// catch may provide its own combo counter; hide the default.
|
||||||
|
// todo: this should be done in an elegant way per ruleset, defining which HUD skin components should be displayed.
|
||||||
|
foreach (var legacyComboCounter in components.OfType<LegacyComboCounter>())
|
||||||
|
legacyComboCounter.HiddenByRulesetImplementation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return components;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(component is CatchSkinComponent catchSkinComponent))
|
if (component is CatchSkinComponent catchSkinComponent)
|
||||||
return null;
|
|
||||||
|
|
||||||
switch (catchSkinComponent.Component)
|
|
||||||
{
|
{
|
||||||
case CatchSkinComponents.Fruit:
|
switch (catchSkinComponent.Component)
|
||||||
if (GetTexture("fruit-pear") != null)
|
{
|
||||||
return new LegacyFruitPiece();
|
case CatchSkinComponents.Fruit:
|
||||||
|
if (GetTexture("fruit-pear") != null)
|
||||||
|
return new LegacyFruitPiece();
|
||||||
|
|
||||||
break;
|
return null;
|
||||||
|
|
||||||
case CatchSkinComponents.Banana:
|
case CatchSkinComponents.Banana:
|
||||||
if (GetTexture("fruit-bananas") != null)
|
if (GetTexture("fruit-bananas") != null)
|
||||||
return new LegacyBananaPiece();
|
return new LegacyBananaPiece();
|
||||||
|
|
||||||
break;
|
return null;
|
||||||
|
|
||||||
case CatchSkinComponents.Droplet:
|
case CatchSkinComponents.Droplet:
|
||||||
if (GetTexture("fruit-drop") != null)
|
if (GetTexture("fruit-drop") != null)
|
||||||
return new LegacyDropletPiece();
|
return new LegacyDropletPiece();
|
||||||
|
|
||||||
break;
|
return null;
|
||||||
|
|
||||||
case CatchSkinComponents.CatcherIdle:
|
case CatchSkinComponents.CatcherIdle:
|
||||||
return this.GetAnimation("fruit-catcher-idle", true, true, true) ??
|
return this.GetAnimation("fruit-catcher-idle", true, true, true) ??
|
||||||
this.GetAnimation("fruit-ryuuta", true, true, true);
|
this.GetAnimation("fruit-ryuuta", true, true, true);
|
||||||
|
|
||||||
case CatchSkinComponents.CatcherFail:
|
case CatchSkinComponents.CatcherFail:
|
||||||
return this.GetAnimation("fruit-catcher-fail", true, true, true) ??
|
return this.GetAnimation("fruit-catcher-fail", true, true, true) ??
|
||||||
this.GetAnimation("fruit-ryuuta", true, true, true);
|
this.GetAnimation("fruit-ryuuta", true, true, true);
|
||||||
|
|
||||||
case CatchSkinComponents.CatcherKiai:
|
case CatchSkinComponents.CatcherKiai:
|
||||||
return this.GetAnimation("fruit-catcher-kiai", true, true, true) ??
|
return this.GetAnimation("fruit-catcher-kiai", true, true, true) ??
|
||||||
this.GetAnimation("fruit-ryuuta", true, true, true);
|
this.GetAnimation("fruit-ryuuta", true, true, true);
|
||||||
|
|
||||||
case CatchSkinComponents.CatchComboCounter:
|
case CatchSkinComponents.CatchComboCounter:
|
||||||
if (providesComboCounter)
|
if (providesComboCounter)
|
||||||
return new LegacyCatchComboCounter(Source);
|
return new LegacyCatchComboCounter(Source);
|
||||||
|
|
||||||
break;
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return Source.GetDrawableComponent(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
|
@ -126,8 +126,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
private float hyperDashTargetPosition;
|
private float hyperDashTargetPosition;
|
||||||
private Bindable<bool> hitLighting;
|
private Bindable<bool> hitLighting;
|
||||||
|
|
||||||
private readonly DrawablePool<HitExplosion> hitExplosionPool;
|
private readonly HitExplosionContainer hitExplosionContainer;
|
||||||
private readonly Container<HitExplosion> hitExplosionContainer;
|
|
||||||
|
|
||||||
private readonly DrawablePool<CaughtFruit> caughtFruitPool;
|
private readonly DrawablePool<CaughtFruit> caughtFruitPool;
|
||||||
private readonly DrawablePool<CaughtBanana> caughtBananaPool;
|
private readonly DrawablePool<CaughtBanana> caughtBananaPool;
|
||||||
@ -148,7 +147,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
hitExplosionPool = new DrawablePool<HitExplosion>(10),
|
|
||||||
caughtFruitPool = new DrawablePool<CaughtFruit>(50),
|
caughtFruitPool = new DrawablePool<CaughtFruit>(50),
|
||||||
caughtBananaPool = new DrawablePool<CaughtBanana>(100),
|
caughtBananaPool = new DrawablePool<CaughtBanana>(100),
|
||||||
// less capacity is needed compared to fruit because droplet is not stacked
|
// less capacity is needed compared to fruit because droplet is not stacked
|
||||||
@ -173,7 +171,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
},
|
},
|
||||||
hitExplosionContainer = new Container<HitExplosion>
|
hitExplosionContainer = new HitExplosionContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
@ -297,7 +295,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
caughtObjectContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject);
|
caughtObjectContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject);
|
||||||
droppedObjectTarget.RemoveAll(d => d.HitObject == drawableObject.HitObject);
|
droppedObjectTarget.RemoveAll(d => d.HitObject == drawableObject.HitObject);
|
||||||
hitExplosionContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -508,15 +505,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addLighting(CatchHitObject hitObject, float x, Color4 colour)
|
private void addLighting(CatchHitObject hitObject, float x, Color4 colour) =>
|
||||||
{
|
hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, x, hitObject.Scale, colour, hitObject.RandomSeed));
|
||||||
HitExplosion hitExplosion = hitExplosionPool.Get();
|
|
||||||
hitExplosion.HitObject = hitObject;
|
|
||||||
hitExplosion.X = x;
|
|
||||||
hitExplosion.Scale = new Vector2(hitObject.Scale);
|
|
||||||
hitExplosion.ObjectColour = colour;
|
|
||||||
hitExplosionContainer.Add(hitExplosion);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CaughtObject getCaughtObject(PalpableCatchHitObject source)
|
private CaughtObject getCaughtObject(PalpableCatchHitObject source)
|
||||||
{
|
{
|
||||||
|
@ -5,31 +5,16 @@ using osu.Framework.Extensions.Color4Extensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
using osu.Framework.Graphics.Effects;
|
||||||
using osu.Framework.Graphics.Pooling;
|
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Objects.Pooling;
|
||||||
|
using osu.Game.Utils;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
public class HitExplosion : PoolableDrawable
|
public class HitExplosion : PoolableDrawableWithLifetime<HitExplosionEntry>
|
||||||
{
|
{
|
||||||
private Color4 objectColour;
|
|
||||||
public CatchHitObject HitObject;
|
|
||||||
|
|
||||||
public Color4 ObjectColour
|
|
||||||
{
|
|
||||||
get => objectColour;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (objectColour == value) return;
|
|
||||||
|
|
||||||
objectColour = value;
|
|
||||||
onColourChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly CircularContainer largeFaint;
|
private readonly CircularContainer largeFaint;
|
||||||
private readonly CircularContainer smallFaint;
|
private readonly CircularContainer smallFaint;
|
||||||
private readonly CircularContainer directionalGlow1;
|
private readonly CircularContainer directionalGlow1;
|
||||||
@ -83,9 +68,19 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PrepareForUse()
|
protected override void OnApply(HitExplosionEntry entry)
|
||||||
{
|
{
|
||||||
base.PrepareForUse();
|
X = entry.Position;
|
||||||
|
Scale = new Vector2(entry.Scale);
|
||||||
|
setColour(entry.ObjectColour);
|
||||||
|
|
||||||
|
using (BeginAbsoluteSequence(entry.LifetimeStart))
|
||||||
|
applyTransforms(entry.RNGSeed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyTransforms(int randomSeed)
|
||||||
|
{
|
||||||
|
ClearTransforms(true);
|
||||||
|
|
||||||
const double duration = 400;
|
const double duration = 400;
|
||||||
|
|
||||||
@ -96,14 +91,13 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
.FadeOut(duration * 2);
|
.FadeOut(duration * 2);
|
||||||
|
|
||||||
const float angle_variangle = 15; // should be less than 45
|
const float angle_variangle = 15; // should be less than 45
|
||||||
directionalGlow1.Rotation = RNG.NextSingle(-angle_variangle, angle_variangle);
|
directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 4);
|
||||||
directionalGlow2.Rotation = RNG.NextSingle(-angle_variangle, angle_variangle);
|
directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 5);
|
||||||
|
|
||||||
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out);
|
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out).Expire();
|
||||||
Expire(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onColourChanged()
|
private void setColour(Color4 objectColour)
|
||||||
{
|
{
|
||||||
const float roundness = 100;
|
const float roundness = 100;
|
||||||
|
|
||||||
|
22
osu.Game.Rulesets.Catch/UI/HitExplosionContainer.cs
Normal file
22
osu.Game.Rulesets.Catch/UI/HitExplosionContainer.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics.Pooling;
|
||||||
|
using osu.Game.Rulesets.Objects.Pooling;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
|
{
|
||||||
|
public class HitExplosionContainer : PooledDrawableWithLifetimeContainer<HitExplosionEntry, HitExplosion>
|
||||||
|
{
|
||||||
|
protected override bool RemoveRewoundEntry => true;
|
||||||
|
|
||||||
|
private readonly DrawablePool<HitExplosion> pool;
|
||||||
|
|
||||||
|
public HitExplosionContainer()
|
||||||
|
{
|
||||||
|
AddInternal(pool = new DrawablePool<HitExplosion>(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override HitExplosion GetDrawable(HitExplosionEntry entry) => pool.Get(d => d.Apply(entry));
|
||||||
|
}
|
||||||
|
}
|
25
osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs
Normal file
25
osu.Game.Rulesets.Catch/UI/HitExplosionEntry.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics.Performance;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
|
{
|
||||||
|
public class HitExplosionEntry : LifetimeEntry
|
||||||
|
{
|
||||||
|
public readonly float Position;
|
||||||
|
public readonly float Scale;
|
||||||
|
public readonly Color4 ObjectColour;
|
||||||
|
public readonly int RNGSeed;
|
||||||
|
|
||||||
|
public HitExplosionEntry(double startTime, float position, float scale, Color4 objectColour, int rngSeed)
|
||||||
|
{
|
||||||
|
LifetimeStart = startTime;
|
||||||
|
Position = position;
|
||||||
|
Scale = scale;
|
||||||
|
ObjectColour = objectColour;
|
||||||
|
RNGSeed = rngSeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() => Schedule(() =>
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
SetContents(() => new FillFlowContainer
|
SetContents(_ => new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new FillFlowContainer
|
SetContents(_ => new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new FillFlowContainer
|
SetContents(_ => new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
if (hitWindows.IsHitResultAllowed(result))
|
if (hitWindows.IsHitResultAllowed(result))
|
||||||
{
|
{
|
||||||
AddStep("Show " + result.GetDescription(), () => SetContents(() =>
|
AddStep("Show " + result.GetDescription(), () => SetContents(_ =>
|
||||||
new DrawableManiaJudgement(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())
|
new DrawableManiaJudgement(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())
|
||||||
{
|
{
|
||||||
Type = result
|
Type = result
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
var pool = new DrawablePool<PoolableHitExplosion>(5);
|
var pool = new DrawablePool<PoolableHitExplosion>(5);
|
||||||
hitExplosionPools.Add(pool);
|
hitExplosionPools.Add(pool);
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new FillFlowContainer
|
SetContents(_ => new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
new StageDefinition { Columns = 2 }
|
new StageDefinition { Columns = 2 }
|
||||||
};
|
};
|
||||||
|
|
||||||
SetContents(() => new ManiaPlayfield(stageDefinitions));
|
SetContents(_ => new ManiaPlayfield(stageDefinitions));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
new StageDefinition { Columns = 2 }
|
new StageDefinition { Columns = 2 }
|
||||||
};
|
};
|
||||||
|
|
||||||
SetContents(() => new ManiaPlayfield(stageDefinitions));
|
SetContents(_ => new ManiaPlayfield(stageDefinitions));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
ManiaAction normalAction = ManiaAction.Key1;
|
ManiaAction normalAction = ManiaAction.Key1;
|
||||||
ManiaAction specialAction = ManiaAction.Special1;
|
ManiaAction specialAction = ManiaAction.Special1;
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }),
|
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }),
|
||||||
_ => new DefaultStageBackground())
|
_ => new DefaultStageBackground())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null)
|
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
public class TestSceneManiaHitObjectSamples : HitObjectSampleTest
|
public class TestSceneManiaHitObjectSamples : HitObjectSampleTest
|
||||||
{
|
{
|
||||||
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
||||||
protected override IResourceStore<byte[]> Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples)));
|
protected override IResourceStore<byte[]> RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneManiaHitObjectSamples)));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests that when a normal sample bank is used, the normal hitsound will be looked up.
|
/// Tests that when a normal sample bank is used, the normal hitsound will be looked up.
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
// Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required.
|
// Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required.
|
||||||
protected override IEnumerable<DifficultyHitObject> SortObjects(IEnumerable<DifficultyHitObject> input) => input;
|
protected override IEnumerable<DifficultyHitObject> SortObjects(IEnumerable<DifficultyHitObject> input) => input;
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||||
{
|
{
|
||||||
new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns)
|
new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns)
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
|
||||||
using osu.Game.Rulesets.Mania.Replays;
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -12,7 +11,7 @@ using osu.Game.Users;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
|
public class ManiaModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
@ -17,8 +18,11 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
|
|
||||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
|
Seed.Value ??= RNG.Next();
|
||||||
|
var rng = new Random((int)Seed.Value);
|
||||||
|
|
||||||
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||||
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
|
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => rng.Next()).ToList();
|
||||||
|
|
||||||
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
|
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return Source.GetDrawableComponent(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable getResult(HitResult result)
|
private Drawable getResult(HitResult result)
|
||||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
int poolIndex = 0;
|
int poolIndex = 0;
|
||||||
|
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
DrawablePool<TestDrawableOsuJudgement> pool;
|
DrawablePool<TestDrawableOsuJudgement> pool;
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private void loadContent(bool automated = true, Func<SkinProvidingContainer> skinProvider = null)
|
private void loadContent(bool automated = true, Func<SkinProvidingContainer> skinProvider = null)
|
||||||
{
|
{
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
var inputManager = automated ? (InputManager)new MovingCursorInputManager() : new OsuInputManager(new OsuRuleset().RulesetInfo);
|
var inputManager = automated ? (InputManager)new MovingCursorInputManager() : new OsuInputManager(new OsuRuleset().RulesetInfo);
|
||||||
var skinContainer = skinProvider?.Invoke() ?? new SkinProvidingContainer(null);
|
var skinContainer = skinProvider?.Invoke() ?? new SkinProvidingContainer(null);
|
||||||
|
@ -23,18 +23,18 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestVariousHitCircles()
|
public void TestVariousHitCircles()
|
||||||
{
|
{
|
||||||
AddStep("Miss Big Single", () => SetContents(() => testSingle(2)));
|
AddStep("Miss Big Single", () => SetContents(_ => testSingle(2)));
|
||||||
AddStep("Miss Medium Single", () => SetContents(() => testSingle(5)));
|
AddStep("Miss Medium Single", () => SetContents(_ => testSingle(5)));
|
||||||
AddStep("Miss Small Single", () => SetContents(() => testSingle(7)));
|
AddStep("Miss Small Single", () => SetContents(_ => testSingle(7)));
|
||||||
AddStep("Hit Big Single", () => SetContents(() => testSingle(2, true)));
|
AddStep("Hit Big Single", () => SetContents(_ => testSingle(2, true)));
|
||||||
AddStep("Hit Medium Single", () => SetContents(() => testSingle(5, true)));
|
AddStep("Hit Medium Single", () => SetContents(_ => testSingle(5, true)));
|
||||||
AddStep("Hit Small Single", () => SetContents(() => testSingle(7, true)));
|
AddStep("Hit Small Single", () => SetContents(_ => testSingle(7, true)));
|
||||||
AddStep("Miss Big Stream", () => SetContents(() => testStream(2)));
|
AddStep("Miss Big Stream", () => SetContents(_ => testStream(2)));
|
||||||
AddStep("Miss Medium Stream", () => SetContents(() => testStream(5)));
|
AddStep("Miss Medium Stream", () => SetContents(_ => testStream(5)));
|
||||||
AddStep("Miss Small Stream", () => SetContents(() => testStream(7)));
|
AddStep("Miss Small Stream", () => SetContents(_ => testStream(7)));
|
||||||
AddStep("Hit Big Stream", () => SetContents(() => testStream(2, true)));
|
AddStep("Hit Big Stream", () => SetContents(_ => testStream(2, true)));
|
||||||
AddStep("Hit Medium Stream", () => SetContents(() => testStream(5, true)));
|
AddStep("Hit Medium Stream", () => SetContents(_ => testStream(5, true)));
|
||||||
AddStep("Hit Small Stream", () => SetContents(() => testStream(7, true)));
|
AddStep("Hit Small Stream", () => SetContents(_ => testStream(7, true)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
||||||
|
30
osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleKiai.cs
Normal file
30
osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleKiai.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneHitCircleKiai : TestSceneHitCircle
|
||||||
|
{
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
var controlPointInfo = new ControlPointInfo();
|
||||||
|
|
||||||
|
controlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true });
|
||||||
|
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPointInfo
|
||||||
|
});
|
||||||
|
|
||||||
|
// track needs to be playing for BeatSyncedContainer to work.
|
||||||
|
Beatmap.Value.Track.Start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -30,28 +30,28 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestCase(true, false)]
|
[TestCase(true, false)]
|
||||||
[TestCase(false, true)]
|
[TestCase(false, true)]
|
||||||
[TestCase(false, false)]
|
[TestCase(false, false)]
|
||||||
public override void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin)
|
public void TestBeatmapComboColours(bool userHasCustomColours, bool useBeatmapSkin)
|
||||||
{
|
{
|
||||||
TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true);
|
PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true));
|
||||||
base.TestBeatmapComboColours(userHasCustomColours, useBeatmapSkin);
|
ConfigureTest(useBeatmapSkin, true, userHasCustomColours);
|
||||||
AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
|
AddAssert("is beatmap skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public override void TestBeatmapComboColoursOverride(bool useBeatmapSkin)
|
public void TestBeatmapComboColoursOverride(bool useBeatmapSkin)
|
||||||
{
|
{
|
||||||
TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true);
|
PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true));
|
||||||
base.TestBeatmapComboColoursOverride(useBeatmapSkin);
|
ConfigureTest(useBeatmapSkin, false, true);
|
||||||
AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
|
AddAssert("is user custom skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public override void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin)
|
public void TestBeatmapComboColoursOverrideWithDefaultColours(bool useBeatmapSkin)
|
||||||
{
|
{
|
||||||
TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, true);
|
PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, true));
|
||||||
base.TestBeatmapComboColoursOverrideWithDefaultColours(useBeatmapSkin);
|
ConfigureTest(useBeatmapSkin, false, false);
|
||||||
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
|
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,10 +59,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestCase(false, true)]
|
[TestCase(false, true)]
|
||||||
[TestCase(true, false)]
|
[TestCase(true, false)]
|
||||||
[TestCase(false, false)]
|
[TestCase(false, false)]
|
||||||
public override void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour)
|
public void TestBeatmapNoComboColours(bool useBeatmapSkin, bool useBeatmapColour)
|
||||||
{
|
{
|
||||||
TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false);
|
PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false));
|
||||||
base.TestBeatmapNoComboColours(useBeatmapSkin, useBeatmapColour);
|
ConfigureTest(useBeatmapSkin, useBeatmapColour, false);
|
||||||
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
|
AddAssert("is default user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,10 +70,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestCase(false, true)]
|
[TestCase(false, true)]
|
||||||
[TestCase(true, false)]
|
[TestCase(true, false)]
|
||||||
[TestCase(false, false)]
|
[TestCase(false, false)]
|
||||||
public override void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour)
|
public void TestBeatmapNoComboColoursSkinOverride(bool useBeatmapSkin, bool useBeatmapColour)
|
||||||
{
|
{
|
||||||
TestBeatmap = new OsuCustomSkinWorkingBeatmap(audio, false);
|
PrepareBeatmap(() => new OsuCustomSkinWorkingBeatmap(audio, false));
|
||||||
base.TestBeatmapNoComboColoursSkinOverride(useBeatmapSkin, useBeatmapColour);
|
ConfigureTest(useBeatmapSkin, useBeatmapColour, true);
|
||||||
AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
|
AddAssert("is custom user skin colours", () => TestPlayer.UsableComboColours.SequenceEqual(TestSkin.Colours));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,54 +30,54 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestVariousSliders()
|
public void TestVariousSliders()
|
||||||
{
|
{
|
||||||
AddStep("Big Single", () => SetContents(() => testSimpleBig()));
|
AddStep("Big Single", () => SetContents(_ => testSimpleBig()));
|
||||||
AddStep("Medium Single", () => SetContents(() => testSimpleMedium()));
|
AddStep("Medium Single", () => SetContents(_ => testSimpleMedium()));
|
||||||
AddStep("Small Single", () => SetContents(() => testSimpleSmall()));
|
AddStep("Small Single", () => SetContents(_ => testSimpleSmall()));
|
||||||
AddStep("Big 1 Repeat", () => SetContents(() => testSimpleBig(1)));
|
AddStep("Big 1 Repeat", () => SetContents(_ => testSimpleBig(1)));
|
||||||
AddStep("Medium 1 Repeat", () => SetContents(() => testSimpleMedium(1)));
|
AddStep("Medium 1 Repeat", () => SetContents(_ => testSimpleMedium(1)));
|
||||||
AddStep("Small 1 Repeat", () => SetContents(() => testSimpleSmall(1)));
|
AddStep("Small 1 Repeat", () => SetContents(_ => testSimpleSmall(1)));
|
||||||
AddStep("Big 2 Repeats", () => SetContents(() => testSimpleBig(2)));
|
AddStep("Big 2 Repeats", () => SetContents(_ => testSimpleBig(2)));
|
||||||
AddStep("Medium 2 Repeats", () => SetContents(() => testSimpleMedium(2)));
|
AddStep("Medium 2 Repeats", () => SetContents(_ => testSimpleMedium(2)));
|
||||||
AddStep("Small 2 Repeats", () => SetContents(() => testSimpleSmall(2)));
|
AddStep("Small 2 Repeats", () => SetContents(_ => testSimpleSmall(2)));
|
||||||
|
|
||||||
AddStep("Slow Slider", () => SetContents(testSlowSpeed)); // slow long sliders take ages already so no repeat steps
|
AddStep("Slow Slider", () => SetContents(_ => testSlowSpeed())); // slow long sliders take ages already so no repeat steps
|
||||||
AddStep("Slow Short Slider", () => SetContents(() => testShortSlowSpeed()));
|
AddStep("Slow Short Slider", () => SetContents(_ => testShortSlowSpeed()));
|
||||||
AddStep("Slow Short Slider 1 Repeats", () => SetContents(() => testShortSlowSpeed(1)));
|
AddStep("Slow Short Slider 1 Repeats", () => SetContents(_ => testShortSlowSpeed(1)));
|
||||||
AddStep("Slow Short Slider 2 Repeats", () => SetContents(() => testShortSlowSpeed(2)));
|
AddStep("Slow Short Slider 2 Repeats", () => SetContents(_ => testShortSlowSpeed(2)));
|
||||||
|
|
||||||
AddStep("Fast Slider", () => SetContents(() => testHighSpeed()));
|
AddStep("Fast Slider", () => SetContents(_ => testHighSpeed()));
|
||||||
AddStep("Fast Slider 1 Repeat", () => SetContents(() => testHighSpeed(1)));
|
AddStep("Fast Slider 1 Repeat", () => SetContents(_ => testHighSpeed(1)));
|
||||||
AddStep("Fast Slider 2 Repeats", () => SetContents(() => testHighSpeed(2)));
|
AddStep("Fast Slider 2 Repeats", () => SetContents(_ => testHighSpeed(2)));
|
||||||
AddStep("Fast Short Slider", () => SetContents(() => testShortHighSpeed()));
|
AddStep("Fast Short Slider", () => SetContents(_ => testShortHighSpeed()));
|
||||||
AddStep("Fast Short Slider 1 Repeat", () => SetContents(() => testShortHighSpeed(1)));
|
AddStep("Fast Short Slider 1 Repeat", () => SetContents(_ => testShortHighSpeed(1)));
|
||||||
AddStep("Fast Short Slider 2 Repeats", () => SetContents(() => testShortHighSpeed(2)));
|
AddStep("Fast Short Slider 2 Repeats", () => SetContents(_ => testShortHighSpeed(2)));
|
||||||
AddStep("Fast Short Slider 6 Repeats", () => SetContents(() => testShortHighSpeed(6)));
|
AddStep("Fast Short Slider 6 Repeats", () => SetContents(_ => testShortHighSpeed(6)));
|
||||||
|
|
||||||
AddStep("Perfect Curve", () => SetContents(() => testPerfect()));
|
AddStep("Perfect Curve", () => SetContents(_ => testPerfect()));
|
||||||
AddStep("Perfect Curve 1 Repeat", () => SetContents(() => testPerfect(1)));
|
AddStep("Perfect Curve 1 Repeat", () => SetContents(_ => testPerfect(1)));
|
||||||
AddStep("Perfect Curve 2 Repeats", () => SetContents(() => testPerfect(2)));
|
AddStep("Perfect Curve 2 Repeats", () => SetContents(_ => testPerfect(2)));
|
||||||
|
|
||||||
AddStep("Linear Slider", () => SetContents(() => testLinear()));
|
AddStep("Linear Slider", () => SetContents(_ => testLinear()));
|
||||||
AddStep("Linear Slider 1 Repeat", () => SetContents(() => testLinear(1)));
|
AddStep("Linear Slider 1 Repeat", () => SetContents(_ => testLinear(1)));
|
||||||
AddStep("Linear Slider 2 Repeats", () => SetContents(() => testLinear(2)));
|
AddStep("Linear Slider 2 Repeats", () => SetContents(_ => testLinear(2)));
|
||||||
|
|
||||||
AddStep("Bezier Slider", () => SetContents(() => testBezier()));
|
AddStep("Bezier Slider", () => SetContents(_ => testBezier()));
|
||||||
AddStep("Bezier Slider 1 Repeat", () => SetContents(() => testBezier(1)));
|
AddStep("Bezier Slider 1 Repeat", () => SetContents(_ => testBezier(1)));
|
||||||
AddStep("Bezier Slider 2 Repeats", () => SetContents(() => testBezier(2)));
|
AddStep("Bezier Slider 2 Repeats", () => SetContents(_ => testBezier(2)));
|
||||||
|
|
||||||
AddStep("Linear Overlapping", () => SetContents(() => testLinearOverlapping()));
|
AddStep("Linear Overlapping", () => SetContents(_ => testLinearOverlapping()));
|
||||||
AddStep("Linear Overlapping 1 Repeat", () => SetContents(() => testLinearOverlapping(1)));
|
AddStep("Linear Overlapping 1 Repeat", () => SetContents(_ => testLinearOverlapping(1)));
|
||||||
AddStep("Linear Overlapping 2 Repeats", () => SetContents(() => testLinearOverlapping(2)));
|
AddStep("Linear Overlapping 2 Repeats", () => SetContents(_ => testLinearOverlapping(2)));
|
||||||
|
|
||||||
AddStep("Catmull Slider", () => SetContents(() => testCatmull()));
|
AddStep("Catmull Slider", () => SetContents(_ => testCatmull()));
|
||||||
AddStep("Catmull Slider 1 Repeat", () => SetContents(() => testCatmull(1)));
|
AddStep("Catmull Slider 1 Repeat", () => SetContents(_ => testCatmull(1)));
|
||||||
AddStep("Catmull Slider 2 Repeats", () => SetContents(() => testCatmull(2)));
|
AddStep("Catmull Slider 2 Repeats", () => SetContents(_ => testCatmull(2)));
|
||||||
|
|
||||||
AddStep("Big Single, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset()));
|
AddStep("Big Single, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset()));
|
||||||
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset(1)));
|
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset(1)));
|
||||||
|
|
||||||
AddStep("Distance Overflow", () => SetContents(() => testDistanceOverflow()));
|
AddStep("Distance Overflow", () => SetContents(_ => testDistanceOverflow()));
|
||||||
AddStep("Distance Overflow 1 Repeat", () => SetContents(() => testDistanceOverflow(1)));
|
AddStep("Distance Overflow 1 Repeat", () => SetContents(_ => testDistanceOverflow(1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -29,15 +29,15 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
public void TestVariousSpinners(bool autoplay)
|
public void TestVariousSpinners(bool autoplay)
|
||||||
{
|
{
|
||||||
string term = autoplay ? "Hit" : "Miss";
|
string term = autoplay ? "Hit" : "Miss";
|
||||||
AddStep($"{term} Big", () => SetContents(() => testSingle(2, autoplay)));
|
AddStep($"{term} Big", () => SetContents(_ => testSingle(2, autoplay)));
|
||||||
AddStep($"{term} Medium", () => SetContents(() => testSingle(5, autoplay)));
|
AddStep($"{term} Medium", () => SetContents(_ => testSingle(5, autoplay)));
|
||||||
AddStep($"{term} Small", () => SetContents(() => testSingle(7, autoplay)));
|
AddStep($"{term} Small", () => SetContents(_ => testSingle(7, autoplay)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSpinningSamplePitchShift()
|
public void TestSpinningSamplePitchShift()
|
||||||
{
|
{
|
||||||
AddStep("Add spinner", () => SetContents(() => testSingle(5, true, 4000)));
|
AddStep("Add spinner", () => SetContents(_ => testSingle(5, true, 4000)));
|
||||||
AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8);
|
AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8);
|
||||||
AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8);
|
AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8);
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
public void TestLongSpinner(bool autoplay)
|
public void TestLongSpinner(bool autoplay)
|
||||||
{
|
{
|
||||||
AddStep("Very long spinner", () => SetContents(() => testSingle(5, autoplay, 4000)));
|
AddStep("Very long spinner", () => SetContents(_ => testSingle(5, autoplay, 4000)));
|
||||||
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
|
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
|
||||||
AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0));
|
AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0));
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
public void TestSuperShortSpinner(bool autoplay)
|
public void TestSuperShortSpinner(bool autoplay)
|
||||||
{
|
{
|
||||||
AddStep("Very short spinner", () => SetContents(() => testSingle(5, autoplay, 200)));
|
AddStep("Very short spinner", () => SetContents(_ => testSingle(5, autoplay, 200)));
|
||||||
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
|
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
|
||||||
AddUntilStep("Short spinner implicitly completes", () => drawableSpinner.Progress == 1);
|
AddUntilStep("Short spinner implicitly completes", () => drawableSpinner.Progress == 1);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||||
{
|
{
|
||||||
new Aim(mods),
|
new Aim(mods),
|
||||||
new Speed(mods)
|
new Speed(mods)
|
||||||
|
@ -6,14 +6,13 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
|
||||||
using osu.Game.Rulesets.Osu.Replays;
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModAutoplay : ModAutoplay<OsuHitObject>
|
public class OsuModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray();
|
||||||
|
|
||||||
|
280
osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
Normal file
280
osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Mod that randomises the positions of the <see cref="HitObject"/>s
|
||||||
|
/// </summary>
|
||||||
|
public class OsuModRandom : ModRandom, IApplicableToBeatmap
|
||||||
|
{
|
||||||
|
public override string Description => "It never gets boring!";
|
||||||
|
public override bool Ranked => false;
|
||||||
|
|
||||||
|
// The relative distance to the edge of the playfield before objects' positions should start to "turn around" and curve towards the middle.
|
||||||
|
// The closer the hit objects draw to the border, the sharper the turn
|
||||||
|
private const float playfield_edge_ratio = 0.375f;
|
||||||
|
|
||||||
|
private static readonly float border_distance_x = OsuPlayfield.BASE_SIZE.X * playfield_edge_ratio;
|
||||||
|
private static readonly float border_distance_y = OsuPlayfield.BASE_SIZE.Y * playfield_edge_ratio;
|
||||||
|
|
||||||
|
private static readonly Vector2 playfield_middle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2);
|
||||||
|
|
||||||
|
private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast;
|
||||||
|
|
||||||
|
private Random rng;
|
||||||
|
|
||||||
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
if (!(beatmap is OsuBeatmap osuBeatmap))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var hitObjects = osuBeatmap.HitObjects;
|
||||||
|
|
||||||
|
Seed.Value ??= RNG.Next();
|
||||||
|
|
||||||
|
rng = new Random((int)Seed.Value);
|
||||||
|
|
||||||
|
RandomObjectInfo previous = null;
|
||||||
|
|
||||||
|
float rateOfChangeMultiplier = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < hitObjects.Count; i++)
|
||||||
|
{
|
||||||
|
var hitObject = hitObjects[i];
|
||||||
|
|
||||||
|
var current = new RandomObjectInfo(hitObject);
|
||||||
|
|
||||||
|
// rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams
|
||||||
|
if (i % 3 == 0)
|
||||||
|
rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1;
|
||||||
|
|
||||||
|
if (hitObject is Spinner)
|
||||||
|
{
|
||||||
|
previous = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyRandomisation(rateOfChangeMultiplier, previous, current);
|
||||||
|
|
||||||
|
hitObject.Position = current.PositionRandomised;
|
||||||
|
|
||||||
|
// update end position as it may have changed as a result of the position update.
|
||||||
|
current.EndPositionRandomised = current.PositionRandomised;
|
||||||
|
|
||||||
|
switch (hitObject)
|
||||||
|
{
|
||||||
|
case Slider slider:
|
||||||
|
// Shift nested objects the same distance as the slider got shifted in the randomisation process
|
||||||
|
// so that moveSliderIntoPlayfield() can determine their relative distances to slider.Position and thus minMargin
|
||||||
|
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal));
|
||||||
|
|
||||||
|
var oldPos = new Vector2(slider.Position.X, slider.Position.Y);
|
||||||
|
|
||||||
|
moveSliderIntoPlayfield(slider, current);
|
||||||
|
|
||||||
|
// Shift them again to move them to their final position after the slider got moved into the playfield
|
||||||
|
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, oldPos));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the final position of the hit object
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Final position of the hit object</returns>
|
||||||
|
private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo previous, RandomObjectInfo current)
|
||||||
|
{
|
||||||
|
if (previous == null)
|
||||||
|
{
|
||||||
|
var playfieldSize = OsuPlayfield.BASE_SIZE;
|
||||||
|
|
||||||
|
current.AngleRad = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI);
|
||||||
|
current.PositionRandomised = new Vector2((float)rng.NextDouble() * playfieldSize.X, (float)rng.NextDouble() * playfieldSize.Y);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float distanceToPrev = Vector2.Distance(previous.EndPositionOriginal, current.PositionOriginal);
|
||||||
|
|
||||||
|
// The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object)
|
||||||
|
// is proportional to the distance between the last and the current hit object
|
||||||
|
// to allow jumps and prevent too sharp turns during streams.
|
||||||
|
var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / playfield_diagonal;
|
||||||
|
|
||||||
|
current.AngleRad = (float)randomAngleRad + previous.AngleRad;
|
||||||
|
if (current.AngleRad < 0)
|
||||||
|
current.AngleRad += 2 * (float)Math.PI;
|
||||||
|
|
||||||
|
var posRelativeToPrev = new Vector2(
|
||||||
|
distanceToPrev * (float)Math.Cos(current.AngleRad),
|
||||||
|
distanceToPrev * (float)Math.Sin(current.AngleRad)
|
||||||
|
);
|
||||||
|
|
||||||
|
posRelativeToPrev = getRotatedVector(previous.EndPositionRandomised, posRelativeToPrev);
|
||||||
|
|
||||||
|
current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X);
|
||||||
|
|
||||||
|
var position = Vector2.Add(previous.EndPositionRandomised, posRelativeToPrev);
|
||||||
|
|
||||||
|
// Move hit objects back into the playfield if they are outside of it,
|
||||||
|
// which would sometimes happen during big jumps otherwise.
|
||||||
|
position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X);
|
||||||
|
position.Y = MathHelper.Clamp(position.Y, 0, OsuPlayfield.BASE_SIZE.Y);
|
||||||
|
|
||||||
|
current.PositionRandomised = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves the <see cref="Slider"/> and all necessary nested <see cref="OsuHitObject"/>s into the <see cref="OsuPlayfield"/> if they aren't already.
|
||||||
|
/// </summary>
|
||||||
|
private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo)
|
||||||
|
{
|
||||||
|
// Min. distances from the slider's position to the playfield border
|
||||||
|
var minMargin = new MarginPadding();
|
||||||
|
|
||||||
|
foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderEndCircle))
|
||||||
|
{
|
||||||
|
if (!(hitObject is OsuHitObject osuHitObject))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var relativePos = Vector2.Subtract(osuHitObject.Position, slider.Position);
|
||||||
|
|
||||||
|
minMargin.Left = Math.Max(minMargin.Left, -relativePos.X);
|
||||||
|
minMargin.Right = Math.Max(minMargin.Right, relativePos.X);
|
||||||
|
minMargin.Top = Math.Max(minMargin.Top, -relativePos.Y);
|
||||||
|
minMargin.Bottom = Math.Max(minMargin.Bottom, relativePos.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slider.Position.X < minMargin.Left)
|
||||||
|
slider.Position = new Vector2(minMargin.Left, slider.Position.Y);
|
||||||
|
else if (slider.Position.X + minMargin.Right > OsuPlayfield.BASE_SIZE.X)
|
||||||
|
slider.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - minMargin.Right, slider.Position.Y);
|
||||||
|
|
||||||
|
if (slider.Position.Y < minMargin.Top)
|
||||||
|
slider.Position = new Vector2(slider.Position.X, minMargin.Top);
|
||||||
|
else if (slider.Position.Y + minMargin.Bottom > OsuPlayfield.BASE_SIZE.Y)
|
||||||
|
slider.Position = new Vector2(slider.Position.X, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom);
|
||||||
|
|
||||||
|
currentObjectInfo.PositionRandomised = slider.Position;
|
||||||
|
currentObjectInfo.EndPositionRandomised = slider.EndPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shifts all nested <see cref="SliderTick"/>s and <see cref="SliderRepeat"/>s by the specified shift.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slider"><see cref="Slider"/> whose nested <see cref="SliderTick"/>s and <see cref="SliderRepeat"/>s should be shifted</param>
|
||||||
|
/// <param name="shift">The <see cref="Vector2"/> the <see cref="Slider"/>'s nested <see cref="SliderTick"/>s and <see cref="SliderRepeat"/>s should be shifted by</param>
|
||||||
|
private void shiftNestedObjects(Slider slider, Vector2 shift)
|
||||||
|
{
|
||||||
|
foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderRepeat))
|
||||||
|
{
|
||||||
|
if (!(hitObject is OsuHitObject osuHitObject))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
osuHitObject.Position = Vector2.Add(osuHitObject.Position, shift);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines the position of the current hit object relative to the previous one.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The position of the current hit object relative to the previous one</returns>
|
||||||
|
private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev)
|
||||||
|
{
|
||||||
|
var relativeRotationDistance = 0f;
|
||||||
|
|
||||||
|
if (prevPosChanged.X < playfield_middle.X)
|
||||||
|
{
|
||||||
|
relativeRotationDistance = Math.Max(
|
||||||
|
(border_distance_x - prevPosChanged.X) / border_distance_x,
|
||||||
|
relativeRotationDistance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
relativeRotationDistance = Math.Max(
|
||||||
|
(prevPosChanged.X - (OsuPlayfield.BASE_SIZE.X - border_distance_x)) / border_distance_x,
|
||||||
|
relativeRotationDistance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevPosChanged.Y < playfield_middle.Y)
|
||||||
|
{
|
||||||
|
relativeRotationDistance = Math.Max(
|
||||||
|
(border_distance_y - prevPosChanged.Y) / border_distance_y,
|
||||||
|
relativeRotationDistance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
relativeRotationDistance = Math.Max(
|
||||||
|
(prevPosChanged.Y - (OsuPlayfield.BASE_SIZE.Y - border_distance_y)) / border_distance_y,
|
||||||
|
relativeRotationDistance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rotateVectorTowardsVector(posRelativeToPrev, playfield_middle - prevPosChanged, relativeRotationDistance / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotates vector "initial" towards vector "destinantion"
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="initial">Vector to rotate to "destination"</param>
|
||||||
|
/// <param name="destination">Vector "initial" should be rotated to</param>
|
||||||
|
/// <param name="relativeDistance">The angle the vector should be rotated relative to the difference between the angles of the the two vectors.</param>
|
||||||
|
/// <returns>Resulting vector</returns>
|
||||||
|
private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance)
|
||||||
|
{
|
||||||
|
var initialAngleRad = Math.Atan2(initial.Y, initial.X);
|
||||||
|
var destAngleRad = Math.Atan2(destination.Y, destination.X);
|
||||||
|
|
||||||
|
var diff = destAngleRad - initialAngleRad;
|
||||||
|
|
||||||
|
while (diff < -Math.PI) diff += 2 * Math.PI;
|
||||||
|
|
||||||
|
while (diff > Math.PI) diff -= 2 * Math.PI;
|
||||||
|
|
||||||
|
var finalAngleRad = initialAngleRad + relativeDistance * diff;
|
||||||
|
|
||||||
|
return new Vector2(
|
||||||
|
initial.Length * (float)Math.Cos(finalAngleRad),
|
||||||
|
initial.Length * (float)Math.Sin(finalAngleRad)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RandomObjectInfo
|
||||||
|
{
|
||||||
|
public float AngleRad { get; set; }
|
||||||
|
|
||||||
|
public Vector2 PositionOriginal { get; }
|
||||||
|
public Vector2 PositionRandomised { get; set; }
|
||||||
|
|
||||||
|
public Vector2 EndPositionOriginal { get; }
|
||||||
|
public Vector2 EndPositionRandomised { get; set; }
|
||||||
|
|
||||||
|
public RandomObjectInfo(OsuHitObject hitObject)
|
||||||
|
{
|
||||||
|
PositionRandomised = PositionOriginal = hitObject.Position;
|
||||||
|
EndPositionRandomised = EndPositionOriginal = hitObject.EndPosition;
|
||||||
|
AngleRad = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Pooling;
|
using osu.Framework.Graphics.Pooling;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Pooling;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||||
@ -12,34 +13,29 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Visualises the <see cref="FollowPoint"/>s between two <see cref="DrawableOsuHitObject"/>s.
|
/// Visualises the <see cref="FollowPoint"/>s between two <see cref="DrawableOsuHitObject"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FollowPointConnection : PoolableDrawable
|
public class FollowPointConnection : PoolableDrawableWithLifetime<FollowPointLifetimeEntry>
|
||||||
{
|
{
|
||||||
// Todo: These shouldn't be constants
|
// Todo: These shouldn't be constants
|
||||||
public const int SPACING = 32;
|
public const int SPACING = 32;
|
||||||
public const double PREEMPT = 800;
|
public const double PREEMPT = 800;
|
||||||
|
|
||||||
public FollowPointLifetimeEntry Entry;
|
|
||||||
public DrawablePool<FollowPoint> Pool;
|
public DrawablePool<FollowPoint> Pool;
|
||||||
|
|
||||||
protected override void PrepareForUse()
|
protected override void OnApply(FollowPointLifetimeEntry entry)
|
||||||
{
|
{
|
||||||
base.PrepareForUse();
|
base.OnApply(entry);
|
||||||
|
|
||||||
Entry.Invalidated += onEntryInvalidated;
|
|
||||||
|
|
||||||
|
entry.Invalidated += onEntryInvalidated;
|
||||||
refreshPoints();
|
refreshPoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void FreeAfterUse()
|
protected override void OnFree(FollowPointLifetimeEntry entry)
|
||||||
{
|
{
|
||||||
base.FreeAfterUse();
|
base.OnFree(entry);
|
||||||
|
|
||||||
Entry.Invalidated -= onEntryInvalidated;
|
|
||||||
|
|
||||||
|
entry.Invalidated -= onEntryInvalidated;
|
||||||
// Return points to the pool.
|
// Return points to the pool.
|
||||||
ClearInternal(false);
|
ClearInternal(false);
|
||||||
|
|
||||||
Entry = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onEntryInvalidated() => Scheduler.AddOnce(refreshPoints);
|
private void onEntryInvalidated() => Scheduler.AddOnce(refreshPoints);
|
||||||
@ -48,8 +44,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
{
|
{
|
||||||
ClearInternal(false);
|
ClearInternal(false);
|
||||||
|
|
||||||
OsuHitObject start = Entry.Start;
|
var entry = Entry;
|
||||||
OsuHitObject end = Entry.End;
|
if (entry?.End == null) return;
|
||||||
|
|
||||||
|
OsuHitObject start = entry.Start;
|
||||||
|
OsuHitObject end = entry.End;
|
||||||
|
|
||||||
double startTime = start.GetEndTime();
|
double startTime = start.GetEndTime();
|
||||||
|
|
||||||
@ -87,14 +86,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
fp.FadeIn(end.TimeFadeIn);
|
fp.FadeIn(end.TimeFadeIn);
|
||||||
fp.ScaleTo(end.Scale, end.TimeFadeIn, Easing.Out);
|
fp.ScaleTo(end.Scale, end.TimeFadeIn, Easing.Out);
|
||||||
fp.MoveTo(pointEndPosition, end.TimeFadeIn, Easing.Out);
|
fp.MoveTo(pointEndPosition, end.TimeFadeIn, Easing.Out);
|
||||||
fp.Delay(fadeOutTime - fadeInTime).FadeOut(end.TimeFadeIn);
|
fp.Delay(fadeOutTime - fadeInTime).FadeOut(end.TimeFadeIn).Expire();
|
||||||
|
|
||||||
finalTransformEndTime = fadeOutTime + end.TimeFadeIn;
|
finalTransformEndTime = fp.LifetimeEnd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: use Expire() on FollowPoints and take lifetime from them when https://github.com/ppy/osu-framework/issues/3300 is fixed.
|
entry.LifetimeEnd = finalTransformEndTime;
|
||||||
Entry.LifetimeEnd = finalTransformEndTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Performance;
|
using osu.Framework.Graphics.Performance;
|
||||||
@ -11,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
{
|
{
|
||||||
public class FollowPointLifetimeEntry : LifetimeEntry
|
public class FollowPointLifetimeEntry : LifetimeEntry
|
||||||
{
|
{
|
||||||
public event Action Invalidated;
|
public event Action? Invalidated;
|
||||||
public readonly OsuHitObject Start;
|
public readonly OsuHitObject Start;
|
||||||
|
|
||||||
public FollowPointLifetimeEntry(OsuHitObject start)
|
public FollowPointLifetimeEntry(OsuHitObject start)
|
||||||
@ -22,9 +24,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
bindEvents();
|
bindEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
private OsuHitObject end;
|
private OsuHitObject? end;
|
||||||
|
|
||||||
public OsuHitObject End
|
public OsuHitObject? End
|
||||||
{
|
{
|
||||||
get => end;
|
get => end;
|
||||||
set
|
set
|
||||||
@ -56,11 +58,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
|
|
||||||
public void UnbindEvents()
|
public void UnbindEvents()
|
||||||
{
|
{
|
||||||
if (Start != null)
|
Start.DefaultsApplied -= onDefaultsApplied;
|
||||||
{
|
Start.PositionBindable.ValueChanged -= onPositionChanged;
|
||||||
Start.DefaultsApplied -= onDefaultsApplied;
|
|
||||||
Start.PositionBindable.ValueChanged -= onPositionChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (End != null)
|
if (End != null)
|
||||||
{
|
{
|
||||||
|
@ -6,43 +6,32 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Performance;
|
|
||||||
using osu.Framework.Graphics.Pooling;
|
using osu.Framework.Graphics.Pooling;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Pooling;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Visualises connections between <see cref="DrawableOsuHitObject"/>s.
|
/// Visualises connections between <see cref="DrawableOsuHitObject"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FollowPointRenderer : CompositeDrawable
|
public class FollowPointRenderer : PooledDrawableWithLifetimeContainer<FollowPointLifetimeEntry, FollowPointConnection>
|
||||||
{
|
{
|
||||||
public override bool RemoveCompletedTransforms => false;
|
public new IReadOnlyList<FollowPointLifetimeEntry> Entries => lifetimeEntries;
|
||||||
|
|
||||||
public IReadOnlyList<FollowPointLifetimeEntry> Entries => lifetimeEntries;
|
|
||||||
|
|
||||||
private DrawablePool<FollowPointConnection> connectionPool;
|
private DrawablePool<FollowPointConnection> connectionPool;
|
||||||
private DrawablePool<FollowPoint> pointPool;
|
private DrawablePool<FollowPoint> pointPool;
|
||||||
|
|
||||||
private readonly List<FollowPointLifetimeEntry> lifetimeEntries = new List<FollowPointLifetimeEntry>();
|
private readonly List<FollowPointLifetimeEntry> lifetimeEntries = new List<FollowPointLifetimeEntry>();
|
||||||
private readonly Dictionary<LifetimeEntry, FollowPointConnection> connectionsInUse = new Dictionary<LifetimeEntry, FollowPointConnection>();
|
|
||||||
private readonly Dictionary<HitObject, IBindable> startTimeMap = new Dictionary<HitObject, IBindable>();
|
private readonly Dictionary<HitObject, IBindable> startTimeMap = new Dictionary<HitObject, IBindable>();
|
||||||
private readonly LifetimeEntryManager lifetimeManager = new LifetimeEntryManager();
|
|
||||||
|
|
||||||
public FollowPointRenderer()
|
|
||||||
{
|
|
||||||
lifetimeManager.EntryBecameAlive += onEntryBecameAlive;
|
|
||||||
lifetimeManager.EntryBecameDead += onEntryBecameDead;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
connectionPool = new DrawablePoolNoLifetime<FollowPointConnection>(1, 200),
|
connectionPool = new DrawablePool<FollowPointConnection>(1, 200),
|
||||||
pointPool = new DrawablePoolNoLifetime<FollowPoint>(50, 1000)
|
pointPool = new DrawablePool<FollowPoint>(50, 1000)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
previousEntry.End = newEntry.Start;
|
previousEntry.End = newEntry.Start;
|
||||||
}
|
}
|
||||||
|
|
||||||
lifetimeManager.AddEntry(newEntry);
|
Add(newEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeEntry(OsuHitObject hitObject)
|
private void removeEntry(OsuHitObject hitObject)
|
||||||
@ -118,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
entry.UnbindEvents();
|
entry.UnbindEvents();
|
||||||
|
|
||||||
lifetimeEntries.RemoveAt(index);
|
lifetimeEntries.RemoveAt(index);
|
||||||
lifetimeManager.RemoveEntry(entry);
|
Remove(entry);
|
||||||
|
|
||||||
if (index > 0)
|
if (index > 0)
|
||||||
{
|
{
|
||||||
@ -131,30 +120,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool CheckChildrenLife()
|
protected override FollowPointConnection GetDrawable(FollowPointLifetimeEntry entry)
|
||||||
{
|
{
|
||||||
bool anyAliveChanged = base.CheckChildrenLife();
|
var connection = connectionPool.Get();
|
||||||
anyAliveChanged |= lifetimeManager.Update(Time.Current);
|
connection.Pool = pointPool;
|
||||||
return anyAliveChanged;
|
connection.Apply(entry);
|
||||||
}
|
return connection;
|
||||||
|
|
||||||
private void onEntryBecameAlive(LifetimeEntry entry)
|
|
||||||
{
|
|
||||||
var connection = connectionPool.Get(c =>
|
|
||||||
{
|
|
||||||
c.Entry = (FollowPointLifetimeEntry)entry;
|
|
||||||
c.Pool = pointPool;
|
|
||||||
});
|
|
||||||
|
|
||||||
connectionsInUse[entry] = connection;
|
|
||||||
|
|
||||||
AddInternal(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onEntryBecameDead(LifetimeEntry entry)
|
|
||||||
{
|
|
||||||
RemoveInternal(connectionsInUse[entry]);
|
|
||||||
connectionsInUse.Remove(entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onStartTimeChanged(OsuHitObject hitObject)
|
private void onStartTimeChanged(OsuHitObject hitObject)
|
||||||
@ -171,16 +142,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
entry.UnbindEvents();
|
entry.UnbindEvents();
|
||||||
lifetimeEntries.Clear();
|
lifetimeEntries.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DrawablePoolNoLifetime<T> : DrawablePool<T>
|
|
||||||
where T : PoolableDrawable, new()
|
|
||||||
{
|
|
||||||
public override bool RemoveWhenNotAlive => false;
|
|
||||||
|
|
||||||
public DrawablePoolNoLifetime(int initialSize, int? maximumSize = null)
|
|
||||||
: base(initialSize, maximumSize)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,8 +182,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
// todo: temporary / arbitrary, used for lifetime optimisation.
|
// todo: temporary / arbitrary, used for lifetime optimisation.
|
||||||
this.Delay(800).FadeOut();
|
this.Delay(800).FadeOut();
|
||||||
|
|
||||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Idle:
|
case ArmedState.Idle:
|
||||||
|
@ -97,8 +97,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.UpdateHitStateTransforms(state);
|
base.UpdateHitStateTransforms(state);
|
||||||
|
|
||||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Idle:
|
case ArmedState.Idle:
|
||||||
|
@ -7,13 +7,14 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking, IHasMainCirclePiece
|
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, IHasMainCirclePiece
|
||||||
{
|
{
|
||||||
public new SliderTailCircle HitObject => (SliderTailCircle)base.HitObject;
|
public new SliderTailCircle HitObject => (SliderTailCircle)base.HitObject;
|
||||||
|
|
||||||
@ -86,8 +87,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
Debug.Assert(HitObject.HitWindows != null);
|
Debug.Assert(HitObject.HitWindows != null);
|
||||||
|
|
||||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Idle:
|
case ArmedState.Idle:
|
||||||
@ -111,7 +110,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateSnakingPosition(Vector2 start, Vector2 end) =>
|
protected override void OnApply()
|
||||||
Position = HitObject.RepeatIndex % 2 == 0 ? end : start;
|
{
|
||||||
|
base.OnApply();
|
||||||
|
|
||||||
|
if (Slider != null)
|
||||||
|
Position = Slider.CurvePositionAt(HitObject.RepeatIndex % 2 == 0 ? 1 : 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,8 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
{
|
{
|
||||||
new OsuModTarget(),
|
new OsuModTarget(),
|
||||||
new OsuModDifficultyAdjust(),
|
new OsuModDifficultyAdjust(),
|
||||||
new OsuModClassic()
|
new OsuModClassic(),
|
||||||
|
new OsuModRandom(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
|
@ -42,6 +42,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Texture = textures.Get(@"Gameplay/osu/disc"),
|
Texture = textures.Get(@"Gameplay/osu/disc"),
|
||||||
},
|
},
|
||||||
|
new KiaiFlash
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
triangles = new TrianglesPiece
|
triangles = new TrianglesPiece
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|
||||||
{
|
|
||||||
public interface IMainCirclePiece
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Begins animating this <see cref="IMainCirclePiece"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="state">The <see cref="ArmedState"/> of the related <see cref="DrawableHitCircle"/>.</param>
|
|
||||||
void Animate(ArmedState state);
|
|
||||||
}
|
|
||||||
}
|
|
43
osu.Game.Rulesets.Osu/Skinning/Default/KiaiFlash.cs
Normal file
43
osu.Game.Rulesets.Osu/Skinning/Default/KiaiFlash.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||||
|
{
|
||||||
|
public class KiaiFlash : BeatSyncedContainer
|
||||||
|
{
|
||||||
|
private const double fade_length = 80;
|
||||||
|
|
||||||
|
private const float flash_opacity = 0.25f;
|
||||||
|
|
||||||
|
public KiaiFlash()
|
||||||
|
{
|
||||||
|
EarlyActivationMilliseconds = 80;
|
||||||
|
Blending = BlendingParameters.Additive;
|
||||||
|
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.White,
|
||||||
|
Alpha = 0f,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||||
|
{
|
||||||
|
if (!effectPoint.KiaiMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Child
|
||||||
|
.FadeTo(flash_opacity, EarlyActivationMilliseconds, Easing.OutQuint)
|
||||||
|
.Then()
|
||||||
|
.FadeOut(timingPoint.BeatLength - fade_length, Easing.OutSine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ using osuTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||||
{
|
{
|
||||||
public class MainCirclePiece : CompositeDrawable, IMainCirclePiece
|
public class MainCirclePiece : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly CirclePiece circle;
|
private readonly CirclePiece circle;
|
||||||
private readonly RingPiece ring;
|
private readonly RingPiece ring;
|
||||||
@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||||
|
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private DrawableHitObject drawableObject { get; set; }
|
private DrawableHitObject drawableObject { get; set; }
|
||||||
@ -53,6 +54,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
|
|
||||||
accentColour.BindTo(drawableObject.AccentColour);
|
accentColour.BindTo(drawableObject.AccentColour);
|
||||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||||
|
armedState.BindTo(drawableObject.State);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -67,16 +69,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
|
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
|
||||||
|
|
||||||
|
armedState.BindValueChanged(animate, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Animate(ArmedState state)
|
private void animate(ValueChangedEvent<ArmedState> state)
|
||||||
{
|
{
|
||||||
|
ClearTransforms(true);
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
|
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
|
||||||
glow.FadeOut(400);
|
glow.FadeOut(400);
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state.NewValue)
|
||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
const double flash_in = 40;
|
const double flash_in = 40;
|
||||||
@ -89,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
|||||||
explode.FadeIn(flash_in);
|
explode.FadeIn(flash_in);
|
||||||
this.ScaleTo(1.5f, 400, Easing.OutQuad);
|
this.ScaleTo(1.5f, 400, Easing.OutQuad);
|
||||||
|
|
||||||
using (BeginDelayedSequence(flash_in, true))
|
using (BeginDelayedSequence(flash_in))
|
||||||
{
|
{
|
||||||
// after the flash, we can hide some elements that were behind it
|
// after the flash, we can hide some elements that were behind it
|
||||||
ring.FadeOut();
|
ring.FadeOut();
|
||||||
|
61
osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingSprite.cs
Normal file
61
osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingSprite.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||||
|
{
|
||||||
|
internal class KiaiFlashingSprite : BeatSyncedContainer
|
||||||
|
{
|
||||||
|
private readonly Sprite mainSprite;
|
||||||
|
private readonly Sprite flashingSprite;
|
||||||
|
|
||||||
|
public Texture Texture
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
mainSprite.Texture = value;
|
||||||
|
flashingSprite.Texture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const float flash_opacity = 0.3f;
|
||||||
|
|
||||||
|
public KiaiFlashingSprite()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
mainSprite = new Sprite
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
},
|
||||||
|
flashingSprite = new Sprite
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Alpha = 0,
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||||
|
{
|
||||||
|
if (!effectPoint.KiaiMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
flashingSprite
|
||||||
|
.FadeTo(flash_opacity)
|
||||||
|
.Then()
|
||||||
|
.FadeOut(timingPoint.BeatLength * 0.75f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,14 +5,12 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -20,7 +18,7 @@ using static osu.Game.Skinning.LegacySkinConfiguration;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||||
{
|
{
|
||||||
public class LegacyMainCirclePiece : CompositeDrawable, IMainCirclePiece
|
public class LegacyMainCirclePiece : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly string priorityLookup;
|
private readonly string priorityLookup;
|
||||||
private readonly bool hasNumber;
|
private readonly bool hasNumber;
|
||||||
@ -33,14 +31,15 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Container<Sprite> circleSprites;
|
private Container circleSprites;
|
||||||
private Sprite hitCircleSprite;
|
private Drawable hitCircleSprite;
|
||||||
private Sprite hitCircleOverlay;
|
private Drawable hitCircleOverlay;
|
||||||
|
|
||||||
private SkinnableSpriteText hitCircleText;
|
private SkinnableSpriteText hitCircleText;
|
||||||
|
|
||||||
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||||
|
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private DrawableHitObject drawableObject { get; set; }
|
private DrawableHitObject drawableObject { get; set; }
|
||||||
@ -72,20 +71,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
circleSprites = new Container<Sprite>
|
circleSprites = new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
hitCircleSprite = new Sprite
|
hitCircleSprite = new KiaiFlashingSprite
|
||||||
{
|
{
|
||||||
Texture = baseTexture,
|
Texture = baseTexture,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
hitCircleOverlay = new Sprite
|
hitCircleOverlay = new KiaiFlashingSprite
|
||||||
{
|
{
|
||||||
Texture = overlayTexture,
|
Texture = overlayTexture,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -115,6 +114,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
accentColour.BindTo(drawableObject.AccentColour);
|
accentColour.BindTo(drawableObject.AccentColour);
|
||||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||||
|
armedState.BindTo(drawableObject.State);
|
||||||
|
|
||||||
Texture getTextureWithFallback(string name)
|
Texture getTextureWithFallback(string name)
|
||||||
{
|
{
|
||||||
@ -139,15 +139,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||||
if (hasNumber)
|
if (hasNumber)
|
||||||
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
||||||
|
|
||||||
|
armedState.BindValueChanged(animate, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Animate(ArmedState state)
|
private void animate(ValueChangedEvent<ArmedState> state)
|
||||||
{
|
{
|
||||||
const double legacy_fade_duration = 240;
|
const double legacy_fade_duration = 240;
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
|
ClearTransforms(true);
|
||||||
|
|
||||||
|
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state.NewValue)
|
||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
||||||
|
@ -34,90 +34,90 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
|
|
||||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
public override Drawable GetDrawableComponent(ISkinComponent component)
|
||||||
{
|
{
|
||||||
if (!(component is OsuSkinComponent osuComponent))
|
if (component is OsuSkinComponent osuComponent)
|
||||||
return null;
|
|
||||||
|
|
||||||
switch (osuComponent.Component)
|
|
||||||
{
|
{
|
||||||
case OsuSkinComponents.FollowPoint:
|
switch (osuComponent.Component)
|
||||||
return this.GetAnimation(component.LookupName, true, false, true, startAtCurrentTime: false);
|
{
|
||||||
|
case OsuSkinComponents.FollowPoint:
|
||||||
|
return this.GetAnimation(component.LookupName, true, false, true, startAtCurrentTime: false);
|
||||||
|
|
||||||
case OsuSkinComponents.SliderFollowCircle:
|
case OsuSkinComponents.SliderFollowCircle:
|
||||||
var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true);
|
var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true);
|
||||||
if (followCircle != null)
|
if (followCircle != null)
|
||||||
// follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x
|
// follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x
|
||||||
followCircle.Scale *= 0.5f;
|
followCircle.Scale *= 0.5f;
|
||||||
return followCircle;
|
return followCircle;
|
||||||
|
|
||||||
case OsuSkinComponents.SliderBall:
|
case OsuSkinComponents.SliderBall:
|
||||||
var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: "");
|
var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: "");
|
||||||
|
|
||||||
// todo: slider ball has a custom frame delay based on velocity
|
// todo: slider ball has a custom frame delay based on velocity
|
||||||
// Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME);
|
// Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME);
|
||||||
|
|
||||||
if (sliderBallContent != null)
|
if (sliderBallContent != null)
|
||||||
return new LegacySliderBall(sliderBallContent);
|
return new LegacySliderBall(sliderBallContent);
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.SliderBody:
|
|
||||||
if (hasHitCircle.Value)
|
|
||||||
return new LegacySliderBody();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.SliderTailHitCircle:
|
|
||||||
if (hasHitCircle.Value)
|
|
||||||
return new LegacyMainCirclePiece("sliderendcircle", false);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.SliderHeadHitCircle:
|
|
||||||
if (hasHitCircle.Value)
|
|
||||||
return new LegacyMainCirclePiece("sliderstartcircle");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.HitCircle:
|
|
||||||
if (hasHitCircle.Value)
|
|
||||||
return new LegacyMainCirclePiece();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.Cursor:
|
|
||||||
if (Source.GetTexture("cursor") != null)
|
|
||||||
return new LegacyCursor();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.CursorTrail:
|
|
||||||
if (Source.GetTexture("cursortrail") != null)
|
|
||||||
return new LegacyCursorTrail();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
case OsuSkinComponents.HitCircleText:
|
|
||||||
if (!this.HasFont(LegacyFont.HitCircle))
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return new LegacySpriteText(LegacyFont.HitCircle)
|
case OsuSkinComponents.SliderBody:
|
||||||
{
|
if (hasHitCircle.Value)
|
||||||
// stable applies a blanket 0.8x scale to hitcircle fonts
|
return new LegacySliderBody();
|
||||||
Scale = new Vector2(0.8f),
|
|
||||||
};
|
|
||||||
|
|
||||||
case OsuSkinComponents.SpinnerBody:
|
return null;
|
||||||
bool hasBackground = Source.GetTexture("spinner-background") != null;
|
|
||||||
|
|
||||||
if (Source.GetTexture("spinner-top") != null && !hasBackground)
|
case OsuSkinComponents.SliderTailHitCircle:
|
||||||
return new LegacyNewStyleSpinner();
|
if (hasHitCircle.Value)
|
||||||
else if (hasBackground)
|
return new LegacyMainCirclePiece("sliderendcircle", false);
|
||||||
return new LegacyOldStyleSpinner();
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.SliderHeadHitCircle:
|
||||||
|
if (hasHitCircle.Value)
|
||||||
|
return new LegacyMainCirclePiece("sliderstartcircle");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.HitCircle:
|
||||||
|
if (hasHitCircle.Value)
|
||||||
|
return new LegacyMainCirclePiece();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.Cursor:
|
||||||
|
if (Source.GetTexture("cursor") != null)
|
||||||
|
return new LegacyCursor();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.CursorTrail:
|
||||||
|
if (Source.GetTexture("cursortrail") != null)
|
||||||
|
return new LegacyCursorTrail();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.HitCircleText:
|
||||||
|
if (!this.HasFont(LegacyFont.HitCircle))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new LegacySpriteText(LegacyFont.HitCircle)
|
||||||
|
{
|
||||||
|
// stable applies a blanket 0.8x scale to hitcircle fonts
|
||||||
|
Scale = new Vector2(0.8f),
|
||||||
|
};
|
||||||
|
|
||||||
|
case OsuSkinComponents.SpinnerBody:
|
||||||
|
bool hasBackground = Source.GetTexture("spinner-background") != null;
|
||||||
|
|
||||||
|
if (Source.GetTexture("spinner-top") != null && !hasBackground)
|
||||||
|
return new LegacyNewStyleSpinner();
|
||||||
|
else if (hasBackground)
|
||||||
|
return new LegacyOldStyleSpinner();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return Source.GetDrawableComponent(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
AddStep("Bar line", () => SetContents(() =>
|
AddStep("Bar line", () => SetContents(_ =>
|
||||||
{
|
{
|
||||||
ScrollingHitObjectContainer hoc;
|
ScrollingHitObjectContainer hoc;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
return cont;
|
return cont;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Bar line (major)", () => SetContents(() =>
|
AddStep("Bar line (major)", () => SetContents(_ =>
|
||||||
{
|
{
|
||||||
ScrollingHitObjectContainer hoc;
|
ScrollingHitObjectContainer hoc;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
AddStep("Drum roll", () => SetContents(() =>
|
AddStep("Drum roll", () => SetContents(_ =>
|
||||||
{
|
{
|
||||||
var hoc = new ScrollingHitObjectContainer();
|
var hoc = new ScrollingHitObjectContainer();
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
return hoc;
|
return hoc;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Drum roll (strong)", () => SetContents(() =>
|
AddStep("Drum roll (strong)", () => SetContents(_ =>
|
||||||
{
|
{
|
||||||
var hoc = new ScrollingHitObjectContainer();
|
var hoc = new ScrollingHitObjectContainer();
|
||||||
|
|
||||||
|
@ -17,25 +17,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
AddStep("Centre hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
|
AddStep("Centre hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Centre hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true))
|
AddStep("Centre hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true))
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Rim hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
|
AddStep("Rim hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Rim hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true))
|
AddStep("Rim hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true))
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -54,16 +54,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
{
|
{
|
||||||
AddStep("set beatmap", () => setBeatmap());
|
AddStep("set beatmap", () => setBeatmap());
|
||||||
|
|
||||||
AddStep("clear state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear)));
|
AddStep("clear state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear)));
|
||||||
AddStep("idle state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle)));
|
AddStep("idle state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle)));
|
||||||
AddStep("kiai state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai)));
|
AddStep("kiai state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai)));
|
||||||
AddStep("fail state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail)));
|
AddStep("fail state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestInitialState()
|
public void TestInitialState()
|
||||||
{
|
{
|
||||||
AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
||||||
|
|
||||||
AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle));
|
AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle));
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
{
|
{
|
||||||
AddStep("set beatmap", () => setBeatmap());
|
AddStep("set beatmap", () => setBeatmap());
|
||||||
|
|
||||||
AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
||||||
|
|
||||||
AddStep("set clear state", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear));
|
AddStep("set clear state", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear));
|
||||||
AddStep("miss", () => mascots.ForEach(mascot => mascot.LastResult.Value = new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }));
|
AddStep("miss", () => mascots.ForEach(mascot => mascot.LastResult.Value = new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }));
|
||||||
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
{
|
{
|
||||||
Beatmap.Value.Track.Start();
|
Beatmap.Value.Track.Start();
|
||||||
|
|
||||||
SetContents(() =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
var ruleset = new TaikoRuleset();
|
var ruleset = new TaikoRuleset();
|
||||||
return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo));
|
return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo));
|
||||||
|
@ -22,16 +22,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestNormalHit()
|
public void TestNormalHit()
|
||||||
{
|
{
|
||||||
AddStep("Great", () => SetContents(() => getContentFor(createHit(HitResult.Great))));
|
AddStep("Great", () => SetContents(_ => getContentFor(createHit(HitResult.Great))));
|
||||||
AddStep("Ok", () => SetContents(() => getContentFor(createHit(HitResult.Ok))));
|
AddStep("Ok", () => SetContents(_ => getContentFor(createHit(HitResult.Ok))));
|
||||||
AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss))));
|
AddStep("Miss", () => SetContents(_ => getContentFor(createHit(HitResult.Miss))));
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(HitResult.Great)]
|
[TestCase(HitResult.Great)]
|
||||||
[TestCase(HitResult.Ok)]
|
[TestCase(HitResult.Ok)]
|
||||||
public void TestStrongHit(HitResult type)
|
public void TestStrongHit(HitResult type)
|
||||||
{
|
{
|
||||||
AddStep("create hit", () => SetContents(() => getContentFor(createStrongHit(type))));
|
AddStep("create hit", () => SetContents(_ => getContentFor(createStrongHit(type))));
|
||||||
AddStep("visualise second hit",
|
AddStep("visualise second hit",
|
||||||
() => this.ChildrenOfType<HitExplosion>()
|
() => this.ChildrenOfType<HitExplosion>()
|
||||||
.ForEach(e => e.VisualiseSecondHit(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement()))));
|
.ForEach(e => e.VisualiseSecondHit(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement()))));
|
||||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new TaikoInputManager(new TaikoRuleset().RulesetInfo)
|
SetContents(_ => new TaikoInputManager(new TaikoRuleset().RulesetInfo)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = new Container
|
Child = new Container
|
||||||
|
@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestKiaiHits()
|
public void TestKiaiHits()
|
||||||
{
|
{
|
||||||
AddStep("rim hit", () => SetContents(() => getContentFor(createHit(HitType.Rim))));
|
AddStep("rim hit", () => SetContents(_ => getContentFor(createHit(HitType.Rim))));
|
||||||
AddStep("centre hit", () => SetContents(() => getContentFor(createHit(HitType.Centre))));
|
AddStep("centre hit", () => SetContents(_ => getContentFor(createHit(HitType.Centre))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable getContentFor(DrawableTestHit hit)
|
private Drawable getContentFor(DrawableTestHit hit)
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
Beatmap.Value.Track.Start();
|
Beatmap.Value.Track.Start();
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo())
|
AddStep("Load playfield", () => SetContents(_ => new TaikoPlayfield(new ControlPointInfo())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
|
|
||||||
public TestSceneTaikoScroller()
|
public TestSceneTaikoScroller()
|
||||||
{
|
{
|
||||||
AddStep("Load scroller", () => SetContents(() =>
|
AddStep("Load scroller", () => SetContents(_ =>
|
||||||
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty())
|
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty())
|
||||||
{
|
{
|
||||||
Clock = new FramedClock(clock),
|
Clock = new FramedClock(clock),
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
{
|
{
|
||||||
protected override Ruleset CreatePlayerRuleset() => new TaikoRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new TaikoRuleset();
|
||||||
|
|
||||||
protected override IResourceStore<byte[]> Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples)));
|
protected override IResourceStore<byte[]> RulesetResources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples)));
|
||||||
|
|
||||||
[TestCase("taiko-normal-hitnormal")]
|
[TestCase("taiko-normal-hitnormal")]
|
||||||
[TestCase("normal-hitnormal")]
|
[TestCase("normal-hitnormal")]
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -79,8 +79,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
// Old osu! used hit sounding to determine various hit type information
|
// Old osu! used hit sounding to determine various hit type information
|
||||||
IList<HitSampleInfo> samples = obj.Samples;
|
IList<HitSampleInfo> samples = obj.Samples;
|
||||||
|
|
||||||
bool strong = samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
|
|
||||||
|
|
||||||
switch (obj)
|
switch (obj)
|
||||||
{
|
{
|
||||||
case IHasDistance distanceData:
|
case IHasDistance distanceData:
|
||||||
@ -94,15 +92,11 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
|
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
|
||||||
{
|
{
|
||||||
IList<HitSampleInfo> currentSamples = allSamples[i];
|
IList<HitSampleInfo> currentSamples = allSamples[i];
|
||||||
bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
|
|
||||||
strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
|
|
||||||
|
|
||||||
yield return new Hit
|
yield return new Hit
|
||||||
{
|
{
|
||||||
StartTime = j,
|
StartTime = j,
|
||||||
Type = isRim ? HitType.Rim : HitType.Centre,
|
|
||||||
Samples = currentSamples,
|
Samples = currentSamples,
|
||||||
IsStrong = strong
|
|
||||||
};
|
};
|
||||||
|
|
||||||
i = (i + 1) % allSamples.Count;
|
i = (i + 1) % allSamples.Count;
|
||||||
@ -117,7 +111,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Samples = obj.Samples,
|
Samples = obj.Samples,
|
||||||
IsStrong = strong,
|
|
||||||
Duration = taikoDuration,
|
Duration = taikoDuration,
|
||||||
TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4
|
TickRate = beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate == 3 ? 3 : 4
|
||||||
};
|
};
|
||||||
@ -143,16 +136,10 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
bool isRimDefinition(HitSampleInfo s) => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE;
|
|
||||||
|
|
||||||
bool isRim = samples.Any(isRimDefinition);
|
|
||||||
|
|
||||||
yield return new Hit
|
yield return new Hit
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
StartTime = obj.StartTime,
|
||||||
Type = isRim ? HitType.Rim : HitType.Centre,
|
|
||||||
Samples = samples,
|
Samples = samples,
|
||||||
IsStrong = strong
|
|
||||||
};
|
};
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||||
{
|
{
|
||||||
new Colour(mods),
|
new Colour(mods),
|
||||||
new Rhythm(mods),
|
new Rhythm(mods),
|
||||||
|
@ -69,7 +69,11 @@ namespace osu.Game.Rulesets.Taiko.Edit
|
|||||||
{
|
{
|
||||||
EditorBeatmap.PerformOnSelection(h =>
|
EditorBeatmap.PerformOnSelection(h =>
|
||||||
{
|
{
|
||||||
if (h is Hit taikoHit) taikoHit.Type = state ? HitType.Rim : HitType.Centre;
|
if (h is Hit taikoHit)
|
||||||
|
{
|
||||||
|
taikoHit.Type = state ? HitType.Rim : HitType.Centre;
|
||||||
|
EditorBeatmap.Update(h);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,14 +4,13 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
|
||||||
using osu.Game.Rulesets.Taiko.Replays;
|
using osu.Game.Rulesets.Taiko.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Mods
|
namespace osu.Game.Rulesets.Taiko.Mods
|
||||||
{
|
{
|
||||||
public class TaikoModAutoplay : ModAutoplay<TaikoHitObject>
|
public class TaikoModAutoplay : ModAutoplay
|
||||||
{
|
{
|
||||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||||
{
|
{
|
||||||
|
@ -20,10 +20,13 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
var taikoBeatmap = (TaikoBeatmap)beatmap;
|
var taikoBeatmap = (TaikoBeatmap)beatmap;
|
||||||
|
|
||||||
|
Seed.Value ??= RNG.Next();
|
||||||
|
var rng = new Random((int)Seed.Value);
|
||||||
|
|
||||||
foreach (var obj in taikoBeatmap.HitObjects)
|
foreach (var obj in taikoBeatmap.HitObjects)
|
||||||
{
|
{
|
||||||
if (obj is Hit hit)
|
if (obj is Hit hit)
|
||||||
hit.Type = RNG.Next(2) == 0 ? HitType.Centre : HitType.Rim;
|
hit.Type = rng.Next(2) == 0 ? HitType.Centre : HitType.Rim;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,25 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
public HitType Type
|
public HitType Type
|
||||||
{
|
{
|
||||||
get => TypeBindable.Value;
|
get => TypeBindable.Value;
|
||||||
set
|
set => TypeBindable.Value = value;
|
||||||
{
|
|
||||||
TypeBindable.Value = value;
|
|
||||||
updateSamplesFromType();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Hit()
|
||||||
|
{
|
||||||
|
TypeBindable.BindValueChanged(_ => updateSamplesFromType());
|
||||||
|
SamplesBindable.BindCollectionChanged((_, __) => updateTypeFromSamples());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTypeFromSamples()
|
||||||
|
{
|
||||||
|
Type = getRimSamples().Any() ? HitType.Rim : HitType.Centre;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an array of any samples which would cause this object to be a "rim" type hit.
|
||||||
|
/// </summary>
|
||||||
|
private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray();
|
||||||
|
|
||||||
private void updateSamplesFromType()
|
private void updateSamplesFromType()
|
||||||
{
|
{
|
||||||
var rimSamples = getRimSamples();
|
var rimSamples = getRimSamples();
|
||||||
@ -42,11 +54,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns an array of any samples which would cause this object to be a "rim" type hit.
|
|
||||||
/// </summary>
|
|
||||||
private HitSampleInfo[] getRimSamples() => Samples.Where(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE).ToArray();
|
|
||||||
|
|
||||||
protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime };
|
protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime };
|
||||||
|
|
||||||
public class StrongNestedHit : StrongNestedHitObject
|
public class StrongNestedHit : StrongNestedHitObject
|
||||||
|
@ -33,14 +33,21 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
public bool IsStrong
|
public bool IsStrong
|
||||||
{
|
{
|
||||||
get => IsStrongBindable.Value;
|
get => IsStrongBindable.Value;
|
||||||
set
|
set => IsStrongBindable.Value = value;
|
||||||
{
|
|
||||||
IsStrongBindable.Value = value;
|
|
||||||
updateSamplesFromStrong();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSamplesFromStrong()
|
protected TaikoStrongableHitObject()
|
||||||
|
{
|
||||||
|
IsStrongBindable.BindValueChanged(_ => updateSamplesFromType());
|
||||||
|
SamplesBindable.BindCollectionChanged((_, __) => updateTypeFromSamples());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTypeFromSamples()
|
||||||
|
{
|
||||||
|
IsStrong = getStrongSamples().Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSamplesFromType()
|
||||||
{
|
{
|
||||||
var strongSamples = getStrongSamples();
|
var strongSamples = getStrongSamples();
|
||||||
|
|
||||||
|
@ -38,98 +38,98 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
return Drawable.Empty().With(d => d.Expire());
|
return Drawable.Empty().With(d => d.Expire());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(component is TaikoSkinComponent taikoComponent))
|
if (component is TaikoSkinComponent taikoComponent)
|
||||||
return null;
|
|
||||||
|
|
||||||
switch (taikoComponent.Component)
|
|
||||||
{
|
{
|
||||||
case TaikoSkinComponents.DrumRollBody:
|
switch (taikoComponent.Component)
|
||||||
if (GetTexture("taiko-roll-middle") != null)
|
{
|
||||||
return new LegacyDrumRoll();
|
case TaikoSkinComponents.DrumRollBody:
|
||||||
|
if (GetTexture("taiko-roll-middle") != null)
|
||||||
|
return new LegacyDrumRoll();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case TaikoSkinComponents.InputDrum:
|
case TaikoSkinComponents.InputDrum:
|
||||||
if (GetTexture("taiko-bar-left") != null)
|
if (GetTexture("taiko-bar-left") != null)
|
||||||
return new LegacyInputDrum();
|
return new LegacyInputDrum();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case TaikoSkinComponents.CentreHit:
|
case TaikoSkinComponents.CentreHit:
|
||||||
case TaikoSkinComponents.RimHit:
|
case TaikoSkinComponents.RimHit:
|
||||||
|
|
||||||
if (GetTexture("taikohitcircle") != null)
|
if (GetTexture("taikohitcircle") != null)
|
||||||
return new LegacyHit(taikoComponent.Component);
|
return new LegacyHit(taikoComponent.Component);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case TaikoSkinComponents.DrumRollTick:
|
case TaikoSkinComponents.DrumRollTick:
|
||||||
return this.GetAnimation("sliderscorepoint", false, false);
|
return this.GetAnimation("sliderscorepoint", false, false);
|
||||||
|
|
||||||
case TaikoSkinComponents.HitTarget:
|
case TaikoSkinComponents.HitTarget:
|
||||||
if (GetTexture("taikobigcircle") != null)
|
if (GetTexture("taikobigcircle") != null)
|
||||||
return new TaikoLegacyHitTarget();
|
return new TaikoLegacyHitTarget();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case TaikoSkinComponents.PlayfieldBackgroundRight:
|
case TaikoSkinComponents.PlayfieldBackgroundRight:
|
||||||
if (GetTexture("taiko-bar-right") != null)
|
if (GetTexture("taiko-bar-right") != null)
|
||||||
return new TaikoLegacyPlayfieldBackgroundRight();
|
return new TaikoLegacyPlayfieldBackgroundRight();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case TaikoSkinComponents.PlayfieldBackgroundLeft:
|
case TaikoSkinComponents.PlayfieldBackgroundLeft:
|
||||||
// This is displayed inside LegacyInputDrum. It is required to be there for layout purposes (can be seen on legacy skins).
|
// This is displayed inside LegacyInputDrum. It is required to be there for layout purposes (can be seen on legacy skins).
|
||||||
if (GetTexture("taiko-bar-right") != null)
|
if (GetTexture("taiko-bar-right") != null)
|
||||||
return Drawable.Empty();
|
return Drawable.Empty();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case TaikoSkinComponents.BarLine:
|
case TaikoSkinComponents.BarLine:
|
||||||
if (GetTexture("taiko-barline") != null)
|
if (GetTexture("taiko-barline") != null)
|
||||||
return new LegacyBarLine();
|
return new LegacyBarLine();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case TaikoSkinComponents.TaikoExplosionMiss:
|
case TaikoSkinComponents.TaikoExplosionMiss:
|
||||||
|
|
||||||
var missSprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false);
|
var missSprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false);
|
||||||
if (missSprite != null)
|
if (missSprite != null)
|
||||||
return new LegacyHitExplosion(missSprite);
|
return new LegacyHitExplosion(missSprite);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case TaikoSkinComponents.TaikoExplosionOk:
|
case TaikoSkinComponents.TaikoExplosionOk:
|
||||||
case TaikoSkinComponents.TaikoExplosionGreat:
|
case TaikoSkinComponents.TaikoExplosionGreat:
|
||||||
|
|
||||||
var hitName = getHitName(taikoComponent.Component);
|
var hitName = getHitName(taikoComponent.Component);
|
||||||
var hitSprite = this.GetAnimation(hitName, true, false);
|
var hitSprite = this.GetAnimation(hitName, true, false);
|
||||||
|
|
||||||
if (hitSprite != null)
|
if (hitSprite != null)
|
||||||
{
|
{
|
||||||
var strongHitSprite = this.GetAnimation($"{hitName}k", true, false);
|
var strongHitSprite = this.GetAnimation($"{hitName}k", true, false);
|
||||||
|
|
||||||
return new LegacyHitExplosion(hitSprite, strongHitSprite);
|
return new LegacyHitExplosion(hitSprite, strongHitSprite);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case TaikoSkinComponents.TaikoExplosionKiai:
|
case TaikoSkinComponents.TaikoExplosionKiai:
|
||||||
// suppress the default kiai explosion if the skin brings its own sprites.
|
// suppress the default kiai explosion if the skin brings its own sprites.
|
||||||
// the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield.
|
// the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield.
|
||||||
if (hasExplosion.Value)
|
if (hasExplosion.Value)
|
||||||
return Drawable.Empty().With(d => d.Expire());
|
return Drawable.Empty().With(d => d.Expire());
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case TaikoSkinComponents.Scroller:
|
case TaikoSkinComponents.Scroller:
|
||||||
if (GetTexture("taiko-slider") != null)
|
if (GetTexture("taiko-slider") != null)
|
||||||
return new LegacyTaikoScroller();
|
return new LegacyTaikoScroller();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
case TaikoSkinComponents.Mascot:
|
case TaikoSkinComponents.Mascot:
|
||||||
return new DrawableTaikoMascot();
|
return new DrawableTaikoMascot();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Source.GetDrawableComponent(component);
|
return Source.GetDrawableComponent(component);
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Performance;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
@ -11,6 +8,11 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
internal class DrumRollHitContainer : ScrollingHitObjectContainer
|
internal class DrumRollHitContainer : ScrollingHitObjectContainer
|
||||||
{
|
{
|
||||||
|
// TODO: this usage is buggy.
|
||||||
|
// Because `LifetimeStart` is set based on scrolling, lifetime is not same as the time when the object is created.
|
||||||
|
// If the `Update` override is removed, it breaks in an obscure way.
|
||||||
|
protected override bool RemoveRewoundEntry => true;
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
@ -23,14 +25,5 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
Remove(flyingHit);
|
Remove(flyingHit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnChildLifetimeBoundaryCrossed(LifetimeBoundaryCrossedEvent e)
|
|
||||||
{
|
|
||||||
base.OnChildLifetimeBoundaryCrossed(e);
|
|
||||||
|
|
||||||
// ensure all old hits are removed on becoming alive (may miss being in the AliveInternalChildren list above).
|
|
||||||
if (e.Kind == LifetimeBoundaryKind.Start && e.Direction == LifetimeBoundaryCrossingDirection.Backward)
|
|
||||||
Remove((DrawableHitObject)e.Child);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
|
|
||||||
protected override Track GetBeatmapTrack() => throw new NotImplementedException();
|
protected override Track GetBeatmapTrack() => throw new NotImplementedException();
|
||||||
|
|
||||||
|
protected override ISkin GetSkin() => throw new NotImplementedException();
|
||||||
|
|
||||||
public override Stream GetStream(string storagePath) => throw new NotImplementedException();
|
public override Stream GetStream(string storagePath) => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,10 @@ namespace osu.Game.Tests.Chat
|
|||||||
[TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456")]
|
[TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456")]
|
||||||
[TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456?whatever")]
|
[TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456?whatever")]
|
||||||
[TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123/456")]
|
[TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123/456")]
|
||||||
[TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc/def")]
|
[TestCase(LinkAction.External, "https://dev.ppy.sh/beatmapsets/abc/def", "https://dev.ppy.sh/beatmapsets/abc/def")]
|
||||||
[TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123")]
|
[TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123")]
|
||||||
[TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123/whatever")]
|
[TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123/whatever")]
|
||||||
[TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc")]
|
[TestCase(LinkAction.External, "https://dev.ppy.sh/beatmapsets/abc", "https://dev.ppy.sh/beatmapsets/abc")]
|
||||||
public void TestBeatmapLinks(LinkAction expectedAction, string expectedArg, string link)
|
public void TestBeatmapLinks(LinkAction expectedAction, string expectedArg, string link)
|
||||||
{
|
{
|
||||||
MessageFormatter.WebsiteRootUrl = "dev.ppy.sh";
|
MessageFormatter.WebsiteRootUrl = "dev.ppy.sh";
|
||||||
@ -489,5 +489,23 @@ namespace osu.Game.Tests.Chat
|
|||||||
Assert.AreEqual(result.Links[2].Url, "\uD83D\uDE00");
|
Assert.AreEqual(result.Links[2].Url, "\uD83D\uDE00");
|
||||||
Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20");
|
Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAbsoluteExternalLinks()
|
||||||
|
{
|
||||||
|
LinkDetails result = MessageFormatter.GetLinkDetails("https://google.com");
|
||||||
|
|
||||||
|
Assert.AreEqual(LinkAction.External, result.Action);
|
||||||
|
Assert.AreEqual("https://google.com", result.Argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRelativeExternalLinks()
|
||||||
|
{
|
||||||
|
LinkDetails result = MessageFormatter.GetLinkDetails("/relative");
|
||||||
|
|
||||||
|
Assert.AreEqual(LinkAction.External, result.Action);
|
||||||
|
Assert.AreEqual("/relative", result.Argument);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user