mirror of
https://github.com/ppy/osu.git
synced 2025-03-21 15:47:19 +08:00
Merge branch 'master' into results-screen-sfx
This commit is contained in:
commit
95852ccdf8
@ -10,14 +10,6 @@ trim_trailing_whitespace = true
|
||||
|
||||
#Roslyn naming styles
|
||||
|
||||
#PascalCase for public and protected members
|
||||
dotnet_naming_style.pascalcase.capitalization = pascal_case
|
||||
dotnet_naming_symbols.public_members.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
||||
dotnet_naming_symbols.public_members.applicable_kinds = property,method,field,event
|
||||
dotnet_naming_rule.public_members_pascalcase.severity = error
|
||||
dotnet_naming_rule.public_members_pascalcase.symbols = public_members
|
||||
dotnet_naming_rule.public_members_pascalcase.style = pascalcase
|
||||
|
||||
#camelCase for private members
|
||||
dotnet_naming_style.camelcase.capitalization = camel_case
|
||||
|
||||
@ -197,4 +189,4 @@ dotnet_diagnostic.IDE0069.severity = none
|
||||
dotnet_diagnostic.CA2225.severity = none
|
||||
|
||||
# Banned APIs
|
||||
dotnet_diagnostic.RS0030.severity = error
|
||||
dotnet_diagnostic.RS0030.severity = error
|
||||
|
@ -97,7 +97,7 @@ Before committing your code, please run a code formatter. This can be achieved b
|
||||
|
||||
We have adopted some cross-platform, compiler integrated analyzers. They can provide warnings when you are editing, building inside IDE or from command line, as-if they are provided by the compiler itself.
|
||||
|
||||
JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`, which is [only supported on Windows](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
|
||||
JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it from PowerShell with `.\InspectCode.ps1`. Alternatively, you can install ReSharper or use Rider to get inline support in your IDE of choice.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.EmptyFreeform
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.EmptyFreeform.Objects;
|
||||
using osu.Game.Rulesets.EmptyFreeform.Replays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
@ -11,7 +10,7 @@ using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Rulesets.EmptyFreeform.Mods
|
||||
{
|
||||
public class EmptyFreeformModAutoplay : ModAutoplay<EmptyFreeformHitObject>
|
||||
public class EmptyFreeformModAutoplay : ModAutoplay
|
||||
{
|
||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||
{
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
@ -4,14 +4,13 @@
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Pippidon.Objects;
|
||||
using osu.Game.Rulesets.Pippidon.Replays;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Rulesets.Pippidon.Mods
|
||||
{
|
||||
public class PippidonModAutoplay : ModAutoplay<PippidonHitObject>
|
||||
public class PippidonModAutoplay : ModAutoplay
|
||||
{
|
||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||
{
|
||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.EmptyScrolling
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.EmptyScrolling.Objects;
|
||||
using osu.Game.Rulesets.EmptyScrolling.Replays;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
@ -11,7 +10,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.EmptyScrolling.Mods
|
||||
{
|
||||
public class EmptyScrollingModAutoplay : ModAutoplay<EmptyScrollingHitObject>
|
||||
public class EmptyScrollingModAutoplay : ModAutoplay
|
||||
{
|
||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||
{
|
||||
|
@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
@ -4,14 +4,13 @@
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Pippidon.Objects;
|
||||
using osu.Game.Rulesets.Pippidon.Replays;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Rulesets.Pippidon.Mods
|
||||
{
|
||||
public class PippidonModAutoplay : ModAutoplay<PippidonHitObject>
|
||||
public class PippidonModAutoplay : ModAutoplay
|
||||
{
|
||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||
{
|
||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Pippidon
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate) => Enumerable.Empty<DifficultyHitObject>();
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[0];
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[0];
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.604.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.528.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.607.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -20,7 +20,8 @@ namespace osu.Android
|
||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
||||
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed" })]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-archive")]
|
||||
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed", "application/x-osu-archive" })]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })]
|
||||
public class OsuGameActivity : AndroidGameActivity
|
||||
{
|
||||
|
@ -57,7 +57,7 @@ namespace osu.Desktop
|
||||
|
||||
private string getStableInstallPath()
|
||||
{
|
||||
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
|
||||
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs")) || File.Exists(Path.Combine(p, "osu!.cfg"));
|
||||
|
||||
string stableInstallPath;
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
</ItemGroup>
|
||||
|
@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
AddStep("enable hit lighting", () => config.SetValue(OsuSetting.HitLighting, true));
|
||||
AddStep("catch fruit", () => attemptCatch(new Fruit()));
|
||||
AddAssert("correct hit lighting colour", () =>
|
||||
catcher.ChildrenOfType<HitExplosion>().First()?.ObjectColour == fruitColour);
|
||||
catcher.ChildrenOfType<HitExplosion>().First()?.Entry?.ObjectColour == fruitColour);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
CircleSize = circleSize
|
||||
};
|
||||
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
var droppedObjectContainer = new Container<CaughtObject>
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
scoreProcessor = new ScoreProcessor();
|
||||
|
||||
SetContents(() => new CatchComboDisplay
|
||||
SetContents(_ => new CatchComboDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -19,22 +19,22 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
AddStep("show pear", () => SetContents(() => createDrawableFruit(0)));
|
||||
AddStep("show grape", () => SetContents(() => createDrawableFruit(1)));
|
||||
AddStep("show pineapple / apple", () => SetContents(() => createDrawableFruit(2)));
|
||||
AddStep("show raspberry / orange", () => SetContents(() => createDrawableFruit(3)));
|
||||
AddStep("show pear", () => SetContents(_ => createDrawableFruit(0)));
|
||||
AddStep("show grape", () => SetContents(_ => createDrawableFruit(1)));
|
||||
AddStep("show pineapple / apple", () => SetContents(_ => createDrawableFruit(2)));
|
||||
AddStep("show raspberry / orange", () => SetContents(_ => createDrawableFruit(3)));
|
||||
|
||||
AddStep("show banana", () => SetContents(createDrawableBanana));
|
||||
AddStep("show banana", () => SetContents(_ => createDrawableBanana()));
|
||||
|
||||
AddStep("show droplet", () => SetContents(() => createDrawableDroplet()));
|
||||
AddStep("show tiny droplet", () => SetContents(createDrawableTinyDroplet));
|
||||
AddStep("show droplet", () => SetContents(_ => createDrawableDroplet()));
|
||||
AddStep("show tiny droplet", () => SetContents(_ => createDrawableTinyDroplet()));
|
||||
|
||||
AddStep("show hyperdash pear", () => SetContents(() => createDrawableFruit(0, true)));
|
||||
AddStep("show hyperdash grape", () => SetContents(() => createDrawableFruit(1, true)));
|
||||
AddStep("show hyperdash pineapple / apple", () => SetContents(() => createDrawableFruit(2, true)));
|
||||
AddStep("show hyperdash raspberry / orange", () => SetContents(() => createDrawableFruit(3, true)));
|
||||
AddStep("show hyperdash pear", () => SetContents(_ => createDrawableFruit(0, true)));
|
||||
AddStep("show hyperdash grape", () => SetContents(_ => createDrawableFruit(1, true)));
|
||||
AddStep("show hyperdash pineapple / apple", () => SetContents(_ => createDrawableFruit(2, true)));
|
||||
AddStep("show hyperdash raspberry / orange", () => SetContents(_ => createDrawableFruit(3, true)));
|
||||
|
||||
AddStep("show hyperdash droplet", () => SetContents(() => createDrawableDroplet(true)));
|
||||
AddStep("show hyperdash droplet", () => SetContents(_ => createDrawableDroplet(true)));
|
||||
}
|
||||
|
||||
private Drawable createDrawableFruit(int indexInBeatmap, bool hyperdash = false) =>
|
||||
|
@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
AddStep("fruit changes visual and hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
|
||||
AddStep("fruit changes visual and hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableFruit(new Fruit
|
||||
{
|
||||
IndexInBeatmapBindable = { BindTarget = indexInBeatmap },
|
||||
HyperDashBindable = { BindTarget = hyperDash },
|
||||
}))));
|
||||
|
||||
AddStep("droplet changes hyper", () => SetContents(() => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
|
||||
AddStep("droplet changes hyper", () => SetContents(_ => new TestDrawableCatchHitObjectSpecimen(new DrawableDroplet(new Droplet
|
||||
{
|
||||
HyperDashBindable = { BindTarget = hyperDash },
|
||||
}))));
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
}
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods)
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||
{
|
||||
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
||||
|
||||
@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
|
||||
return new Skill[]
|
||||
{
|
||||
new Movement(mods, halfCatcherWidth),
|
||||
new Movement(mods, halfCatcherWidth, clockRate),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
||||
/// </summary>
|
||||
public readonly double StrainTime;
|
||||
|
||||
public readonly double ClockRate;
|
||||
|
||||
public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
|
||||
: base(hitObject, lastObject, clockRate)
|
||||
{
|
||||
@ -37,7 +35,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
||||
|
||||
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
|
||||
StrainTime = Math.Max(40, DeltaTime);
|
||||
ClockRate = clockRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +28,21 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
private float lastDistanceMoved;
|
||||
private double lastStrainTime;
|
||||
|
||||
public Movement(Mod[] mods, float halfCatcherWidth)
|
||||
/// <summary>
|
||||
/// The speed multiplier applied to the player's catcher.
|
||||
/// </summary>
|
||||
private readonly double catcherSpeedMultiplier;
|
||||
|
||||
public Movement(Mod[] mods, float halfCatcherWidth, double clockRate)
|
||||
: base(mods)
|
||||
{
|
||||
HalfCatcherWidth = halfCatcherWidth;
|
||||
|
||||
// In catch, clockrate adjustments do not only affect the timings of hitobjects,
|
||||
// but also the speed of the player's catcher, which has an impact on difficulty
|
||||
// TODO: Support variable clockrates caused by mods such as ModTimeRamp
|
||||
// (perhaps by using IApplicableToRate within the CatchDifficultyHitObject constructor to set a catcher speed for each object before processing)
|
||||
catcherSpeedMultiplier = clockRate;
|
||||
}
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
@ -48,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
|
||||
float distanceMoved = playerPosition - lastPlayerPosition.Value;
|
||||
|
||||
double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catchCurrent.ClockRate);
|
||||
double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catcherSpeedMultiplier);
|
||||
|
||||
double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510);
|
||||
double sqrtStrain = Math.Sqrt(weightedStrainTime);
|
||||
@ -81,7 +92,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
playerPosition = catchCurrent.NormalizedPosition;
|
||||
}
|
||||
|
||||
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
|
||||
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catcherSpeedMultiplier, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
|
||||
}
|
||||
|
||||
lastPlayerPosition = playerPosition;
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
@ -11,7 +10,7 @@ using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModAutoplay : ModAutoplay<CatchHitObject>
|
||||
public class CatchModAutoplay : ModAutoplay
|
||||
{
|
||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||
{
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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.Bindables;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
@ -9,21 +8,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
/// <summary>
|
||||
/// Represents a <see cref="Fruit"/> caught by the catcher.
|
||||
/// </summary>
|
||||
public class CaughtFruit : CaughtObject, IHasFruitState
|
||||
public class CaughtFruit : CaughtObject
|
||||
{
|
||||
public Bindable<FruitVisualRepresentation> VisualRepresentation { get; } = new Bindable<FruitVisualRepresentation>();
|
||||
|
||||
public CaughtFruit()
|
||||
: base(CatchSkinComponents.Fruit, _ => new FruitPiece())
|
||||
{
|
||||
}
|
||||
|
||||
public override void CopyStateFrom(IHasCatchObjectState objectState)
|
||||
{
|
||||
base.CopyStateFrom(objectState);
|
||||
|
||||
var fruitState = (IHasFruitState)objectState;
|
||||
VisualRepresentation.Value = fruitState.VisualRepresentation.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
public PalpableCatchHitObject HitObject { get; private set; }
|
||||
public Bindable<Color4> AccentColour { get; } = new Bindable<Color4>();
|
||||
public Bindable<bool> HyperDash { get; } = new Bindable<bool>();
|
||||
public Bindable<int> IndexInBeatmap { get; } = new Bindable<int>();
|
||||
|
||||
public Vector2 DisplaySize => Size * Scale;
|
||||
|
||||
@ -51,6 +52,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
Rotation = objectState.DisplayRotation;
|
||||
AccentColour.Value = objectState.AccentColour.Value;
|
||||
HyperDash.Value = objectState.HyperDash.Value;
|
||||
IndexInBeatmap.Value = objectState.IndexInBeatmap.Value;
|
||||
}
|
||||
|
||||
protected override void FreeAfterUse()
|
||||
|
@ -3,17 +3,14 @@
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
{
|
||||
public class DrawableFruit : DrawablePalpableCatchHitObject, IHasFruitState
|
||||
public class DrawableFruit : DrawablePalpableCatchHitObject
|
||||
{
|
||||
public Bindable<FruitVisualRepresentation> VisualRepresentation { get; } = new Bindable<FruitVisualRepresentation>();
|
||||
|
||||
public DrawableFruit()
|
||||
: this(null)
|
||||
{
|
||||
@ -27,11 +24,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
IndexInBeatmap.BindValueChanged(change =>
|
||||
{
|
||||
VisualRepresentation.Value = (FruitVisualRepresentation)(change.NewValue % 4);
|
||||
}, true);
|
||||
|
||||
ScalingContainer.Child = new SkinnableDrawable(
|
||||
new CatchSkinComponent(CatchSkinComponents.Fruit),
|
||||
_ => new FruitPiece());
|
||||
@ -44,12 +36,4 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
ScalingContainer.RotateTo((RandomSingle(1) - 0.5f) * 40);
|
||||
}
|
||||
}
|
||||
|
||||
public enum FruitVisualRepresentation
|
||||
{
|
||||
Pear,
|
||||
Grape,
|
||||
Pineapple,
|
||||
Raspberry,
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
|
||||
Bindable<bool> HyperDash { get; }
|
||||
|
||||
Bindable<int> IndexInBeatmap { get; }
|
||||
|
||||
Vector2 DisplaySize { get; }
|
||||
|
||||
float DisplayRotation { get; }
|
||||
|
@ -1,15 +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.Framework.Bindables;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a visual state of a <see cref="Fruit"/>.
|
||||
/// </summary>
|
||||
public interface IHasFruitState : IHasCatchObjectState
|
||||
{
|
||||
Bindable<FruitVisualRepresentation> VisualRepresentation { get; }
|
||||
}
|
||||
}
|
@ -9,5 +9,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
public class Fruit : PalpableCatchHitObject
|
||||
{
|
||||
public override Judgement CreateJudgement() => new CatchJudgement();
|
||||
|
||||
public static FruitVisualRepresentation GetVisualRepresentation(int indexInBeatmap) => (FruitVisualRepresentation)(indexInBeatmap % 4);
|
||||
}
|
||||
}
|
||||
|
13
osu.Game.Rulesets.Catch/Objects/FruitVisualRepresentation.cs
Normal file
13
osu.Game.Rulesets.Catch/Objects/FruitVisualRepresentation.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public enum FruitVisualRepresentation
|
||||
{
|
||||
Pear,
|
||||
Grape,
|
||||
Pineapple,
|
||||
Raspberry,
|
||||
}
|
||||
}
|
@ -1,22 +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.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Scoring
|
||||
{
|
||||
public class CatchHitWindows : HitWindows
|
||||
{
|
||||
public override bool IsHitResultAllowed(HitResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case HitResult.Great:
|
||||
case HitResult.Miss:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
{
|
||||
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
|
||||
public readonly Bindable<bool> HyperDash = new Bindable<bool>();
|
||||
public readonly Bindable<int> IndexInBeatmap = new Bindable<int>();
|
||||
|
||||
[Resolved]
|
||||
protected IHasCatchObjectState ObjectState { get; private set; }
|
||||
@ -37,6 +38,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
|
||||
AccentColour.BindTo(ObjectState.AccentColour);
|
||||
HyperDash.BindTo(ObjectState.HyperDash);
|
||||
IndexInBeatmap.BindTo(ObjectState.IndexInBeatmap);
|
||||
|
||||
HyperDash.BindValueChanged(hyper =>
|
||||
{
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
{
|
||||
@ -39,8 +39,10 @@ namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
var fruitState = (IHasFruitState)ObjectState;
|
||||
VisualRepresentation.BindTo(fruitState.VisualRepresentation);
|
||||
IndexInBeatmap.BindValueChanged(index =>
|
||||
{
|
||||
VisualRepresentation.Value = Fruit.GetVisualRepresentation(index.NewValue);
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
public class LegacyBananaPiece : LegacyCatchHitObjectPiece
|
||||
{
|
@ -13,12 +13,13 @@ using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
public abstract class LegacyCatchHitObjectPiece : PoolableDrawable
|
||||
{
|
||||
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
|
||||
public readonly Bindable<bool> HyperDash = new Bindable<bool>();
|
||||
public readonly Bindable<int> IndexInBeatmap = new Bindable<int>();
|
||||
|
||||
private readonly Sprite colouredSprite;
|
||||
private readonly Sprite overlaySprite;
|
||||
@ -64,6 +65,7 @@ namespace osu.Game.Rulesets.Catch.Skinning
|
||||
|
||||
AccentColour.BindTo(ObjectState.AccentColour);
|
||||
HyperDash.BindTo(ObjectState.HyperDash);
|
||||
IndexInBeatmap.BindTo(ObjectState.IndexInBeatmap);
|
||||
|
||||
hyperSprite.Colour = Skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashFruit)?.Value ??
|
||||
Skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
@ -4,7 +4,7 @@
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
public class LegacyDropletPiece : LegacyCatchHitObjectPiece
|
||||
{
|
@ -1,23 +1,20 @@
|
||||
// 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.Bindables;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
internal class LegacyFruitPiece : LegacyCatchHitObjectPiece
|
||||
{
|
||||
public readonly Bindable<FruitVisualRepresentation> VisualRepresentation = new Bindable<FruitVisualRepresentation>();
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
var fruitState = (IHasFruitState)ObjectState;
|
||||
VisualRepresentation.BindTo(fruitState.VisualRepresentation);
|
||||
|
||||
VisualRepresentation.BindValueChanged(visual => setTexture(visual.NewValue), true);
|
||||
IndexInBeatmap.BindValueChanged(index =>
|
||||
{
|
||||
setTexture(Fruit.GetVisualRepresentation(index.NewValue));
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void setTexture(FruitVisualRepresentation visualRepresentation)
|
||||
|
@ -126,8 +126,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
private float hyperDashTargetPosition;
|
||||
private Bindable<bool> hitLighting;
|
||||
|
||||
private readonly DrawablePool<HitExplosion> hitExplosionPool;
|
||||
private readonly Container<HitExplosion> hitExplosionContainer;
|
||||
private readonly HitExplosionContainer hitExplosionContainer;
|
||||
|
||||
private readonly DrawablePool<CaughtFruit> caughtFruitPool;
|
||||
private readonly DrawablePool<CaughtBanana> caughtBananaPool;
|
||||
@ -148,7 +147,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
hitExplosionPool = new DrawablePool<HitExplosion>(10),
|
||||
caughtFruitPool = new DrawablePool<CaughtFruit>(50),
|
||||
caughtBananaPool = new DrawablePool<CaughtBanana>(100),
|
||||
// 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,
|
||||
Alpha = 0,
|
||||
},
|
||||
hitExplosionContainer = new Container<HitExplosion>
|
||||
hitExplosionContainer = new HitExplosionContainer
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
@ -297,7 +295,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
caughtObjectContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject);
|
||||
droppedObjectTarget.RemoveAll(d => d.HitObject == drawableObject.HitObject);
|
||||
hitExplosionContainer.RemoveAll(d => d.HitObject == drawableObject.HitObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -508,15 +505,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
return position;
|
||||
}
|
||||
|
||||
private void addLighting(CatchHitObject hitObject, float x, Color4 colour)
|
||||
{
|
||||
HitExplosion hitExplosion = hitExplosionPool.Get();
|
||||
hitExplosion.HitObject = hitObject;
|
||||
hitExplosion.X = x;
|
||||
hitExplosion.Scale = new Vector2(hitObject.Scale);
|
||||
hitExplosion.ObjectColour = colour;
|
||||
hitExplosionContainer.Add(hitExplosion);
|
||||
}
|
||||
private void addLighting(CatchHitObject hitObject, float x, Color4 colour) =>
|
||||
hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, x, hitObject.Scale, colour, hitObject.RandomSeed));
|
||||
|
||||
private CaughtObject getCaughtObject(PalpableCatchHitObject source)
|
||||
{
|
||||
|
@ -5,31 +5,16 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Objects.Pooling;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
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 smallFaint;
|
||||
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;
|
||||
|
||||
@ -96,14 +91,13 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
.FadeOut(duration * 2);
|
||||
|
||||
const float angle_variangle = 15; // should be less than 45
|
||||
directionalGlow1.Rotation = RNG.NextSingle(-angle_variangle, angle_variangle);
|
||||
directionalGlow2.Rotation = RNG.NextSingle(-angle_variangle, angle_variangle);
|
||||
directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 4);
|
||||
directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 5);
|
||||
|
||||
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out);
|
||||
Expire(true);
|
||||
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out).Expire();
|
||||
}
|
||||
|
||||
private void onColourChanged()
|
||||
private void setColour(Color4 objectColour)
|
||||
{
|
||||
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]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
SetContents(() => new FillFlowContainer
|
||||
SetContents(_ => new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() => new FillFlowContainer
|
||||
SetContents(_ => new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() => new FillFlowContainer
|
||||
SetContents(_ => new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
if (hitWindows.IsHitResultAllowed(result))
|
||||
{
|
||||
AddStep("Show " + result.GetDescription(), () => SetContents(() =>
|
||||
AddStep("Show " + result.GetDescription(), () => SetContents(_ =>
|
||||
new DrawableManiaJudgement(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())
|
||||
{
|
||||
Type = result
|
||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
var pool = new DrawablePool<PoolableHitExplosion>(5);
|
||||
hitExplosionPools.Add(pool);
|
||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() => new FillFlowContainer
|
||||
SetContents(_ => new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
new StageDefinition { Columns = 2 }
|
||||
};
|
||||
|
||||
SetContents(() => new ManiaPlayfield(stageDefinitions));
|
||||
SetContents(_ => new ManiaPlayfield(stageDefinitions));
|
||||
});
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
new StageDefinition { Columns = 2 }
|
||||
};
|
||||
|
||||
SetContents(() => new ManiaPlayfield(stageDefinitions));
|
||||
SetContents(_ => new ManiaPlayfield(stageDefinitions));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
ManiaAction normalAction = ManiaAction.Key1;
|
||||
ManiaAction specialAction = ManiaAction.Special1;
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }),
|
||||
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }),
|
||||
_ => new DefaultStageBackground())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null)
|
||||
SetContents(_ => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
// Sorting is done in CreateDifficultyHitObjects, since the full list of hitobjects is required.
|
||||
protected override IEnumerable<DifficultyHitObject> SortObjects(IEnumerable<DifficultyHitObject> input) => input;
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||
{
|
||||
new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns)
|
||||
};
|
||||
|
@ -4,7 +4,6 @@
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
@ -12,7 +11,7 @@ using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
|
||||
public class ManiaModAutoplay : ModAutoplay
|
||||
{
|
||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Utils;
|
||||
@ -17,8 +18,11 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
Seed.Value ??= RNG.Next();
|
||||
var rng = new Random((int)Seed.Value);
|
||||
|
||||
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
|
||||
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => rng.Next()).ToList();
|
||||
|
||||
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
int poolIndex = 0;
|
||||
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
DrawablePool<TestDrawableOsuJudgement> pool;
|
||||
|
||||
|
@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private void loadContent(bool automated = true, Func<SkinProvidingContainer> skinProvider = null)
|
||||
{
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
var inputManager = automated ? (InputManager)new MovingCursorInputManager() : new OsuInputManager(new OsuRuleset().RulesetInfo);
|
||||
var skinContainer = skinProvider?.Invoke() ?? new SkinProvidingContainer(null);
|
||||
|
@ -23,18 +23,18 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[Test]
|
||||
public void TestVariousHitCircles()
|
||||
{
|
||||
AddStep("Miss Big Single", () => SetContents(() => testSingle(2)));
|
||||
AddStep("Miss Medium Single", () => SetContents(() => testSingle(5)));
|
||||
AddStep("Miss Small Single", () => SetContents(() => testSingle(7)));
|
||||
AddStep("Hit Big Single", () => SetContents(() => testSingle(2, true)));
|
||||
AddStep("Hit Medium Single", () => SetContents(() => testSingle(5, true)));
|
||||
AddStep("Hit Small Single", () => SetContents(() => testSingle(7, true)));
|
||||
AddStep("Miss Big Stream", () => SetContents(() => testStream(2)));
|
||||
AddStep("Miss Medium Stream", () => SetContents(() => testStream(5)));
|
||||
AddStep("Miss Small Stream", () => SetContents(() => testStream(7)));
|
||||
AddStep("Hit Big Stream", () => SetContents(() => testStream(2, true)));
|
||||
AddStep("Hit Medium Stream", () => SetContents(() => testStream(5, true)));
|
||||
AddStep("Hit Small Stream", () => SetContents(() => testStream(7, true)));
|
||||
AddStep("Miss Big Single", () => SetContents(_ => testSingle(2)));
|
||||
AddStep("Miss Medium Single", () => SetContents(_ => testSingle(5)));
|
||||
AddStep("Miss Small Single", () => SetContents(_ => testSingle(7)));
|
||||
AddStep("Hit Big Single", () => SetContents(_ => testSingle(2, true)));
|
||||
AddStep("Hit Medium Single", () => SetContents(_ => testSingle(5, true)));
|
||||
AddStep("Hit Small Single", () => SetContents(_ => testSingle(7, true)));
|
||||
AddStep("Miss Big Stream", () => SetContents(_ => testStream(2)));
|
||||
AddStep("Miss Medium Stream", () => SetContents(_ => testStream(5)));
|
||||
AddStep("Miss Small Stream", () => SetContents(_ => testStream(7)));
|
||||
AddStep("Hit Big Stream", () => SetContents(_ => testStream(2, true)));
|
||||
AddStep("Hit Medium Stream", () => SetContents(_ => testStream(5, true)));
|
||||
AddStep("Hit Small Stream", () => SetContents(_ => testStream(7, true)));
|
||||
}
|
||||
|
||||
private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
||||
|
30
osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleKiai.cs
Normal file
30
osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleKiai.cs
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneHitCircleKiai : TestSceneHitCircle
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
var controlPointInfo = new ControlPointInfo();
|
||||
|
||||
controlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||
controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true });
|
||||
|
||||
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
|
||||
{
|
||||
ControlPointInfo = controlPointInfo
|
||||
});
|
||||
|
||||
// track needs to be playing for BeatSyncedContainer to work.
|
||||
Beatmap.Value.Track.Start();
|
||||
});
|
||||
}
|
||||
}
|
@ -30,54 +30,54 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[Test]
|
||||
public void TestVariousSliders()
|
||||
{
|
||||
AddStep("Big Single", () => SetContents(() => testSimpleBig()));
|
||||
AddStep("Medium Single", () => SetContents(() => testSimpleMedium()));
|
||||
AddStep("Small Single", () => SetContents(() => testSimpleSmall()));
|
||||
AddStep("Big 1 Repeat", () => SetContents(() => testSimpleBig(1)));
|
||||
AddStep("Medium 1 Repeat", () => SetContents(() => testSimpleMedium(1)));
|
||||
AddStep("Small 1 Repeat", () => SetContents(() => testSimpleSmall(1)));
|
||||
AddStep("Big 2 Repeats", () => SetContents(() => testSimpleBig(2)));
|
||||
AddStep("Medium 2 Repeats", () => SetContents(() => testSimpleMedium(2)));
|
||||
AddStep("Small 2 Repeats", () => SetContents(() => testSimpleSmall(2)));
|
||||
AddStep("Big Single", () => SetContents(_ => testSimpleBig()));
|
||||
AddStep("Medium Single", () => SetContents(_ => testSimpleMedium()));
|
||||
AddStep("Small Single", () => SetContents(_ => testSimpleSmall()));
|
||||
AddStep("Big 1 Repeat", () => SetContents(_ => testSimpleBig(1)));
|
||||
AddStep("Medium 1 Repeat", () => SetContents(_ => testSimpleMedium(1)));
|
||||
AddStep("Small 1 Repeat", () => SetContents(_ => testSimpleSmall(1)));
|
||||
AddStep("Big 2 Repeats", () => SetContents(_ => testSimpleBig(2)));
|
||||
AddStep("Medium 2 Repeats", () => SetContents(_ => testSimpleMedium(2)));
|
||||
AddStep("Small 2 Repeats", () => SetContents(_ => testSimpleSmall(2)));
|
||||
|
||||
AddStep("Slow Slider", () => SetContents(testSlowSpeed)); // slow long sliders take ages already so no repeat steps
|
||||
AddStep("Slow Short Slider", () => SetContents(() => testShortSlowSpeed()));
|
||||
AddStep("Slow Short Slider 1 Repeats", () => SetContents(() => testShortSlowSpeed(1)));
|
||||
AddStep("Slow Short Slider 2 Repeats", () => SetContents(() => testShortSlowSpeed(2)));
|
||||
AddStep("Slow Slider", () => SetContents(_ => testSlowSpeed())); // slow long sliders take ages already so no repeat steps
|
||||
AddStep("Slow Short Slider", () => SetContents(_ => testShortSlowSpeed()));
|
||||
AddStep("Slow Short Slider 1 Repeats", () => SetContents(_ => testShortSlowSpeed(1)));
|
||||
AddStep("Slow Short Slider 2 Repeats", () => SetContents(_ => testShortSlowSpeed(2)));
|
||||
|
||||
AddStep("Fast Slider", () => SetContents(() => testHighSpeed()));
|
||||
AddStep("Fast Slider 1 Repeat", () => SetContents(() => testHighSpeed(1)));
|
||||
AddStep("Fast Slider 2 Repeats", () => SetContents(() => testHighSpeed(2)));
|
||||
AddStep("Fast Short Slider", () => SetContents(() => testShortHighSpeed()));
|
||||
AddStep("Fast Short Slider 1 Repeat", () => SetContents(() => testShortHighSpeed(1)));
|
||||
AddStep("Fast Short Slider 2 Repeats", () => SetContents(() => testShortHighSpeed(2)));
|
||||
AddStep("Fast Short Slider 6 Repeats", () => SetContents(() => testShortHighSpeed(6)));
|
||||
AddStep("Fast Slider", () => SetContents(_ => testHighSpeed()));
|
||||
AddStep("Fast Slider 1 Repeat", () => SetContents(_ => testHighSpeed(1)));
|
||||
AddStep("Fast Slider 2 Repeats", () => SetContents(_ => testHighSpeed(2)));
|
||||
AddStep("Fast Short Slider", () => SetContents(_ => testShortHighSpeed()));
|
||||
AddStep("Fast Short Slider 1 Repeat", () => SetContents(_ => testShortHighSpeed(1)));
|
||||
AddStep("Fast Short Slider 2 Repeats", () => SetContents(_ => testShortHighSpeed(2)));
|
||||
AddStep("Fast Short Slider 6 Repeats", () => SetContents(_ => testShortHighSpeed(6)));
|
||||
|
||||
AddStep("Perfect Curve", () => SetContents(() => testPerfect()));
|
||||
AddStep("Perfect Curve 1 Repeat", () => SetContents(() => testPerfect(1)));
|
||||
AddStep("Perfect Curve 2 Repeats", () => SetContents(() => testPerfect(2)));
|
||||
AddStep("Perfect Curve", () => SetContents(_ => testPerfect()));
|
||||
AddStep("Perfect Curve 1 Repeat", () => SetContents(_ => testPerfect(1)));
|
||||
AddStep("Perfect Curve 2 Repeats", () => SetContents(_ => testPerfect(2)));
|
||||
|
||||
AddStep("Linear Slider", () => SetContents(() => testLinear()));
|
||||
AddStep("Linear Slider 1 Repeat", () => SetContents(() => testLinear(1)));
|
||||
AddStep("Linear Slider 2 Repeats", () => SetContents(() => testLinear(2)));
|
||||
AddStep("Linear Slider", () => SetContents(_ => testLinear()));
|
||||
AddStep("Linear Slider 1 Repeat", () => SetContents(_ => testLinear(1)));
|
||||
AddStep("Linear Slider 2 Repeats", () => SetContents(_ => testLinear(2)));
|
||||
|
||||
AddStep("Bezier Slider", () => SetContents(() => testBezier()));
|
||||
AddStep("Bezier Slider 1 Repeat", () => SetContents(() => testBezier(1)));
|
||||
AddStep("Bezier Slider 2 Repeats", () => SetContents(() => testBezier(2)));
|
||||
AddStep("Bezier Slider", () => SetContents(_ => testBezier()));
|
||||
AddStep("Bezier Slider 1 Repeat", () => SetContents(_ => testBezier(1)));
|
||||
AddStep("Bezier Slider 2 Repeats", () => SetContents(_ => testBezier(2)));
|
||||
|
||||
AddStep("Linear Overlapping", () => SetContents(() => testLinearOverlapping()));
|
||||
AddStep("Linear Overlapping 1 Repeat", () => SetContents(() => testLinearOverlapping(1)));
|
||||
AddStep("Linear Overlapping 2 Repeats", () => SetContents(() => testLinearOverlapping(2)));
|
||||
AddStep("Linear Overlapping", () => SetContents(_ => testLinearOverlapping()));
|
||||
AddStep("Linear Overlapping 1 Repeat", () => SetContents(_ => testLinearOverlapping(1)));
|
||||
AddStep("Linear Overlapping 2 Repeats", () => SetContents(_ => testLinearOverlapping(2)));
|
||||
|
||||
AddStep("Catmull Slider", () => SetContents(() => testCatmull()));
|
||||
AddStep("Catmull Slider 1 Repeat", () => SetContents(() => testCatmull(1)));
|
||||
AddStep("Catmull Slider 2 Repeats", () => SetContents(() => testCatmull(2)));
|
||||
AddStep("Catmull Slider", () => SetContents(_ => testCatmull()));
|
||||
AddStep("Catmull Slider 1 Repeat", () => SetContents(_ => testCatmull(1)));
|
||||
AddStep("Catmull Slider 2 Repeats", () => SetContents(_ => testCatmull(2)));
|
||||
|
||||
AddStep("Big Single, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset()));
|
||||
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(() => testSimpleBigLargeStackOffset(1)));
|
||||
AddStep("Big Single, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset()));
|
||||
AddStep("Big 1 Repeat, Large StackOffset", () => SetContents(_ => testSimpleBigLargeStackOffset(1)));
|
||||
|
||||
AddStep("Distance Overflow", () => SetContents(() => testDistanceOverflow()));
|
||||
AddStep("Distance Overflow 1 Repeat", () => SetContents(() => testDistanceOverflow(1)));
|
||||
AddStep("Distance Overflow", () => SetContents(_ => testDistanceOverflow()));
|
||||
AddStep("Distance Overflow 1 Repeat", () => SetContents(_ => testDistanceOverflow(1)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -29,15 +29,15 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
public void TestVariousSpinners(bool autoplay)
|
||||
{
|
||||
string term = autoplay ? "Hit" : "Miss";
|
||||
AddStep($"{term} Big", () => SetContents(() => testSingle(2, autoplay)));
|
||||
AddStep($"{term} Medium", () => SetContents(() => testSingle(5, autoplay)));
|
||||
AddStep($"{term} Small", () => SetContents(() => testSingle(7, autoplay)));
|
||||
AddStep($"{term} Big", () => SetContents(_ => testSingle(2, autoplay)));
|
||||
AddStep($"{term} Medium", () => SetContents(_ => testSingle(5, autoplay)));
|
||||
AddStep($"{term} Small", () => SetContents(_ => testSingle(7, autoplay)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSpinningSamplePitchShift()
|
||||
{
|
||||
AddStep("Add spinner", () => SetContents(() => testSingle(5, true, 4000)));
|
||||
AddStep("Add spinner", () => SetContents(_ => testSingle(5, true, 4000)));
|
||||
AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8);
|
||||
AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8);
|
||||
|
||||
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase(true)]
|
||||
public void TestLongSpinner(bool autoplay)
|
||||
{
|
||||
AddStep("Very long spinner", () => SetContents(() => testSingle(5, autoplay, 4000)));
|
||||
AddStep("Very long spinner", () => SetContents(_ => testSingle(5, autoplay, 4000)));
|
||||
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
|
||||
AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0));
|
||||
}
|
||||
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase(true)]
|
||||
public void TestSuperShortSpinner(bool autoplay)
|
||||
{
|
||||
AddStep("Very short spinner", () => SetContents(() => testSingle(5, autoplay, 200)));
|
||||
AddStep("Very short spinner", () => SetContents(_ => testSingle(5, autoplay, 200)));
|
||||
AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult);
|
||||
AddUntilStep("Short spinner implicitly completes", () => drawableSpinner.Progress == 1);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
||||
}
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||
{
|
||||
new Aim(mods),
|
||||
new Speed(mods)
|
||||
|
@ -6,14 +6,13 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModAutoplay : ModAutoplay<OsuHitObject>
|
||||
public class OsuModAutoplay : ModAutoplay
|
||||
{
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray();
|
||||
|
||||
|
280
osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
Normal file
280
osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
Normal file
@ -0,0 +1,280 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
/// <summary>
|
||||
/// Mod that randomises the positions of the <see cref="HitObject"/>s
|
||||
/// </summary>
|
||||
public class OsuModRandom : ModRandom, IApplicableToBeatmap
|
||||
{
|
||||
public override string Description => "It never gets boring!";
|
||||
public override bool Ranked => false;
|
||||
|
||||
// The relative distance to the edge of the playfield before objects' positions should start to "turn around" and curve towards the middle.
|
||||
// The closer the hit objects draw to the border, the sharper the turn
|
||||
private const float playfield_edge_ratio = 0.375f;
|
||||
|
||||
private static readonly float border_distance_x = OsuPlayfield.BASE_SIZE.X * playfield_edge_ratio;
|
||||
private static readonly float border_distance_y = OsuPlayfield.BASE_SIZE.Y * playfield_edge_ratio;
|
||||
|
||||
private static readonly Vector2 playfield_middle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2);
|
||||
|
||||
private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast;
|
||||
|
||||
private Random rng;
|
||||
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
if (!(beatmap is OsuBeatmap osuBeatmap))
|
||||
return;
|
||||
|
||||
var hitObjects = osuBeatmap.HitObjects;
|
||||
|
||||
Seed.Value ??= RNG.Next();
|
||||
|
||||
rng = new Random((int)Seed.Value);
|
||||
|
||||
RandomObjectInfo previous = null;
|
||||
|
||||
float rateOfChangeMultiplier = 0;
|
||||
|
||||
for (int i = 0; i < hitObjects.Count; i++)
|
||||
{
|
||||
var hitObject = hitObjects[i];
|
||||
|
||||
var current = new RandomObjectInfo(hitObject);
|
||||
|
||||
// rateOfChangeMultiplier only changes every i iterations to prevent shaky-line-shaped streams
|
||||
if (i % 3 == 0)
|
||||
rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1;
|
||||
|
||||
if (hitObject is Spinner)
|
||||
{
|
||||
previous = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
applyRandomisation(rateOfChangeMultiplier, previous, current);
|
||||
|
||||
hitObject.Position = current.PositionRandomised;
|
||||
|
||||
// update end position as it may have changed as a result of the position update.
|
||||
current.EndPositionRandomised = current.PositionRandomised;
|
||||
|
||||
switch (hitObject)
|
||||
{
|
||||
case Slider slider:
|
||||
// Shift nested objects the same distance as the slider got shifted in the randomisation process
|
||||
// so that moveSliderIntoPlayfield() can determine their relative distances to slider.Position and thus minMargin
|
||||
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal));
|
||||
|
||||
var oldPos = new Vector2(slider.Position.X, slider.Position.Y);
|
||||
|
||||
moveSliderIntoPlayfield(slider, current);
|
||||
|
||||
// Shift them again to move them to their final position after the slider got moved into the playfield
|
||||
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, oldPos));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
previous = current;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the final position of the hit object
|
||||
/// </summary>
|
||||
/// <returns>Final position of the hit object</returns>
|
||||
private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo previous, RandomObjectInfo current)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
var playfieldSize = OsuPlayfield.BASE_SIZE;
|
||||
|
||||
current.AngleRad = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI);
|
||||
current.PositionRandomised = new Vector2((float)rng.NextDouble() * playfieldSize.X, (float)rng.NextDouble() * playfieldSize.Y);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float distanceToPrev = Vector2.Distance(previous.EndPositionOriginal, current.PositionOriginal);
|
||||
|
||||
// The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object)
|
||||
// is proportional to the distance between the last and the current hit object
|
||||
// to allow jumps and prevent too sharp turns during streams.
|
||||
var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / playfield_diagonal;
|
||||
|
||||
current.AngleRad = (float)randomAngleRad + previous.AngleRad;
|
||||
if (current.AngleRad < 0)
|
||||
current.AngleRad += 2 * (float)Math.PI;
|
||||
|
||||
var posRelativeToPrev = new Vector2(
|
||||
distanceToPrev * (float)Math.Cos(current.AngleRad),
|
||||
distanceToPrev * (float)Math.Sin(current.AngleRad)
|
||||
);
|
||||
|
||||
posRelativeToPrev = getRotatedVector(previous.EndPositionRandomised, posRelativeToPrev);
|
||||
|
||||
current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X);
|
||||
|
||||
var position = Vector2.Add(previous.EndPositionRandomised, posRelativeToPrev);
|
||||
|
||||
// Move hit objects back into the playfield if they are outside of it,
|
||||
// which would sometimes happen during big jumps otherwise.
|
||||
position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X);
|
||||
position.Y = MathHelper.Clamp(position.Y, 0, OsuPlayfield.BASE_SIZE.Y);
|
||||
|
||||
current.PositionRandomised = position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves the <see cref="Slider"/> and all necessary nested <see cref="OsuHitObject"/>s into the <see cref="OsuPlayfield"/> if they aren't already.
|
||||
/// </summary>
|
||||
private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo)
|
||||
{
|
||||
// Min. distances from the slider's position to the playfield border
|
||||
var minMargin = new MarginPadding();
|
||||
|
||||
foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderEndCircle))
|
||||
{
|
||||
if (!(hitObject is OsuHitObject osuHitObject))
|
||||
continue;
|
||||
|
||||
var relativePos = Vector2.Subtract(osuHitObject.Position, slider.Position);
|
||||
|
||||
minMargin.Left = Math.Max(minMargin.Left, -relativePos.X);
|
||||
minMargin.Right = Math.Max(minMargin.Right, relativePos.X);
|
||||
minMargin.Top = Math.Max(minMargin.Top, -relativePos.Y);
|
||||
minMargin.Bottom = Math.Max(minMargin.Bottom, relativePos.Y);
|
||||
}
|
||||
|
||||
if (slider.Position.X < minMargin.Left)
|
||||
slider.Position = new Vector2(minMargin.Left, slider.Position.Y);
|
||||
else if (slider.Position.X + minMargin.Right > OsuPlayfield.BASE_SIZE.X)
|
||||
slider.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - minMargin.Right, slider.Position.Y);
|
||||
|
||||
if (slider.Position.Y < minMargin.Top)
|
||||
slider.Position = new Vector2(slider.Position.X, minMargin.Top);
|
||||
else if (slider.Position.Y + minMargin.Bottom > OsuPlayfield.BASE_SIZE.Y)
|
||||
slider.Position = new Vector2(slider.Position.X, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom);
|
||||
|
||||
currentObjectInfo.PositionRandomised = slider.Position;
|
||||
currentObjectInfo.EndPositionRandomised = slider.EndPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shifts all nested <see cref="SliderTick"/>s and <see cref="SliderRepeat"/>s by the specified shift.
|
||||
/// </summary>
|
||||
/// <param name="slider"><see cref="Slider"/> whose nested <see cref="SliderTick"/>s and <see cref="SliderRepeat"/>s should be shifted</param>
|
||||
/// <param name="shift">The <see cref="Vector2"/> the <see cref="Slider"/>'s nested <see cref="SliderTick"/>s and <see cref="SliderRepeat"/>s should be shifted by</param>
|
||||
private void shiftNestedObjects(Slider slider, Vector2 shift)
|
||||
{
|
||||
foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderRepeat))
|
||||
{
|
||||
if (!(hitObject is OsuHitObject osuHitObject))
|
||||
continue;
|
||||
|
||||
osuHitObject.Position = Vector2.Add(osuHitObject.Position, shift);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the position of the current hit object relative to the previous one.
|
||||
/// </summary>
|
||||
/// <returns>The position of the current hit object relative to the previous one</returns>
|
||||
private Vector2 getRotatedVector(Vector2 prevPosChanged, Vector2 posRelativeToPrev)
|
||||
{
|
||||
var relativeRotationDistance = 0f;
|
||||
|
||||
if (prevPosChanged.X < playfield_middle.X)
|
||||
{
|
||||
relativeRotationDistance = Math.Max(
|
||||
(border_distance_x - prevPosChanged.X) / border_distance_x,
|
||||
relativeRotationDistance
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
relativeRotationDistance = Math.Max(
|
||||
(prevPosChanged.X - (OsuPlayfield.BASE_SIZE.X - border_distance_x)) / border_distance_x,
|
||||
relativeRotationDistance
|
||||
);
|
||||
}
|
||||
|
||||
if (prevPosChanged.Y < playfield_middle.Y)
|
||||
{
|
||||
relativeRotationDistance = Math.Max(
|
||||
(border_distance_y - prevPosChanged.Y) / border_distance_y,
|
||||
relativeRotationDistance
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
relativeRotationDistance = Math.Max(
|
||||
(prevPosChanged.Y - (OsuPlayfield.BASE_SIZE.Y - border_distance_y)) / border_distance_y,
|
||||
relativeRotationDistance
|
||||
);
|
||||
}
|
||||
|
||||
return rotateVectorTowardsVector(posRelativeToPrev, playfield_middle - prevPosChanged, relativeRotationDistance / 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates vector "initial" towards vector "destinantion"
|
||||
/// </summary>
|
||||
/// <param name="initial">Vector to rotate to "destination"</param>
|
||||
/// <param name="destination">Vector "initial" should be rotated to</param>
|
||||
/// <param name="relativeDistance">The angle the vector should be rotated relative to the difference between the angles of the the two vectors.</param>
|
||||
/// <returns>Resulting vector</returns>
|
||||
private Vector2 rotateVectorTowardsVector(Vector2 initial, Vector2 destination, float relativeDistance)
|
||||
{
|
||||
var initialAngleRad = Math.Atan2(initial.Y, initial.X);
|
||||
var destAngleRad = Math.Atan2(destination.Y, destination.X);
|
||||
|
||||
var diff = destAngleRad - initialAngleRad;
|
||||
|
||||
while (diff < -Math.PI) diff += 2 * Math.PI;
|
||||
|
||||
while (diff > Math.PI) diff -= 2 * Math.PI;
|
||||
|
||||
var finalAngleRad = initialAngleRad + relativeDistance * diff;
|
||||
|
||||
return new Vector2(
|
||||
initial.Length * (float)Math.Cos(finalAngleRad),
|
||||
initial.Length * (float)Math.Sin(finalAngleRad)
|
||||
);
|
||||
}
|
||||
|
||||
private class RandomObjectInfo
|
||||
{
|
||||
public float AngleRad { get; set; }
|
||||
|
||||
public Vector2 PositionOriginal { get; }
|
||||
public Vector2 PositionRandomised { get; set; }
|
||||
|
||||
public Vector2 EndPositionOriginal { get; }
|
||||
public Vector2 EndPositionRandomised { get; set; }
|
||||
|
||||
public RandomObjectInfo(OsuHitObject hitObject)
|
||||
{
|
||||
PositionRandomised = PositionOriginal = hitObject.Position;
|
||||
EndPositionRandomised = EndPositionOriginal = hitObject.EndPosition;
|
||||
AngleRad = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Pooling;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
@ -12,34 +13,29 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
/// <summary>
|
||||
/// Visualises the <see cref="FollowPoint"/>s between two <see cref="DrawableOsuHitObject"/>s.
|
||||
/// </summary>
|
||||
public class FollowPointConnection : PoolableDrawable
|
||||
public class FollowPointConnection : PoolableDrawableWithLifetime<FollowPointLifetimeEntry>
|
||||
{
|
||||
// Todo: These shouldn't be constants
|
||||
public const int SPACING = 32;
|
||||
public const double PREEMPT = 800;
|
||||
|
||||
public FollowPointLifetimeEntry Entry;
|
||||
public DrawablePool<FollowPoint> Pool;
|
||||
|
||||
protected override void PrepareForUse()
|
||||
protected override void OnApply(FollowPointLifetimeEntry entry)
|
||||
{
|
||||
base.PrepareForUse();
|
||||
|
||||
Entry.Invalidated += onEntryInvalidated;
|
||||
base.OnApply(entry);
|
||||
|
||||
entry.Invalidated += onEntryInvalidated;
|
||||
refreshPoints();
|
||||
}
|
||||
|
||||
protected override void FreeAfterUse()
|
||||
protected override void OnFree(FollowPointLifetimeEntry entry)
|
||||
{
|
||||
base.FreeAfterUse();
|
||||
|
||||
Entry.Invalidated -= onEntryInvalidated;
|
||||
base.OnFree(entry);
|
||||
|
||||
entry.Invalidated -= onEntryInvalidated;
|
||||
// Return points to the pool.
|
||||
ClearInternal(false);
|
||||
|
||||
Entry = null;
|
||||
}
|
||||
|
||||
private void onEntryInvalidated() => Scheduler.AddOnce(refreshPoints);
|
||||
@ -48,8 +44,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
{
|
||||
ClearInternal(false);
|
||||
|
||||
OsuHitObject start = Entry.Start;
|
||||
OsuHitObject end = Entry.End;
|
||||
var entry = Entry;
|
||||
if (entry?.End == null) return;
|
||||
|
||||
OsuHitObject start = entry.Start;
|
||||
OsuHitObject end = entry.End;
|
||||
|
||||
double startTime = start.GetEndTime();
|
||||
|
||||
@ -87,14 +86,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
fp.FadeIn(end.TimeFadeIn);
|
||||
fp.ScaleTo(end.Scale, 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>
|
||||
|
@ -1,6 +1,8 @@
|
||||
// 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.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Performance;
|
||||
@ -11,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
{
|
||||
public class FollowPointLifetimeEntry : LifetimeEntry
|
||||
{
|
||||
public event Action Invalidated;
|
||||
public event Action? Invalidated;
|
||||
public readonly OsuHitObject Start;
|
||||
|
||||
public FollowPointLifetimeEntry(OsuHitObject start)
|
||||
@ -22,9 +24,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
bindEvents();
|
||||
}
|
||||
|
||||
private OsuHitObject end;
|
||||
private OsuHitObject? end;
|
||||
|
||||
public OsuHitObject End
|
||||
public OsuHitObject? End
|
||||
{
|
||||
get => end;
|
||||
set
|
||||
@ -56,11 +58,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
|
||||
public void UnbindEvents()
|
||||
{
|
||||
if (Start != null)
|
||||
{
|
||||
Start.DefaultsApplied -= onDefaultsApplied;
|
||||
Start.PositionBindable.ValueChanged -= onPositionChanged;
|
||||
}
|
||||
Start.DefaultsApplied -= onDefaultsApplied;
|
||||
Start.PositionBindable.ValueChanged -= onPositionChanged;
|
||||
|
||||
if (End != null)
|
||||
{
|
||||
|
@ -6,43 +6,32 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Performance;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Pooling;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
{
|
||||
/// <summary>
|
||||
/// Visualises connections between <see cref="DrawableOsuHitObject"/>s.
|
||||
/// </summary>
|
||||
public class FollowPointRenderer : CompositeDrawable
|
||||
public class FollowPointRenderer : PooledDrawableWithLifetimeContainer<FollowPointLifetimeEntry, FollowPointConnection>
|
||||
{
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
|
||||
public IReadOnlyList<FollowPointLifetimeEntry> Entries => lifetimeEntries;
|
||||
public new IReadOnlyList<FollowPointLifetimeEntry> Entries => lifetimeEntries;
|
||||
|
||||
private DrawablePool<FollowPointConnection> connectionPool;
|
||||
private DrawablePool<FollowPoint> pointPool;
|
||||
|
||||
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 LifetimeEntryManager lifetimeManager = new LifetimeEntryManager();
|
||||
|
||||
public FollowPointRenderer()
|
||||
{
|
||||
lifetimeManager.EntryBecameAlive += onEntryBecameAlive;
|
||||
lifetimeManager.EntryBecameDead += onEntryBecameDead;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
connectionPool = new DrawablePoolNoLifetime<FollowPointConnection>(1, 200),
|
||||
pointPool = new DrawablePoolNoLifetime<FollowPoint>(50, 1000)
|
||||
connectionPool = new DrawablePool<FollowPointConnection>(1, 200),
|
||||
pointPool = new DrawablePool<FollowPoint>(50, 1000)
|
||||
};
|
||||
}
|
||||
|
||||
@ -107,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
previousEntry.End = newEntry.Start;
|
||||
}
|
||||
|
||||
lifetimeManager.AddEntry(newEntry);
|
||||
Add(newEntry);
|
||||
}
|
||||
|
||||
private void removeEntry(OsuHitObject hitObject)
|
||||
@ -118,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
entry.UnbindEvents();
|
||||
|
||||
lifetimeEntries.RemoveAt(index);
|
||||
lifetimeManager.RemoveEntry(entry);
|
||||
Remove(entry);
|
||||
|
||||
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();
|
||||
anyAliveChanged |= lifetimeManager.Update(Time.Current);
|
||||
return anyAliveChanged;
|
||||
}
|
||||
|
||||
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);
|
||||
var connection = connectionPool.Get();
|
||||
connection.Pool = pointPool;
|
||||
connection.Apply(entry);
|
||||
return connection;
|
||||
}
|
||||
|
||||
private void onStartTimeChanged(OsuHitObject hitObject)
|
||||
@ -171,16 +142,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
entry.UnbindEvents();
|
||||
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.
|
||||
this.Delay(800).FadeOut();
|
||||
|
||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
|
@ -97,8 +97,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
base.UpdateHitStateTransforms(state);
|
||||
|
||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
@ -154,7 +152,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
while (Math.Abs(aimRotation - Arrow.Rotation) > 180)
|
||||
aimRotation += aimRotation < Arrow.Rotation ? 360 : -360;
|
||||
|
||||
if (!hasRotation)
|
||||
// The clock may be paused in a scenario like the editor.
|
||||
if (!hasRotation || !Clock.IsRunning)
|
||||
{
|
||||
Arrow.Rotation = aimRotation;
|
||||
hasRotation = true;
|
||||
|
@ -87,8 +87,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
Debug.Assert(HitObject.HitWindows != null);
|
||||
|
||||
(CirclePiece.Drawable as IMainCirclePiece)?.Animate(state);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
|
@ -164,7 +164,8 @@ namespace osu.Game.Rulesets.Osu
|
||||
{
|
||||
new OsuModTarget(),
|
||||
new OsuModDifficultyAdjust(),
|
||||
new OsuModClassic()
|
||||
new OsuModClassic(),
|
||||
new OsuModRandom(),
|
||||
};
|
||||
|
||||
case ModType.Automation:
|
||||
|
@ -42,6 +42,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
Origin = Anchor.Centre,
|
||||
Texture = textures.Get(@"Gameplay/osu/disc"),
|
||||
},
|
||||
new KiaiFlash
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
triangles = new TrianglesPiece
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
@ -1,17 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public interface IMainCirclePiece
|
||||
{
|
||||
/// <summary>
|
||||
/// Begins animating this <see cref="IMainCirclePiece"/>.
|
||||
/// </summary>
|
||||
/// <param name="state">The <see cref="ArmedState"/> of the related <see cref="DrawableHitCircle"/>.</param>
|
||||
void Animate(ArmedState state);
|
||||
}
|
||||
}
|
43
osu.Game.Rulesets.Osu/Skinning/Default/KiaiFlash.cs
Normal file
43
osu.Game.Rulesets.Osu/Skinning/Default/KiaiFlash.cs
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public class KiaiFlash : BeatSyncedContainer
|
||||
{
|
||||
private const double fade_length = 80;
|
||||
|
||||
private const float flash_opacity = 0.25f;
|
||||
|
||||
public KiaiFlash()
|
||||
{
|
||||
EarlyActivationMilliseconds = 80;
|
||||
Blending = BlendingParameters.Additive;
|
||||
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
Alpha = 0f,
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||
{
|
||||
if (!effectPoint.KiaiMode)
|
||||
return;
|
||||
|
||||
Child
|
||||
.FadeTo(flash_opacity, EarlyActivationMilliseconds, Easing.OutQuint)
|
||||
.Then()
|
||||
.FadeOut(timingPoint.BeatLength - fade_length, Easing.OutSine);
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public class MainCirclePiece : CompositeDrawable, IMainCirclePiece
|
||||
public class MainCirclePiece : CompositeDrawable
|
||||
{
|
||||
private readonly CirclePiece circle;
|
||||
private readonly RingPiece ring;
|
||||
@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
|
||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
||||
|
||||
[Resolved]
|
||||
private DrawableHitObject drawableObject { get; set; }
|
||||
@ -53,6 +54,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||
armedState.BindTo(drawableObject.State);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -67,16 +69,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
}, true);
|
||||
|
||||
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
|
||||
|
||||
armedState.BindValueChanged(animate, true);
|
||||
}
|
||||
|
||||
public void Animate(ArmedState state)
|
||||
private void animate(ValueChangedEvent<ArmedState> state)
|
||||
{
|
||||
ClearTransforms(true);
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.StateUpdateTime))
|
||||
glow.FadeOut(400);
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
{
|
||||
switch (state)
|
||||
switch (state.NewValue)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
const double flash_in = 40;
|
||||
@ -89,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
explode.FadeIn(flash_in);
|
||||
this.ScaleTo(1.5f, 400, Easing.OutQuad);
|
||||
|
||||
using (BeginDelayedSequence(flash_in, true))
|
||||
using (BeginDelayedSequence(flash_in))
|
||||
{
|
||||
// after the flash, we can hide some elements that were behind it
|
||||
ring.FadeOut();
|
||||
|
61
osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingSprite.cs
Normal file
61
osu.Game.Rulesets.Osu/Skinning/Legacy/KiaiFlashingSprite.cs
Normal file
@ -0,0 +1,61 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
internal class KiaiFlashingSprite : BeatSyncedContainer
|
||||
{
|
||||
private readonly Sprite mainSprite;
|
||||
private readonly Sprite flashingSprite;
|
||||
|
||||
public Texture Texture
|
||||
{
|
||||
set
|
||||
{
|
||||
mainSprite.Texture = value;
|
||||
flashingSprite.Texture = value;
|
||||
}
|
||||
}
|
||||
|
||||
private const float flash_opacity = 0.3f;
|
||||
|
||||
public KiaiFlashingSprite()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
mainSprite = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
flashingSprite = new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Alpha = 0,
|
||||
Blending = BlendingParameters.Additive,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||
{
|
||||
if (!effectPoint.KiaiMode)
|
||||
return;
|
||||
|
||||
flashingSprite
|
||||
.FadeTo(flash_opacity)
|
||||
.Then()
|
||||
.FadeOut(timingPoint.BeatLength * 0.75f);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,14 +5,12 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@ -20,7 +18,7 @@ using static osu.Game.Skinning.LegacySkinConfiguration;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyMainCirclePiece : CompositeDrawable, IMainCirclePiece
|
||||
public class LegacyMainCirclePiece : CompositeDrawable
|
||||
{
|
||||
private readonly string priorityLookup;
|
||||
private readonly bool hasNumber;
|
||||
@ -33,14 +31,15 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||
}
|
||||
|
||||
private Container<Sprite> circleSprites;
|
||||
private Sprite hitCircleSprite;
|
||||
private Sprite hitCircleOverlay;
|
||||
private Container circleSprites;
|
||||
private Drawable hitCircleSprite;
|
||||
private Drawable hitCircleOverlay;
|
||||
|
||||
private SkinnableSpriteText hitCircleText;
|
||||
|
||||
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||
private readonly IBindable<int> indexInCurrentCombo = new Bindable<int>();
|
||||
private readonly IBindable<ArmedState> armedState = new Bindable<ArmedState>();
|
||||
|
||||
[Resolved]
|
||||
private DrawableHitObject drawableObject { get; set; }
|
||||
@ -72,20 +71,20 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
circleSprites = new Container<Sprite>
|
||||
circleSprites = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new[]
|
||||
{
|
||||
hitCircleSprite = new Sprite
|
||||
hitCircleSprite = new KiaiFlashingSprite
|
||||
{
|
||||
Texture = baseTexture,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
hitCircleOverlay = new Sprite
|
||||
hitCircleOverlay = new KiaiFlashingSprite
|
||||
{
|
||||
Texture = overlayTexture,
|
||||
Anchor = Anchor.Centre,
|
||||
@ -115,6 +114,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
|
||||
armedState.BindTo(drawableObject.State);
|
||||
|
||||
Texture getTextureWithFallback(string name)
|
||||
{
|
||||
@ -139,15 +139,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||
if (hasNumber)
|
||||
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
||||
|
||||
armedState.BindValueChanged(animate, true);
|
||||
}
|
||||
|
||||
public void Animate(ArmedState state)
|
||||
private void animate(ValueChangedEvent<ArmedState> state)
|
||||
{
|
||||
const double legacy_fade_duration = 240;
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
|
||||
ClearTransforms(true);
|
||||
|
||||
using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime))
|
||||
{
|
||||
switch (state)
|
||||
switch (state.NewValue)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddStep("Bar line", () => SetContents(() =>
|
||||
AddStep("Bar line", () => SetContents(_ =>
|
||||
{
|
||||
ScrollingHitObjectContainer hoc;
|
||||
|
||||
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
return cont;
|
||||
}));
|
||||
|
||||
AddStep("Bar line (major)", () => SetContents(() =>
|
||||
AddStep("Bar line (major)", () => SetContents(_ =>
|
||||
{
|
||||
ScrollingHitObjectContainer hoc;
|
||||
|
||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddStep("Drum roll", () => SetContents(() =>
|
||||
AddStep("Drum roll", () => SetContents(_ =>
|
||||
{
|
||||
var hoc = new ScrollingHitObjectContainer();
|
||||
|
||||
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
return hoc;
|
||||
}));
|
||||
|
||||
AddStep("Drum roll (strong)", () => SetContents(() =>
|
||||
AddStep("Drum roll (strong)", () => SetContents(_ =>
|
||||
{
|
||||
var hoc = new ScrollingHitObjectContainer();
|
||||
|
||||
|
@ -17,25 +17,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddStep("Centre hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
|
||||
AddStep("Centre hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}));
|
||||
|
||||
AddStep("Centre hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true))
|
||||
AddStep("Centre hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true))
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}));
|
||||
|
||||
AddStep("Rim hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
|
||||
AddStep("Rim hit", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}));
|
||||
|
||||
AddStep("Rim hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true))
|
||||
AddStep("Rim hit (strong)", () => SetContents(_ => new DrawableHit(createHitAtCurrentTime(true))
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
@ -54,16 +54,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
{
|
||||
AddStep("set beatmap", () => setBeatmap());
|
||||
|
||||
AddStep("clear state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear)));
|
||||
AddStep("idle state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle)));
|
||||
AddStep("kiai state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai)));
|
||||
AddStep("fail state", () => SetContents(() => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail)));
|
||||
AddStep("clear state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Clear)));
|
||||
AddStep("idle state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Idle)));
|
||||
AddStep("kiai state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Kiai)));
|
||||
AddStep("fail state", () => SetContents(_ => new TaikoMascotAnimation(TaikoMascotAnimationState.Fail)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInitialState()
|
||||
{
|
||||
AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
||||
AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
||||
|
||||
AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle));
|
||||
}
|
||||
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
{
|
||||
AddStep("set beatmap", () => setBeatmap());
|
||||
|
||||
AddStep("create mascot", () => SetContents(() => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
||||
AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
||||
|
||||
AddStep("set clear state", () => mascots.ForEach(mascot => mascot.State.Value = TaikoMascotAnimationState.Clear));
|
||||
AddStep("miss", () => mascots.ForEach(mascot => mascot.LastResult.Value = new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }));
|
||||
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
{
|
||||
Beatmap.Value.Track.Start();
|
||||
|
||||
SetContents(() =>
|
||||
SetContents(_ =>
|
||||
{
|
||||
var ruleset = new TaikoRuleset();
|
||||
return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo));
|
||||
|
@ -22,16 +22,16 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[Test]
|
||||
public void TestNormalHit()
|
||||
{
|
||||
AddStep("Great", () => SetContents(() => getContentFor(createHit(HitResult.Great))));
|
||||
AddStep("Ok", () => SetContents(() => getContentFor(createHit(HitResult.Ok))));
|
||||
AddStep("Miss", () => SetContents(() => getContentFor(createHit(HitResult.Miss))));
|
||||
AddStep("Great", () => SetContents(_ => getContentFor(createHit(HitResult.Great))));
|
||||
AddStep("Ok", () => SetContents(_ => getContentFor(createHit(HitResult.Ok))));
|
||||
AddStep("Miss", () => SetContents(_ => getContentFor(createHit(HitResult.Miss))));
|
||||
}
|
||||
|
||||
[TestCase(HitResult.Great)]
|
||||
[TestCase(HitResult.Ok)]
|
||||
public void TestStrongHit(HitResult type)
|
||||
{
|
||||
AddStep("create hit", () => SetContents(() => getContentFor(createStrongHit(type))));
|
||||
AddStep("create hit", () => SetContents(_ => getContentFor(createStrongHit(type))));
|
||||
AddStep("visualise second hit",
|
||||
() => this.ChildrenOfType<HitExplosion>()
|
||||
.ForEach(e => e.VisualiseSecondHit(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement()))));
|
||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
SetContents(() => new TaikoInputManager(new TaikoRuleset().RulesetInfo)
|
||||
SetContents(_ => new TaikoInputManager(new TaikoRuleset().RulesetInfo)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new Container
|
||||
|
@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
[Test]
|
||||
public void TestKiaiHits()
|
||||
{
|
||||
AddStep("rim hit", () => SetContents(() => getContentFor(createHit(HitType.Rim))));
|
||||
AddStep("centre hit", () => SetContents(() => getContentFor(createHit(HitType.Centre))));
|
||||
AddStep("rim hit", () => SetContents(_ => getContentFor(createHit(HitType.Rim))));
|
||||
AddStep("centre hit", () => SetContents(_ => getContentFor(createHit(HitType.Centre))));
|
||||
}
|
||||
|
||||
private Drawable getContentFor(DrawableTestHit hit)
|
||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
Beatmap.Value.Track.Start();
|
||||
});
|
||||
|
||||
AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo())
|
||||
AddStep("Load playfield", () => SetContents(_ => new TaikoPlayfield(new ControlPointInfo())
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
|
||||
public TestSceneTaikoScroller()
|
||||
{
|
||||
AddStep("Load scroller", () => SetContents(() =>
|
||||
AddStep("Load scroller", () => SetContents(_ =>
|
||||
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.Scroller), _ => Empty())
|
||||
{
|
||||
Clock = new FramedClock(clock),
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||
{
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods) => new Skill[]
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||
{
|
||||
new Colour(mods),
|
||||
new Rhythm(mods),
|
||||
|
@ -4,14 +4,13 @@
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Replays;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
public class TaikoModAutoplay : ModAutoplay<TaikoHitObject>
|
||||
public class TaikoModAutoplay : ModAutoplay
|
||||
{
|
||||
public override Score CreateReplayScore(IBeatmap beatmap, IReadOnlyList<Mod> mods) => new Score
|
||||
{
|
||||
|
@ -20,10 +20,13 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
||||
{
|
||||
var taikoBeatmap = (TaikoBeatmap)beatmap;
|
||||
|
||||
Seed.Value ??= RNG.Next();
|
||||
var rng = new Random((int)Seed.Value);
|
||||
|
||||
foreach (var obj in taikoBeatmap.HitObjects)
|
||||
{
|
||||
if (obj is Hit hit)
|
||||
hit.Type = RNG.Next(2) == 0 ? HitType.Centre : HitType.Rim;
|
||||
hit.Type = rng.Next(2) == 0 ? HitType.Centre : HitType.Rim;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
// 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.Containers;
|
||||
using osu.Framework.Graphics.Performance;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
|
||||
@ -11,6 +8,11 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
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()
|
||||
{
|
||||
base.Update();
|
||||
@ -23,14 +25,5 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -489,5 +489,23 @@ namespace osu.Game.Tests.Chat
|
||||
Assert.AreEqual(result.Links[2].Url, "\uD83D\uDE00");
|
||||
Assert.AreEqual(result.Links[3].Url, "\uD83D\uDE20");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAbsoluteExternalLinks()
|
||||
{
|
||||
LinkDetails result = MessageFormatter.GetLinkDetails("https://google.com");
|
||||
|
||||
Assert.AreEqual(LinkAction.External, result.Action);
|
||||
Assert.AreEqual("https://google.com", result.Argument);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRelativeExternalLinks()
|
||||
{
|
||||
LinkDetails result = MessageFormatter.GetLinkDetails("/relative");
|
||||
|
||||
Assert.AreEqual(LinkAction.External, result.Action);
|
||||
Assert.AreEqual("/relative", result.Argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +92,41 @@ namespace osu.Game.Tests.Gameplay
|
||||
AddAssert("Lifetime is correct", () => dho.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY && entry.LifetimeStart == TestDrawableHitObject.LIFETIME_ON_APPLY);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDrawableLifetimeUpdateOnEntryLifetimeChange()
|
||||
{
|
||||
TestDrawableHitObject dho = null;
|
||||
TestLifetimeEntry entry = null;
|
||||
AddStep("Create DHO", () =>
|
||||
{
|
||||
dho = new TestDrawableHitObject(null);
|
||||
dho.Apply(entry = new TestLifetimeEntry(new HitObject()));
|
||||
Child = dho;
|
||||
});
|
||||
|
||||
AddStep("Set entry lifetime", () =>
|
||||
{
|
||||
entry.LifetimeStart = 777;
|
||||
entry.LifetimeEnd = 888;
|
||||
});
|
||||
AddAssert("Drawable lifetime is updated", () => dho.LifetimeStart == 777 && dho.LifetimeEnd == 888);
|
||||
|
||||
AddStep("KeepAlive = true", () => entry.KeepAlive = true);
|
||||
AddAssert("Drawable lifetime is updated", () => dho.LifetimeStart == double.MinValue && dho.LifetimeEnd == double.MaxValue);
|
||||
|
||||
AddStep("Modify start time", () => entry.HitObject.StartTime = 100);
|
||||
AddAssert("Drawable lifetime is correct", () => dho.LifetimeStart == double.MinValue);
|
||||
|
||||
AddStep("Set LifetimeStart", () => dho.LifetimeStart = 666);
|
||||
AddAssert("Lifetime change is blocked", () => dho.LifetimeStart == double.MinValue);
|
||||
|
||||
AddStep("Set LifetimeEnd", () => dho.LifetimeEnd = 999);
|
||||
AddAssert("Lifetime change is blocked", () => dho.LifetimeEnd == double.MaxValue);
|
||||
|
||||
AddStep("KeepAlive = false", () => entry.KeepAlive = false);
|
||||
AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 666 && dho.LifetimeEnd == 999);
|
||||
}
|
||||
|
||||
private class TestDrawableHitObject : DrawableHitObject
|
||||
{
|
||||
public const double INITIAL_LIFETIME_OFFSET = 100;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user