Merge branch 'master' into adjustable-map-settings
@ -176,8 +176,8 @@ dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
|||||||
#Style - C# 8 features
|
#Style - C# 8 features
|
||||||
csharp_prefer_static_local_function = true:warning
|
csharp_prefer_static_local_function = true:warning
|
||||||
csharp_prefer_simple_using_statement = true:silent
|
csharp_prefer_simple_using_statement = true:silent
|
||||||
csharp_style_prefer_index_operator = false:none
|
csharp_style_prefer_index_operator = true:warning
|
||||||
csharp_style_prefer_range_operator = false:none
|
csharp_style_prefer_range_operator = true:warning
|
||||||
csharp_style_prefer_switch_expression = false:none
|
csharp_style_prefer_switch_expression = false:none
|
||||||
|
|
||||||
#Supressing roslyn built-in analyzers
|
#Supressing roslyn built-in analyzers
|
||||||
|
58
CodeAnalysis/osu.ruleset
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RuleSet Name="osu! Rule Set" Description=" " ToolsVersion="16.0">
|
||||||
|
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
|
||||||
|
<Rule Id="CA1016" Action="None" />
|
||||||
|
<Rule Id="CA1028" Action="None" />
|
||||||
|
<Rule Id="CA1031" Action="None" />
|
||||||
|
<Rule Id="CA1034" Action="None" />
|
||||||
|
<Rule Id="CA1036" Action="None" />
|
||||||
|
<Rule Id="CA1040" Action="None" />
|
||||||
|
<Rule Id="CA1044" Action="None" />
|
||||||
|
<Rule Id="CA1051" Action="None" />
|
||||||
|
<Rule Id="CA1054" Action="None" />
|
||||||
|
<Rule Id="CA1056" Action="None" />
|
||||||
|
<Rule Id="CA1062" Action="None" />
|
||||||
|
<Rule Id="CA1063" Action="None" />
|
||||||
|
<Rule Id="CA1067" Action="None" />
|
||||||
|
<Rule Id="CA1707" Action="None" />
|
||||||
|
<Rule Id="CA1710" Action="None" />
|
||||||
|
<Rule Id="CA1714" Action="None" />
|
||||||
|
<Rule Id="CA1716" Action="None" />
|
||||||
|
<Rule Id="CA1717" Action="None" />
|
||||||
|
<Rule Id="CA1720" Action="None" />
|
||||||
|
<Rule Id="CA1721" Action="None" />
|
||||||
|
<Rule Id="CA1724" Action="None" />
|
||||||
|
<Rule Id="CA1801" Action="None" />
|
||||||
|
<Rule Id="CA1806" Action="None" />
|
||||||
|
<Rule Id="CA1812" Action="None" />
|
||||||
|
<Rule Id="CA1814" Action="None" />
|
||||||
|
<Rule Id="CA1815" Action="None" />
|
||||||
|
<Rule Id="CA1819" Action="None" />
|
||||||
|
<Rule Id="CA1822" Action="None" />
|
||||||
|
<Rule Id="CA1823" Action="None" />
|
||||||
|
<Rule Id="CA2007" Action="None" />
|
||||||
|
<Rule Id="CA2214" Action="None" />
|
||||||
|
<Rule Id="CA2227" Action="None" />
|
||||||
|
</Rules>
|
||||||
|
<Rules AnalyzerId="Microsoft.CodeQuality.CSharp.Analyzers" RuleNamespace="Microsoft.CodeQuality.CSharp.Analyzers">
|
||||||
|
<Rule Id="CA1001" Action="None" />
|
||||||
|
<Rule Id="CA1032" Action="None" />
|
||||||
|
</Rules>
|
||||||
|
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
|
||||||
|
<Rule Id="CA1303" Action="None" />
|
||||||
|
<Rule Id="CA1304" Action="None" />
|
||||||
|
<Rule Id="CA1305" Action="None" />
|
||||||
|
<Rule Id="CA1307" Action="None" />
|
||||||
|
<Rule Id="CA1308" Action="None" />
|
||||||
|
<Rule Id="CA1816" Action="None" />
|
||||||
|
<Rule Id="CA1826" Action="None" />
|
||||||
|
<Rule Id="CA2000" Action="None" />
|
||||||
|
<Rule Id="CA2008" Action="None" />
|
||||||
|
<Rule Id="CA2213" Action="None" />
|
||||||
|
<Rule Id="CA2235" Action="None" />
|
||||||
|
</Rules>
|
||||||
|
<Rules AnalyzerId="Microsoft.NetCore.CSharp.Analyzers" RuleNamespace="Microsoft.NetCore.CSharp.Analyzers">
|
||||||
|
<Rule Id="CA1309" Action="Warning" />
|
||||||
|
<Rule Id="CA2201" Action="Warning" />
|
||||||
|
</Rules>
|
||||||
|
</RuleSet>
|
@ -18,7 +18,11 @@
|
|||||||
<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="2.9.8" 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" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Code Analysis">
|
||||||
|
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis\osu.ruleset</CodeAnalysisRuleSet>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="Documentation">
|
<PropertyGroup Label="Documentation">
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#addin "nuget:?package=CodeFileSanity&version=0.0.33"
|
#addin "nuget:?package=CodeFileSanity&version=0.0.33"
|
||||||
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.2.1"
|
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.3.0"
|
||||||
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
|
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
|
||||||
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
|
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
|
||||||
|
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
{
|
{
|
||||||
|
"sdk": {
|
||||||
|
"allowPrerelease": false,
|
||||||
|
"rollForward": "minor",
|
||||||
|
"version": "3.1.100"
|
||||||
|
},
|
||||||
"msbuild-sdks": {
|
"msbuild-sdks": {
|
||||||
"Microsoft.Build.Traversal": "2.0.24"
|
"Microsoft.Build.Traversal": "2.0.24"
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1210.1" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1215.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
120
osu.Desktop/DiscordRichPresence.cs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// 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 DiscordRPC;
|
||||||
|
using DiscordRPC.Message;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using LogLevel = osu.Framework.Logging.LogLevel;
|
||||||
|
using User = osu.Game.Users.User;
|
||||||
|
|
||||||
|
namespace osu.Desktop
|
||||||
|
{
|
||||||
|
internal class DiscordRichPresence : Component
|
||||||
|
{
|
||||||
|
private const string client_id = "367827983903490050";
|
||||||
|
|
||||||
|
private DiscordRpcClient client;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||||
|
|
||||||
|
private Bindable<User> user;
|
||||||
|
|
||||||
|
private readonly IBindable<UserStatus> status = new Bindable<UserStatus>();
|
||||||
|
private readonly IBindable<UserActivity> activity = new Bindable<UserActivity>();
|
||||||
|
|
||||||
|
private readonly RichPresence presence = new RichPresence
|
||||||
|
{
|
||||||
|
Assets = new Assets { LargeImageKey = "osu_logo_lazer", }
|
||||||
|
};
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IAPIProvider provider)
|
||||||
|
{
|
||||||
|
client = new DiscordRpcClient(client_id)
|
||||||
|
{
|
||||||
|
SkipIdenticalPresence = false // handles better on discord IPC loss, see updateStatus call in onReady.
|
||||||
|
};
|
||||||
|
|
||||||
|
client.OnReady += onReady;
|
||||||
|
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network, LogLevel.Error);
|
||||||
|
client.OnConnectionFailed += (_, e) => Logger.Log($"An connection occurred with Discord RPC Client: {e.Type}", LoggingTarget.Network, LogLevel.Error);
|
||||||
|
|
||||||
|
(user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u =>
|
||||||
|
{
|
||||||
|
status.UnbindBindings();
|
||||||
|
status.BindTo(u.NewValue.Status);
|
||||||
|
|
||||||
|
activity.UnbindBindings();
|
||||||
|
activity.BindTo(u.NewValue.Activity);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
ruleset.BindValueChanged(_ => updateStatus());
|
||||||
|
status.BindValueChanged(_ => updateStatus());
|
||||||
|
activity.BindValueChanged(_ => updateStatus());
|
||||||
|
|
||||||
|
client.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onReady(object _, ReadyMessage __)
|
||||||
|
{
|
||||||
|
Logger.Log("Discord RPC Client ready.", LoggingTarget.Network, LogLevel.Debug);
|
||||||
|
updateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStatus()
|
||||||
|
{
|
||||||
|
if (status.Value is UserStatusOffline)
|
||||||
|
{
|
||||||
|
client.ClearPresence();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.Value is UserStatusOnline && activity.Value != null)
|
||||||
|
{
|
||||||
|
presence.State = activity.Value.Status;
|
||||||
|
presence.Details = getDetails(activity.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
presence.State = "Idle";
|
||||||
|
presence.Details = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update user information
|
||||||
|
presence.Assets.LargeImageText = $"{user.Value.Username}" + (user.Value.Statistics?.Ranks.Global > 0 ? $" (rank #{user.Value.Statistics.Ranks.Global:N0})" : string.Empty);
|
||||||
|
|
||||||
|
// update ruleset
|
||||||
|
presence.Assets.SmallImageKey = ruleset.Value.ID <= 3 ? $"mode_{ruleset.Value.ID}" : "mode_custom";
|
||||||
|
presence.Assets.SmallImageText = ruleset.Value.Name;
|
||||||
|
|
||||||
|
client.SetPresence(presence);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string getDetails(UserActivity activity)
|
||||||
|
{
|
||||||
|
switch (activity)
|
||||||
|
{
|
||||||
|
case UserActivity.SoloGame solo:
|
||||||
|
return solo.Beatmap.ToString();
|
||||||
|
|
||||||
|
case UserActivity.Editing edit:
|
||||||
|
return edit.Beatmap.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
client.Dispose();
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -60,6 +60,8 @@ namespace osu.Desktop
|
|||||||
else
|
else
|
||||||
Add(new SimpleUpdateManager());
|
Add(new SimpleUpdateManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoadComponentAsync(new DiscordRichPresence(), Add);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)
|
protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
|
||||||
|
<PackageReference Include="DiscordRichPresence" Version="1.0.121" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Resources">
|
<ItemGroup Label="Resources">
|
||||||
<EmbeddedResource Include="lazer.ico" />
|
<EmbeddedResource Include="lazer.ico" />
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
protected override Player CreatePlayer(Ruleset ruleset)
|
protected override Player CreatePlayer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
|
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
|
||||||
return base.CreatePlayer(ruleset);
|
return base.CreatePlayer(ruleset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), beatmap, Array.Empty<Mod>())
|
drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), beatmap.GetPlayableBeatmap(new CatchRuleset().RulesetInfo))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
private void addToPlayfield(DrawableCatchHitObject drawable)
|
private void addToPlayfield(DrawableCatchHitObject drawable)
|
||||||
{
|
{
|
||||||
foreach (var mod in Mods.Value.OfType<IApplicableToDrawableHitObjects>())
|
foreach (var mod in SelectedMods.Value.OfType<IApplicableToDrawableHitObjects>())
|
||||||
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(drawable);
|
drawableRuleset.Playfield.Add(drawable);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
using osu.Game.Rulesets.Catch.Mods;
|
using osu.Game.Rulesets.Catch.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
@ -12,9 +13,10 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(CatchModHidden) }).ToList();
|
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(CatchModHidden) }).ToList();
|
||||||
|
|
||||||
public TestSceneDrawableHitObjectsHidden()
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
Mods.Value = new[] { new CatchModHidden() };
|
SelectedMods.Value = new[] { new CatchModHidden() };
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,14 +16,20 @@ using osu.Game.Rulesets.Replays.Types;
|
|||||||
using osu.Game.Beatmaps.Legacy;
|
using osu.Game.Beatmaps.Legacy;
|
||||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Difficulty;
|
using osu.Game.Rulesets.Catch.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Catch.Scoring;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch
|
namespace osu.Game.Rulesets.Catch
|
||||||
{
|
{
|
||||||
public class CatchRuleset : Ruleset
|
public class CatchRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableCatchRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableCatchRuleset(this, beatmap, mods);
|
||||||
|
|
||||||
|
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new CatchScoreProcessor(beatmap);
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
|
||||||
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
|
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
|
||||||
|
|
||||||
@ -51,7 +57,9 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
||||||
yield return new CatchModSuddenDeath();
|
yield return new CatchModSuddenDeath();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Autoplay))
|
if (mods.HasFlag(LegacyMods.Cinema))
|
||||||
|
yield return new CatchModCinema();
|
||||||
|
else if (mods.HasFlag(LegacyMods.Autoplay))
|
||||||
yield return new CatchModAutoplay();
|
yield return new CatchModAutoplay();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Easy))
|
if (mods.HasFlag(LegacyMods.Easy))
|
||||||
@ -107,7 +115,7 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
return new Mod[]
|
return new Mod[]
|
||||||
{
|
{
|
||||||
new MultiMod(new CatchModAutoplay(), new ModCinema()),
|
new MultiMod(new CatchModAutoplay(), new CatchModCinema()),
|
||||||
new CatchModRelax(),
|
new CatchModRelax(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -118,7 +126,7 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return new Mod[] { };
|
return Array.Empty<Mod>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,10 +143,5 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
public override int? LegacyID => 2;
|
public override int? LegacyID => 2;
|
||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
|
||||||
|
|
||||||
public CatchRuleset(RulesetInfo rulesetInfo = null)
|
|
||||||
: base(rulesetInfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
osu.Game.Rulesets.Catch/Mods/CatchModCinema.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// 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.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Catch.Replays;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
|
{
|
||||||
|
public class CatchModCinema : ModCinema<CatchHitObject>
|
||||||
|
{
|
||||||
|
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
|
||||||
|
{
|
||||||
|
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!salad!" } },
|
||||||
|
Replay = new CatchAutoGenerator(beatmap).Generate(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModNightcore : ModNightcore
|
public class CatchModNightcore : ModNightcore<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.06;
|
public override double ScoreMultiplier => 1.06;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
path.ControlPoints.AddRange(value.ControlPoints);
|
path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
|
||||||
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
|
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,23 +2,21 @@
|
|||||||
// 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.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Scoring
|
namespace osu.Game.Rulesets.Catch.Scoring
|
||||||
{
|
{
|
||||||
public class CatchScoreProcessor : ScoreProcessor<CatchHitObject>
|
public class CatchScoreProcessor : ScoreProcessor
|
||||||
{
|
{
|
||||||
public CatchScoreProcessor(DrawableRuleset<CatchHitObject> drawableRuleset)
|
public CatchScoreProcessor(IBeatmap beatmap)
|
||||||
: base(drawableRuleset)
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private float hpDrainRate;
|
private float hpDrainRate;
|
||||||
|
|
||||||
protected override void ApplyBeatmap(Beatmap<CatchHitObject> beatmap)
|
protected override void ApplyBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
base.ApplyBeatmap(beatmap);
|
base.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
|
@ -10,10 +10,8 @@ using osu.Game.Replays;
|
|||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||||
using osu.Game.Rulesets.Catch.Replays;
|
using osu.Game.Rulesets.Catch.Replays;
|
||||||
using osu.Game.Rulesets.Catch.Scoring;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
@ -25,15 +23,13 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
protected override bool UserScrollSpeedAdjustment => false;
|
protected override bool UserScrollSpeedAdjustment => false;
|
||||||
|
|
||||||
public DrawableCatchRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableCatchRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
Direction.Value = ScrollingDirection.Down;
|
Direction.Value = ScrollingDirection.Down;
|
||||||
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
|
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
|
||||||
|
|
||||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, CreateDrawableRepresentation);
|
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, CreateDrawableRepresentation);
|
||||||
|
@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
prevNoteTimes.RemoveAt(0);
|
prevNoteTimes.RemoveAt(0);
|
||||||
prevNoteTimes.Add(newNoteTime);
|
prevNoteTimes.Add(newNoteTime);
|
||||||
|
|
||||||
density = (prevNoteTimes[prevNoteTimes.Count - 1] - prevNoteTimes[0]) / prevNoteTimes.Count;
|
density = (prevNoteTimes[^1] - prevNoteTimes[0]) / prevNoteTimes.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double lastTime;
|
private double lastTime;
|
||||||
|
@ -5,7 +5,7 @@ using System.Linq;
|
|||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||||
{
|
{
|
||||||
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
|||||||
protected override double StrainValueOf(DifficultyHitObject current)
|
protected override double StrainValueOf(DifficultyHitObject current)
|
||||||
{
|
{
|
||||||
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
||||||
var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime;
|
var endTime = maniaCurrent.BaseObject.GetEndTime();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||||
{
|
{
|
||||||
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
|||||||
protected override double StrainValueOf(DifficultyHitObject current)
|
protected override double StrainValueOf(DifficultyHitObject current)
|
||||||
{
|
{
|
||||||
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
||||||
var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime;
|
var endTime = maniaCurrent.BaseObject.GetEndTime();
|
||||||
|
|
||||||
double holdFactor = 1.0; // Factor in case something else is held
|
double holdFactor = 1.0; // Factor in case something else is held
|
||||||
double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
|
double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
{
|
{
|
||||||
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
|
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
|
||||||
|
|
||||||
public DrawableManiaEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableManiaEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
|
|
||||||
public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns;
|
public int TotalColumns => ((ManiaPlayfield)drawableRuleset.Playfield).TotalColumns;
|
||||||
|
|
||||||
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
{
|
{
|
||||||
drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
|
drawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
|
||||||
|
|
||||||
|
@ -25,14 +25,20 @@ using osu.Game.Rulesets.Mania.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Mania.Configuration;
|
using osu.Game.Rulesets.Mania.Configuration;
|
||||||
using osu.Game.Rulesets.Mania.Difficulty;
|
using osu.Game.Rulesets.Mania.Difficulty;
|
||||||
using osu.Game.Rulesets.Mania.Edit;
|
using osu.Game.Rulesets.Mania.Edit;
|
||||||
|
using osu.Game.Rulesets.Mania.Scoring;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania
|
namespace osu.Game.Rulesets.Mania
|
||||||
{
|
{
|
||||||
public class ManiaRuleset : Ruleset
|
public class ManiaRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableManiaRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableManiaRuleset(this, beatmap, mods);
|
||||||
|
|
||||||
|
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new ManiaScoreProcessor(beatmap);
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
|
||||||
|
|
||||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
|
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
|
||||||
|
|
||||||
public const string SHORT_NAME = "mania";
|
public const string SHORT_NAME = "mania";
|
||||||
@ -51,7 +57,9 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
||||||
yield return new ManiaModSuddenDeath();
|
yield return new ManiaModSuddenDeath();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Autoplay))
|
if (mods.HasFlag(LegacyMods.Cinema))
|
||||||
|
yield return new ManiaModCinema();
|
||||||
|
else if (mods.HasFlag(LegacyMods.Autoplay))
|
||||||
yield return new ManiaModAutoplay();
|
yield return new ManiaModAutoplay();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Easy))
|
if (mods.HasFlag(LegacyMods.Easy))
|
||||||
@ -149,7 +157,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
return new Mod[]
|
return new Mod[]
|
||||||
{
|
{
|
||||||
new MultiMod(new ManiaModAutoplay(), new ModCinema()),
|
new MultiMod(new ManiaModAutoplay(), new ManiaModCinema()),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Fun:
|
case ModType.Fun:
|
||||||
@ -159,7 +167,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return new Mod[] { };
|
return Array.Empty<Mod>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,11 +187,6 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
|
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
|
||||||
|
|
||||||
public ManiaRuleset(RulesetInfo rulesetInfo = null)
|
|
||||||
: base(rulesetInfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IEnumerable<int> AvailableVariants
|
public override IEnumerable<int> AvailableVariants
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -269,7 +272,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
return stage1Bindings.Concat(stage2Bindings);
|
return stage1Bindings.Concat(stage2Bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new KeyBinding[0];
|
return Array.Empty<KeyBinding>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetVariantName(int variant)
|
public override string GetVariantName(int variant)
|
||||||
|
22
osu.Game.Rulesets.Mania/Mods/ManiaModCinema.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
|
{
|
||||||
|
public class ManiaModCinema : ModCinema<ManiaHitObject>
|
||||||
|
{
|
||||||
|
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
|
||||||
|
{
|
||||||
|
ScoreInfo = new ScoreInfo { User = new User { Username = "osu!topus!" } },
|
||||||
|
Replay = new ManiaAutoGenerator((ManiaBeatmap)beatmap).Generate(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModNightcore : ModNightcore
|
public class ManiaModNightcore : ModNightcore<ManiaHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,11 @@
|
|||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Scoring
|
namespace osu.Game.Rulesets.Mania.Scoring
|
||||||
{
|
{
|
||||||
internal class ManiaScoreProcessor : ScoreProcessor<ManiaHitObject>
|
internal class ManiaScoreProcessor : ScoreProcessor
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hit HP multiplier at OD = 0.
|
/// The hit HP multiplier at OD = 0.
|
||||||
@ -51,12 +49,12 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private double hpMultiplier = 1;
|
private double hpMultiplier = 1;
|
||||||
|
|
||||||
public ManiaScoreProcessor(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
public ManiaScoreProcessor(IBeatmap beatmap)
|
||||||
: base(drawableRuleset)
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyBeatmap(Beatmap<ManiaHitObject> beatmap)
|
protected override void ApplyBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
base.ApplyBeatmap(beatmap);
|
base.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
@ -65,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
|||||||
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
|
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SimulateAutoplay(Beatmap<ManiaHitObject> beatmap)
|
protected override void SimulateAutoplay(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
@ -14,11 +14,9 @@ using osu.Game.Rulesets.Mania.Configuration;
|
|||||||
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.Replays;
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
using osu.Game.Rulesets.Mania.Scoring;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
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.Scoring;
|
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -39,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||||
|
|
||||||
public DrawableManiaRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
BarLines = new BarLineGenerator<BarLine>(Beatmap).BarLines;
|
BarLines = new BarLineGenerator<BarLine>(Beatmap).BarLines;
|
||||||
@ -67,8 +65,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages);
|
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages);
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
|
||||||
|
|
||||||
public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns;
|
public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns;
|
||||||
|
|
||||||
protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
|
protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
|
||||||
|
After Width: | Height: | Size: 4.4 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/default-0.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/default-1.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/default-2.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/default-3.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/default-4.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/default-5.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/default-6.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/default-7.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/default-8.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/default-9.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/hit0.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/hit100.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/hit300.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/hit50.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/hitcircle.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 6.9 KiB |
2
osu.Game.Rulesets.Osu.Tests/Resources/old-skin/skin.ini
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[General]
|
||||||
|
Version: 1.0
|
@ -19,9 +19,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
private Skin metricsSkin;
|
private Skin metricsSkin;
|
||||||
private Skin defaultSkin;
|
private Skin defaultSkin;
|
||||||
private Skin specialSkin;
|
private Skin specialSkin;
|
||||||
|
private Skin oldSkin;
|
||||||
|
|
||||||
protected SkinnableTestScene()
|
protected SkinnableTestScene()
|
||||||
: base(2, 2)
|
: base(2, 3)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
metricsSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), audio, true);
|
metricsSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), audio, true);
|
||||||
defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
|
defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
|
||||||
specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/special_skin"), audio, true);
|
specialSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/special_skin"), audio, true);
|
||||||
|
oldSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/old_skin"), audio, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetContents(Func<Drawable> creationFunction)
|
public void SetContents(Func<Drawable> creationFunction)
|
||||||
@ -41,6 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
Cell(1).Child = createProvider(metricsSkin, creationFunction);
|
Cell(1).Child = createProvider(metricsSkin, creationFunction);
|
||||||
Cell(2).Child = createProvider(defaultSkin, creationFunction);
|
Cell(2).Child = createProvider(defaultSkin, creationFunction);
|
||||||
Cell(3).Child = createProvider(specialSkin, creationFunction);
|
Cell(3).Child = createProvider(specialSkin, creationFunction);
|
||||||
|
Cell(4).Child = createProvider(oldSkin, creationFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable createProvider(Skin skin, Func<Drawable> creationFunction)
|
private Drawable createProvider(Skin skin, Func<Drawable> creationFunction)
|
||||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
var drawable = CreateDrawableHitCircle(circle, auto);
|
var drawable = CreateDrawableHitCircle(circle, auto);
|
||||||
|
|
||||||
foreach (var mod in Mods.Value.OfType<IApplicableToDrawableHitObjects>())
|
foreach (var mod in SelectedMods.Value.OfType<IApplicableToDrawableHitObjects>())
|
||||||
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
||||||
|
|
||||||
return drawable;
|
return drawable;
|
||||||
|
@ -14,9 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||||
|
|
||||||
public TestSceneHitCircleHidden()
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
Mods.Value = new[] { new OsuModHidden() };
|
SelectedMods.Value = new[] { new OsuModHidden() };
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
protected override Player CreatePlayer(Ruleset ruleset)
|
protected override Player CreatePlayer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
Mods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), };
|
SelectedMods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), };
|
||||||
|
|
||||||
return base.CreatePlayer(ruleset);
|
return base.CreatePlayer(ruleset);
|
||||||
}
|
}
|
||||||
|
@ -362,7 +362,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
var drawable = CreateDrawableSlider(slider);
|
var drawable = CreateDrawableSlider(slider);
|
||||||
|
|
||||||
foreach (var mod in Mods.Value.OfType<IApplicableToDrawableHitObjects>())
|
foreach (var mod in SelectedMods.Value.OfType<IApplicableToDrawableHitObjects>())
|
||||||
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
||||||
|
|
||||||
drawable.OnNewResult += onNewResult;
|
drawable.OnNewResult += onNewResult;
|
||||||
|
@ -14,9 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||||
|
|
||||||
public TestSceneSliderHidden()
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
Mods.Value = new[] { new OsuModHidden() };
|
SelectedMods.Value = new[] { new OsuModHidden() };
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,11 +286,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great;
|
private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great;
|
||||||
|
|
||||||
private bool assertHeadMissTailTracked() => judgementResults[judgementResults.Count - 2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
|
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
|
||||||
|
|
||||||
private bool assertMidSliderJudgements() => judgementResults[judgementResults.Count - 2].Type == HitResult.Great;
|
private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.Great;
|
||||||
|
|
||||||
private bool assertMidSliderJudgementFail() => judgementResults[judgementResults.Count - 2].Type == HitResult.Miss;
|
private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.Miss;
|
||||||
|
|
||||||
private ScoreAccessibleReplayPlayer currentPlayer;
|
private ScoreAccessibleReplayPlayer currentPlayer;
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
Depth = depthIndex++
|
Depth = depthIndex++
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var mod in Mods.Value.OfType<IApplicableToDrawableHitObjects>())
|
foreach (var mod in SelectedMods.Value.OfType<IApplicableToDrawableHitObjects>())
|
||||||
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
||||||
|
|
||||||
Add(drawable);
|
Add(drawable);
|
||||||
|
@ -14,9 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||||
|
|
||||||
public TestSceneSpinnerHidden()
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
{
|
{
|
||||||
Mods.Value = new[] { new OsuModHidden() };
|
SelectedMods.Value = new[] { new OsuModHidden() };
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,21 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddAssert("is rotation absolute almost same", () => Precision.AlmostEquals(drawableSpinner.Disc.RotationAbsolute, estimatedRotation, 100));
|
AddAssert("is rotation absolute almost same", () => Precision.AlmostEquals(drawableSpinner.Disc.RotationAbsolute, estimatedRotation, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSpinPerMinuteOnRewind()
|
||||||
|
{
|
||||||
|
double estimatedSpm = 0;
|
||||||
|
|
||||||
|
addSeekStep(2500);
|
||||||
|
AddStep("retrieve spm", () => estimatedSpm = drawableSpinner.SpmCounter.SpinsPerMinute);
|
||||||
|
|
||||||
|
addSeekStep(5000);
|
||||||
|
AddAssert("spm still valid", () => Precision.AlmostEquals(drawableSpinner.SpmCounter.SpinsPerMinute, estimatedSpm, 1.0));
|
||||||
|
|
||||||
|
addSeekStep(2500);
|
||||||
|
AddAssert("spm still valid", () => Precision.AlmostEquals(drawableSpinner.SpmCounter.SpinsPerMinute, estimatedSpm, 1.0));
|
||||||
|
}
|
||||||
|
|
||||||
private void addSeekStep(double time)
|
private void addSeekStep(double time)
|
||||||
{
|
{
|
||||||
AddStep($"seek to {time}", () => track.Seek(time));
|
AddStep($"seek to {time}", () => track.Seek(time));
|
||||||
@ -84,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
new Spinner
|
new Spinner
|
||||||
{
|
{
|
||||||
Position = new Vector2(256, 192),
|
Position = new Vector2(256, 192),
|
||||||
EndTime = 5000,
|
EndTime = 6000,
|
||||||
},
|
},
|
||||||
// placeholder object to avoid hitting the results screen
|
// placeholder object to avoid hitting the results screen
|
||||||
new HitObject
|
new HitObject
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Lines;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A visualisation of the line between two <see cref="PathControlPointPiece"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public class PathControlPointConnectionPiece : CompositeDrawable
|
||||||
|
{
|
||||||
|
public PathControlPoint ControlPoint;
|
||||||
|
|
||||||
|
private readonly Path path;
|
||||||
|
private readonly Slider slider;
|
||||||
|
|
||||||
|
private IBindable<Vector2> sliderPosition;
|
||||||
|
private IBindable<int> pathVersion;
|
||||||
|
|
||||||
|
public PathControlPointConnectionPiece(Slider slider, PathControlPoint controlPoint)
|
||||||
|
{
|
||||||
|
this.slider = slider;
|
||||||
|
ControlPoint = controlPoint;
|
||||||
|
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = path = new SmoothPath
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
PathRadius = 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
sliderPosition = slider.PositionBindable.GetBoundCopy();
|
||||||
|
sliderPosition.BindValueChanged(_ => updateConnectingPath());
|
||||||
|
|
||||||
|
pathVersion = slider.Path.Version.GetBoundCopy();
|
||||||
|
pathVersion.BindValueChanged(_ => updateConnectingPath());
|
||||||
|
|
||||||
|
updateConnectingPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the path connecting this control point to the next one.
|
||||||
|
/// </summary>
|
||||||
|
private void updateConnectingPath()
|
||||||
|
{
|
||||||
|
Position = slider.StackedPosition + ControlPoint.Position.Value;
|
||||||
|
|
||||||
|
path.ClearVertices();
|
||||||
|
|
||||||
|
int index = slider.Path.ControlPoints.IndexOf(ControlPoint) + 1;
|
||||||
|
|
||||||
|
if (index == 0 || index == slider.Path.ControlPoints.Count)
|
||||||
|
return;
|
||||||
|
|
||||||
|
path.AddVertex(Vector2.Zero);
|
||||||
|
path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
|
||||||
|
|
||||||
|
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Lines;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -19,6 +18,9 @@ using osuTK.Input;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A visualisation of a single <see cref="PathControlPoint"/> in a <see cref="Slider"/>.
|
||||||
|
/// </summary>
|
||||||
public class PathControlPointPiece : BlueprintPiece<Slider>
|
public class PathControlPointPiece : BlueprintPiece<Slider>
|
||||||
{
|
{
|
||||||
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
|
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
|
||||||
@ -28,7 +30,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
public readonly PathControlPoint ControlPoint;
|
public readonly PathControlPoint ControlPoint;
|
||||||
|
|
||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
private readonly Path path;
|
|
||||||
private readonly Container marker;
|
private readonly Container marker;
|
||||||
private readonly Drawable markerRing;
|
private readonly Drawable markerRing;
|
||||||
|
|
||||||
@ -39,12 +40,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
private OsuColour colours { get; set; }
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
private IBindable<Vector2> sliderPosition;
|
private IBindable<Vector2> sliderPosition;
|
||||||
private IBindable<int> pathVersion;
|
private IBindable<Vector2> controlPointPosition;
|
||||||
|
|
||||||
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
|
public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
|
||||||
{
|
{
|
||||||
this.slider = slider;
|
this.slider = slider;
|
||||||
|
|
||||||
ControlPoint = controlPoint;
|
ControlPoint = controlPoint;
|
||||||
|
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
@ -52,11 +52,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
path = new SmoothPath
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
PathRadius = 1
|
|
||||||
},
|
|
||||||
marker = new Container
|
marker = new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -96,20 +91,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
sliderPosition = slider.PositionBindable.GetBoundCopy();
|
sliderPosition = slider.PositionBindable.GetBoundCopy();
|
||||||
sliderPosition.BindValueChanged(_ => updateDisplay());
|
sliderPosition.BindValueChanged(_ => updateMarkerDisplay());
|
||||||
|
|
||||||
pathVersion = slider.Path.Version.GetBoundCopy();
|
controlPointPosition = ControlPoint.Position.GetBoundCopy();
|
||||||
pathVersion.BindValueChanged(_ => updateDisplay());
|
controlPointPosition.BindValueChanged(_ => updateMarkerDisplay());
|
||||||
|
|
||||||
IsSelected.BindValueChanged(_ => updateMarkerDisplay());
|
IsSelected.BindValueChanged(_ => updateMarkerDisplay());
|
||||||
|
|
||||||
updateDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateDisplay()
|
|
||||||
{
|
|
||||||
updateMarkerDisplay();
|
updateMarkerDisplay();
|
||||||
updateConnectingPath();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The connecting path is excluded from positional input
|
// The connecting path is excluded from positional input
|
||||||
@ -189,26 +178,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
colour = Color4.White;
|
colour = Color4.White;
|
||||||
marker.Colour = colour;
|
marker.Colour = colour;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the path connecting this control point to the previous one.
|
|
||||||
/// </summary>
|
|
||||||
private void updateConnectingPath()
|
|
||||||
{
|
|
||||||
path.ClearVertices();
|
|
||||||
|
|
||||||
int index = slider.Path.ControlPoints.IndexOf(ControlPoint);
|
|
||||||
|
|
||||||
if (index == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (++index != slider.Path.ControlPoints.Count)
|
|
||||||
{
|
|
||||||
path.AddVertex(Vector2.Zero);
|
|
||||||
path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
internal readonly Container<PathControlPointPiece> Pieces;
|
internal readonly Container<PathControlPointPiece> Pieces;
|
||||||
|
|
||||||
|
private readonly Container<PathControlPointConnectionPiece> connections;
|
||||||
|
|
||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
|
|
||||||
private readonly bool allowSelection;
|
private readonly bool allowSelection;
|
||||||
@ -42,7 +44,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
InternalChild = Pieces = new Container<PathControlPointPiece> { RelativeSizeAxes = Axes.Both };
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
connections = new Container<PathControlPointConnectionPiece> { RelativeSizeAxes = Axes.Both },
|
||||||
|
Pieces = new Container<PathControlPointPiece> { RelativeSizeAxes = Axes.Both }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -62,19 +68,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
foreach (var point in controlPoints)
|
foreach (var point in controlPoints)
|
||||||
{
|
{
|
||||||
var piece = new PathControlPointPiece(slider, point);
|
Pieces.Add(new PathControlPointPiece(slider, point).With(d =>
|
||||||
|
{
|
||||||
|
if (allowSelection)
|
||||||
|
d.RequestSelection = selectPiece;
|
||||||
|
}));
|
||||||
|
|
||||||
if (allowSelection)
|
connections.Add(new PathControlPointConnectionPiece(slider, point));
|
||||||
piece.RequestSelection = selectPiece;
|
|
||||||
|
|
||||||
Pieces.Add(piece);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeControlPoints(IEnumerable<PathControlPoint> controlPoints)
|
private void removeControlPoints(IEnumerable<PathControlPoint> controlPoints)
|
||||||
{
|
{
|
||||||
foreach (var point in controlPoints)
|
foreach (var point in controlPoints)
|
||||||
|
{
|
||||||
Pieces.RemoveAll(p => p.ControlPoint == point);
|
Pieces.RemoveAll(p => p.ControlPoint == point);
|
||||||
|
connections.RemoveAll(c => c.ControlPoint == point);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
|
@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
protected override bool OnDoubleClick(DoubleClickEvent e)
|
protected override bool OnDoubleClick(DoubleClickEvent e)
|
||||||
{
|
{
|
||||||
// Todo: This should all not occur on double click, but rather if the previous control point is hovered.
|
// Todo: This should all not occur on double click, but rather if the previous control point is hovered.
|
||||||
segmentStart = HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 1];
|
segmentStart = HitObject.Path.ControlPoints[^1];
|
||||||
segmentStart.Type.Value = PathType.Linear;
|
segmentStart.Type.Value = PathType.Linear;
|
||||||
|
|
||||||
currentSegmentLength = 1;
|
currentSegmentLength = 1;
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const double editor_hit_object_fade_out_extension = 500;
|
private const double editor_hit_object_fade_out_extension = 500;
|
||||||
|
|
||||||
public DrawableOsuEditRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -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 JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
public class OsuDistanceSnapGrid : CircularDistanceSnapGrid
|
public class OsuDistanceSnapGrid : CircularDistanceSnapGrid
|
||||||
{
|
{
|
||||||
public OsuDistanceSnapGrid(OsuHitObject hitObject, [CanBeNull] OsuHitObject nextHitObject = null)
|
public OsuDistanceSnapGrid(OsuHitObject hitObject, [CanBeNull] OsuHitObject nextHitObject = null)
|
||||||
: base(hitObject.StackedPosition, hitObject.StartTime, nextHitObject?.StartTime)
|
: base(hitObject.StackedEndPosition, hitObject.GetEndTime(), nextHitObject?.StartTime)
|
||||||
{
|
{
|
||||||
Masking = true;
|
Masking = true;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
=> new DrawableOsuEditRuleset(ruleset, beatmap, mods);
|
=> new DrawableOsuEditRuleset(ruleset, beatmap, mods);
|
||||||
|
|
||||||
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
|
||||||
@ -92,7 +92,24 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
OsuHitObject sourceObject = EditorBeatmap.HitObjects[sourceIndex];
|
OsuHitObject sourceObject = EditorBeatmap.HitObjects[sourceIndex];
|
||||||
OsuHitObject targetObject = sourceIndex + targetOffset < EditorBeatmap.HitObjects.Count ? EditorBeatmap.HitObjects[sourceIndex + targetOffset] : null;
|
|
||||||
|
int targetIndex = sourceIndex + targetOffset;
|
||||||
|
OsuHitObject targetObject = null;
|
||||||
|
|
||||||
|
// Keep advancing the target object while its start time falls before the end time of the source object
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (targetIndex >= EditorBeatmap.HitObjects.Count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (EditorBeatmap.HitObjects[targetIndex].StartTime >= sourceObject.GetEndTime())
|
||||||
|
{
|
||||||
|
targetObject = EditorBeatmap.HitObjects[targetIndex];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
return new OsuDistanceSnapGrid(sourceObject, targetObject);
|
return new OsuDistanceSnapGrid(sourceObject, targetObject);
|
||||||
}
|
}
|
||||||
|
25
osu.Game.Rulesets.Osu/Mods/OsuModCinema.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
|
{
|
||||||
|
public class OsuModCinema : ModCinema<OsuHitObject>
|
||||||
|
{
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).Append(typeof(OsuModSpunOut)).ToArray();
|
||||||
|
|
||||||
|
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
|
||||||
|
{
|
||||||
|
ScoreInfo = new ScoreInfo { User = new User { Username = "Autoplay" } },
|
||||||
|
Replay = new OsuAutoGenerator(beatmap).Generate()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,11 @@
|
|||||||
// 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.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModNightcore : ModNightcore
|
public class OsuModNightcore : ModNightcore<OsuHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => 1.12;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
@ -54,13 +55,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case DrawableSlider slider:
|
case DrawableSlider slider:
|
||||||
slider.AccentColour.BindValueChanged(_ =>
|
slider.Body.OnSkinChanged += () => applySliderState(slider);
|
||||||
{
|
applySliderState(slider);
|
||||||
//will trigger on skin change.
|
|
||||||
slider.Body.AccentColour = slider.AccentColour.Value.Opacity(0);
|
|
||||||
slider.Body.BorderColour = slider.AccentColour.Value;
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DrawableSpinner spinner:
|
case DrawableSpinner spinner:
|
||||||
@ -69,5 +65,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applySliderState(DrawableSlider slider)
|
||||||
|
{
|
||||||
|
((PlaySliderBody)slider.Body.Drawable).AccentColour = slider.AccentColour.Value.Opacity(0);
|
||||||
|
((PlaySliderBody)slider.Body.Drawable).BorderColour = slider.AccentColour.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
@ -98,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
|
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
|
||||||
{
|
{
|
||||||
bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0;
|
bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0;
|
||||||
List<Vector2> curve = drawableSlider.Body.CurrentCurve;
|
List<Vector2> curve = ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
|
||||||
|
|
||||||
Position = isRepeatAtEnd ? end : start;
|
Position = isRepeatAtEnd ? end : start;
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Configuration;
|
|
||||||
using osu.Game.Rulesets.Osu.Skinning;
|
using osu.Game.Rulesets.Osu.Skinning;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -24,8 +23,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
public DrawableSliderHead HeadCircle => headContainer.Child;
|
public DrawableSliderHead HeadCircle => headContainer.Child;
|
||||||
public DrawableSliderTail TailCircle => tailContainer.Child;
|
public DrawableSliderTail TailCircle => tailContainer.Child;
|
||||||
|
|
||||||
public readonly SnakingSliderBody Body;
|
|
||||||
public readonly SliderBall Ball;
|
public readonly SliderBall Ball;
|
||||||
|
public readonly SkinnableDrawable Body;
|
||||||
|
|
||||||
|
private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody;
|
||||||
|
|
||||||
private readonly Container<DrawableSliderHead> headContainer;
|
private readonly Container<DrawableSliderHead> headContainer;
|
||||||
private readonly Container<DrawableSliderTail> tailContainer;
|
private readonly Container<DrawableSliderTail> tailContainer;
|
||||||
@ -37,10 +38,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
||||||
private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
|
private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
|
||||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||||
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
|
||||||
private OsuRulesetConfigManager config { get; set; }
|
|
||||||
|
|
||||||
public DrawableSlider(Slider s)
|
public DrawableSlider(Slider s)
|
||||||
: base(s)
|
: base(s)
|
||||||
@ -51,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
Body = new SnakingSliderBody(s),
|
Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling),
|
||||||
tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
||||||
repeatContainer = new Container<DrawableRepeatPoint> { RelativeSizeAxes = Axes.Both },
|
repeatContainer = new Container<DrawableRepeatPoint> { RelativeSizeAxes = Axes.Both },
|
||||||
Ball = new SliderBall(s, this)
|
Ball = new SliderBall(s, this)
|
||||||
@ -70,28 +67,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
config?.BindWith(OsuRulesetSetting.SnakingInSliders, Body.SnakingIn);
|
|
||||||
config?.BindWith(OsuRulesetSetting.SnakingOutSliders, Body.SnakingOut);
|
|
||||||
|
|
||||||
positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
||||||
stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
||||||
scaleBindable.BindValueChanged(scale =>
|
scaleBindable.BindValueChanged(scale => Ball.Scale = new Vector2(scale.NewValue));
|
||||||
{
|
|
||||||
updatePathRadius();
|
|
||||||
Ball.Scale = new Vector2(scale.NewValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
positionBindable.BindTo(HitObject.PositionBindable);
|
positionBindable.BindTo(HitObject.PositionBindable);
|
||||||
stackHeightBindable.BindTo(HitObject.StackHeightBindable);
|
stackHeightBindable.BindTo(HitObject.StackHeightBindable);
|
||||||
scaleBindable.BindTo(HitObject.ScaleBindable);
|
scaleBindable.BindTo(HitObject.ScaleBindable);
|
||||||
pathVersion.BindTo(slider.Path.Version);
|
|
||||||
|
|
||||||
pathVersion.BindValueChanged(_ => Body.Refresh());
|
|
||||||
|
|
||||||
AccentColour.BindValueChanged(colour =>
|
AccentColour.BindValueChanged(colour =>
|
||||||
{
|
{
|
||||||
Body.AccentColour = colour.NewValue;
|
|
||||||
|
|
||||||
foreach (var drawableHitObject in NestedHitObjects)
|
foreach (var drawableHitObject in NestedHitObjects)
|
||||||
drawableHitObject.AccentColour.Value = colour.NewValue;
|
drawableHitObject.AccentColour.Value = colour.NewValue;
|
||||||
}, true);
|
}, true);
|
||||||
@ -169,16 +154,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
double completionProgress = Math.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
|
double completionProgress = Math.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
|
||||||
|
|
||||||
Ball.UpdateProgress(completionProgress);
|
Ball.UpdateProgress(completionProgress);
|
||||||
Body.UpdateProgress(completionProgress);
|
sliderBody?.UpdateProgress(completionProgress);
|
||||||
|
|
||||||
foreach (DrawableHitObject hitObject in NestedHitObjects)
|
foreach (DrawableHitObject hitObject in NestedHitObjects)
|
||||||
{
|
{
|
||||||
if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(slider.Path.PositionAt(Body.SnakedStart ?? 0), slider.Path.PositionAt(Body.SnakedEnd ?? 0));
|
if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(slider.Path.PositionAt(sliderBody?.SnakedStart ?? 0), slider.Path.PositionAt(sliderBody?.SnakedEnd ?? 0));
|
||||||
if (hitObject is IRequireTracking t) t.Tracking = Ball.Tracking;
|
if (hitObject is IRequireTracking t) t.Tracking = Ball.Tracking;
|
||||||
}
|
}
|
||||||
|
|
||||||
Size = Body.Size;
|
Size = sliderBody?.Size ?? Vector2.Zero;
|
||||||
OriginPosition = Body.PathOffset;
|
OriginPosition = sliderBody?.PathOffset ?? Vector2.Zero;
|
||||||
|
|
||||||
if (DrawSize != Vector2.Zero)
|
if (DrawSize != Vector2.Zero)
|
||||||
{
|
{
|
||||||
@ -192,28 +177,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
public override void OnKilled()
|
public override void OnKilled()
|
||||||
{
|
{
|
||||||
base.OnKilled();
|
base.OnKilled();
|
||||||
Body.RecyclePath();
|
sliderBody?.RecyclePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
private float sliderPathRadius;
|
|
||||||
|
|
||||||
protected override void ApplySkin(ISkinSource skin, bool allowFallback)
|
protected override void ApplySkin(ISkinSource skin, bool allowFallback)
|
||||||
{
|
{
|
||||||
base.ApplySkin(skin, allowFallback);
|
base.ApplySkin(skin, allowFallback);
|
||||||
|
|
||||||
Body.BorderSize = skin.GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.SliderBorderSize)?.Value ?? SliderBody.DEFAULT_BORDER_SIZE;
|
|
||||||
sliderPathRadius = skin.GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.SliderPathRadius)?.Value ?? OsuHitObject.OBJECT_RADIUS;
|
|
||||||
updatePathRadius();
|
|
||||||
|
|
||||||
Body.AccentColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderTrackOverride)?.Value ?? AccentColour.Value;
|
|
||||||
Body.BorderColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBorder)?.Value ?? Color4.White;
|
|
||||||
|
|
||||||
bool allowBallTint = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false;
|
bool allowBallTint = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false;
|
||||||
Ball.Colour = allowBallTint ? AccentColour.Value : Color4.White;
|
Ball.Colour = allowBallTint ? AccentColour.Value : Color4.White;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius;
|
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
if (userTriggered || Time.Current < slider.EndTime)
|
if (userTriggered || Time.Current < slider.EndTime)
|
||||||
@ -264,6 +238,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
public Drawable ProxiedLayer => HeadCircle.ProxiedLayer;
|
public Drawable ProxiedLayer => HeadCircle.ProxiedLayer;
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Body.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => sliderBody?.ReceivePositionalInputAt(screenSpacePos) ?? base.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
|
private class DefaultSliderBody : PlaySliderBody
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
public readonly SpinnerDisc Disc;
|
public readonly SpinnerDisc Disc;
|
||||||
public readonly SpinnerTicks Ticks;
|
public readonly SpinnerTicks Ticks;
|
||||||
private readonly SpinnerSpmCounter spmCounter;
|
public readonly SpinnerSpmCounter SpmCounter;
|
||||||
|
|
||||||
private readonly Container mainContainer;
|
private readonly Container mainContainer;
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
spmCounter = new SpinnerSpmCounter
|
SpmCounter = new SpinnerSpmCounter
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -177,8 +177,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
Disc.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false;
|
Disc.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false;
|
||||||
if (!spmCounter.IsPresent && Disc.Tracking)
|
if (!SpmCounter.IsPresent && Disc.Tracking)
|
||||||
spmCounter.FadeIn(HitObject.TimeFadeIn);
|
SpmCounter.FadeIn(HitObject.TimeFadeIn);
|
||||||
|
|
||||||
base.Update();
|
base.Update();
|
||||||
}
|
}
|
||||||
@ -189,7 +189,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
circle.Rotation = Disc.Rotation;
|
circle.Rotation = Disc.Rotation;
|
||||||
Ticks.Rotation = Disc.Rotation;
|
Ticks.Rotation = Disc.Rotation;
|
||||||
spmCounter.SetRotation(Disc.RotationAbsolute);
|
SpmCounter.SetRotation(Disc.RotationAbsolute);
|
||||||
|
|
||||||
float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
|
float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
|
||||||
Disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint);
|
Disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint);
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
// 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.Lines;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||||
|
{
|
||||||
|
public abstract class DrawableSliderPath : SmoothPath
|
||||||
|
{
|
||||||
|
protected const float BORDER_PORTION = 0.128f;
|
||||||
|
protected const float GRADIENT_PORTION = 1 - BORDER_PORTION;
|
||||||
|
|
||||||
|
private const float border_max_size = 8f;
|
||||||
|
private const float border_min_size = 0f;
|
||||||
|
|
||||||
|
private Color4 borderColour = Color4.White;
|
||||||
|
|
||||||
|
public Color4 BorderColour
|
||||||
|
{
|
||||||
|
get => borderColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (borderColour == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
borderColour = value;
|
||||||
|
|
||||||
|
InvalidateTexture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 accentColour = Color4.White;
|
||||||
|
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get => accentColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (accentColour == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
accentColour = value;
|
||||||
|
|
||||||
|
InvalidateTexture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float borderSize = 1;
|
||||||
|
|
||||||
|
public float BorderSize
|
||||||
|
{
|
||||||
|
get => borderSize;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (borderSize == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (value < border_min_size || value > border_max_size)
|
||||||
|
return;
|
||||||
|
|
||||||
|
borderSize = value;
|
||||||
|
|
||||||
|
InvalidateTexture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected float CalculatedBorderPortion => BorderSize * BORDER_PORTION;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
|
using osu.Game.Rulesets.Osu.Skinning;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||||
|
{
|
||||||
|
public abstract class PlaySliderBody : SnakingSliderBody
|
||||||
|
{
|
||||||
|
private IBindable<float> scaleBindable;
|
||||||
|
private IBindable<int> pathVersion;
|
||||||
|
private IBindable<Color4> accentColour;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private DrawableHitObject drawableObject { get; set; }
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private OsuRulesetConfigManager config { get; set; }
|
||||||
|
|
||||||
|
private Slider slider;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin)
|
||||||
|
{
|
||||||
|
slider = (Slider)drawableObject.HitObject;
|
||||||
|
|
||||||
|
scaleBindable = slider.ScaleBindable.GetBoundCopy();
|
||||||
|
scaleBindable.BindValueChanged(scale => PathRadius = OsuHitObject.OBJECT_RADIUS * scale.NewValue, true);
|
||||||
|
|
||||||
|
pathVersion = slider.Path.Version.GetBoundCopy();
|
||||||
|
pathVersion.BindValueChanged(_ => Refresh());
|
||||||
|
|
||||||
|
accentColour = drawableObject.AccentColour.GetBoundCopy();
|
||||||
|
accentColour.BindValueChanged(accent => updateAccentColour(skin, accent.NewValue), true);
|
||||||
|
|
||||||
|
config?.BindWith(OsuRulesetSetting.SnakingInSliders, SnakingIn);
|
||||||
|
config?.BindWith(OsuRulesetSetting.SnakingOutSliders, SnakingOut);
|
||||||
|
|
||||||
|
BorderSize = skin.GetConfig<OsuSkinConfiguration, float>(OsuSkinConfiguration.SliderBorderSize)?.Value ?? 1;
|
||||||
|
BorderColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderBorder)?.Value ?? Color4.White;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAccentColour(ISkinSource skin, Color4 defaultAccentColour)
|
||||||
|
=> AccentColour = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.SliderTrackOverride)?.Value ?? defaultAccentColour;
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Lines;
|
using osu.Framework.Graphics.Lines;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -12,9 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
{
|
{
|
||||||
public abstract class SliderBody : CompositeDrawable
|
public abstract class SliderBody : CompositeDrawable
|
||||||
{
|
{
|
||||||
public const float DEFAULT_BORDER_SIZE = 1;
|
private DrawableSliderPath path;
|
||||||
|
|
||||||
private SliderPath path;
|
|
||||||
|
|
||||||
protected Path Path => path;
|
protected Path Path => path;
|
||||||
|
|
||||||
@ -80,19 +79,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialises a new <see cref="SliderPath"/>, releasing all resources retained by the old one.
|
/// Initialises a new <see cref="DrawableSliderPath"/>, releasing all resources retained by the old one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void RecyclePath()
|
public virtual void RecyclePath()
|
||||||
{
|
{
|
||||||
InternalChild = path = new SliderPath
|
InternalChild = path = CreateSliderPath().With(p =>
|
||||||
{
|
{
|
||||||
Position = path?.Position ?? Vector2.Zero,
|
p.Position = path?.Position ?? Vector2.Zero;
|
||||||
PathRadius = path?.PathRadius ?? 10,
|
p.PathRadius = path?.PathRadius ?? 10;
|
||||||
AccentColour = path?.AccentColour ?? Color4.White,
|
p.AccentColour = path?.AccentColour ?? Color4.White;
|
||||||
BorderColour = path?.BorderColour ?? Color4.White,
|
p.BorderColour = path?.BorderColour ?? Color4.White;
|
||||||
BorderSize = path?.BorderSize ?? DEFAULT_BORDER_SIZE,
|
p.BorderSize = path?.BorderSize ?? 1;
|
||||||
Vertices = path?.Vertices ?? Array.Empty<Vector2>()
|
p.Vertices = path?.Vertices ?? Array.Empty<Vector2>();
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
|
||||||
@ -103,77 +102,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
/// <param name="vertices">The vertices</param>
|
/// <param name="vertices">The vertices</param>
|
||||||
protected void SetVertices(IReadOnlyList<Vector2> vertices) => path.Vertices = vertices;
|
protected void SetVertices(IReadOnlyList<Vector2> vertices) => path.Vertices = vertices;
|
||||||
|
|
||||||
private class SliderPath : SmoothPath
|
protected virtual DrawableSliderPath CreateSliderPath() => new DefaultDrawableSliderPath();
|
||||||
|
|
||||||
|
private class DefaultDrawableSliderPath : DrawableSliderPath
|
||||||
{
|
{
|
||||||
private const float border_max_size = 8f;
|
|
||||||
private const float border_min_size = 0f;
|
|
||||||
|
|
||||||
private const float border_portion = 0.128f;
|
|
||||||
private const float gradient_portion = 1 - border_portion;
|
|
||||||
|
|
||||||
private const float opacity_at_centre = 0.3f;
|
private const float opacity_at_centre = 0.3f;
|
||||||
private const float opacity_at_edge = 0.8f;
|
private const float opacity_at_edge = 0.8f;
|
||||||
|
|
||||||
private Color4 borderColour = Color4.White;
|
|
||||||
|
|
||||||
public Color4 BorderColour
|
|
||||||
{
|
|
||||||
get => borderColour;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (borderColour == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
borderColour = value;
|
|
||||||
|
|
||||||
InvalidateTexture();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Color4 accentColour = Color4.White;
|
|
||||||
|
|
||||||
public Color4 AccentColour
|
|
||||||
{
|
|
||||||
get => accentColour;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (accentColour == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
accentColour = value;
|
|
||||||
|
|
||||||
InvalidateTexture();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float borderSize = DEFAULT_BORDER_SIZE;
|
|
||||||
|
|
||||||
public float BorderSize
|
|
||||||
{
|
|
||||||
get => borderSize;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (borderSize == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (value < border_min_size || value > border_max_size)
|
|
||||||
return;
|
|
||||||
|
|
||||||
borderSize = value;
|
|
||||||
|
|
||||||
InvalidateTexture();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float calculatedBorderPortion => BorderSize * border_portion;
|
|
||||||
|
|
||||||
protected override Color4 ColourAt(float position)
|
protected override Color4 ColourAt(float position)
|
||||||
{
|
{
|
||||||
if (calculatedBorderPortion != 0f && position <= calculatedBorderPortion)
|
if (CalculatedBorderPortion != 0f && position <= CalculatedBorderPortion)
|
||||||
return BorderColour;
|
return BorderColour;
|
||||||
|
|
||||||
position -= calculatedBorderPortion;
|
position -= CalculatedBorderPortion;
|
||||||
return new Color4(AccentColour.R, AccentColour.G, AccentColour.B, (opacity_at_edge - (opacity_at_edge - opacity_at_centre) * position / gradient_portion) * AccentColour.A);
|
return new Color4(AccentColour.R, AccentColour.G, AccentColour.B, (opacity_at_edge - (opacity_at_edge - opacity_at_centre) * position / GRADIENT_PORTION) * AccentColour.A);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -50,16 +51,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private Vector2 snakedPathOffset;
|
private Vector2 snakedPathOffset;
|
||||||
|
|
||||||
private readonly Slider slider;
|
private Slider slider;
|
||||||
|
|
||||||
public SnakingSliderBody(Slider slider)
|
|
||||||
{
|
|
||||||
this.slider = slider;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(DrawableHitObject drawableObject)
|
||||||
{
|
{
|
||||||
|
slider = (Slider)drawableObject.HitObject;
|
||||||
|
|
||||||
Refresh();
|
Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
@ -62,6 +63,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
|
|
||||||
public void SetRotation(float currentRotation)
|
public void SetRotation(float currentRotation)
|
||||||
{
|
{
|
||||||
|
// Never calculate SPM by same time of record to avoid 0 / 0 = NaN or X / 0 = Infinity result.
|
||||||
|
if (Precision.AlmostEquals(0, Time.Elapsed))
|
||||||
|
return;
|
||||||
|
|
||||||
// If we've gone back in time, it's fine to work with a fresh set of records for now
|
// If we've gone back in time, it's fine to work with a fresh set of records for now
|
||||||
if (records.Count > 0 && Time.Current < records.Last().Time)
|
if (records.Count > 0 && Time.Current < records.Last().Time)
|
||||||
records.Clear();
|
records.Clear();
|
||||||
@ -71,6 +76,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
var record = records.Peek();
|
var record = records.Peek();
|
||||||
while (records.Count > 0 && Time.Current - records.Peek().Time > spm_count_duration)
|
while (records.Count > 0 && Time.Current - records.Peek().Time > spm_count_duration)
|
||||||
record = records.Dequeue();
|
record = records.Dequeue();
|
||||||
|
|
||||||
SpinsPerMinute = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360;
|
SpinsPerMinute = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
path.ControlPoints.AddRange(value.ControlPoints);
|
path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
|
||||||
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
|
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,16 +23,23 @@ using osu.Game.Rulesets.Difficulty;
|
|||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
using osu.Game.Rulesets.Osu.Configuration;
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
using osu.Game.Rulesets.Osu.Difficulty;
|
using osu.Game.Rulesets.Osu.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Osu.Scoring;
|
||||||
using osu.Game.Rulesets.Osu.Skinning;
|
using osu.Game.Rulesets.Osu.Skinning;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu
|
namespace osu.Game.Rulesets.Osu
|
||||||
{
|
{
|
||||||
public class OsuRuleset : Ruleset
|
public class OsuRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableOsuRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableOsuRuleset(this, beatmap, mods);
|
||||||
|
|
||||||
|
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new OsuScoreProcessor(beatmap);
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
|
||||||
|
|
||||||
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);
|
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);
|
||||||
|
|
||||||
public const string SHORT_NAME = "osu";
|
public const string SHORT_NAME = "osu";
|
||||||
@ -60,7 +67,9 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
if (mods.HasFlag(LegacyMods.Autopilot))
|
if (mods.HasFlag(LegacyMods.Autopilot))
|
||||||
yield return new OsuModAutopilot();
|
yield return new OsuModAutopilot();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Autoplay))
|
if (mods.HasFlag(LegacyMods.Cinema))
|
||||||
|
yield return new OsuModCinema();
|
||||||
|
else if (mods.HasFlag(LegacyMods.Autoplay))
|
||||||
yield return new OsuModAutoplay();
|
yield return new OsuModAutoplay();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Easy))
|
if (mods.HasFlag(LegacyMods.Easy))
|
||||||
@ -127,7 +136,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
return new Mod[]
|
return new Mod[]
|
||||||
{
|
{
|
||||||
new MultiMod(new OsuModAutoplay(), new ModCinema()),
|
new MultiMod(new OsuModAutoplay(), new OsuModCinema()),
|
||||||
new OsuModRelax(),
|
new OsuModRelax(),
|
||||||
new OsuModAutopilot(),
|
new OsuModAutopilot(),
|
||||||
};
|
};
|
||||||
@ -150,7 +159,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return new Mod[] { };
|
return Array.Empty<Mod>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,10 +184,5 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();
|
||||||
|
|
||||||
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
|
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
|
||||||
|
|
||||||
public OsuRuleset(RulesetInfo rulesetInfo = null)
|
|
||||||
: base(rulesetInfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
ReverseArrow,
|
ReverseArrow,
|
||||||
HitCircleText,
|
HitCircleText,
|
||||||
SliderFollowCircle,
|
SliderFollowCircle,
|
||||||
SliderBall
|
SliderBall,
|
||||||
|
SliderBody,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,9 +156,9 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
// TODO: Shouldn't the spinner always spin in the same direction?
|
// TODO: Shouldn't the spinner always spin in the same direction?
|
||||||
if (h is Spinner)
|
if (h is Spinner)
|
||||||
{
|
{
|
||||||
calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[Frames.Count - 1]).Position, out startPosition, out spinnerDirection);
|
calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection);
|
||||||
|
|
||||||
Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[Frames.Count - 1]).Position;
|
Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[^1]).Position;
|
||||||
|
|
||||||
if (spinCentreOffset.Length > SPIN_RADIUS)
|
if (spinCentreOffset.Length > SPIN_RADIUS)
|
||||||
{
|
{
|
||||||
@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
|
|
||||||
private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing)
|
private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing)
|
||||||
{
|
{
|
||||||
OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[Frames.Count - 1];
|
OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[^1];
|
||||||
|
|
||||||
// Wait until Auto could "see and react" to the next note.
|
// Wait until Auto could "see and react" to the next note.
|
||||||
double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime);
|
double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime);
|
||||||
@ -363,7 +363,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
|
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
|
||||||
if (Frames[Frames.Count - 1].Time <= endFrame.Time)
|
if (Frames[^1].Time <= endFrame.Time)
|
||||||
AddFrameToReplay(endFrame);
|
AddFrameToReplay(endFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,22 +5,20 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Scoring
|
namespace osu.Game.Rulesets.Osu.Scoring
|
||||||
{
|
{
|
||||||
internal class OsuScoreProcessor : ScoreProcessor<OsuHitObject>
|
internal class OsuScoreProcessor : ScoreProcessor
|
||||||
{
|
{
|
||||||
public OsuScoreProcessor(DrawableRuleset<OsuHitObject> drawableRuleset)
|
public OsuScoreProcessor(IBeatmap beatmap)
|
||||||
: base(drawableRuleset)
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private float hpDrainRate;
|
private float hpDrainRate;
|
||||||
|
|
||||||
protected override void ApplyBeatmap(Beatmap<OsuHitObject> beatmap)
|
protected override void ApplyBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
base.ApplyBeatmap(beatmap);
|
base.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
|
@ -28,18 +28,18 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
InternalChildren = new[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
|
ExpandTarget = new NonPlayfieldSprite
|
||||||
|
{
|
||||||
|
Texture = skin.GetTexture("cursor"),
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
},
|
||||||
new NonPlayfieldSprite
|
new NonPlayfieldSprite
|
||||||
{
|
{
|
||||||
Texture = skin.GetTexture("cursormiddle"),
|
Texture = skin.GetTexture("cursormiddle"),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
ExpandTarget = new NonPlayfieldSprite
|
|
||||||
{
|
|
||||||
Texture = skin.GetTexture("cursor"),
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
56
osu.Game.Rulesets.Osu/Skinning/LegacySliderBody.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// 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 osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.MathUtils;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Skinning
|
||||||
|
{
|
||||||
|
public class LegacySliderBody : PlaySliderBody
|
||||||
|
{
|
||||||
|
protected override DrawableSliderPath CreateSliderPath() => new LegacyDrawableSliderPath();
|
||||||
|
|
||||||
|
private class LegacyDrawableSliderPath : DrawableSliderPath
|
||||||
|
{
|
||||||
|
private const float shadow_portion = 1 - (OsuLegacySkinTransformer.LEGACY_CIRCLE_RADIUS / OsuHitObject.OBJECT_RADIUS);
|
||||||
|
|
||||||
|
public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, base.AccentColour.A * 0.70f);
|
||||||
|
|
||||||
|
protected override Color4 ColourAt(float position)
|
||||||
|
{
|
||||||
|
float realBorderPortion = shadow_portion + CalculatedBorderPortion;
|
||||||
|
float realGradientPortion = 1 - realBorderPortion;
|
||||||
|
|
||||||
|
if (position <= shadow_portion)
|
||||||
|
return new Color4(0f, 0f, 0f, 0.25f * position / shadow_portion);
|
||||||
|
|
||||||
|
if (position <= realBorderPortion)
|
||||||
|
return BorderColour;
|
||||||
|
|
||||||
|
position -= realBorderPortion;
|
||||||
|
|
||||||
|
Color4 outerColour = AccentColour.Darken(0.1f);
|
||||||
|
Color4 innerColour = lighten(AccentColour, 0.5f);
|
||||||
|
|
||||||
|
return Interpolation.ValueAt(position / realGradientPortion, outerColour, innerColour, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lightens a colour in a way more friendly to dark or strong colours.
|
||||||
|
/// </summary>
|
||||||
|
private static Color4 lighten(Color4 color, float amount)
|
||||||
|
{
|
||||||
|
amount *= 0.5f;
|
||||||
|
return new Color4(
|
||||||
|
Math.Min(1, color.R * (1 + 0.5f * amount) + 1 * amount),
|
||||||
|
Math.Min(1, color.G * (1 + 0.5f * amount) + 1 * amount),
|
||||||
|
Math.Min(1, color.B * (1 + 0.5f * amount) + 1 * amount),
|
||||||
|
color.A);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
/// Their hittable area is 128px, but the actual circle portion is 118px.
|
/// Their hittable area is 128px, but the actual circle portion is 118px.
|
||||||
/// We must account for some gameplay elements such as slider bodies, where this padding is not present.
|
/// We must account for some gameplay elements such as slider bodies, where this padding is not present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float legacy_circle_radius = 64 - 5;
|
public const float LEGACY_CIRCLE_RADIUS = 64 - 5;
|
||||||
|
|
||||||
public OsuLegacySkinTransformer(ISkinSource source)
|
public OsuLegacySkinTransformer(ISkinSource source)
|
||||||
{
|
{
|
||||||
@ -73,6 +73,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.SliderBody:
|
||||||
|
if (hasHitCircle.Value)
|
||||||
|
return new LegacySliderBody();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
case OsuSkinComponents.HitCircle:
|
case OsuSkinComponents.HitCircle:
|
||||||
if (hasHitCircle.Value)
|
if (hasHitCircle.Value)
|
||||||
return new LegacyMainCirclePiece();
|
return new LegacyMainCirclePiece();
|
||||||
@ -124,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
case OsuSkinConfiguration.SliderPathRadius:
|
case OsuSkinConfiguration.SliderPathRadius:
|
||||||
if (hasHitCircle.Value)
|
if (hasHitCircle.Value)
|
||||||
return SkinUtils.As<TValue>(new BindableFloat(legacy_circle_radius));
|
return SkinUtils.As<TValue>(new BindableFloat(LEGACY_CIRCLE_RADIUS));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
private void addPart(Vector2 screenSpacePosition)
|
private void addPart(Vector2 screenSpacePosition)
|
||||||
{
|
{
|
||||||
parts[currentIndex].Position = screenSpacePosition;
|
parts[currentIndex].Position = screenSpacePosition;
|
||||||
parts[currentIndex].Time = time;
|
parts[currentIndex].Time = time + 1;
|
||||||
++parts[currentIndex].InvalidationID;
|
++parts[currentIndex].InvalidationID;
|
||||||
|
|
||||||
currentIndex = (currentIndex + 1) % max_sprites;
|
currentIndex = (currentIndex + 1) % max_sprites;
|
||||||
@ -201,7 +201,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
||||||
private Vector2 size;
|
private Vector2 size;
|
||||||
|
|
||||||
private readonly TrailBatch vertexBatch = new TrailBatch(max_sprites, 1);
|
private readonly QuadBatch<TexturedTrailVertex> vertexBatch = new QuadBatch<TexturedTrailVertex>(max_sprites, 1);
|
||||||
|
|
||||||
public TrailDrawNode(CursorTrail source)
|
public TrailDrawNode(CursorTrail source)
|
||||||
: base(source)
|
: base(source)
|
||||||
@ -227,23 +227,52 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
shader.Bind();
|
shader.Bind();
|
||||||
shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time);
|
shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time);
|
||||||
|
|
||||||
for (int i = 0; i < parts.Length; ++i)
|
texture.TextureGL.Bind();
|
||||||
|
|
||||||
|
RectangleF textureRect = texture.GetTextureRect();
|
||||||
|
|
||||||
|
foreach (var part in parts)
|
||||||
{
|
{
|
||||||
if (parts[i].InvalidationID == -1)
|
if (part.InvalidationID == -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
vertexBatch.DrawTime = parts[i].Time;
|
if (time - part.Time >= 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
Vector2 pos = parts[i].Position;
|
vertexBatch.Add(new TexturedTrailVertex
|
||||||
|
{
|
||||||
|
Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y + size.Y / 2),
|
||||||
|
TexturePosition = textureRect.BottomLeft,
|
||||||
|
Colour = DrawColourInfo.Colour.BottomLeft.Linear,
|
||||||
|
Time = part.Time
|
||||||
|
});
|
||||||
|
|
||||||
DrawQuad(
|
vertexBatch.Add(new TexturedTrailVertex
|
||||||
texture,
|
{
|
||||||
new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
|
Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y + size.Y / 2),
|
||||||
DrawColourInfo.Colour,
|
TexturePosition = textureRect.BottomRight,
|
||||||
null,
|
Colour = DrawColourInfo.Colour.BottomRight.Linear,
|
||||||
vertexBatch.AddAction);
|
Time = part.Time
|
||||||
|
});
|
||||||
|
|
||||||
|
vertexBatch.Add(new TexturedTrailVertex
|
||||||
|
{
|
||||||
|
Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y - size.Y / 2),
|
||||||
|
TexturePosition = textureRect.TopRight,
|
||||||
|
Colour = DrawColourInfo.Colour.TopRight.Linear,
|
||||||
|
Time = part.Time
|
||||||
|
});
|
||||||
|
|
||||||
|
vertexBatch.Add(new TexturedTrailVertex
|
||||||
|
{
|
||||||
|
Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y - size.Y / 2),
|
||||||
|
TexturePosition = textureRect.TopLeft,
|
||||||
|
Colour = DrawColourInfo.Colour.TopLeft.Linear,
|
||||||
|
Time = part.Time
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vertexBatch.Draw();
|
||||||
shader.Unbind();
|
shader.Unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,25 +282,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
vertexBatch.Dispose();
|
vertexBatch.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: This shouldn't exist, but is currently used to reduce allocations by caching variable-capturing closures.
|
|
||||||
private class TrailBatch : QuadBatch<TexturedTrailVertex>
|
|
||||||
{
|
|
||||||
public new readonly Action<TexturedVertex2D> AddAction;
|
|
||||||
public float DrawTime;
|
|
||||||
|
|
||||||
public TrailBatch(int size, int maxBuffers)
|
|
||||||
: base(size, maxBuffers)
|
|
||||||
{
|
|
||||||
AddAction = v => Add(new TexturedTrailVertex
|
|
||||||
{
|
|
||||||
Position = v.Position,
|
|
||||||
TexturePosition = v.TexturePosition,
|
|
||||||
Time = DrawTime + 1,
|
|
||||||
Colour = v.Colour,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
@ -14,8 +14,6 @@ using osu.Game.Rulesets.Osu.Configuration;
|
|||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Replays;
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
using osu.Game.Rulesets.Osu.Scoring;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -26,15 +24,13 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
|
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
|
||||||
|
|
||||||
public DrawableOsuRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableOsuRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this);
|
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new OsuPlayfield();
|
protected override Playfield CreatePlayfield() => new OsuPlayfield();
|
||||||
|
|
||||||
protected override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo);
|
protected override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo);
|
||||||
|
@ -11,7 +11,6 @@ using osu.Framework.MathUtils;
|
|||||||
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.Mods;
|
|
||||||
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.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
@ -91,7 +90,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = 768,
|
Height = 768,
|
||||||
Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap, Array.Empty<Mod>()) }
|
Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap.GetPlayableBeatmap(new TaikoRuleset().RulesetInfo)) }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
protected override Player CreatePlayer(Ruleset ruleset)
|
protected override Player CreatePlayer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
Mods.Value = Mods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray();
|
SelectedMods.Value = SelectedMods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray();
|
||||||
return new ScoreAccessiblePlayer();
|
return new ScoreAccessiblePlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
osu.Game.Rulesets.Taiko/Mods/TaikoModCinema.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// 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.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Replays;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Mods
|
||||||
|
{
|
||||||
|
public class TaikoModCinema : ModCinema<TaikoHitObject>
|
||||||
|
{
|
||||||
|
public override Score CreateReplayScore(IBeatmap beatmap) => new Score
|
||||||
|
{
|
||||||
|
ScoreInfo = new ScoreInfo { User = new User { Username = "mekkadosu!" } },
|
||||||
|
Replay = new TaikoAutoGenerator(beatmap).Generate(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,11 @@
|
|||||||
// 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.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Mods
|
namespace osu.Game.Rulesets.Taiko.Mods
|
||||||
{
|
{
|
||||||
public class TaikoModNightcore : ModNightcore
|
public class TaikoModNightcore : ModNightcore<TaikoHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.12;
|
public override double ScoreMultiplier => 1.12;
|
||||||
}
|
}
|
||||||
|
@ -105,19 +105,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class DrawableTaikoHitObject<TaikoHitType> : DrawableTaikoHitObject
|
public abstract class DrawableTaikoHitObject<TTaikoHit> : DrawableTaikoHitObject
|
||||||
where TaikoHitType : TaikoHitObject
|
where TTaikoHit : TaikoHitObject
|
||||||
{
|
{
|
||||||
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
|
public override Vector2 OriginPosition => new Vector2(DrawHeight / 2);
|
||||||
|
|
||||||
public new TaikoHitType HitObject;
|
public new TTaikoHit HitObject;
|
||||||
|
|
||||||
protected readonly Vector2 BaseSize;
|
protected readonly Vector2 BaseSize;
|
||||||
protected readonly TaikoPiece MainPiece;
|
protected readonly TaikoPiece MainPiece;
|
||||||
|
|
||||||
private readonly Container<DrawableStrongNestedHit> strongHitContainer;
|
private readonly Container<DrawableStrongNestedHit> strongHitContainer;
|
||||||
|
|
||||||
protected DrawableTaikoHitObject(TaikoHitType hitObject)
|
protected DrawableTaikoHitObject(TTaikoHit hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
HitObject = hitObject;
|
HitObject = hitObject;
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
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;
|
||||||
using osu.Game.Rulesets.Taiko.Judgements;
|
using osu.Game.Rulesets.Taiko.Judgements;
|
||||||
using osu.Game.Rulesets.Taiko.Scoring;
|
using osu.Game.Rulesets.Taiko.Scoring;
|
||||||
@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
|||||||
base.CreateNestedHitObjects();
|
base.CreateNestedHitObjects();
|
||||||
|
|
||||||
if (IsStrong)
|
if (IsStrong)
|
||||||
AddNested(new StrongHitObject { StartTime = (this as IHasEndTime)?.EndTime ?? StartTime });
|
AddNested(new StrongHitObject { StartTime = this.GetEndTime() });
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new TaikoJudgement();
|
public override Judgement CreateJudgement() => new TaikoJudgement();
|
||||||
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.Taiko.Beatmaps;
|
using osu.Game.Rulesets.Taiko.Beatmaps;
|
||||||
@ -39,9 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
|||||||
for (int i = 0; i < Beatmap.HitObjects.Count; i++)
|
for (int i = 0; i < Beatmap.HitObjects.Count; i++)
|
||||||
{
|
{
|
||||||
TaikoHitObject h = Beatmap.HitObjects[i];
|
TaikoHitObject h = Beatmap.HitObjects[i];
|
||||||
|
double endTime = h.GetEndTime();
|
||||||
IHasEndTime endTimeData = h as IHasEndTime;
|
|
||||||
double endTime = endTimeData?.EndTime ?? h.StartTime;
|
|
||||||
|
|
||||||
switch (h)
|
switch (h)
|
||||||
{
|
{
|
||||||
|
@ -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.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Scoring
|
namespace osu.Game.Rulesets.Taiko.Scoring
|
||||||
{
|
{
|
||||||
internal class TaikoScoreProcessor : ScoreProcessor<TaikoHitObject>
|
internal class TaikoScoreProcessor : ScoreProcessor
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A value used for calculating <see cref="hpMultiplier"/>.
|
/// A value used for calculating <see cref="hpMultiplier"/>.
|
||||||
@ -31,16 +31,16 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private double hpMissMultiplier;
|
private double hpMissMultiplier;
|
||||||
|
|
||||||
public TaikoScoreProcessor(DrawableRuleset<TaikoHitObject> drawableRuleset)
|
public TaikoScoreProcessor(IBeatmap beatmap)
|
||||||
: base(drawableRuleset)
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyBeatmap(Beatmap<TaikoHitObject> beatmap)
|
protected override void ApplyBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
base.ApplyBeatmap(beatmap);
|
base.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
|
hpMultiplier = 1 / (object_count_factor * beatmap.HitObjects.OfType<Hit>().Count() * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
|
||||||
|
|
||||||
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
|
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.0018, 0.0075, 0.0120);
|
||||||
}
|
}
|
||||||
|
@ -15,15 +15,21 @@ using osu.Game.Rulesets.Replays.Types;
|
|||||||
using osu.Game.Rulesets.Taiko.Replays;
|
using osu.Game.Rulesets.Taiko.Replays;
|
||||||
using osu.Game.Beatmaps.Legacy;
|
using osu.Game.Beatmaps.Legacy;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Beatmaps;
|
using osu.Game.Rulesets.Taiko.Beatmaps;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty;
|
using osu.Game.Rulesets.Taiko.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Taiko.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko
|
namespace osu.Game.Rulesets.Taiko
|
||||||
{
|
{
|
||||||
public class TaikoRuleset : Ruleset
|
public class TaikoRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableTaikoRuleset(this, beatmap, mods);
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => new DrawableTaikoRuleset(this, beatmap, mods);
|
||||||
|
|
||||||
|
public override ScoreProcessor CreateScoreProcessor(IBeatmap beatmap) => new TaikoScoreProcessor(beatmap);
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
|
||||||
|
|
||||||
public const string SHORT_NAME = "taiko";
|
public const string SHORT_NAME = "taiko";
|
||||||
@ -50,7 +56,9 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
else if (mods.HasFlag(LegacyMods.SuddenDeath))
|
||||||
yield return new TaikoModSuddenDeath();
|
yield return new TaikoModSuddenDeath();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Autoplay))
|
if (mods.HasFlag(LegacyMods.Cinema))
|
||||||
|
yield return new TaikoModCinema();
|
||||||
|
else if (mods.HasFlag(LegacyMods.Autoplay))
|
||||||
yield return new TaikoModAutoplay();
|
yield return new TaikoModAutoplay();
|
||||||
|
|
||||||
if (mods.HasFlag(LegacyMods.Easy))
|
if (mods.HasFlag(LegacyMods.Easy))
|
||||||
@ -106,7 +114,7 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
case ModType.Automation:
|
case ModType.Automation:
|
||||||
return new Mod[]
|
return new Mod[]
|
||||||
{
|
{
|
||||||
new MultiMod(new TaikoModAutoplay(), new ModCinema()),
|
new MultiMod(new TaikoModAutoplay(), new TaikoModCinema()),
|
||||||
new TaikoModRelax(),
|
new TaikoModRelax(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,7 +125,7 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return new Mod[] { };
|
return Array.Empty<Mod>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,10 +142,5 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
public override int? LegacyID => 1;
|
public override int? LegacyID => 1;
|
||||||
|
|
||||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
|
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
|
||||||
|
|
||||||
public TaikoRuleset(RulesetInfo rulesetInfo = null)
|
|
||||||
: base(rulesetInfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,8 @@ using System.Collections.Generic;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
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.Rulesets.Taiko.Objects.Drawables;
|
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.Scoring;
|
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.Taiko.Replays;
|
using osu.Game.Rulesets.Taiko.Replays;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
|
|
||||||
protected override bool UserScrollSpeedAdjustment => false;
|
protected override bool UserScrollSpeedAdjustment => false;
|
||||||
|
|
||||||
public DrawableTaikoRuleset(Ruleset ruleset, IWorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableTaikoRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
Direction.Value = ScrollingDirection.Left;
|
Direction.Value = ScrollingDirection.Left;
|
||||||
@ -40,8 +38,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
|
new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
|
|
||||||
|
|
||||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer();
|
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer();
|
||||||
|
|
||||||
protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
|
protected override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
|
||||||
|
54
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
|
using osu.Game.IO;
|
||||||
|
using osu.Game.IO.Serialization;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Beatmaps.Formats
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class LegacyBeatmapEncoderTest
|
||||||
|
{
|
||||||
|
private const string normal = "Soleily - Renatus (Gamu) [Insane].osu";
|
||||||
|
|
||||||
|
private static IEnumerable<string> allBeatmaps => TestResources.GetStore().GetAvailableResources().Where(res => res.EndsWith(".osu"));
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(allBeatmaps))]
|
||||||
|
public void TestDecodeEncodedBeatmap(string name)
|
||||||
|
{
|
||||||
|
var decoded = decode(normal, out var encoded);
|
||||||
|
|
||||||
|
Assert.That(decoded.HitObjects.Count, Is.EqualTo(encoded.HitObjects.Count));
|
||||||
|
Assert.That(encoded.Serialize(), Is.EqualTo(decoded.Serialize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Beatmap decode(string filename, out Beatmap encoded)
|
||||||
|
{
|
||||||
|
using (var stream = TestResources.OpenResource(filename))
|
||||||
|
using (var sr = new LineBufferedReader(stream))
|
||||||
|
{
|
||||||
|
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
|
||||||
|
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
using (var sw = new StreamWriter(ms))
|
||||||
|
using (var sr2 = new LineBufferedReader(ms))
|
||||||
|
{
|
||||||
|
new LegacyBeatmapEncoder(legacyDecoded).Encode(sw);
|
||||||
|
sw.Flush();
|
||||||
|
|
||||||
|
ms.Position = 0;
|
||||||
|
|
||||||
|
encoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr2);
|
||||||
|
return legacyDecoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,7 +45,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
cpi.Add(1000, new DifficultyControlPoint()); // is redundant
|
cpi.Add(1000, new DifficultyControlPoint()); // is redundant
|
||||||
|
|
||||||
Assert.That(cpi.Groups.Count, Is.EqualTo(0));
|
Assert.That(cpi.Groups.Count, Is.EqualTo(0));
|
||||||
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(0));
|
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0));
|
||||||
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0));
|
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0));
|
||||||
|
|
||||||
cpi.Add(1000, new DifficultyControlPoint { SpeedMultiplier = 2 }); // is not redundant
|
cpi.Add(1000, new DifficultyControlPoint { SpeedMultiplier = 2 }); // is not redundant
|
||||||
@ -60,18 +60,18 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
var cpi = new ControlPointInfo();
|
var cpi = new ControlPointInfo();
|
||||||
|
|
||||||
cpi.Add(0, new SampleControlPoint()); // is redundant
|
cpi.Add(0, new SampleControlPoint()); // is *not* redundant, special exception for first sample point
|
||||||
cpi.Add(1000, new SampleControlPoint()); // is redundant
|
cpi.Add(1000, new SampleControlPoint()); // is redundant
|
||||||
|
|
||||||
Assert.That(cpi.Groups.Count, Is.EqualTo(0));
|
|
||||||
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(0));
|
|
||||||
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0));
|
|
||||||
|
|
||||||
cpi.Add(1000, new SampleControlPoint { SampleVolume = 50 }); // is not redundant
|
|
||||||
|
|
||||||
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
|
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
|
||||||
Assert.That(cpi.SamplePoints.Count, Is.EqualTo(1));
|
Assert.That(cpi.SamplePoints.Count, Is.EqualTo(1));
|
||||||
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(1));
|
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(1));
|
||||||
|
|
||||||
|
cpi.Add(1000, new SampleControlPoint { SampleVolume = 50 }); // is not redundant
|
||||||
|
|
||||||
|
Assert.That(cpi.Groups.Count, Is.EqualTo(2));
|
||||||
|
Assert.That(cpi.SamplePoints.Count, Is.EqualTo(2));
|
||||||
|
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -83,7 +83,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
cpi.Add(1000, new EffectControlPoint()); // is redundant
|
cpi.Add(1000, new EffectControlPoint()); // is redundant
|
||||||
|
|
||||||
Assert.That(cpi.Groups.Count, Is.EqualTo(0));
|
Assert.That(cpi.Groups.Count, Is.EqualTo(0));
|
||||||
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(0));
|
Assert.That(cpi.EffectPoints.Count, Is.EqualTo(0));
|
||||||
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0));
|
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0));
|
||||||
|
|
||||||
cpi.Add(1000, new EffectControlPoint { KiaiMode = true }); // is not redundant
|
cpi.Add(1000, new EffectControlPoint { KiaiMode = true }); // is not redundant
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
Assert.AreEqual(0, stack.Count);
|
Assert.AreEqual(0, stack.Count);
|
||||||
|
|
||||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||||
{
|
{
|
||||||
int unused = stack[0];
|
int unused = stack[0];
|
||||||
});
|
});
|
||||||
@ -55,7 +55,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
// e.g. indices 3, 4, 5, 6 (out of range)
|
// e.g. indices 3, 4, 5, 6 (out of range)
|
||||||
for (int i = stack.Count; i < stack.Count + capacity; i++)
|
for (int i = stack.Count; i < stack.Count + capacity; i++)
|
||||||
{
|
{
|
||||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||||
{
|
{
|
||||||
int unused = stack[i];
|
int unused = stack[i];
|
||||||
});
|
});
|
||||||
@ -80,7 +80,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
// e.g. indices 3, 4, 5, 6 (out of range)
|
// e.g. indices 3, 4, 5, 6 (out of range)
|
||||||
for (int i = stack.Count; i < stack.Count + capacity; i++)
|
for (int i = stack.Count; i < stack.Count + capacity; i++)
|
||||||
{
|
{
|
||||||
Assert.Throws<IndexOutOfRangeException>(() =>
|
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||||
{
|
{
|
||||||
int unused = stack[i];
|
int unused = stack[i];
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,9 @@ namespace osu.Game.Tests.Resources
|
|||||||
{
|
{
|
||||||
public static class TestResources
|
public static class TestResources
|
||||||
{
|
{
|
||||||
public static Stream OpenResource(string name) => new DllResourceStore("osu.Game.Tests.dll").GetStream($"Resources/{name}");
|
public static DllResourceStore GetStore() => new DllResourceStore("osu.Game.Tests.dll");
|
||||||
|
|
||||||
|
public static Stream OpenResource(string name) => GetStore().GetStream($"Resources/{name}");
|
||||||
|
|
||||||
public static Stream GetTestBeatmapStream(bool virtualTrack = false) => new DllResourceStore("osu.Game.Resources.dll").GetStream($"Beatmaps/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz");
|
public static Stream GetTestBeatmapStream(bool virtualTrack = false) => new DllResourceStore("osu.Game.Resources.dll").GetStream($"Beatmaps/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz");
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -10,6 +11,7 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.IO.Archives;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
@ -154,7 +156,30 @@ namespace osu.Game.Tests.Scores.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ScoreInfo> loadIntoOsu(OsuGameBase osu, ScoreInfo score)
|
[Test]
|
||||||
|
public async Task TestOnlineScoreIsAvailableLocally()
|
||||||
|
{
|
||||||
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestOnlineScoreIsAvailableLocally"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var osu = await loadOsu(host);
|
||||||
|
|
||||||
|
await loadIntoOsu(osu, new ScoreInfo { OnlineScoreID = 2 }, new TestArchiveReader());
|
||||||
|
|
||||||
|
var scoreManager = osu.Dependencies.Get<ScoreManager>();
|
||||||
|
|
||||||
|
// Note: A new score reference is used here since the import process mutates the original object to set an ID
|
||||||
|
Assert.That(scoreManager.IsAvailableLocally(new ScoreInfo { OnlineScoreID = 2 }));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
host.Exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<ScoreInfo> loadIntoOsu(OsuGameBase osu, ScoreInfo score, ArchiveReader archive = null)
|
||||||
{
|
{
|
||||||
var beatmapManager = osu.Dependencies.Get<BeatmapManager>();
|
var beatmapManager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
@ -165,7 +190,7 @@ namespace osu.Game.Tests.Scores.IO
|
|||||||
score.Ruleset = new OsuRuleset().RulesetInfo;
|
score.Ruleset = new OsuRuleset().RulesetInfo;
|
||||||
|
|
||||||
var scoreManager = osu.Dependencies.Get<ScoreManager>();
|
var scoreManager = osu.Dependencies.Get<ScoreManager>();
|
||||||
await scoreManager.Import(score);
|
await scoreManager.Import(score, archive);
|
||||||
|
|
||||||
return scoreManager.GetAllUsableScores().FirstOrDefault();
|
return scoreManager.GetAllUsableScores().FirstOrDefault();
|
||||||
}
|
}
|
||||||
@ -196,5 +221,23 @@ namespace osu.Game.Tests.Scores.IO
|
|||||||
|
|
||||||
Assert.IsTrue(task.Wait(timeout), failureMessage);
|
Assert.IsTrue(task.Wait(timeout), failureMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestArchiveReader : ArchiveReader
|
||||||
|
{
|
||||||
|
public TestArchiveReader()
|
||||||
|
: base("test_archive")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Stream GetStream(string name) => new MemoryStream();
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<string> Filenames => new[] { "test_file.osr" };
|
||||||
|
|
||||||
|
public override Stream GetUnderlyingStream() => new MemoryStream();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
427
osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
// 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 System.Threading;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Input.States;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens;
|
||||||
|
using osu.Game.Screens.Backgrounds;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Screens.Play.PlayerSettings;
|
||||||
|
using osu.Game.Screens.Select;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Background
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneUserDimBackgrounds : ManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(ScreenWithBeatmapBackground),
|
||||||
|
typeof(PlayerLoader),
|
||||||
|
typeof(Player),
|
||||||
|
typeof(UserDimContainer),
|
||||||
|
typeof(OsuScreen)
|
||||||
|
};
|
||||||
|
|
||||||
|
private DummySongSelect songSelect;
|
||||||
|
private TestPlayerLoader playerLoader;
|
||||||
|
private TestPlayer player;
|
||||||
|
private BeatmapManager manager;
|
||||||
|
private RulesetStore rulesets;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(GameHost host, AudioManager audio)
|
||||||
|
{
|
||||||
|
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||||
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
|
||||||
|
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
||||||
|
|
||||||
|
manager.Import(TestResources.GetTestBeatmapForImport()).Wait();
|
||||||
|
|
||||||
|
Beatmap.SetDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public virtual void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
Child = new OsuScreenStack(songSelect = new DummySongSelect())
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if <see cref="PlayerLoader"/> properly triggers the visual settings preview when a user hovers over the visual settings panel.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void PlayerLoaderSettingsHoverTest()
|
||||||
|
{
|
||||||
|
setupUserSettings();
|
||||||
|
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer { BlockLoad = true })));
|
||||||
|
AddUntilStep("Wait for Player Loader to load", () => playerLoader?.IsLoaded ?? false);
|
||||||
|
AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
|
||||||
|
AddStep("Trigger background preview", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(playerLoader.ScreenPos);
|
||||||
|
InputManager.MoveMouseTo(playerLoader.VisualSettingsPos);
|
||||||
|
});
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||||
|
AddStep("Stop background preview", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// In the case of a user triggering the dim preview the instant player gets loaded, then moving the cursor off of the visual settings:
|
||||||
|
/// The OnHover of PlayerLoader will trigger, which could potentially cause visual settings to be unapplied unless checked for in PlayerLoader.
|
||||||
|
/// We need to check that in this scenario, the dim and blur is still properly applied after entering player.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void PlayerLoaderTransitionTest()
|
||||||
|
{
|
||||||
|
performFullSetup();
|
||||||
|
AddStep("Trigger hover event", () => playerLoader.TriggerOnHover());
|
||||||
|
AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Make sure the background is fully invisible (Alpha == 0) when the background should be disabled by the storyboard.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void StoryboardBackgroundVisibilityTest()
|
||||||
|
{
|
||||||
|
performFullSetup();
|
||||||
|
createFakeStoryboard();
|
||||||
|
AddStep("Enable Storyboard", () =>
|
||||||
|
{
|
||||||
|
player.ReplacesBackground.Value = true;
|
||||||
|
player.StoryboardEnabled.Value = true;
|
||||||
|
});
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible);
|
||||||
|
AddStep("Disable Storyboard", () =>
|
||||||
|
{
|
||||||
|
player.ReplacesBackground.Value = false;
|
||||||
|
player.StoryboardEnabled.Value = false;
|
||||||
|
});
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && !player.IsStoryboardVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When exiting player, the screen that it suspends/exits to needs to have a fully visible (Alpha == 1) background.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void StoryboardTransitionTest()
|
||||||
|
{
|
||||||
|
performFullSetup();
|
||||||
|
createFakeStoryboard();
|
||||||
|
AddStep("Exit to song select", () => player.Exit());
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Background is visible", () => songSelect.IsBackgroundVisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensure <see cref="UserDimContainer"/> is properly accepting user-defined visual changes for a background.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void DisableUserDimBackgroundTest()
|
||||||
|
{
|
||||||
|
performFullSetup();
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||||
|
AddStep("Enable user dim", () => songSelect.DimEnabled.Value = false);
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled());
|
||||||
|
AddStep("Disable user dim", () => songSelect.DimEnabled.Value = true);
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensure <see cref="UserDimContainer"/> is properly accepting user-defined visual changes for a storyboard.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void DisableUserDimStoryboardTest()
|
||||||
|
{
|
||||||
|
performFullSetup();
|
||||||
|
createFakeStoryboard();
|
||||||
|
AddStep("Enable Storyboard", () =>
|
||||||
|
{
|
||||||
|
player.ReplacesBackground.Value = true;
|
||||||
|
player.StoryboardEnabled.Value = true;
|
||||||
|
});
|
||||||
|
AddStep("Enable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = true);
|
||||||
|
AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f);
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Storyboard is invisible", () => !player.IsStoryboardVisible);
|
||||||
|
AddStep("Disable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = false);
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Storyboard is visible", () => player.IsStoryboardVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the visual settings container retains dim and blur when pausing
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void PauseTest()
|
||||||
|
{
|
||||||
|
performFullSetup(true);
|
||||||
|
AddStep("Pause", () => player.Pause());
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||||
|
AddStep("Unpause", () => player.Resume());
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the visual settings container removes user dim when suspending <see cref="Player"/> for <see cref="SoloResults"/>
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TransitionTest()
|
||||||
|
{
|
||||||
|
performFullSetup();
|
||||||
|
FadeAccessibleResults results = null;
|
||||||
|
AddStep("Transition to Results", () => player.Push(results =
|
||||||
|
new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } })));
|
||||||
|
AddUntilStep("Wait for results is current", () => results.IsCurrentScreen());
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is undimmed, original background retained", () =>
|
||||||
|
songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if background gets undimmed and unblurred when leaving <see cref="Player"/> for <see cref="PlaySongSelect"/>
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TransitionOutTest()
|
||||||
|
{
|
||||||
|
performFullSetup();
|
||||||
|
AddStep("Exit to song select", () => player.Exit());
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBlurCorrect());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if hovering on the visual settings dialogue after resuming from player still previews the background dim.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void ResumeFromPlayerTest()
|
||||||
|
{
|
||||||
|
performFullSetup();
|
||||||
|
AddStep("Move mouse to Visual Settings", () => InputManager.MoveMouseTo(playerLoader.VisualSettingsPos));
|
||||||
|
AddStep("Resume PlayerLoader", () => player.Restart());
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||||
|
AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
|
||||||
|
waitForDim();
|
||||||
|
AddAssert("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForDim() => AddWaitStep("Wait for dim", 5);
|
||||||
|
|
||||||
|
private void createFakeStoryboard() => AddStep("Create storyboard", () =>
|
||||||
|
{
|
||||||
|
player.StoryboardEnabled.Value = false;
|
||||||
|
player.ReplacesBackground.Value = false;
|
||||||
|
player.DimmableStoryboard.Add(new OsuSpriteText
|
||||||
|
{
|
||||||
|
Size = new Vector2(500, 50),
|
||||||
|
Alpha = 1,
|
||||||
|
Colour = Color4.White,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Text = "THIS IS A STORYBOARD",
|
||||||
|
Font = new FontUsage(size: 50)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
private void performFullSetup(bool allowPause = false)
|
||||||
|
{
|
||||||
|
setupUserSettings();
|
||||||
|
|
||||||
|
AddStep("Start player loader", () => songSelect.Push(playerLoader = new TestPlayerLoader(player = new TestPlayer(allowPause))));
|
||||||
|
|
||||||
|
AddUntilStep("Wait for Player Loader to load", () => playerLoader.IsLoaded);
|
||||||
|
AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
|
||||||
|
AddUntilStep("Wait for player to load", () => player.IsLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupUserSettings()
|
||||||
|
{
|
||||||
|
AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null);
|
||||||
|
AddStep("Set default user settings", () =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray();
|
||||||
|
songSelect.DimLevel.Value = 0.7f;
|
||||||
|
songSelect.BlurLevel.Value = 0.4f;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
rulesets?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DummySongSelect : PlaySongSelect
|
||||||
|
{
|
||||||
|
protected override BackgroundScreen CreateBackground()
|
||||||
|
{
|
||||||
|
FadeAccessibleBackground background = new FadeAccessibleBackground(Beatmap.Value);
|
||||||
|
DimEnabled.BindTo(background.EnableUserDim);
|
||||||
|
return background;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly Bindable<bool> DimEnabled = new Bindable<bool>();
|
||||||
|
public readonly Bindable<double> DimLevel = new Bindable<double>();
|
||||||
|
public readonly Bindable<double> BlurLevel = new Bindable<double>();
|
||||||
|
|
||||||
|
public new BeatmapCarousel Carousel => base.Carousel;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuConfigManager config)
|
||||||
|
{
|
||||||
|
config.BindWith(OsuSetting.DimLevel, DimLevel);
|
||||||
|
config.BindWith(OsuSetting.BlurLevel, BlurLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsBackgroundDimmed() => ((FadeAccessibleBackground)Background).CurrentColour == OsuColour.Gray(1f - ((FadeAccessibleBackground)Background).CurrentDim);
|
||||||
|
|
||||||
|
public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White;
|
||||||
|
|
||||||
|
public bool IsUserBlurApplied() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR);
|
||||||
|
|
||||||
|
public bool IsUserBlurDisabled() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(0);
|
||||||
|
|
||||||
|
public bool IsBackgroundInvisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 0;
|
||||||
|
|
||||||
|
public bool IsBackgroundVisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 1;
|
||||||
|
|
||||||
|
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Make sure every time a screen gets pushed, the background doesn't get replaced
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Whether or not the original background (The one created in DummySongSelect) is still the current background</returns>
|
||||||
|
public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FadeAccessibleResults : SoloResults
|
||||||
|
{
|
||||||
|
public FadeAccessibleResults(ScoreInfo score)
|
||||||
|
: base(score)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
|
||||||
|
|
||||||
|
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestPlayer : Visual.TestPlayer
|
||||||
|
{
|
||||||
|
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
|
||||||
|
|
||||||
|
public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard;
|
||||||
|
|
||||||
|
// Whether or not the player should be allowed to load.
|
||||||
|
public bool BlockLoad;
|
||||||
|
|
||||||
|
public Bindable<bool> StoryboardEnabled;
|
||||||
|
public readonly Bindable<bool> ReplacesBackground = new Bindable<bool>();
|
||||||
|
public readonly Bindable<bool> IsPaused = new Bindable<bool>();
|
||||||
|
|
||||||
|
public TestPlayer(bool allowPause = true)
|
||||||
|
: base(allowPause)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsStoryboardVisible => DimmableStoryboard.ContentDisplayed;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuConfigManager config, CancellationToken token)
|
||||||
|
{
|
||||||
|
while (BlockLoad && !token.IsCancellationRequested)
|
||||||
|
Thread.Sleep(1);
|
||||||
|
|
||||||
|
StoryboardEnabled = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
||||||
|
ReplacesBackground.BindTo(Background.StoryboardReplacesBackground);
|
||||||
|
DrawableRuleset.IsPaused.BindTo(IsPaused);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestPlayerLoader : PlayerLoader
|
||||||
|
{
|
||||||
|
public VisualSettings VisualSettingsPos => VisualSettings;
|
||||||
|
public BackgroundScreen ScreenPos => Background;
|
||||||
|
|
||||||
|
public TestPlayerLoader(Player player)
|
||||||
|
: base(() => player)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TriggerOnHover() => OnHover(new HoverEvent(new InputState()));
|
||||||
|
|
||||||
|
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
|
||||||
|
|
||||||
|
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FadeAccessibleBackground : BackgroundScreenBeatmap
|
||||||
|
{
|
||||||
|
protected override DimmableBackground CreateFadeContainer() => dimmable = new TestDimmableBackground { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
|
public Color4 CurrentColour => dimmable.CurrentColour;
|
||||||
|
|
||||||
|
public float CurrentAlpha => dimmable.CurrentAlpha;
|
||||||
|
|
||||||
|
public float CurrentDim => dimmable.DimLevel;
|
||||||
|
|
||||||
|
public Vector2 CurrentBlur => Background.BlurSigma;
|
||||||
|
|
||||||
|
private TestDimmableBackground dimmable;
|
||||||
|
|
||||||
|
public FadeAccessibleBackground(WorkingBeatmap beatmap)
|
||||||
|
: base(beatmap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestDimmableBackground : BackgroundScreenBeatmap.DimmableBackground
|
||||||
|
{
|
||||||
|
public Color4 CurrentColour => Content.Colour;
|
||||||
|
public float CurrentAlpha => Content.Alpha;
|
||||||
|
|
||||||
|
public new float DimLevel => base.DimLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|