mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 12:17:26 +08:00
Merge branch 'master' into catch-hyperdash-catcher-colouring
This commit is contained in:
commit
77e5e131c9
@ -4,3 +4,5 @@ M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(
|
|||||||
M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead.
|
M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead.
|
||||||
T:System.IComparable;Don't use non-generic IComparable. Use generic version instead.
|
T:System.IComparable;Don't use non-generic IComparable. Use generic version instead.
|
||||||
M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
|
M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
|
||||||
|
T:Microsoft.EntityFrameworkCore.Internal.EnumerableExtensions;Don't use internal extension methods.
|
||||||
|
T:Microsoft.EntityFrameworkCore.Internal.TypeExtensions;Don't use internal extension methods.
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
<EmbeddedResource Include="Resources\**\*.*" />
|
<EmbeddedResource Include="Resources\**\*.*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Code Analysis">
|
<ItemGroup Label="Code Analysis">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="2.9.8" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.0.0" PrivateAssets="All" />
|
||||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="3.0.0" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Code Analysis">
|
<PropertyGroup Label="Code Analysis">
|
||||||
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.412.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.427.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.421.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.508.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -18,7 +18,8 @@ namespace osu.Android
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string versionName = packageInfo.VersionCode.ToString();
|
// todo: needs checking before play store redeploy.
|
||||||
|
string versionName = packageInfo.VersionName;
|
||||||
// undo play store version garbling
|
// undo play store version garbling
|
||||||
return new Version(int.Parse(versionName.Substring(0, 4)), int.Parse(versionName.Substring(4, 4)), int.Parse(versionName.Substring(8, 1)));
|
return new Version(int.Parse(versionName.Substring(0, 4)), int.Parse(versionName.Substring(4, 4)), int.Parse(versionName.Substring(8, 1)));
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,14 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Win32;
|
||||||
using osu.Desktop.Overlays;
|
using osu.Desktop.Overlays;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
using Microsoft.Win32;
|
|
||||||
using osu.Desktop.Updater;
|
using osu.Desktop.Updater;
|
||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Platform.Windows;
|
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
@ -37,7 +36,11 @@ namespace osu.Desktop
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Host is DesktopGameHost desktopHost)
|
if (Host is DesktopGameHost desktopHost)
|
||||||
return new StableStorage(desktopHost);
|
{
|
||||||
|
string stablePath = getStableInstallPath();
|
||||||
|
if (!string.IsNullOrEmpty(stablePath))
|
||||||
|
return new DesktopStorage(stablePath, desktopHost);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@ -47,6 +50,35 @@ namespace osu.Desktop
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string getStableInstallPath()
|
||||||
|
{
|
||||||
|
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
|
||||||
|
|
||||||
|
string stableInstallPath;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
||||||
|
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
|
||||||
|
|
||||||
|
if (checkExists(stableInstallPath))
|
||||||
|
return stableInstallPath;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
|
||||||
|
if (checkExists(stableInstallPath))
|
||||||
|
return stableInstallPath;
|
||||||
|
|
||||||
|
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
|
||||||
|
if (checkExists(stableInstallPath))
|
||||||
|
return stableInstallPath;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected override UpdateManager CreateUpdateManager()
|
protected override UpdateManager CreateUpdateManager()
|
||||||
{
|
{
|
||||||
switch (RuntimeInfo.OS)
|
switch (RuntimeInfo.OS)
|
||||||
@ -111,45 +143,5 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
|
Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A method of accessing an osu-stable install in a controlled fashion.
|
|
||||||
/// </summary>
|
|
||||||
private class StableStorage : WindowsStorage
|
|
||||||
{
|
|
||||||
protected override string LocateBasePath()
|
|
||||||
{
|
|
||||||
static bool checkExists(string p) => Directory.Exists(Path.Combine(p, "Songs"));
|
|
||||||
|
|
||||||
string stableInstallPath;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
|
||||||
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
|
|
||||||
|
|
||||||
if (checkExists(stableInstallPath))
|
|
||||||
return stableInstallPath;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
|
|
||||||
if (checkExists(stableInstallPath))
|
|
||||||
return stableInstallPath;
|
|
||||||
|
|
||||||
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
|
|
||||||
if (checkExists(stableInstallPath))
|
|
||||||
return stableInstallPath;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StableStorage(DesktopGameHost host)
|
|
||||||
: base(string.Empty, host)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||||
|
|
||||||
[TestCase(4.2058561036909863d, "diffcalc-test")]
|
[TestCase(4.050601681491468d, "diffcalc-test")]
|
||||||
public void Test(double expected, string name)
|
public void Test(double expected, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expected, name);
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
{
|
{
|
||||||
public class CatchDifficultyCalculator : DifficultyCalculator
|
public class CatchDifficultyCalculator : DifficultyCalculator
|
||||||
{
|
{
|
||||||
private const double star_scaling_factor = 0.145;
|
private const double star_scaling_factor = 0.153;
|
||||||
|
|
||||||
protected override int SectionLength => 750;
|
protected override int SectionLength => 750;
|
||||||
|
|
||||||
@ -73,6 +73,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
{
|
{
|
||||||
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
||||||
|
|
||||||
|
// For circle sizes above 5.5, reduce the catcher width further to simulate imperfect gameplay.
|
||||||
|
halfCatcherWidth *= 1 - (Math.Max(0, beatmap.BeatmapInfo.BaseDifficulty.CircleSize - 5.5f) * 0.0625f);
|
||||||
|
|
||||||
return new Skill[]
|
return new Skill[]
|
||||||
{
|
{
|
||||||
new Movement(halfCatcherWidth),
|
new Movement(halfCatcherWidth),
|
||||||
|
@ -52,8 +52,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
|
|
||||||
// Longer maps are worth more
|
// Longer maps are worth more
|
||||||
double lengthBonus =
|
double lengthBonus =
|
||||||
0.95 + 0.4 * Math.Min(1.0, numTotalHits / 3000.0) +
|
0.95f + 0.3f * Math.Min(1.0f, numTotalHits / 2500.0f) +
|
||||||
(numTotalHits > 3000 ? Math.Log10(numTotalHits / 3000.0) * 0.5 : 0.0);
|
(numTotalHits > 2500 ? (float)Math.Log10(numTotalHits / 2500.0f) * 0.475f : 0.0f);
|
||||||
|
|
||||||
// Longer maps are worth more
|
// Longer maps are worth more
|
||||||
value *= lengthBonus;
|
value *= lengthBonus;
|
||||||
@ -63,19 +63,28 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
|
|
||||||
// Combo scaling
|
// Combo scaling
|
||||||
if (Attributes.MaxCombo > 0)
|
if (Attributes.MaxCombo > 0)
|
||||||
value *= Math.Min(Math.Pow(Attributes.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
|
value *= Math.Min(Math.Pow(Score.MaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
|
||||||
|
|
||||||
double approachRateFactor = 1.0;
|
float approachRate = (float)Attributes.ApproachRate;
|
||||||
if (Attributes.ApproachRate > 9.0)
|
float approachRateFactor = 1.0f;
|
||||||
approachRateFactor += 0.1 * (Attributes.ApproachRate - 9.0); // 10% for each AR above 9
|
if (approachRate > 9.0f)
|
||||||
else if (Attributes.ApproachRate < 8.0)
|
approachRateFactor += 0.1f * (approachRate - 9.0f); // 10% for each AR above 9
|
||||||
approachRateFactor += 0.025 * (8.0 - Attributes.ApproachRate); // 2.5% for each AR below 8
|
if (approachRate > 10.0f)
|
||||||
|
approachRateFactor += 0.1f * (approachRate - 10.0f); // Additional 10% at AR 11, 30% total
|
||||||
|
else if (approachRate < 8.0f)
|
||||||
|
approachRateFactor += 0.025f * (8.0f - approachRate); // 2.5% for each AR below 8
|
||||||
|
|
||||||
value *= approachRateFactor;
|
value *= approachRateFactor;
|
||||||
|
|
||||||
if (mods.Any(m => m is ModHidden))
|
if (mods.Any(m => m is ModHidden))
|
||||||
// Hiddens gives nothing on max approach rate, and more the lower it is
|
{
|
||||||
value *= 1.05 + 0.075 * (10.0 - Math.Min(10.0, Attributes.ApproachRate)); // 7.5% for each AR below 10
|
value *= 1.05 + 0.075 * (10.0 - Math.Min(10.0, Attributes.ApproachRate)); // 7.5% for each AR below 10
|
||||||
|
// Hiddens gives almost nothing on max approach rate, and more the lower it is
|
||||||
|
if (approachRate <= 10.0f)
|
||||||
|
value *= 1.05f + 0.075f * (10.0f - approachRate); // 7.5% for each AR below 10
|
||||||
|
else if (approachRate > 10.0f)
|
||||||
|
value *= 1.01f + 0.04f * (11.0f - Math.Min(11.0f, approachRate)); // 5% at AR 10, 1% at AR 11
|
||||||
|
}
|
||||||
|
|
||||||
if (mods.Any(m => m is ModFlashlight))
|
if (mods.Any(m => m is ModFlashlight))
|
||||||
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
|
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
|
||||||
|
@ -21,10 +21,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
|||||||
public readonly float LastNormalizedPosition;
|
public readonly float LastNormalizedPosition;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Milliseconds elapsed since the start time of the previous <see cref="CatchDifficultyHitObject"/>, with a minimum of 25ms.
|
/// Milliseconds elapsed since the start time of the previous <see cref="CatchDifficultyHitObject"/>, with a minimum of 40ms.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly double StrainTime;
|
public readonly double StrainTime;
|
||||||
|
|
||||||
|
public readonly double ClockRate;
|
||||||
|
|
||||||
public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
|
public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
|
||||||
: base(hitObject, lastObject, clockRate)
|
: base(hitObject, lastObject, clockRate)
|
||||||
{
|
{
|
||||||
@ -34,8 +36,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
|||||||
NormalizedPosition = BaseObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
NormalizedPosition = BaseObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
||||||
LastNormalizedPosition = LastObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
LastNormalizedPosition = LastObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
||||||
|
|
||||||
// Every strain interval is hard capped at the equivalent of 600 BPM streaming speed as a safety measure
|
// Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
|
||||||
StrainTime = Math.Max(25, DeltaTime);
|
StrainTime = Math.Max(40, DeltaTime);
|
||||||
|
ClockRate = clockRate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
{
|
{
|
||||||
private const float absolute_player_positioning_error = 16f;
|
private const float absolute_player_positioning_error = 16f;
|
||||||
private const float normalized_hitobject_radius = 41.0f;
|
private const float normalized_hitobject_radius = 41.0f;
|
||||||
private const double direction_change_bonus = 12.5;
|
private const double direction_change_bonus = 21.0;
|
||||||
|
|
||||||
protected override double SkillMultiplier => 850;
|
protected override double SkillMultiplier => 900;
|
||||||
protected override double StrainDecayBase => 0.2;
|
protected override double StrainDecayBase => 0.2;
|
||||||
|
|
||||||
protected override double DecayWeight => 0.94;
|
protected override double DecayWeight => 0.94;
|
||||||
@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
|
|
||||||
private float? lastPlayerPosition;
|
private float? lastPlayerPosition;
|
||||||
private float lastDistanceMoved;
|
private float lastDistanceMoved;
|
||||||
|
private double lastStrainTime;
|
||||||
|
|
||||||
public Movement(float halfCatcherWidth)
|
public Movement(float halfCatcherWidth)
|
||||||
{
|
{
|
||||||
@ -45,47 +46,47 @@ namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
|||||||
|
|
||||||
float distanceMoved = playerPosition - lastPlayerPosition.Value;
|
float distanceMoved = playerPosition - lastPlayerPosition.Value;
|
||||||
|
|
||||||
double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.3) / 500;
|
double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catchCurrent.ClockRate);
|
||||||
double sqrtStrain = Math.Sqrt(catchCurrent.StrainTime);
|
|
||||||
|
|
||||||
double bonus = 0;
|
double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510);
|
||||||
|
double sqrtStrain = Math.Sqrt(weightedStrainTime);
|
||||||
|
|
||||||
// Direction changes give an extra point!
|
double edgeDashBonus = 0;
|
||||||
|
|
||||||
|
// Direction change bonus.
|
||||||
if (Math.Abs(distanceMoved) > 0.1)
|
if (Math.Abs(distanceMoved) > 0.1)
|
||||||
{
|
{
|
||||||
if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved))
|
if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved))
|
||||||
{
|
{
|
||||||
double bonusFactor = Math.Min(absolute_player_positioning_error, Math.Abs(distanceMoved)) / absolute_player_positioning_error;
|
double bonusFactor = Math.Min(50, Math.Abs(distanceMoved)) / 50;
|
||||||
|
double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(lastDistanceMoved)) / 70, 0.38);
|
||||||
|
|
||||||
distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor;
|
distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 16) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0);
|
||||||
|
|
||||||
// Bonus for tougher direction switches and "almost" hyperdashes at this point
|
|
||||||
if (catchCurrent.LastObject.DistanceToHyperDash <= 10 / CatchPlayfield.BASE_WIDTH)
|
|
||||||
bonus = 0.3 * bonusFactor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Base bonus for every movement, giving some weight to streams.
|
// Base bonus for every movement, giving some weight to streams.
|
||||||
distanceAddition += 7.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain;
|
distanceAddition += 12.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bonus for "almost" hyperdashes at corner points
|
// Bonus for edge dashes.
|
||||||
if (catchCurrent.LastObject.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
|
if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f / CatchPlayfield.BASE_WIDTH)
|
||||||
{
|
{
|
||||||
if (!catchCurrent.LastObject.HyperDash)
|
if (!catchCurrent.LastObject.HyperDash)
|
||||||
bonus += 1.0;
|
edgeDashBonus += 5.7;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// After a hyperdash we ARE in the correct position. Always!
|
// After a hyperdash we ARE in the correct position. Always!
|
||||||
playerPosition = catchCurrent.NormalizedPosition;
|
playerPosition = catchCurrent.NormalizedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
distanceAddition *= 1.0 + bonus * ((10 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10);
|
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
|
||||||
}
|
}
|
||||||
|
|
||||||
lastPlayerPosition = playerPosition;
|
lastPlayerPosition = playerPosition;
|
||||||
lastDistanceMoved = distanceMoved;
|
lastDistanceMoved = distanceMoved;
|
||||||
|
lastStrainTime = catchCurrent.StrainTime;
|
||||||
|
|
||||||
return distanceAddition / catchCurrent.StrainTime;
|
return distanceAddition / weightedStrainTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,6 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
AccentColour = Color4.OrangeRed,
|
AccentColour = Color4.OrangeRed,
|
||||||
Clock = new FramedClock(new StopwatchClock()), // No scroll
|
Clock = new FramedClock(new StopwatchClock()), // No scroll
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("change direction", () => ((ScrollingTestContainer)HitObjectContainer).Flip());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both };
|
protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both };
|
||||||
|
221
osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs
Normal file
221
osu.Game.Rulesets.Mania.Tests/TestSceneManiaHitObjectComposer.cs
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Edit;
|
||||||
|
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
public class TestSceneManiaHitObjectComposer : EditorClockTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(ManiaBlueprintContainer)
|
||||||
|
};
|
||||||
|
|
||||||
|
private TestComposer composer;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
|
{
|
||||||
|
BeatDivisor.Value = 8;
|
||||||
|
Clock.Seek(0);
|
||||||
|
|
||||||
|
Child = composer = new TestComposer { RelativeSizeAxes = Axes.Both };
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragOffscreenSelectionVerticallyUpScroll()
|
||||||
|
{
|
||||||
|
DrawableHitObject lastObject = null;
|
||||||
|
Vector2 originalPosition = Vector2.Zero;
|
||||||
|
|
||||||
|
setScrollStep(ScrollingDirection.Up);
|
||||||
|
|
||||||
|
AddStep("seek to last object", () =>
|
||||||
|
{
|
||||||
|
lastObject = this.ChildrenOfType<DrawableHitObject>().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last());
|
||||||
|
Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects));
|
||||||
|
|
||||||
|
AddStep("click last object", () =>
|
||||||
|
{
|
||||||
|
originalPosition = lastObject.DrawPosition;
|
||||||
|
|
||||||
|
InputManager.MoveMouseTo(lastObject);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("move mouse downwards", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(lastObject, new Vector2(0, 20));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0));
|
||||||
|
AddAssert("hitobjects moved downwards", () => lastObject.DrawPosition.Y - originalPosition.Y > 0);
|
||||||
|
AddAssert("hitobjects not moved too far", () => lastObject.DrawPosition.Y - originalPosition.Y < 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragOffscreenSelectionVerticallyDownScroll()
|
||||||
|
{
|
||||||
|
DrawableHitObject lastObject = null;
|
||||||
|
Vector2 originalPosition = Vector2.Zero;
|
||||||
|
|
||||||
|
setScrollStep(ScrollingDirection.Down);
|
||||||
|
|
||||||
|
AddStep("seek to last object", () =>
|
||||||
|
{
|
||||||
|
lastObject = this.ChildrenOfType<DrawableHitObject>().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last());
|
||||||
|
Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects));
|
||||||
|
|
||||||
|
AddStep("click last object", () =>
|
||||||
|
{
|
||||||
|
originalPosition = lastObject.DrawPosition;
|
||||||
|
|
||||||
|
InputManager.MoveMouseTo(lastObject);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("move mouse upwards", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(lastObject, new Vector2(0, -20));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("hitobjects not moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 0));
|
||||||
|
AddAssert("hitobjects moved upwards", () => originalPosition.Y - lastObject.DrawPosition.Y > 0);
|
||||||
|
AddAssert("hitobjects not moved too far", () => originalPosition.Y - lastObject.DrawPosition.Y < 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragOffscreenSelectionHorizontally()
|
||||||
|
{
|
||||||
|
DrawableHitObject lastObject = null;
|
||||||
|
Vector2 originalPosition = Vector2.Zero;
|
||||||
|
|
||||||
|
setScrollStep(ScrollingDirection.Down);
|
||||||
|
|
||||||
|
AddStep("seek to last object", () =>
|
||||||
|
{
|
||||||
|
lastObject = this.ChildrenOfType<DrawableHitObject>().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last());
|
||||||
|
Clock.Seek(composer.EditorBeatmap.HitObjects.Last().StartTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("select all objects", () => composer.EditorBeatmap.SelectedHitObjects.AddRange(composer.EditorBeatmap.HitObjects));
|
||||||
|
|
||||||
|
AddStep("click last object", () =>
|
||||||
|
{
|
||||||
|
originalPosition = lastObject.DrawPosition;
|
||||||
|
|
||||||
|
InputManager.MoveMouseTo(lastObject);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("move mouse right", () =>
|
||||||
|
{
|
||||||
|
var firstColumn = composer.Composer.Playfield.GetColumn(0);
|
||||||
|
var secondColumn = composer.Composer.Playfield.GetColumn(1);
|
||||||
|
|
||||||
|
InputManager.MoveMouseTo(lastObject, new Vector2(secondColumn.ScreenSpaceDrawQuad.Centre.X - firstColumn.ScreenSpaceDrawQuad.Centre.X + 1, 0));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("hitobjects moved columns", () => composer.EditorBeatmap.HitObjects.All(h => ((ManiaHitObject)h).Column == 1));
|
||||||
|
|
||||||
|
// Todo: They'll move vertically by the height of a note since there's no snapping and the selection point is the middle of the note.
|
||||||
|
AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y <= DefaultNotePiece.NOTE_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragHoldNoteSelectionVertically()
|
||||||
|
{
|
||||||
|
setScrollStep(ScrollingDirection.Down);
|
||||||
|
|
||||||
|
AddStep("setup beatmap", () =>
|
||||||
|
{
|
||||||
|
composer.EditorBeatmap.Clear();
|
||||||
|
composer.EditorBeatmap.Add(new HoldNote
|
||||||
|
{
|
||||||
|
Column = 1,
|
||||||
|
EndTime = 200
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
DrawableHoldNote holdNote = null;
|
||||||
|
|
||||||
|
AddStep("grab hold note", () =>
|
||||||
|
{
|
||||||
|
holdNote = this.ChildrenOfType<DrawableHoldNote>().Single();
|
||||||
|
InputManager.MoveMouseTo(holdNote);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("move drag upwards", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(holdNote, new Vector2(0, -100));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("head note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.BottomLeft, holdNote.Head.ScreenSpaceDrawQuad.BottomLeft));
|
||||||
|
AddAssert("tail note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.TopLeft, holdNote.Tail.ScreenSpaceDrawQuad.BottomLeft));
|
||||||
|
|
||||||
|
AddAssert("head blueprint positioned correctly", () => this.ChildrenOfType<HoldNoteNoteSelectionBlueprint>().ElementAt(0).DrawPosition == holdNote.Head.DrawPosition);
|
||||||
|
AddAssert("tail blueprint positioned correctly", () => this.ChildrenOfType<HoldNoteNoteSelectionBlueprint>().ElementAt(1).DrawPosition == holdNote.Tail.DrawPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setScrollStep(ScrollingDirection direction)
|
||||||
|
=> AddStep($"set scroll direction = {direction}", () => ((Bindable<ScrollingDirection>)composer.Composer.ScrollingInfo.Direction).Value = direction);
|
||||||
|
|
||||||
|
private class TestComposer : CompositeDrawable
|
||||||
|
{
|
||||||
|
[Cached(typeof(EditorBeatmap))]
|
||||||
|
[Cached(typeof(IBeatSnapProvider))]
|
||||||
|
public readonly EditorBeatmap EditorBeatmap;
|
||||||
|
|
||||||
|
public readonly ManiaHitObjectComposer Composer;
|
||||||
|
|
||||||
|
public TestComposer()
|
||||||
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
EditorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 }))
|
||||||
|
{
|
||||||
|
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo }
|
||||||
|
},
|
||||||
|
Composer = new ManiaHitObjectComposer(new ManiaRuleset())
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
EditorBeatmap.Add(new Note { StartTime = 100 * i });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,59 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
public class TestSceneNotePlacementBlueprint : ManiaPlacementBlueprintTestScene
|
public class TestSceneNotePlacementBlueprint : ManiaPlacementBlueprintTestScene
|
||||||
{
|
{
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
|
{
|
||||||
|
this.ChildrenOfType<HitObjectContainer>().ForEach(c => c.Clear());
|
||||||
|
|
||||||
|
ResetPlacement();
|
||||||
|
|
||||||
|
((ScrollingTestContainer)HitObjectContainer).Direction = ScrollingDirection.Down;
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPlaceBeforeCurrentTimeDownwards()
|
||||||
|
{
|
||||||
|
AddStep("move mouse before current time", () => InputManager.MoveMouseTo(this.ChildrenOfType<Column>().Single().ScreenSpaceDrawQuad.BottomLeft - new Vector2(0, 10)));
|
||||||
|
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("note start time < 0", () => getNote().StartTime < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPlaceAfterCurrentTimeDownwards()
|
||||||
|
{
|
||||||
|
AddStep("move mouse after current time", () => InputManager.MoveMouseTo(this.ChildrenOfType<Column>().Single()));
|
||||||
|
|
||||||
|
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddAssert("note start time > 0", () => getNote().StartTime > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Note getNote() => this.ChildrenOfType<DrawableNote>().FirstOrDefault()?.HitObject;
|
||||||
|
|
||||||
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableNote((Note)hitObject);
|
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableNote((Note)hitObject);
|
||||||
protected override PlacementBlueprint CreateBlueprint() => new NotePlacementBlueprint();
|
protected override PlacementBlueprint CreateBlueprint() => new NotePlacementBlueprint();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -13,7 +13,6 @@ using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
|
|||||||
using osu.Game.Rulesets.Mania.MathUtils;
|
using osu.Game.Rulesets.Mania.MathUtils;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Game.Audio;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps
|
namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||||
{
|
{
|
||||||
@ -67,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition || h is ManiaHitObject);
|
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition);
|
||||||
|
|
||||||
protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original)
|
protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original)
|
||||||
{
|
{
|
||||||
@ -239,8 +238,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
StartTime = HitObject.StartTime,
|
StartTime = HitObject.StartTime,
|
||||||
Duration = endTimeData.Duration,
|
Duration = endTimeData.Duration,
|
||||||
Column = column,
|
Column = column,
|
||||||
Head = { Samples = sampleInfoListAt(HitObject.StartTime) },
|
Samples = HitObject.Samples,
|
||||||
Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) },
|
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (HitObject is IHasXPosition)
|
else if (HitObject is IHasXPosition)
|
||||||
@ -255,22 +254,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
return pattern;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the sample info list at a point in time.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="time">The time to retrieve the sample info list from.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private IList<HitSampleInfo> sampleInfoListAt(double time)
|
|
||||||
{
|
|
||||||
if (!(HitObject is IHasCurve curveData))
|
|
||||||
return HitObject.Samples;
|
|
||||||
|
|
||||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
|
|
||||||
|
|
||||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
|
||||||
return curveData.NodeSamples[index];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -505,16 +505,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var holdNote = new HoldNote
|
newObject = new HoldNote
|
||||||
{
|
{
|
||||||
StartTime = startTime,
|
StartTime = startTime,
|
||||||
Column = column,
|
|
||||||
Duration = endTime - startTime,
|
Duration = endTime - startTime,
|
||||||
Head = { Samples = sampleInfoListAt(startTime) },
|
Column = column,
|
||||||
Tail = { Samples = sampleInfoListAt(endTime) }
|
Samples = HitObject.Samples,
|
||||||
|
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples
|
||||||
};
|
};
|
||||||
|
|
||||||
newObject = holdNote;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern.Add(newObject);
|
pattern.Add(newObject);
|
||||||
|
@ -64,21 +64,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
if (holdNote)
|
if (holdNote)
|
||||||
{
|
{
|
||||||
var hold = new HoldNote
|
newObject = new HoldNote
|
||||||
{
|
{
|
||||||
StartTime = HitObject.StartTime,
|
StartTime = HitObject.StartTime,
|
||||||
|
Duration = endTime - HitObject.StartTime,
|
||||||
Column = column,
|
Column = column,
|
||||||
Duration = endTime - HitObject.StartTime
|
Samples = HitObject.Samples,
|
||||||
|
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hold.Head.Samples == null)
|
|
||||||
hold.Head.Samples = new List<HitSampleInfo>();
|
|
||||||
|
|
||||||
hold.Head.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_NORMAL });
|
|
||||||
|
|
||||||
hold.Tail.Samples = HitObject.Samples;
|
|
||||||
|
|
||||||
newObject = hold;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
|
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -46,6 +47,12 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
bodyPiece.Height = (bottomPosition - topPosition).Y;
|
bodyPiece.Height = (bottomPosition - topPosition).Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnMouseUp(MouseUpEvent e)
|
||||||
|
{
|
||||||
|
base.OnMouseUp(e);
|
||||||
|
EndPlacement(true);
|
||||||
|
}
|
||||||
|
|
||||||
private double originalStartTime;
|
private double originalStartTime;
|
||||||
|
|
||||||
public override void UpdatePosition(Vector2 screenSpacePosition)
|
public override void UpdatePosition(Vector2 screenSpacePosition)
|
||||||
|
@ -76,5 +76,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override Quad SelectionQuad => ScreenSpaceDrawQuad;
|
public override Quad SelectionQuad => ScreenSpaceDrawQuad;
|
||||||
|
|
||||||
|
public override Vector2 SelectionPoint => DrawableObject.Head.ScreenSpaceDrawQuad.Centre;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,16 +50,10 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
return base.OnMouseDown(e);
|
return base.OnMouseDown(e);
|
||||||
|
|
||||||
HitObject.Column = Column.Index;
|
HitObject.Column = Column.Index;
|
||||||
BeginPlacement(TimeAt(e.ScreenSpaceMousePosition));
|
BeginPlacement(TimeAt(e.ScreenSpaceMousePosition), true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnMouseUp(MouseUpEvent e)
|
|
||||||
{
|
|
||||||
EndPlacement(true);
|
|
||||||
base.OnMouseUp(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdatePosition(Vector2 screenSpacePosition)
|
public override void UpdatePosition(Vector2 screenSpacePosition)
|
||||||
{
|
{
|
||||||
if (!PlacementActive)
|
if (!PlacementActive)
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -15,13 +13,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
{
|
{
|
||||||
public class ManiaSelectionBlueprint : OverlaySelectionBlueprint
|
public class ManiaSelectionBlueprint : OverlaySelectionBlueprint
|
||||||
{
|
{
|
||||||
public Vector2 ScreenSpaceDragPosition { get; private set; }
|
|
||||||
public Vector2 DragPosition { get; private set; }
|
|
||||||
|
|
||||||
public new DrawableManiaHitObject DrawableObject => (DrawableManiaHitObject)base.DrawableObject;
|
public new DrawableManiaHitObject DrawableObject => (DrawableManiaHitObject)base.DrawableObject;
|
||||||
|
|
||||||
protected IClock EditorClock { get; private set; }
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IScrollingInfo scrollingInfo { get; set; }
|
private IScrollingInfo scrollingInfo { get; set; }
|
||||||
|
|
||||||
@ -34,12 +27,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
RelativeSizeAxes = Axes.None;
|
RelativeSizeAxes = Axes.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(IAdjustableClock clock)
|
|
||||||
{
|
|
||||||
EditorClock = clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
@ -47,22 +34,6 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
Position = Parent.ToLocalSpace(DrawableObject.ToScreenSpace(Vector2.Zero));
|
Position = Parent.ToLocalSpace(DrawableObject.ToScreenSpace(Vector2.Zero));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(MouseDownEvent e)
|
|
||||||
{
|
|
||||||
ScreenSpaceDragPosition = e.ScreenSpaceMousePosition;
|
|
||||||
DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition);
|
|
||||||
|
|
||||||
return base.OnMouseDown(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDrag(DragEvent e)
|
|
||||||
{
|
|
||||||
base.OnDrag(e);
|
|
||||||
|
|
||||||
ScreenSpaceDragPosition = e.ScreenSpaceMousePosition;
|
|
||||||
DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Show()
|
public override void Show()
|
||||||
{
|
{
|
||||||
DrawableObject.AlwaysAlive = true;
|
DrawableObject.AlwaysAlive = true;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
|
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
|
||||||
@ -26,5 +27,15 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
Width = SnappedWidth;
|
Width = SnappedWidth;
|
||||||
Position = SnappedMousePosition;
|
Position = SnappedMousePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
base.OnMouseDown(e);
|
||||||
|
|
||||||
|
// Place the note immediately.
|
||||||
|
EndPlacement(true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,5 +30,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
|
|
||||||
return base.CreateBlueprintFor(hitObject);
|
return base.CreateBlueprintFor(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -37,7 +38,33 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||||
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
=> dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||||
|
|
||||||
public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns;
|
public ManiaPlayfield Playfield => ((ManiaPlayfield)drawableRuleset.Playfield);
|
||||||
|
|
||||||
|
public IScrollingInfo ScrollingInfo => drawableRuleset.ScrollingInfo;
|
||||||
|
|
||||||
|
public int TotalColumns => Playfield.TotalColumns;
|
||||||
|
|
||||||
|
public override (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time)
|
||||||
|
{
|
||||||
|
var hoc = Playfield.GetColumn(0).HitObjectContainer;
|
||||||
|
|
||||||
|
float targetPosition = hoc.ToLocalSpace(ToScreenSpace(position)).Y;
|
||||||
|
|
||||||
|
if (drawableRuleset.ScrollingInfo.Direction.Value == ScrollingDirection.Down)
|
||||||
|
{
|
||||||
|
// We're dealing with screen coordinates in which the position decreases towards the centre of the screen resulting in an increase in start time.
|
||||||
|
// The scrolling algorithm instead assumes a top anchor meaning an increase in time corresponds to an increase in position,
|
||||||
|
// so when scrolling downwards the coordinates need to be flipped.
|
||||||
|
targetPosition = hoc.DrawHeight - targetPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
double targetTime = drawableRuleset.ScrollingInfo.Algorithm.TimeAt(targetPosition,
|
||||||
|
EditorClock.CurrentTime,
|
||||||
|
drawableRuleset.ScrollingInfo.TimeRange.Value,
|
||||||
|
hoc.DrawHeight);
|
||||||
|
|
||||||
|
return base.GetSnappedPosition(position, targetTime);
|
||||||
|
}
|
||||||
|
|
||||||
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
{
|
{
|
||||||
|
@ -4,11 +4,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Rulesets.Edit;
|
|
||||||
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
|
||||||
@ -22,85 +19,16 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private IManiaHitObjectComposer composer { get; set; }
|
private IManiaHitObjectComposer composer { get; set; }
|
||||||
|
|
||||||
private IClock editorClock;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(IAdjustableClock clock)
|
|
||||||
{
|
|
||||||
editorClock = clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool HandleMovement(MoveSelectionEvent moveEvent)
|
public override bool HandleMovement(MoveSelectionEvent moveEvent)
|
||||||
{
|
{
|
||||||
var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint;
|
var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint;
|
||||||
int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column;
|
int lastColumn = maniaBlueprint.DrawableObject.HitObject.Column;
|
||||||
|
|
||||||
adjustOrigins(maniaBlueprint);
|
|
||||||
performDragMovement(moveEvent);
|
|
||||||
performColumnMovement(lastColumn, moveEvent);
|
performColumnMovement(lastColumn, moveEvent);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ensures that the position of hitobjects remains centred to the mouse position.
|
|
||||||
/// E.g. The hitobject position will change if the editor scrolls while a hitobject is dragged.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reference">The <see cref="ManiaSelectionBlueprint"/> that received the drag event.</param>
|
|
||||||
private void adjustOrigins(ManiaSelectionBlueprint reference)
|
|
||||||
{
|
|
||||||
var referenceParent = (HitObjectContainer)reference.DrawableObject.Parent;
|
|
||||||
|
|
||||||
float offsetFromReferenceOrigin = reference.DragPosition.Y - reference.DrawableObject.OriginPosition.Y;
|
|
||||||
float targetPosition = referenceParent.ToLocalSpace(reference.ScreenSpaceDragPosition).Y - offsetFromReferenceOrigin;
|
|
||||||
|
|
||||||
// Flip the vertical coordinate space when scrolling downwards
|
|
||||||
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
|
||||||
targetPosition -= referenceParent.DrawHeight;
|
|
||||||
|
|
||||||
float movementDelta = targetPosition - reference.DrawableObject.Position.Y;
|
|
||||||
|
|
||||||
foreach (var b in SelectedBlueprints.OfType<ManiaSelectionBlueprint>())
|
|
||||||
b.DrawableObject.Y += movementDelta;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void performDragMovement(MoveSelectionEvent moveEvent)
|
|
||||||
{
|
|
||||||
float delta = moveEvent.InstantDelta.Y;
|
|
||||||
|
|
||||||
// When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen.
|
|
||||||
// This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height.
|
|
||||||
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
|
||||||
delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: probably wrong
|
|
||||||
|
|
||||||
foreach (var selectionBlueprint in SelectedBlueprints)
|
|
||||||
{
|
|
||||||
var b = (OverlaySelectionBlueprint)selectionBlueprint;
|
|
||||||
|
|
||||||
var hitObject = b.DrawableObject;
|
|
||||||
var objectParent = (HitObjectContainer)hitObject.Parent;
|
|
||||||
|
|
||||||
// StartTime could be used to adjust the position if only one movement event was received per frame.
|
|
||||||
// However this is not the case and ScrollingHitObjectContainer performs movement in UpdateAfterChildren() so the position must also be updated to be valid for further movement events
|
|
||||||
hitObject.Y += delta;
|
|
||||||
|
|
||||||
float targetPosition = hitObject.Position.Y;
|
|
||||||
|
|
||||||
// The scrolling algorithm always assumes an anchor at the top of the screen, so the position must be flipped when scrolling downwards to reflect a top anchor
|
|
||||||
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
|
|
||||||
targetPosition = -targetPosition;
|
|
||||||
|
|
||||||
objectParent.Remove(hitObject);
|
|
||||||
|
|
||||||
hitObject.HitObject.StartTime = scrollingInfo.Algorithm.TimeAt(targetPosition,
|
|
||||||
editorClock.CurrentTime,
|
|
||||||
scrollingInfo.TimeRange.Value,
|
|
||||||
objectParent.DrawHeight);
|
|
||||||
|
|
||||||
objectParent.Add(hitObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent)
|
private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent)
|
||||||
{
|
{
|
||||||
var currentColumn = composer.ColumnAt(moveEvent.ScreenSpacePosition);
|
var currentColumn = composer.ColumnAt(moveEvent.ScreenSpacePosition);
|
||||||
|
@ -1,18 +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.Graphics;
|
|
||||||
using osu.Game.Rulesets.Edit;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Edit.Masks
|
|
||||||
{
|
|
||||||
public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint
|
|
||||||
{
|
|
||||||
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
|
|
||||||
: base(drawableObject)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -51,7 +51,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
|
|
||||||
AddRangeInternal(new[]
|
AddRangeInternal(new[]
|
||||||
{
|
{
|
||||||
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece())
|
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
})
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X
|
RelativeSizeAxes = Axes.X
|
||||||
},
|
},
|
||||||
@ -127,6 +130,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void PlaySamples()
|
||||||
|
{
|
||||||
|
// Samples are played by the head/tail notes.
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
@ -13,11 +13,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public abstract class DrawableManiaHitObject : DrawableHitObject<ManiaHitObject>
|
public abstract class DrawableManiaHitObject : DrawableHitObject<ManiaHitObject>
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Whether this <see cref="DrawableManiaHitObject"/> should always remain alive.
|
|
||||||
/// </summary>
|
|
||||||
internal bool AlwaysAlive;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="ManiaAction"/> which causes this <see cref="DrawableManiaHitObject{TObject}"/> to be hit.
|
/// The <see cref="ManiaAction"/> which causes this <see cref="DrawableManiaHitObject{TObject}"/> to be hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -54,7 +49,62 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
Direction.BindValueChanged(OnDirectionChanged, true);
|
Direction.BindValueChanged(OnDirectionChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool ShouldBeAlive => AlwaysAlive || base.ShouldBeAlive;
|
private double computedLifetimeStart;
|
||||||
|
|
||||||
|
public override double LifetimeStart
|
||||||
|
{
|
||||||
|
get => base.LifetimeStart;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
computedLifetimeStart = value;
|
||||||
|
|
||||||
|
if (!AlwaysAlive)
|
||||||
|
base.LifetimeStart = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double computedLifetimeEnd;
|
||||||
|
|
||||||
|
public override double LifetimeEnd
|
||||||
|
{
|
||||||
|
get => base.LifetimeEnd;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
computedLifetimeEnd = value;
|
||||||
|
|
||||||
|
if (!AlwaysAlive)
|
||||||
|
base.LifetimeEnd = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool alwaysAlive;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this <see cref="DrawableManiaHitObject"/> should always remain alive.
|
||||||
|
/// </summary>
|
||||||
|
internal bool AlwaysAlive
|
||||||
|
{
|
||||||
|
get => alwaysAlive;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (alwaysAlive == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
alwaysAlive = value;
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
// Set the base lifetimes directly, to avoid mangling the computed lifetimes
|
||||||
|
base.LifetimeStart = double.MinValue;
|
||||||
|
base.LifetimeEnd = double.MaxValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LifetimeStart = computedLifetimeStart;
|
||||||
|
LifetimeEnd = computedLifetimeEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
|
protected virtual void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
|||||||
|
|
||||||
public DefaultBodyPiece()
|
public DefaultBodyPiece()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
Blending = BlendingParameters.Additive;
|
Blending = BlendingParameters.Additive;
|
||||||
|
|
||||||
AddLayout(subtractionCache);
|
AddLayout(subtractionCache);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
@ -28,6 +30,8 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
duration = value;
|
duration = value;
|
||||||
|
|
||||||
|
if (Tail != null)
|
||||||
Tail.StartTime = EndTime;
|
Tail.StartTime = EndTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +42,11 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
base.StartTime = value;
|
base.StartTime = value;
|
||||||
|
|
||||||
|
if (Head != null)
|
||||||
Head.StartTime = value;
|
Head.StartTime = value;
|
||||||
|
|
||||||
|
if (Tail != null)
|
||||||
Tail.StartTime = EndTime;
|
Tail.StartTime = EndTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,20 +57,26 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
base.Column = value;
|
base.Column = value;
|
||||||
|
|
||||||
|
if (Head != null)
|
||||||
Head.Column = value;
|
Head.Column = value;
|
||||||
|
|
||||||
|
if (Tail != null)
|
||||||
Tail.Column = value;
|
Tail.Column = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<IList<HitSampleInfo>> NodeSamples { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The head note of the hold.
|
/// The head note of the hold.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly Note Head = new Note();
|
public Note Head { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The tail note of the hold.
|
/// The tail note of the hold.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly TailNote Tail = new TailNote();
|
public TailNote Tail { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time between ticks of this hold.
|
/// The time between ticks of this hold.
|
||||||
@ -83,8 +97,19 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
|
|
||||||
createTicks();
|
createTicks();
|
||||||
|
|
||||||
AddNested(Head);
|
AddNested(Head = new Note
|
||||||
AddNested(Tail);
|
{
|
||||||
|
StartTime = StartTime,
|
||||||
|
Column = Column,
|
||||||
|
Samples = getNodeSamples(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
AddNested(Tail = new TailNote
|
||||||
|
{
|
||||||
|
StartTime = EndTime,
|
||||||
|
Column = Column,
|
||||||
|
Samples = getNodeSamples((NodeSamples?.Count - 1) ?? 1),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTicks()
|
private void createTicks()
|
||||||
@ -105,5 +130,8 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||||
|
|
||||||
|
private IList<HitSampleInfo> getNodeSamples(int nodeIndex) =>
|
||||||
|
nodeIndex < NodeSamples?.Count ? NodeSamples[nodeIndex] : Samples;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,12 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Game.Rulesets.Mania.Objects.Types;
|
using osu.Game.Rulesets.Mania.Objects.Types;
|
||||||
using osu.Game.Rulesets.Mania.Scoring;
|
using osu.Game.Rulesets.Mania.Scoring;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects
|
namespace osu.Game.Rulesets.Mania.Objects
|
||||||
{
|
{
|
||||||
public abstract class ManiaHitObject : HitObject, IHasColumn
|
public abstract class ManiaHitObject : HitObject, IHasColumn, IHasXPosition
|
||||||
{
|
{
|
||||||
public readonly Bindable<int> ColumnBindable = new Bindable<int>();
|
public readonly Bindable<int> ColumnBindable = new Bindable<int>();
|
||||||
|
|
||||||
@ -20,5 +21,11 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
|
protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
|
||||||
|
|
||||||
|
#region LegacyBeatmapEncoder
|
||||||
|
|
||||||
|
float IHasXPosition.X => Column;
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -48,6 +50,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config;
|
||||||
|
|
||||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||||
|
private readonly Bindable<double> configTimeRange = new BindableDouble();
|
||||||
|
|
||||||
|
// Stores the current speed adjustment active in gameplay.
|
||||||
|
private readonly Track speedAdjustmentTrack = new TrackVirtual(0);
|
||||||
|
|
||||||
public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
@ -58,6 +64,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
foreach (var mod in Mods.OfType<IApplicableToTrack>())
|
||||||
|
mod.ApplyToTrack(speedAdjustmentTrack);
|
||||||
|
|
||||||
bool isForCurrentRuleset = Beatmap.BeatmapInfo.Ruleset.Equals(Ruleset.RulesetInfo);
|
bool isForCurrentRuleset = Beatmap.BeatmapInfo.Ruleset.Equals(Ruleset.RulesetInfo);
|
||||||
|
|
||||||
foreach (var p in ControlPoints)
|
foreach (var p in ControlPoints)
|
||||||
@ -76,7 +85,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
|
Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection);
|
||||||
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
|
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
|
||||||
|
|
||||||
Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange);
|
Config.BindWith(ManiaRulesetSetting.ScrollTime, configTimeRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AdjustScrollSpeed(int amount)
|
protected override void AdjustScrollSpeed(int amount)
|
||||||
@ -86,10 +95,19 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
private double relativeTimeRange
|
private double relativeTimeRange
|
||||||
{
|
{
|
||||||
get => MAX_TIME_RANGE / TimeRange.Value;
|
get => MAX_TIME_RANGE / configTimeRange.Value;
|
||||||
set => TimeRange.Value = MAX_TIME_RANGE / value;
|
set => configTimeRange.Value = MAX_TIME_RANGE / value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
updateTimeRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTimeRange() => TimeRange.Value = configTimeRange.Value * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the column that intersects a screen-space position.
|
/// Retrieves the column that intersects a screen-space position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
foreach (var column in stage.Columns)
|
foreach (var column in stage.Columns)
|
||||||
{
|
{
|
||||||
if (column.ReceivePositionalInputAt(screenSpacePosition))
|
if (column.ReceivePositionalInputAt(new Vector2(screenSpacePosition.X, column.ScreenSpaceDrawQuad.Centre.Y)))
|
||||||
{
|
{
|
||||||
found = column;
|
found = column;
|
||||||
break;
|
break;
|
||||||
@ -87,6 +87,31 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a <see cref="Column"/> by index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The index of the column.</param>
|
||||||
|
/// <returns>The <see cref="Column"/> corresponding to the given index.</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">If <paramref name="index"/> is less than 0 or greater than <see cref="TotalColumns"/>.</exception>
|
||||||
|
public Column GetColumn(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index > TotalColumns - 1)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
|
||||||
|
foreach (var stage in stages)
|
||||||
|
{
|
||||||
|
if (index >= stage.Columns.Count)
|
||||||
|
{
|
||||||
|
index -= stage.Columns.Count;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stage.Columns[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the total amount of columns across all stages in this playfield.
|
/// Retrieves the total amount of columns across all stages in this playfield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -399,7 +399,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public TestSlider()
|
public TestSlider()
|
||||||
{
|
{
|
||||||
DefaultsApplied += () =>
|
DefaultsApplied += _ =>
|
||||||
{
|
{
|
||||||
HeadCircle.HitWindows = new TestHitWindows();
|
HeadCircle.HitWindows = new TestHitWindows();
|
||||||
TailCircle.HitWindows = new TestHitWindows();
|
TailCircle.HitWindows = new TestHitWindows();
|
||||||
|
@ -259,6 +259,23 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
assertControlPointType(2, PathType.PerfectCurve);
|
assertControlPointType(2, PathType.PerfectCurve);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeginPlacementWithoutReleasingMouse()
|
||||||
|
{
|
||||||
|
addMovementStep(new Vector2(200));
|
||||||
|
AddStep("press left button", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
|
||||||
|
addMovementStep(new Vector2(400, 200));
|
||||||
|
AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
|
||||||
|
addClickStep(MouseButton.Right);
|
||||||
|
|
||||||
|
assertPlaced(true);
|
||||||
|
assertLength(200);
|
||||||
|
assertControlPointCount(2);
|
||||||
|
assertControlPointType(0, PathType.Linear);
|
||||||
|
}
|
||||||
|
|
||||||
private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position)));
|
private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position)));
|
||||||
|
|
||||||
private void addClickStep(MouseButton button)
|
private void addClickStep(MouseButton button)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -6,6 +6,7 @@ using osu.Game.Rulesets.Edit;
|
|||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
||||||
{
|
{
|
||||||
@ -28,16 +29,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
|||||||
circlePiece.UpdateFrom(HitObject);
|
circlePiece.UpdateFrom(HitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
if (e.Button == MouseButton.Left)
|
||||||
{
|
{
|
||||||
EndPlacement(true);
|
EndPlacement(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdatePosition(Vector2 screenSpacePosition)
|
return base.OnMouseDown(e);
|
||||||
{
|
}
|
||||||
BeginPlacement();
|
|
||||||
HitObject.Position = ToLocalSpace(screenSpacePosition);
|
public override void UpdatePosition(Vector2 screenSpacePosition) => HitObject.Position = ToLocalSpace(screenSpacePosition);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
|
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
|
||||||
|
|
||||||
public readonly BindableBool IsSelected = new BindableBool();
|
public readonly BindableBool IsSelected = new BindableBool();
|
||||||
|
|
||||||
public readonly PathControlPoint ControlPoint;
|
public readonly PathControlPoint ControlPoint;
|
||||||
|
|
||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
@ -146,6 +145,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
protected override bool OnDragStart(DragStartEvent e)
|
protected override bool OnDragStart(DragStartEvent e)
|
||||||
{
|
{
|
||||||
|
if (RequestSelection == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (e.Button == MouseButton.Left)
|
if (e.Button == MouseButton.Left)
|
||||||
{
|
{
|
||||||
changeHandler?.BeginChange();
|
changeHandler?.BeginChange();
|
||||||
|
@ -82,8 +82,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
|
if (e.Button != MouseButton.Left)
|
||||||
|
return base.OnMouseDown(e);
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case PlacementState.Initial:
|
case PlacementState.Initial:
|
||||||
@ -91,9 +94,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PlacementState.Body:
|
case PlacementState.Body:
|
||||||
if (e.Button != MouseButton.Left)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (canPlaceNewControlPoint(out var lastPoint))
|
if (canPlaceNewControlPoint(out var lastPoint))
|
||||||
{
|
{
|
||||||
// Place a new point by detatching the current cursor.
|
// Place a new point by detatching the current cursor.
|
||||||
@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
currentSegmentLength = 1;
|
currentSegmentLength = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
private void bindEvents(DrawableOsuHitObject drawableObject)
|
private void bindEvents(DrawableOsuHitObject drawableObject)
|
||||||
{
|
{
|
||||||
drawableObject.HitObject.PositionBindable.BindValueChanged(_ => scheduleRefresh());
|
drawableObject.HitObject.PositionBindable.BindValueChanged(_ => scheduleRefresh());
|
||||||
drawableObject.HitObject.DefaultsApplied += scheduleRefresh;
|
drawableObject.HitObject.DefaultsApplied += _ => scheduleRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleRefresh()
|
private void scheduleRefresh()
|
||||||
|
29
osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs
Normal file
29
osu.Game.Rulesets.Taiko.Tests/DrawableTestHit.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
|
{
|
||||||
|
internal class DrawableTestHit : DrawableTaikoHitObject
|
||||||
|
{
|
||||||
|
private readonly HitResult type;
|
||||||
|
|
||||||
|
public DrawableTestHit(Hit hit, HitResult type = HitResult.Great)
|
||||||
|
: base(hit)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Result.Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnPressed(TaikoAction action) => false;
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,111 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Taiko.Skinning;
|
||||||
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneDrawableBarLine : TaikoSkinnableTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
||||||
|
{
|
||||||
|
typeof(DrawableBarLine),
|
||||||
|
typeof(LegacyBarLine),
|
||||||
|
typeof(BarLine),
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
[Cached(typeof(IScrollingInfo))]
|
||||||
|
private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo
|
||||||
|
{
|
||||||
|
Direction = { Value = ScrollingDirection.Left },
|
||||||
|
TimeRange = { Value = 5000 },
|
||||||
|
};
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AddStep("Bar line", () => SetContents(() =>
|
||||||
|
{
|
||||||
|
ScrollingHitObjectContainer hoc;
|
||||||
|
|
||||||
|
var cont = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Height = 0.8f,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new TaikoPlayfield(new ControlPointInfo()),
|
||||||
|
hoc = new ScrollingHitObjectContainer()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
hoc.Add(new DrawableBarLine(createBarLineAtCurrentTime())
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
});
|
||||||
|
|
||||||
|
return cont;
|
||||||
|
}));
|
||||||
|
|
||||||
|
AddStep("Bar line (major)", () => SetContents(() =>
|
||||||
|
{
|
||||||
|
ScrollingHitObjectContainer hoc;
|
||||||
|
|
||||||
|
var cont = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Height = 0.8f,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new TaikoPlayfield(new ControlPointInfo()),
|
||||||
|
hoc = new ScrollingHitObjectContainer()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
hoc.Add(new DrawableBarLineMajor(createBarLineAtCurrentTime(true))
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
});
|
||||||
|
|
||||||
|
return cont;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BarLine createBarLineAtCurrentTime(bool major = false)
|
||||||
|
{
|
||||||
|
var barline = new BarLine
|
||||||
|
{
|
||||||
|
Major = major,
|
||||||
|
StartTime = Time.Current + 2000,
|
||||||
|
};
|
||||||
|
|
||||||
|
var cpi = new ControlPointInfo();
|
||||||
|
cpi.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
|
||||||
|
barline.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
|
return barline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,8 +21,6 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
||||||
{
|
{
|
||||||
typeof(DrawableHit),
|
typeof(DrawableHit),
|
||||||
typeof(DrawableCentreHit),
|
|
||||||
typeof(DrawableRimHit),
|
|
||||||
typeof(LegacyHit),
|
typeof(LegacyHit),
|
||||||
typeof(LegacyCirclePiece),
|
typeof(LegacyCirclePiece),
|
||||||
}).ToList();
|
}).ToList();
|
||||||
@ -30,25 +28,25 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
AddStep("Centre hit", () => SetContents(() => new DrawableCentreHit(createHitAtCurrentTime())
|
AddStep("Centre hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Centre hit (strong)", () => SetContents(() => new DrawableCentreHit(createHitAtCurrentTime(true))
|
AddStep("Centre hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true))
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Rim hit", () => SetContents(() => new DrawableRimHit(createHitAtCurrentTime())
|
AddStep("Rim hit", () => SetContents(() => new DrawableHit(createHitAtCurrentTime())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("Rim hit (strong)", () => SetContents(() => new DrawableRimHit(createHitAtCurrentTime(true))
|
AddStep("Rim hit (strong)", () => SetContents(() => new DrawableHit(createHitAtCurrentTime(true))
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Taiko.Skinning;
|
||||||
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneHitExplosion : TaikoSkinnableTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
||||||
|
{
|
||||||
|
typeof(HitExplosion),
|
||||||
|
typeof(LegacyHitExplosion),
|
||||||
|
typeof(DefaultHitExplosion),
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AddStep("Great", () => SetContents(() => getContentFor(HitResult.Great)));
|
||||||
|
AddStep("Good", () => SetContents(() => getContentFor(HitResult.Good)));
|
||||||
|
AddStep("Miss", () => SetContents(() => getContentFor(HitResult.Miss)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable getContentFor(HitResult type)
|
||||||
|
{
|
||||||
|
DrawableTaikoHitObject hit;
|
||||||
|
|
||||||
|
return new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
hit = createHit(type),
|
||||||
|
new HitExplosion(hit)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private DrawableTaikoHitObject createHit(HitResult type) => new DrawableTestHit(new Hit { StartTime = Time.Current }, type);
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,11 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Taiko.Beatmaps;
|
||||||
using osu.Game.Rulesets.Taiko.Skinning;
|
using osu.Game.Rulesets.Taiko.Skinning;
|
||||||
using osu.Game.Rulesets.Taiko.UI;
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
@ -18,8 +21,9 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[]
|
||||||
{
|
{
|
||||||
typeof(HitTarget),
|
typeof(TaikoHitTarget),
|
||||||
typeof(LegacyHitTarget),
|
typeof(TaikoLegacyHitTarget),
|
||||||
|
typeof(PlayfieldBackgroundRight),
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
[Cached(typeof(IScrollingInfo))]
|
[Cached(typeof(IScrollingInfo))]
|
||||||
@ -31,12 +35,30 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
|
|
||||||
public TestSceneTaikoPlayfield()
|
public TestSceneTaikoPlayfield()
|
||||||
{
|
{
|
||||||
|
TaikoBeatmap beatmap;
|
||||||
|
bool kiai = false;
|
||||||
|
|
||||||
|
AddStep("set beatmap", () =>
|
||||||
|
{
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(beatmap = new TaikoBeatmap());
|
||||||
|
|
||||||
|
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 });
|
||||||
|
|
||||||
|
Beatmap.Value.Track.Start();
|
||||||
|
});
|
||||||
|
|
||||||
AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo())
|
AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo())
|
||||||
{
|
{
|
||||||
Height = 0.4f,
|
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
AddRepeatStep("change height", () => this.ChildrenOfType<TaikoPlayfield>().ForEach(p => p.Height = Math.Max(0.2f, (p.Height + 0.2f) % 1f)), 50);
|
||||||
|
|
||||||
|
AddStep("Toggle kiai", () =>
|
||||||
|
{
|
||||||
|
Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new EffectControlPoint { KiaiMode = (kiai = !kiai) });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneHits : OsuTestScene
|
public class TestSceneHits : OsuTestScene
|
||||||
{
|
{
|
||||||
private const double default_duration = 1000;
|
private const double default_duration = 3000;
|
||||||
private const float scroll_time = 1000;
|
private const float scroll_time = 1000;
|
||||||
|
|
||||||
protected override double TimePerAction => default_duration * 2;
|
protected override double TimePerAction => default_duration * 2;
|
||||||
@ -45,6 +45,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
AddStep("Miss :(", addMissJudgement);
|
AddStep("Miss :(", addMissJudgement);
|
||||||
AddStep("DrumRoll", () => addDrumRoll(false));
|
AddStep("DrumRoll", () => addDrumRoll(false));
|
||||||
AddStep("Strong DrumRoll", () => addDrumRoll(true));
|
AddStep("Strong DrumRoll", () => addDrumRoll(true));
|
||||||
|
AddStep("Kiai DrumRoll", () => addDrumRoll(true, kiai: true));
|
||||||
AddStep("Swell", () => addSwell());
|
AddStep("Swell", () => addSwell());
|
||||||
AddStep("Centre", () => addCentreHit(false));
|
AddStep("Centre", () => addCentreHit(false));
|
||||||
AddStep("Strong Centre", () => addCentreHit(true));
|
AddStep("Strong Centre", () => addCentreHit(true));
|
||||||
@ -148,6 +149,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
|
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
|
||||||
|
|
||||||
|
Add(h);
|
||||||
|
|
||||||
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
|
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,6 +166,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
|
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
|
||||||
|
|
||||||
|
Add(h);
|
||||||
|
|
||||||
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
|
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(h, new JudgementResult(new HitObject(), new TaikoJudgement()) { Type = hitResult });
|
||||||
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great });
|
((TaikoPlayfield)drawableRuleset.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new HitObject(), new TaikoStrongJudgement()) { Type = HitResult.Great });
|
||||||
}
|
}
|
||||||
@ -192,7 +197,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
drawableRuleset.Playfield.Add(new DrawableSwell(swell));
|
drawableRuleset.Playfield.Add(new DrawableSwell(swell));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addDrumRoll(bool strong, double duration = default_duration)
|
private void addDrumRoll(bool strong, double duration = default_duration, bool kiai = false)
|
||||||
{
|
{
|
||||||
addBarLine(true);
|
addBarLine(true);
|
||||||
addBarLine(true, scroll_time + duration);
|
addBarLine(true, scroll_time + duration);
|
||||||
@ -202,9 +207,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
StartTime = drawableRuleset.Playfield.Time.Current + scroll_time,
|
StartTime = drawableRuleset.Playfield.Time.Current + scroll_time,
|
||||||
IsStrong = strong,
|
IsStrong = strong,
|
||||||
Duration = duration,
|
Duration = duration,
|
||||||
|
TickRate = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
d.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
var cpi = new ControlPointInfo();
|
||||||
|
cpi.Add(-10000, new EffectControlPoint { KiaiMode = kiai });
|
||||||
|
|
||||||
|
d.ApplyDefaults(cpi, new BeatmapDifficulty());
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(new DrawableDrumRoll(d));
|
drawableRuleset.Playfield.Add(new DrawableDrumRoll(d));
|
||||||
}
|
}
|
||||||
@ -219,7 +228,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(new DrawableCentreHit(h));
|
drawableRuleset.Playfield.Add(new DrawableHit(h));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addRimHit(bool strong)
|
private void addRimHit(bool strong)
|
||||||
@ -232,7 +241,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(new DrawableRimHit(h));
|
drawableRuleset.Playfield.Add(new DrawableHit(h));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestStrongNestedHit : DrawableStrongNestedHit
|
private class TestStrongNestedHit : DrawableStrongNestedHit
|
||||||
@ -244,13 +253,5 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
public override bool OnPressed(TaikoAction action) => false;
|
public override bool OnPressed(TaikoAction action) => false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DrawableTestHit : DrawableHitObject<TaikoHitObject>
|
|
||||||
{
|
|
||||||
public DrawableTestHit(TaikoHitObject hitObject)
|
|
||||||
: base(hitObject)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
|
||||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
/// osu! is generally slower than taiko, so a factor is added to increase
|
/// osu! is generally slower than taiko, so a factor is added to increase
|
||||||
/// speed. This must be used everywhere slider length or beat length is used.
|
/// speed. This must be used everywhere slider length or beat length is used.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float legacy_velocity_multiplier = 1.4f;
|
public const float LEGACY_VELOCITY_MULTIPLIER = 1.4f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Because swells are easier in taiko than spinners are in osu!,
|
/// Because swells are easier in taiko than spinners are in osu!,
|
||||||
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
// Rewrite the beatmap info to add the slider velocity multiplier
|
// Rewrite the beatmap info to add the slider velocity multiplier
|
||||||
original.BeatmapInfo = original.BeatmapInfo.Clone();
|
original.BeatmapInfo = original.BeatmapInfo.Clone();
|
||||||
original.BeatmapInfo.BaseDifficulty = original.BeatmapInfo.BaseDifficulty.Clone();
|
original.BeatmapInfo.BaseDifficulty = original.BeatmapInfo.BaseDifficulty.Clone();
|
||||||
original.BeatmapInfo.BaseDifficulty.SliderMultiplier *= legacy_velocity_multiplier;
|
original.BeatmapInfo.BaseDifficulty.SliderMultiplier *= LEGACY_VELOCITY_MULTIPLIER;
|
||||||
|
|
||||||
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original);
|
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original);
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;
|
double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;
|
||||||
|
|
||||||
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
|
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
|
||||||
double distance = distanceData.Distance * spans * legacy_velocity_multiplier;
|
double distance = distanceData.Distance * spans * LEGACY_VELOCITY_MULTIPLIER;
|
||||||
|
|
||||||
// The velocity of the taiko hit object - calculated as the velocity of a drum roll
|
// The velocity of the taiko hit object - calculated as the velocity of a drum roll
|
||||||
double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength;
|
double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength;
|
||||||
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -27,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The visual line tracker.
|
/// The visual line tracker.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Box Tracker;
|
protected SkinnableDrawable Line;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The bar line.
|
/// The bar line.
|
||||||
@ -45,13 +46,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
Width = tracker_width;
|
Width = tracker_width;
|
||||||
|
|
||||||
AddInternal(Tracker = new Box
|
AddInternal(Line = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.BarLine), _ => new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
EdgeSmoothness = new Vector2(0.5f, 0),
|
||||||
|
})
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
Alpha = 0.75f,
|
||||||
EdgeSmoothness = new Vector2(0.5f, 0),
|
|
||||||
Alpha = 0.75f
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Tracker.Alpha = 1f;
|
Line.Alpha = 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -1,21 +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.Taiko.Objects.Drawables.Pieces;
|
|
||||||
using osu.Game.Skinning;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|
||||||
{
|
|
||||||
public class DrawableCentreHit : DrawableHit
|
|
||||||
{
|
|
||||||
public override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
|
|
||||||
|
|
||||||
public DrawableCentreHit(Hit hit)
|
|
||||||
: base(hit)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.CentreHit),
|
|
||||||
_ => new CentreHitCirclePiece(), confineMode: ConfineMode.ScaleToFit);
|
|
||||||
}
|
|
||||||
}
|
|
@ -121,8 +121,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
int countHit = NestedHitObjects.Count(o => o.IsHit);
|
int countHit = NestedHitObjects.Count(o => o.IsHit);
|
||||||
|
|
||||||
if (countHit >= HitObject.RequiredGoodHits)
|
if (countHit >= HitObject.RequiredGoodHits)
|
||||||
|
{
|
||||||
ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good);
|
ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ApplyResult(r => r.Type = HitResult.Miss);
|
ApplyResult(r => r.Type = HitResult.Miss);
|
||||||
}
|
}
|
||||||
|
@ -12,14 +12,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public class DrawableDrumRollTick : DrawableTaikoHitObject<DrumRollTick>
|
public class DrawableDrumRollTick : DrawableTaikoHitObject<DrumRollTick>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The hit type corresponding to the <see cref="TaikoAction"/> that the user pressed to hit this <see cref="DrawableDrumRollTick"/>.
|
||||||
|
/// </summary>
|
||||||
|
public HitType JudgementType;
|
||||||
|
|
||||||
public DrawableDrumRollTick(DrumRollTick tick)
|
public DrawableDrumRollTick(DrumRollTick tick)
|
||||||
: base(tick)
|
: base(tick)
|
||||||
{
|
{
|
||||||
FillMode = FillMode.Fit;
|
FillMode = FillMode.Fit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool DisplayResult => false;
|
|
||||||
|
|
||||||
protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollTick),
|
protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollTick),
|
||||||
_ => new TickPiece
|
_ => new TickPiece
|
||||||
{
|
{
|
||||||
@ -51,7 +54,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool OnPressed(TaikoAction action) => UpdateResult(true);
|
public override bool OnPressed(TaikoAction action)
|
||||||
|
{
|
||||||
|
JudgementType = action == TaikoAction.LeftRim || action == TaikoAction.RightRim ? HitType.Rim : HitType.Centre;
|
||||||
|
return UpdateResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this);
|
protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this);
|
||||||
|
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
// 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.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A hit used specifically for drum rolls, where spawning flying hits is required.
|
||||||
|
/// </summary>
|
||||||
|
public class DrawableFlyingHit : DrawableHit
|
||||||
|
{
|
||||||
|
public DrawableFlyingHit(DrawableDrumRollTick drumRollTick)
|
||||||
|
: base(new IgnoreHit
|
||||||
|
{
|
||||||
|
StartTime = drumRollTick.HitObject.StartTime + drumRollTick.Result.TimeOffset,
|
||||||
|
IsStrong = drumRollTick.HitObject.IsStrong,
|
||||||
|
Type = drumRollTick.JudgementType
|
||||||
|
})
|
||||||
|
{
|
||||||
|
HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,31 +8,45 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||||
{
|
{
|
||||||
public abstract class DrawableHit : DrawableTaikoHitObject<Hit>
|
public class DrawableHit : DrawableTaikoHitObject<Hit>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A list of keys which can result in hits for this HitObject.
|
/// A list of keys which can result in hits for this HitObject.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract TaikoAction[] HitActions { get; }
|
public TaikoAction[] HitActions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The action that caused this <see cref="DrawableHit"/> to be hit.
|
/// The action that caused this <see cref="DrawableHit"/> to be hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TaikoAction? HitAction { get; private set; }
|
public TaikoAction? HitAction
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
private bool validActionPressed;
|
private bool validActionPressed;
|
||||||
|
|
||||||
private bool pressHandledThisFrame;
|
private bool pressHandledThisFrame;
|
||||||
|
|
||||||
protected DrawableHit(Hit hit)
|
public DrawableHit(Hit hit)
|
||||||
: base(hit)
|
: base(hit)
|
||||||
{
|
{
|
||||||
FillMode = FillMode.Fit;
|
FillMode = FillMode.Fit;
|
||||||
|
|
||||||
|
HitActions =
|
||||||
|
HitObject.Type == HitType.Centre
|
||||||
|
? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre }
|
||||||
|
: new[] { TaikoAction.LeftRim, TaikoAction.RightRim };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override SkinnableDrawable CreateMainPiece() => HitObject.Type == HitType.Centre
|
||||||
|
? new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.CentreHit), _ => new CentreHitCirclePiece(), confineMode: ConfineMode.ScaleToFit)
|
||||||
|
: new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.RimHit), _ => new RimHitCirclePiece(), confineMode: ConfineMode.ScaleToFit);
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
Debug.Assert(HitObject.HitWindows != null);
|
Debug.Assert(HitObject.HitWindows != null);
|
||||||
@ -58,7 +72,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
if (pressHandledThisFrame)
|
if (pressHandledThisFrame)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (Judged)
|
if (Judged)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -66,14 +79,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
|
|
||||||
// Only count this as handled if the new judgement is a hit
|
// Only count this as handled if the new judgement is a hit
|
||||||
var result = UpdateResult(true);
|
var result = UpdateResult(true);
|
||||||
|
|
||||||
if (IsHit)
|
if (IsHit)
|
||||||
HitAction = action;
|
HitAction = action;
|
||||||
|
|
||||||
// Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded
|
// Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded
|
||||||
// E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note
|
// E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note
|
||||||
pressHandledThisFrame = true;
|
pressHandledThisFrame = true;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +92,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
if (action == HitAction)
|
if (action == HitAction)
|
||||||
HitAction = null;
|
HitAction = null;
|
||||||
|
|
||||||
base.OnReleased(action);
|
base.OnReleased(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,8 +102,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
// The input manager processes all input prior to us updating, so this is the perfect time
|
// The input manager processes all input prior to us updating, so this is the perfect time
|
||||||
// for us to remove the extra press blocking, before input is handled in the next frame
|
// for us to remove the extra press blocking, before input is handled in the next frame
|
||||||
pressHandledThisFrame = false;
|
pressHandledThisFrame = false;
|
||||||
|
|
||||||
Size = BaseSize * Parent.RelativeChildSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateStateTransforms(ArmedState state)
|
protected override void UpdateStateTransforms(ArmedState state)
|
||||||
|
@ -1,21 +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.Taiko.Objects.Drawables.Pieces;
|
|
||||||
using osu.Game.Skinning;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|
||||||
{
|
|
||||||
public class DrawableRimHit : DrawableHit
|
|
||||||
{
|
|
||||||
public override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
|
|
||||||
|
|
||||||
public DrawableRimHit(Hit hit)
|
|
||||||
: base(hit)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.RimHit),
|
|
||||||
_ => new RimHitCirclePiece(), confineMode: ConfineMode.ScaleToFit);
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,15 +3,20 @@
|
|||||||
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.Taiko.Beatmaps;
|
||||||
using osu.Game.Rulesets.Taiko.Judgements;
|
using osu.Game.Rulesets.Taiko.Judgements;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Objects
|
namespace osu.Game.Rulesets.Taiko.Objects
|
||||||
{
|
{
|
||||||
public class DrumRoll : TaikoHitObject, IHasEndTime
|
public class DrumRoll : TaikoHitObject, IHasCurve
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
|
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
|
||||||
@ -26,6 +31,11 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
|
|
||||||
public double Duration { get; set; }
|
public double Duration { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Velocity of this <see cref="DrumRoll"/>.
|
||||||
|
/// </summary>
|
||||||
|
public double Velocity { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Numer of ticks per beat length.
|
/// Numer of ticks per beat length.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -54,6 +64,10 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||||
|
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
|
||||||
|
|
||||||
|
double scoringDistance = base_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
|
||||||
|
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||||
|
|
||||||
tickSpacing = timingPoint.BeatLength / TickRate;
|
tickSpacing = timingPoint.BeatLength / TickRate;
|
||||||
overallDifficulty = difficulty.OverallDifficulty;
|
overallDifficulty = difficulty.OverallDifficulty;
|
||||||
@ -93,5 +107,18 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
public override Judgement CreateJudgement() => new TaikoDrumRollJudgement();
|
public override Judgement CreateJudgement() => new TaikoDrumRollJudgement();
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||||
|
|
||||||
|
#region LegacyBeatmapEncoder
|
||||||
|
|
||||||
|
double IHasDistance.Distance => Duration * Velocity;
|
||||||
|
|
||||||
|
int IHasRepeats.RepeatCount { get => 0; set { } }
|
||||||
|
|
||||||
|
List<IList<HitSampleInfo>> IHasRepeats.NodeSamples => new List<IList<HitSampleInfo>>();
|
||||||
|
|
||||||
|
SliderPath IHasCurve.Path
|
||||||
|
=> new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
osu.Game.Rulesets.Taiko/Objects/IgnoreHit.cs
Normal file
12
osu.Game.Rulesets.Taiko/Objects/IgnoreHit.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// 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.Judgements;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Objects
|
||||||
|
{
|
||||||
|
public class IgnoreHit : Hit
|
||||||
|
{
|
||||||
|
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
||||||
|
}
|
||||||
|
}
|
27
osu.Game.Rulesets.Taiko/Skinning/LegacyBarLine.cs
Normal file
27
osu.Game.Rulesets.Taiko/Skinning/LegacyBarLine.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Skinning
|
||||||
|
{
|
||||||
|
public class LegacyBarLine : Sprite
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin)
|
||||||
|
{
|
||||||
|
Texture = skin.GetTexture("taiko-barline");
|
||||||
|
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Size = new Vector2(1, 0.88f);
|
||||||
|
FillMode = FillMode.Fill;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs
Normal file
37
osu.Game.Rulesets.Taiko/Skinning/LegacyHitExplosion.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// 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;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Skinning
|
||||||
|
{
|
||||||
|
public class LegacyHitExplosion : CompositeDrawable
|
||||||
|
{
|
||||||
|
public LegacyHitExplosion(Drawable sprite)
|
||||||
|
{
|
||||||
|
InternalChild = sprite;
|
||||||
|
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
const double animation_time = 120;
|
||||||
|
|
||||||
|
this.FadeInFromZero(animation_time).Then().FadeOut(animation_time * 1.5);
|
||||||
|
|
||||||
|
this.ScaleTo(0.6f)
|
||||||
|
.Then().ScaleTo(1.1f, animation_time * 0.8)
|
||||||
|
.Then().ScaleTo(0.9f, animation_time * 0.4)
|
||||||
|
.Then().ScaleTo(1f, animation_time * 0.2);
|
||||||
|
|
||||||
|
Expire(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,41 +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.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
|
||||||
using osu.Game.Skinning;
|
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Skinning
|
|
||||||
{
|
|
||||||
public class LegacyHitTarget : CompositeDrawable
|
|
||||||
{
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(ISkinSource skin)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
|
||||||
new Sprite
|
|
||||||
{
|
|
||||||
Texture = skin.GetTexture("approachcircle"),
|
|
||||||
Scale = new Vector2(0.73f),
|
|
||||||
Alpha = 0.7f,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
},
|
|
||||||
new Sprite
|
|
||||||
{
|
|
||||||
Texture = skin.GetTexture("taikobigcircle"),
|
|
||||||
Scale = new Vector2(0.7f),
|
|
||||||
Alpha = 0.5f,
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,15 +20,19 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
{
|
{
|
||||||
private LegacyHalfDrum left;
|
private LegacyHalfDrum left;
|
||||||
private LegacyHalfDrum right;
|
private LegacyHalfDrum right;
|
||||||
|
private Container content;
|
||||||
|
|
||||||
public LegacyInputDrum()
|
public LegacyInputDrum()
|
||||||
{
|
{
|
||||||
Size = new Vector2(180, 200);
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin)
|
private void load(ISkinSource skin)
|
||||||
{
|
{
|
||||||
|
Child = content = new Container
|
||||||
|
{
|
||||||
|
Size = new Vector2(180, 200),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Sprite
|
new Sprite
|
||||||
@ -51,6 +55,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
RimAction = TaikoAction.RightRim,
|
RimAction = TaikoAction.RightRim,
|
||||||
CentreAction = TaikoAction.RightCentre
|
CentreAction = TaikoAction.RightCentre
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// this will be used in the future for stable skin alignment. keeping here for reference.
|
// this will be used in the future for stable skin alignment. keeping here for reference.
|
||||||
@ -60,7 +65,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
const float ratio = 1.6f;
|
const float ratio = 1.6f;
|
||||||
|
|
||||||
// because the right half is flipped, we need to position using width - position to get the true "topleft" origin position
|
// because the right half is flipped, we need to position using width - position to get the true "topleft" origin position
|
||||||
float negativeScaleAdjust = Width / ratio;
|
float negativeScaleAdjust = content.Width / ratio;
|
||||||
|
|
||||||
if (skin.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.1m)
|
if (skin.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.1m)
|
||||||
{
|
{
|
||||||
@ -78,6 +83,15 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
// Relying on RelativeSizeAxes.Both + FillMode.Fit doesn't work due to the precise pixel layout requirements.
|
||||||
|
// This is a bit ugly but makes the non-legacy implementations a lot cleaner to implement.
|
||||||
|
content.Scale = new Vector2(DrawHeight / content.Size.Y);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A half-drum. Contains one centre and one rim hit.
|
/// A half-drum. Contains one centre and one rim hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
59
osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs
Normal file
59
osu.Game.Rulesets.Taiko/Skinning/TaikoLegacyHitTarget.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets.Taiko.UI;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Skinning
|
||||||
|
{
|
||||||
|
public class TaikoLegacyHitTarget : CompositeDrawable
|
||||||
|
{
|
||||||
|
private Container content;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = content = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Sprite
|
||||||
|
{
|
||||||
|
Texture = skin.GetTexture("approachcircle"),
|
||||||
|
Scale = new Vector2(0.73f),
|
||||||
|
Alpha = 0.47f, // eyeballed to match stable
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
},
|
||||||
|
new Sprite
|
||||||
|
{
|
||||||
|
Texture = skin.GetTexture("taikobigcircle"),
|
||||||
|
Scale = new Vector2(0.7f),
|
||||||
|
Alpha = 0.22f, // eyeballed to match stable
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
// Relying on RelativeSizeAxes.Both + FillMode.Fit doesn't work due to the precise pixel layout requirements.
|
||||||
|
// This is a bit ugly but makes the non-legacy implementations a lot cleaner to implement.
|
||||||
|
content.Scale = new Vector2(DrawHeight / TaikoPlayfield.DEFAULT_HEIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Skinning
|
||||||
|
{
|
||||||
|
public class TaikoLegacyPlayfieldBackgroundRight : BeatSyncedContainer
|
||||||
|
{
|
||||||
|
private Sprite kiai;
|
||||||
|
|
||||||
|
private bool kiaiDisplayed;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Sprite
|
||||||
|
{
|
||||||
|
Texture = skin.GetTexture("taiko-bar-right"),
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Size = Vector2.One,
|
||||||
|
},
|
||||||
|
kiai = new Sprite
|
||||||
|
{
|
||||||
|
Texture = skin.GetTexture("taiko-bar-right-glow"),
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Size = Vector2.One,
|
||||||
|
Alpha = 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
|
||||||
|
{
|
||||||
|
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
|
||||||
|
|
||||||
|
if (effectPoint.KiaiMode != kiaiDisplayed)
|
||||||
|
{
|
||||||
|
kiaiDisplayed = effectPoint.KiaiMode;
|
||||||
|
|
||||||
|
kiai.ClearTransforms();
|
||||||
|
kiai.FadeTo(kiaiDisplayed ? 1 : 0, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -52,7 +53,36 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
|
|
||||||
case TaikoSkinComponents.HitTarget:
|
case TaikoSkinComponents.HitTarget:
|
||||||
if (GetTexture("taikobigcircle") != null)
|
if (GetTexture("taikobigcircle") != null)
|
||||||
return new LegacyHitTarget();
|
return new TaikoLegacyHitTarget();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case TaikoSkinComponents.PlayfieldBackgroundRight:
|
||||||
|
if (GetTexture("taiko-bar-right") != null)
|
||||||
|
return new TaikoLegacyPlayfieldBackgroundRight();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case TaikoSkinComponents.PlayfieldBackgroundLeft:
|
||||||
|
// This is displayed inside LegacyInputDrum. It is required to be there for layout purposes (can be seen on legacy skins).
|
||||||
|
if (GetTexture("taiko-bar-right") != null)
|
||||||
|
return Drawable.Empty();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case TaikoSkinComponents.BarLine:
|
||||||
|
if (GetTexture("taiko-barline") != null)
|
||||||
|
return new LegacyBarLine();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case TaikoSkinComponents.TaikoExplosionGood:
|
||||||
|
case TaikoSkinComponents.TaikoExplosionGreat:
|
||||||
|
case TaikoSkinComponents.TaikoExplosionMiss:
|
||||||
|
|
||||||
|
var sprite = this.GetAnimation(getHitName(taikoComponent.Component), true, false);
|
||||||
|
if (sprite != null)
|
||||||
|
return new LegacyHitExplosion(sprite);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -60,6 +90,23 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
return source.GetDrawableComponent(component);
|
return source.GetDrawableComponent(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string getHitName(TaikoSkinComponents component)
|
||||||
|
{
|
||||||
|
switch (component)
|
||||||
|
{
|
||||||
|
case TaikoSkinComponents.TaikoExplosionMiss:
|
||||||
|
return "taiko-hit0";
|
||||||
|
|
||||||
|
case TaikoSkinComponents.TaikoExplosionGood:
|
||||||
|
return "taiko-hit100";
|
||||||
|
|
||||||
|
case TaikoSkinComponents.TaikoExplosionGreat:
|
||||||
|
return "taiko-hit300";
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(component), "Invalid result type");
|
||||||
|
}
|
||||||
|
|
||||||
public Texture GetTexture(string componentName) => source.GetTexture(componentName);
|
public Texture GetTexture(string componentName) => source.GetTexture(componentName);
|
||||||
|
|
||||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => source.GetSample(new LegacyTaikoSampleInfo(sampleInfo));
|
public SampleChannel GetSample(ISampleInfo sampleInfo) => source.GetSample(new LegacyTaikoSampleInfo(sampleInfo));
|
||||||
|
@ -11,6 +11,12 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
DrumRollBody,
|
DrumRollBody,
|
||||||
DrumRollTick,
|
DrumRollTick,
|
||||||
Swell,
|
Swell,
|
||||||
HitTarget
|
HitTarget,
|
||||||
|
PlayfieldBackgroundLeft,
|
||||||
|
PlayfieldBackgroundRight,
|
||||||
|
BarLine,
|
||||||
|
TaikoExplosionMiss,
|
||||||
|
TaikoExplosionGood,
|
||||||
|
TaikoExplosionGreat,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
59
osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs
Normal file
59
osu.Game.Rulesets.Taiko/UI/DefaultHitExplosion.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
|
{
|
||||||
|
internal class DefaultHitExplosion : CircularContainer
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private DrawableHitObject judgedObject { get; set; }
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
BorderColour = Color4.White;
|
||||||
|
BorderThickness = 1;
|
||||||
|
|
||||||
|
Blending = BlendingParameters.Additive;
|
||||||
|
|
||||||
|
Alpha = 0.15f;
|
||||||
|
Masking = true;
|
||||||
|
|
||||||
|
if (judgedObject.Result.Type == HitResult.Miss)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
|
||||||
|
|
||||||
|
InternalChildren = new[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = isRim ? colours.BlueDarker : colours.PinkDarker,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
this.ScaleTo(3f, 1000, Easing.OutQuint);
|
||||||
|
this.FadeOut(500);
|
||||||
|
|
||||||
|
Expire(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -49,10 +49,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
switch (h)
|
switch (h)
|
||||||
{
|
{
|
||||||
case Hit hit:
|
case Hit hit:
|
||||||
if (hit.Type == HitType.Centre)
|
return new DrawableHit(hit);
|
||||||
return new DrawableCentreHit(hit);
|
|
||||||
else
|
|
||||||
return new DrawableRimHit(hit);
|
|
||||||
|
|
||||||
case DrumRoll drumRoll:
|
case DrumRoll drumRoll:
|
||||||
return new DrawableDrumRoll(drumRoll);
|
return new DrawableDrumRoll(drumRoll);
|
||||||
|
35
osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs
Normal file
35
osu.Game.Rulesets.Taiko/UI/DrumRollHitContainer.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// 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.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
|
{
|
||||||
|
internal class DrumRollHitContainer : ScrollingHitObjectContainer
|
||||||
|
{
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
// Remove any auxiliary hit notes that were spawned during a drum roll but subsequently rewound.
|
||||||
|
for (var i = AliveInternalChildren.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var flyingHit = (DrawableFlyingHit)AliveInternalChildren[i];
|
||||||
|
if (Time.Current <= flyingHit.HitObject.StartTime)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,15 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.UI
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
{
|
{
|
||||||
@ -20,55 +20,49 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
public override bool RemoveWhenNotAlive => true;
|
public override bool RemoveWhenNotAlive => true;
|
||||||
|
|
||||||
|
[Cached(typeof(DrawableHitObject))]
|
||||||
public readonly DrawableHitObject JudgedObject;
|
public readonly DrawableHitObject JudgedObject;
|
||||||
|
|
||||||
private readonly Box innerFill;
|
private SkinnableDrawable skinnable;
|
||||||
|
|
||||||
private readonly bool isRim;
|
public override double LifetimeStart => skinnable.Drawable.LifetimeStart;
|
||||||
|
|
||||||
public HitExplosion(DrawableHitObject judgedObject, bool isRim)
|
public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd;
|
||||||
|
|
||||||
|
public HitExplosion(DrawableHitObject judgedObject)
|
||||||
{
|
{
|
||||||
this.isRim = isRim;
|
|
||||||
|
|
||||||
JudgedObject = judgedObject;
|
JudgedObject = judgedObject;
|
||||||
|
|
||||||
Anchor = Anchor.CentreLeft;
|
Anchor = Anchor.Centre;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE);
|
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE);
|
||||||
|
|
||||||
RelativePositionAxes = Axes.Both;
|
RelativePositionAxes = Axes.Both;
|
||||||
|
|
||||||
BorderColour = Color4.White;
|
|
||||||
BorderThickness = 1;
|
|
||||||
|
|
||||||
Alpha = 0.15f;
|
|
||||||
Masking = true;
|
|
||||||
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
innerFill = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load()
|
||||||
{
|
{
|
||||||
innerFill.Colour = isRim ? colours.BlueDarker : colours.PinkDarker;
|
Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(getComponentName(JudgedObject.Result?.Type ?? HitResult.Great)), _ => new DefaultHitExplosion());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
private TaikoSkinComponents getComponentName(HitResult resultType)
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
switch (resultType)
|
||||||
|
{
|
||||||
|
case HitResult.Miss:
|
||||||
|
return TaikoSkinComponents.TaikoExplosionMiss;
|
||||||
|
|
||||||
this.ScaleTo(3f, 1000, Easing.OutQuint);
|
case HitResult.Good:
|
||||||
this.FadeOut(500);
|
return TaikoSkinComponents.TaikoExplosionGood;
|
||||||
|
|
||||||
Expire(true);
|
case HitResult.Great:
|
||||||
|
return TaikoSkinComponents.TaikoExplosionGreat;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(resultType), "Invalid result type");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -31,7 +31,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
sampleMapping = new DrumSampleMapping(controlPoints);
|
sampleMapping = new DrumSampleMapping(controlPoints);
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
FillMode = FillMode.Fit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -40,6 +39,8 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
Child = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.InputDrum), _ => new Container
|
Child = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.InputDrum), _ => new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
FillMode = FillMode.Fit,
|
||||||
|
Scale = new Vector2(0.9f),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new TaikoHalfDrum(false)
|
new TaikoHalfDrum(false)
|
||||||
|
@ -18,14 +18,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
public override bool RemoveWhenNotAlive => true;
|
public override bool RemoveWhenNotAlive => true;
|
||||||
|
|
||||||
public readonly DrawableHitObject JudgedObject;
|
public readonly DrawableHitObject JudgedObject;
|
||||||
|
private readonly HitType type;
|
||||||
|
|
||||||
private readonly bool isRim;
|
public KiaiHitExplosion(DrawableHitObject judgedObject, HitType type)
|
||||||
|
|
||||||
public KiaiHitExplosion(DrawableHitObject judgedObject, bool isRim)
|
|
||||||
{
|
{
|
||||||
this.isRim = isRim;
|
|
||||||
|
|
||||||
JudgedObject = judgedObject;
|
JudgedObject = judgedObject;
|
||||||
|
this.type = type;
|
||||||
|
|
||||||
Anchor = Anchor.CentreLeft;
|
Anchor = Anchor.CentreLeft;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
@ -33,6 +31,8 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1);
|
Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1);
|
||||||
|
|
||||||
|
Blending = BlendingParameters.Additive;
|
||||||
|
|
||||||
Masking = true;
|
Masking = true;
|
||||||
Alpha = 0.25f;
|
Alpha = 0.25f;
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Glow,
|
Type = EdgeEffectType.Glow,
|
||||||
Colour = isRim ? colours.BlueDarker : colours.PinkDarker,
|
Colour = type == HitType.Rim ? colours.BlueDarker : colours.PinkDarker,
|
||||||
Radius = 60,
|
Radius = 60,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
37
osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundLeft.cs
Normal file
37
osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundLeft.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
|
{
|
||||||
|
internal class PlayfieldBackgroundLeft : CompositeDrawable
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colours.Gray1,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = 10,
|
||||||
|
Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundRight.cs
Normal file
61
osu.Game.Rulesets.Taiko/UI/PlayfieldBackgroundRight.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.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
|
{
|
||||||
|
public class PlayfieldBackgroundRight : CompositeDrawable
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Name = "Transparent playfield background";
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Masking = true;
|
||||||
|
BorderColour = colours.Gray1;
|
||||||
|
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Colour = Color4.Black.Opacity(0.2f),
|
||||||
|
Radius = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colours.Gray0,
|
||||||
|
Alpha = 0.6f
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Name = "Border",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
MaskingSmoothness = 0,
|
||||||
|
BorderThickness = 2,
|
||||||
|
AlwaysPresent = true,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
AlwaysPresent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,14 +13,14 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A component that is displayed at the hit position in the taiko playfield.
|
/// A component that is displayed at the hit position in the taiko playfield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class HitTarget : Container
|
internal class TaikoHitTarget : Container
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Thickness of all drawn line pieces.
|
/// Thickness of all drawn line pieces.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float border_thickness = 2.5f;
|
private const float border_thickness = 2.5f;
|
||||||
|
|
||||||
public HitTarget()
|
public TaikoHitTarget()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
@ -41,7 +41,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
FillMode = FillMode.Fit,
|
|
||||||
Scale = new Vector2(TaikoHitObject.DEFAULT_STRONG_SIZE),
|
Scale = new Vector2(TaikoHitObject.DEFAULT_STRONG_SIZE),
|
||||||
Masking = true,
|
Masking = true,
|
||||||
BorderColour = Color4.White,
|
BorderColour = Color4.White,
|
||||||
@ -63,7 +62,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
FillMode = FillMode.Fit,
|
|
||||||
Scale = new Vector2(TaikoHitObject.DEFAULT_SIZE),
|
Scale = new Vector2(TaikoHitObject.DEFAULT_SIZE),
|
||||||
Masking = true,
|
Masking = true,
|
||||||
BorderColour = Color4.White,
|
BorderColour = Color4.White,
|
@ -3,11 +3,8 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Effects;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -18,193 +15,138 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
|||||||
using osu.Game.Rulesets.Taiko.Judgements;
|
using osu.Game.Rulesets.Taiko.Judgements;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.UI
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
{
|
{
|
||||||
public class TaikoPlayfield : ScrollingPlayfield
|
public class TaikoPlayfield : ScrollingPlayfield
|
||||||
{
|
{
|
||||||
|
private readonly ControlPointInfo controlPoints;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default height of a <see cref="TaikoPlayfield"/> when inside a <see cref="DrawableTaikoRuleset"/>.
|
/// Default height of a <see cref="TaikoPlayfield"/> when inside a <see cref="DrawableTaikoRuleset"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const float DEFAULT_HEIGHT = 178;
|
public const float DEFAULT_HEIGHT = 178;
|
||||||
|
|
||||||
/// <summary>
|
private Container<HitExplosion> hitExplosionContainer;
|
||||||
/// The offset from <see cref="left_area_size"/> which the center of the hit target lies at.
|
private Container<KiaiHitExplosion> kiaiExplosionContainer;
|
||||||
/// </summary>
|
private JudgementContainer<DrawableTaikoJudgement> judgementContainer;
|
||||||
public const float HIT_TARGET_OFFSET = 100;
|
private ScrollingHitObjectContainer drumRollHitContainer;
|
||||||
|
internal Drawable HitTarget;
|
||||||
|
|
||||||
/// <summary>
|
private ProxyContainer topLevelHitContainer;
|
||||||
/// The size of the left area of the playfield. This area contains the input drum.
|
private ProxyContainer barlineContainer;
|
||||||
/// </summary>
|
private Container rightArea;
|
||||||
private const float left_area_size = 240;
|
private Container leftArea;
|
||||||
|
|
||||||
private readonly Container<HitExplosion> hitExplosionContainer;
|
private Container hitTargetOffsetContent;
|
||||||
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
|
|
||||||
private readonly JudgementContainer<DrawableTaikoJudgement> judgementContainer;
|
|
||||||
internal readonly Drawable HitTarget;
|
|
||||||
|
|
||||||
private readonly ProxyContainer topLevelHitContainer;
|
|
||||||
private readonly ProxyContainer barlineContainer;
|
|
||||||
|
|
||||||
private readonly Container overlayBackgroundContainer;
|
|
||||||
private readonly Container backgroundContainer;
|
|
||||||
|
|
||||||
private readonly Box overlayBackground;
|
|
||||||
private readonly Box background;
|
|
||||||
|
|
||||||
public TaikoPlayfield(ControlPointInfo controlPoints)
|
public TaikoPlayfield(ControlPointInfo controlPoints)
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
this.controlPoints = controlPoints;
|
||||||
{
|
|
||||||
backgroundContainer = new Container
|
|
||||||
{
|
|
||||||
Name = "Transparent playfield background",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Masking = true,
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Shadow,
|
|
||||||
Colour = Color4.Black.Opacity(0.2f),
|
|
||||||
Radius = 5,
|
|
||||||
},
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
background = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0.6f
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
new Container
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
InternalChildren = new[]
|
||||||
|
{
|
||||||
|
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundRight), _ => new PlayfieldBackgroundRight()),
|
||||||
|
rightArea = new Container
|
||||||
{
|
{
|
||||||
Name = "Right area",
|
Name = "Right area",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Left = left_area_size },
|
RelativePositionAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Masked elements before hit objects",
|
Name = "Masked elements before hit objects",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
|
FillMode = FillMode.Fit,
|
||||||
Masking = true,
|
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
hitExplosionContainer = new Container<HitExplosion>
|
hitExplosionContainer = new Container<HitExplosion>
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
FillMode = FillMode.Fit,
|
|
||||||
Blending = BlendingParameters.Additive,
|
|
||||||
},
|
},
|
||||||
HitTarget = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.HitTarget), _ => new HitTarget())
|
HitTarget = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.HitTarget), _ => new TaikoHitTarget())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
FillMode = FillMode.Fit
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
hitTargetOffsetContent = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
barlineContainer = new ProxyContainer
|
barlineContainer = new ProxyContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }
|
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = "Hit objects",
|
Name = "Hit objects",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
|
Children = new Drawable[]
|
||||||
Masking = true,
|
{
|
||||||
Child = HitObjectContainer
|
HitObjectContainer,
|
||||||
|
drumRollHitContainer = new DrumRollHitContainer()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
kiaiExplosionContainer = new Container<KiaiHitExplosion>
|
kiaiExplosionContainer = new Container<KiaiHitExplosion>
|
||||||
{
|
{
|
||||||
Name = "Kiai hit explosions",
|
Name = "Kiai hit explosions",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
FillMode = FillMode.Fit,
|
FillMode = FillMode.Fit,
|
||||||
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
|
|
||||||
Blending = BlendingParameters.Additive
|
|
||||||
},
|
},
|
||||||
judgementContainer = new JudgementContainer<DrawableTaikoJudgement>
|
judgementContainer = new JudgementContainer<DrawableTaikoJudgement>
|
||||||
{
|
{
|
||||||
Name = "Judgements",
|
Name = "Judgements",
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
|
|
||||||
Blending = BlendingParameters.Additive
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
overlayBackgroundContainer = new Container
|
}
|
||||||
|
},
|
||||||
|
leftArea = new Container
|
||||||
{
|
{
|
||||||
Name = "Left overlay",
|
Name = "Left overlay",
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Size = new Vector2(left_area_size, 1),
|
FillMode = FillMode.Fit,
|
||||||
|
BorderColour = colours.Gray0,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
overlayBackground = new Box
|
new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.PlayfieldBackgroundLeft), _ => new PlayfieldBackgroundLeft()),
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
new InputDrum(controlPoints)
|
new InputDrum(controlPoints)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreLeft,
|
||||||
Scale = new Vector2(0.9f),
|
|
||||||
Margin = new MarginPadding { Right = 20 }
|
|
||||||
},
|
},
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopRight,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Width = 10,
|
|
||||||
Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Name = "Border",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Masking = true,
|
|
||||||
MaskingSmoothness = 0,
|
|
||||||
BorderThickness = 2,
|
|
||||||
AlwaysPresent = true,
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0,
|
|
||||||
AlwaysPresent = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
topLevelHitContainer = new ProxyContainer
|
topLevelHitContainer = new ProxyContainer
|
||||||
{
|
{
|
||||||
Name = "Top level hit objects",
|
Name = "Top level hit objects",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
}
|
},
|
||||||
|
drumRollHitContainer.CreateProxy()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
protected override void Update()
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
{
|
||||||
overlayBackgroundContainer.BorderColour = colours.Gray0;
|
base.Update();
|
||||||
overlayBackground.Colour = colours.Gray1;
|
|
||||||
|
|
||||||
backgroundContainer.BorderColour = colours.Gray1;
|
// Padding is required to be updated for elements which are based on "absolute" X sized elements.
|
||||||
background.Colour = colours.Gray0;
|
// This is basically allowing for correct alignment as relative pieces move around them.
|
||||||
|
rightArea.Padding = new MarginPadding { Left = leftArea.DrawWidth };
|
||||||
|
hitTargetOffsetContent.Padding = new MarginPadding { Left = HitTarget.DrawWidth / 2 };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Add(DrawableHitObject h)
|
public override void Add(DrawableHitObject h)
|
||||||
{
|
{
|
||||||
h.OnNewResult += OnNewResult;
|
h.OnNewResult += OnNewResult;
|
||||||
|
|
||||||
base.Add(h);
|
base.Add(h);
|
||||||
|
|
||||||
switch (h)
|
switch (h)
|
||||||
@ -223,7 +165,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
if (!DisplayJudgements.Value)
|
if (!DisplayJudgements.Value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!judgedObject.DisplayResult)
|
if (!judgedObject.DisplayResult)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -234,6 +175,15 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit();
|
hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TaikoDrumRollTickJudgement _:
|
||||||
|
if (!result.IsHit)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var drawableTick = (DrawableDrumRollTick)judgedObject;
|
||||||
|
|
||||||
|
addDrumRollHit(drawableTick);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject)
|
judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject)
|
||||||
{
|
{
|
||||||
@ -246,17 +196,23 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
if (!result.IsHit)
|
if (!result.IsHit)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
|
var type = (judgedObject.HitObject as Hit)?.Type ?? HitType.Centre;
|
||||||
|
|
||||||
hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
|
|
||||||
|
|
||||||
if (judgedObject.HitObject.Kiai)
|
|
||||||
kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim));
|
|
||||||
|
|
||||||
|
addExplosion(judgedObject, type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addDrumRollHit(DrawableDrumRollTick drawableTick) =>
|
||||||
|
drumRollHitContainer.Add(new DrawableFlyingHit(drawableTick));
|
||||||
|
|
||||||
|
private void addExplosion(DrawableHitObject drawableObject, HitType type)
|
||||||
|
{
|
||||||
|
hitExplosionContainer.Add(new HitExplosion(drawableObject));
|
||||||
|
if (drawableObject.HitObject.Kiai)
|
||||||
|
kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type));
|
||||||
|
}
|
||||||
|
|
||||||
private class ProxyContainer : LifetimeManagementContainer
|
private class ProxyContainer : LifetimeManagementContainer
|
||||||
{
|
{
|
||||||
public new MarginPadding Padding
|
public new MarginPadding Padding
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.EntityFrameworkCore.Internal;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -137,7 +137,7 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
var hitCircle = new HitCircle { StartTime = 1000 };
|
var hitCircle = new HitCircle { StartTime = 1000 };
|
||||||
editorBeatmap.Add(hitCircle);
|
editorBeatmap.Add(hitCircle);
|
||||||
Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1));
|
Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1));
|
||||||
Assert.That(editorBeatmap.HitObjects.IndexOf(hitCircle), Is.EqualTo(3));
|
Assert.That(Array.IndexOf(editorBeatmap.HitObjects.ToArray(), hitCircle), Is.EqualTo(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -161,7 +161,7 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
|
|
||||||
hitCircle.StartTime = 0;
|
hitCircle.StartTime = 0;
|
||||||
Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1));
|
Assert.That(editorBeatmap.HitObjects.Count(h => h == hitCircle), Is.EqualTo(1));
|
||||||
Assert.That(editorBeatmap.HitObjects.IndexOf(hitCircle), Is.EqualTo(1));
|
Assert.That(Array.IndexOf(editorBeatmap.HitObjects.ToArray(), hitCircle), Is.EqualTo(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -5,7 +5,7 @@ using NUnit.Framework;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Editor
|
namespace osu.Game.Tests.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class EditorChangeHandlerTest
|
public class EditorChangeHandlerTest
|
@ -17,7 +17,7 @@ using osu.Game.Screens.Edit;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
|
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Editor
|
namespace osu.Game.Tests.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class LegacyEditorBeatmapPatcherTest
|
public class LegacyEditorBeatmapPatcherTest
|
||||||
@ -304,6 +304,31 @@ namespace osu.Game.Tests.Editor
|
|||||||
runTest(patch);
|
runTest(patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestChangeHitObjectAtSameTime()
|
||||||
|
{
|
||||||
|
current.AddRange(new[]
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 500, Position = new Vector2(50) },
|
||||||
|
new HitCircle { StartTime = 500, Position = new Vector2(100) },
|
||||||
|
new HitCircle { StartTime = 500, Position = new Vector2(150) },
|
||||||
|
new HitCircle { StartTime = 500, Position = new Vector2(200) },
|
||||||
|
});
|
||||||
|
|
||||||
|
var patch = new OsuBeatmap
|
||||||
|
{
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 500, Position = new Vector2(150) },
|
||||||
|
new HitCircle { StartTime = 500, Position = new Vector2(100) },
|
||||||
|
new HitCircle { StartTime = 500, Position = new Vector2(50) },
|
||||||
|
new HitCircle { StartTime = 500, Position = new Vector2(200) },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
runTest(patch);
|
||||||
|
}
|
||||||
|
|
||||||
private void runTest(IBeatmap patch)
|
private void runTest(IBeatmap patch)
|
||||||
{
|
{
|
||||||
// Due to the method of testing, "patch" comes in without having been decoded via a beatmap decoder.
|
// Due to the method of testing, "patch" comes in without having been decoded via a beatmap decoder.
|
@ -14,7 +14,7 @@ using osu.Game.Rulesets.Osu.Edit;
|
|||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Editor
|
namespace osu.Game.Tests.Editing
|
||||||
{
|
{
|
||||||
[HeadlessTest]
|
[HeadlessTest]
|
||||||
public class TestSceneHitObjectComposerDistanceSnapping : EditorClockTestScene
|
public class TestSceneHitObjectComposerDistanceSnapping : EditorClockTestScene
|
85
osu.Game.Tests/NonVisual/PeriodTrackerTest.cs
Normal file
85
osu.Game.Tests/NonVisual/PeriodTrackerTest.cs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.NonVisual
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class PeriodTrackerTest
|
||||||
|
{
|
||||||
|
private static readonly Period[] single_period = { new Period(1.0, 2.0) };
|
||||||
|
|
||||||
|
private static readonly Period[] unordered_periods =
|
||||||
|
{
|
||||||
|
new Period(-9.1, -8.3),
|
||||||
|
new Period(-3.4, 2.1),
|
||||||
|
new Period(9.0, 50.0),
|
||||||
|
new Period(5.25, 10.50)
|
||||||
|
};
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCheckValueInsideSinglePeriod()
|
||||||
|
{
|
||||||
|
var tracker = new PeriodTracker(single_period);
|
||||||
|
|
||||||
|
var period = single_period.Single();
|
||||||
|
Assert.IsTrue(tracker.IsInAny(period.Start));
|
||||||
|
Assert.IsTrue(tracker.IsInAny(getMidpoint(period)));
|
||||||
|
Assert.IsTrue(tracker.IsInAny(period.End));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCheckValuesInsidePeriods()
|
||||||
|
{
|
||||||
|
var tracker = new PeriodTracker(unordered_periods);
|
||||||
|
|
||||||
|
foreach (var period in unordered_periods)
|
||||||
|
Assert.IsTrue(tracker.IsInAny(getMidpoint(period)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCheckValuesInRandomOrder()
|
||||||
|
{
|
||||||
|
var tracker = new PeriodTracker(unordered_periods);
|
||||||
|
|
||||||
|
foreach (var period in unordered_periods.OrderBy(_ => RNG.Next()))
|
||||||
|
Assert.IsTrue(tracker.IsInAny(getMidpoint(period)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCheckValuesOutOfPeriods()
|
||||||
|
{
|
||||||
|
var tracker = new PeriodTracker(new[]
|
||||||
|
{
|
||||||
|
new Period(1.0, 2.0),
|
||||||
|
new Period(3.0, 4.0)
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.IsFalse(tracker.IsInAny(0.9), "Time before first period is being considered inside");
|
||||||
|
|
||||||
|
Assert.IsFalse(tracker.IsInAny(2.1), "Time right after first period is being considered inside");
|
||||||
|
Assert.IsFalse(tracker.IsInAny(2.9), "Time right before second period is being considered inside");
|
||||||
|
|
||||||
|
Assert.IsFalse(tracker.IsInAny(4.1), "Time after last period is being considered inside");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestReversedPeriodHandling()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentException>(() =>
|
||||||
|
{
|
||||||
|
_ = new PeriodTracker(new[]
|
||||||
|
{
|
||||||
|
new Period(2.0, 1.0)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getMidpoint(Period period) => period.Start + (period.End - period.Start) / 2;
|
||||||
|
}
|
||||||
|
}
|
30
osu.Game.Tests/Resources/sample-beatmap-catch.osu
Normal file
30
osu.Game.Tests/Resources/sample-beatmap-catch.osu
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[General]
|
||||||
|
SampleSet: Normal
|
||||||
|
StackLeniency: 0.7
|
||||||
|
Mode: 2
|
||||||
|
|
||||||
|
[Difficulty]
|
||||||
|
HPDrainRate:3
|
||||||
|
CircleSize:5
|
||||||
|
OverallDifficulty:8
|
||||||
|
ApproachRate:8
|
||||||
|
SliderMultiplier:3.59999990463257
|
||||||
|
SliderTickRate:2
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
24,352.941176470588,4,1,1,100,1,0
|
||||||
|
6376,-50,4,1,1,100,0,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
32,183,24,5,0,0:0:0:0:
|
||||||
|
106,123,200,1,10,0:0:0:0:
|
||||||
|
199,108,376,1,2,0:0:0:0:
|
||||||
|
305,105,553,5,4,0:0:0:0:
|
||||||
|
386,112,729,1,14,0:0:0:0:
|
||||||
|
486,197,906,5,12,0:0:0:0:
|
||||||
|
14,199,1082,2,0,L|473:198,1,449.999988079071
|
||||||
|
14,199,1700,6,6,P|248:33|490:222,1,629.9999833107,0|8,0:0|0:0,0:0:0:0:
|
||||||
|
10,190,2494,2,8,B|252:29|254:335|468:167,1,449.999988079071,10|12,0:0|0:0,0:0:0:0:
|
||||||
|
256,192,3112,12,0,3906,0:0:0:0:
|
39
osu.Game.Tests/Resources/sample-beatmap-mania.osu
Normal file
39
osu.Game.Tests/Resources/sample-beatmap-mania.osu
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[General]
|
||||||
|
SampleSet: Normal
|
||||||
|
StackLeniency: 0.7
|
||||||
|
Mode: 3
|
||||||
|
|
||||||
|
[Difficulty]
|
||||||
|
HPDrainRate:3
|
||||||
|
CircleSize:5
|
||||||
|
OverallDifficulty:8
|
||||||
|
ApproachRate:8
|
||||||
|
SliderMultiplier:3.59999990463257
|
||||||
|
SliderTickRate:2
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
24,352.941176470588,4,1,1,100,1,0
|
||||||
|
6376,-50,4,1,1,100,0,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
51,192,24,1,0,0:0:0:0:
|
||||||
|
153,192,200,1,0,0:0:0:0:
|
||||||
|
358,192,376,1,0,0:0:0:0:
|
||||||
|
460,192,553,1,0,0:0:0:0:
|
||||||
|
460,192,729,128,0,1435:0:0:0:0:
|
||||||
|
358,192,906,128,0,1612:0:0:0:0:
|
||||||
|
256,192,1082,128,0,1788:0:0:0:0:
|
||||||
|
153,192,1259,128,0,1965:0:0:0:0:
|
||||||
|
51,192,1435,128,0,2141:0:0:0:0:
|
||||||
|
51,192,2318,1,12,0:0:0:0:
|
||||||
|
153,192,2318,1,4,0:0:0:0:
|
||||||
|
256,192,2318,1,6,0:0:0:0:
|
||||||
|
358,192,2318,1,14,0:0:0:0:
|
||||||
|
460,192,2318,1,0,0:0:0:0:
|
||||||
|
51,192,2494,128,0,2582:0:0:0:0:
|
||||||
|
153,192,2494,128,14,2582:0:0:0:0:
|
||||||
|
256,192,2494,128,6,2582:0:0:0:0:
|
||||||
|
358,192,2494,128,4,2582:0:0:0:0:
|
||||||
|
460,192,2494,128,12,2582:0:0:0:0:
|
42
osu.Game.Tests/Resources/sample-beatmap-taiko.osu
Normal file
42
osu.Game.Tests/Resources/sample-beatmap-taiko.osu
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[General]
|
||||||
|
SampleSet: Normal
|
||||||
|
StackLeniency: 0.7
|
||||||
|
Mode: 1
|
||||||
|
|
||||||
|
[Difficulty]
|
||||||
|
HPDrainRate:3
|
||||||
|
CircleSize:5
|
||||||
|
OverallDifficulty:8
|
||||||
|
ApproachRate:8
|
||||||
|
SliderMultiplier:3.59999990463257
|
||||||
|
SliderTickRate:2
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
24,352.941176470588,4,1,1,100,1,0
|
||||||
|
6376,-50,4,1,1,100,0,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
231,129,24,1,0,0:0:0:0:
|
||||||
|
231,129,200,1,0,0:0:0:0:
|
||||||
|
231,129,376,1,0,0:0:0:0:
|
||||||
|
231,129,553,1,0,0:0:0:0:
|
||||||
|
231,129,729,1,0,0:0:0:0:
|
||||||
|
373,132,906,1,4,0:0:0:0:
|
||||||
|
373,132,1082,1,4,0:0:0:0:
|
||||||
|
373,132,1259,1,4,0:0:0:0:
|
||||||
|
373,132,1435,1,4,0:0:0:0:
|
||||||
|
231,129,1788,1,8,0:0:0:0:
|
||||||
|
231,129,1964,1,8,0:0:0:0:
|
||||||
|
231,129,2140,1,8,0:0:0:0:
|
||||||
|
231,129,2317,1,8,0:0:0:0:
|
||||||
|
231,129,2493,1,8,0:0:0:0:
|
||||||
|
373,132,2670,1,12,0:0:0:0:
|
||||||
|
373,132,2846,1,12,0:0:0:0:
|
||||||
|
373,132,3023,1,12,0:0:0:0:
|
||||||
|
373,132,3199,1,12,0:0:0:0:
|
||||||
|
51,189,3553,2,0,L|150:188,1,89.9999976158143
|
||||||
|
52,191,3906,2,0,L|512:189,1,449.999988079071
|
||||||
|
26,196,4612,2,4,L|501:195,1,449.999988079071
|
||||||
|
17,242,5318,2,10,P|250:69|495:243,1,629.9999833107,0|8,0:0|0:0,0:0:0:0:
|
@ -14,7 +14,7 @@ using osu.Game.Screens.Edit.Compose.Components;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
public class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene
|
public class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Beatmaps;
|
|||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Compose;
|
using osu.Game.Screens.Edit.Compose;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneComposeScreen : EditorClockTestScene
|
public class TestSceneComposeScreen : EditorClockTestScene
|
@ -13,7 +13,7 @@ using osu.Game.Screens.Edit.Compose.Components;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
public class TestSceneDistanceSnapGrid : EditorClockTestScene
|
public class TestSceneDistanceSnapGrid : EditorClockTestScene
|
||||||
{
|
{
|
@ -4,33 +4,27 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Rulesets.Edit;
|
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
public class TestSceneEditorChangeStates : ScreenTestScene
|
public class TestSceneEditorChangeStates : EditorTestScene
|
||||||
{
|
{
|
||||||
|
public TestSceneEditorChangeStates()
|
||||||
|
: base(new OsuRuleset())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private EditorBeatmap editorBeatmap;
|
private EditorBeatmap editorBeatmap;
|
||||||
private TestEditor editor;
|
|
||||||
|
|
||||||
public override void SetUpSteps()
|
public override void SetUpSteps()
|
||||||
{
|
{
|
||||||
base.SetUpSteps();
|
base.SetUpSteps();
|
||||||
|
|
||||||
AddStep("load editor", () =>
|
AddStep("get beatmap", () => editorBeatmap = Editor.ChildrenOfType<EditorBeatmap>().Single());
|
||||||
{
|
|
||||||
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
|
||||||
LoadScreen(editor = new TestEditor());
|
|
||||||
});
|
|
||||||
|
|
||||||
AddUntilStep("wait for editor to load", () => editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
|
|
||||||
&& editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true);
|
|
||||||
AddStep("get beatmap", () => editorBeatmap = editor.ChildrenOfType<EditorBeatmap>().Single());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -158,11 +152,13 @@ namespace osu.Game.Tests.Visual.Editor
|
|||||||
AddAssert("no hitobject added", () => addedObject == null);
|
AddAssert("no hitobject added", () => addedObject == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addUndoSteps() => AddStep("undo", () => editor.Undo());
|
private void addUndoSteps() => AddStep("undo", () => ((TestEditor)Editor).Undo());
|
||||||
|
|
||||||
private void addRedoSteps() => AddStep("redo", () => editor.Redo());
|
private void addRedoSteps() => AddStep("redo", () => ((TestEditor)Editor).Redo());
|
||||||
|
|
||||||
private class TestEditor : Screens.Edit.Editor
|
protected override Editor CreateEditor() => new TestEditor();
|
||||||
|
|
||||||
|
private class TestEditor : Editor
|
||||||
{
|
{
|
||||||
public new void Undo() => base.Undo();
|
public new void Undo() => base.Undo();
|
||||||
|
|
@ -7,7 +7,7 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Screens.Edit.Components.RadioButtons;
|
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneEditorComposeRadioButtons : OsuTestScene
|
public class TestSceneEditorComposeRadioButtons : OsuTestScene
|
@ -10,7 +10,7 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Screens.Edit.Components.Menus;
|
using osu.Game.Screens.Edit.Components.Menus;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneEditorMenuBar : OsuTestScene
|
public class TestSceneEditorMenuBar : OsuTestScene
|
@ -13,7 +13,7 @@ using osu.Game.Rulesets.Osu.Objects;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneEditorSeekSnapping : EditorClockTestScene
|
public class TestSceneEditorSeekSnapping : EditorClockTestScene
|
@ -10,7 +10,7 @@ using osu.Game.Rulesets.Osu;
|
|||||||
using osu.Game.Screens.Edit.Components.Timelines.Summary;
|
using osu.Game.Screens.Edit.Components.Timelines.Summary;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneEditorSummaryTimeline : EditorClockTestScene
|
public class TestSceneEditorSummaryTimeline : EditorClockTestScene
|
@ -20,7 +20,7 @@ using osu.Game.Screens.Edit;
|
|||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneHitObjectComposer : EditorClockTestScene
|
public class TestSceneHitObjectComposer : EditorClockTestScene
|
@ -9,7 +9,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Screens.Edit.Components;
|
using osu.Game.Screens.Edit.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestScenePlaybackControl : OsuTestScene
|
public class TestScenePlaybackControl : OsuTestScene
|
@ -7,7 +7,7 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneTimelineBlueprintContainer : TimelineTestScene
|
public class TestSceneTimelineBlueprintContainer : TimelineTestScene
|
@ -8,7 +8,7 @@ using osu.Game.Screens.Edit.Compose.Components;
|
|||||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneTimelineTickDisplay : TimelineTestScene
|
public class TestSceneTimelineTickDisplay : TimelineTestScene
|
@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Beatmaps;
|
|||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Timing;
|
using osu.Game.Screens.Edit.Timing;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Editor
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneTimingScreen : EditorClockTestScene
|
public class TestSceneTimingScreen : EditorClockTestScene
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user