1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 22:33:05 +08:00

Merge remote-tracking branch 'upstream/master' into colour-score-meter

This commit is contained in:
Dean Herbert 2020-01-24 11:45:33 +09:00
commit c390348eab
419 changed files with 5911 additions and 2668 deletions

3
.gitignore vendored
View File

@ -331,3 +331,6 @@ fastlane/report.xml
# inspectcode
inspectcodereport.xml
inspectcode
# BenchmarkDotNet
/BenchmarkDotNet.Artifacts

View File

@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Benchmarks" type="DotNetProject" factoryName=".NET Project">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/netcoreapp3.1/osu.Game.Benchmarks.dll" />
<option name="PROGRAM_PARAMETERS" value="--filter *" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/netcoreapp3.1" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.1" />
<method v="2">
<option name="Build" enabled="true" />
</method>
</configuration>
</component>

View File

@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! SDL" type="DotNetProject" factoryName=".NET Project" folderName="osu!">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.1/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="--sdl" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/osu.Desktop/osu.Desktop.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.0" />
<method v="2">
<option name="Build" enabled="true" />
</method>
</configuration>
</component>

19
.vscode/launch.json vendored
View File

@ -1,6 +1,7 @@
{
"version": "0.2.0",
"configurations": [{
"configurations": [
{
"name": "osu! (Debug)",
"type": "coreclr",
"request": "launch",
@ -50,7 +51,8 @@
}
},
"console": "internalConsole"
}, {
},
{
"name": "osu! (Tests, Release)",
"type": "coreclr",
"request": "launch",
@ -139,6 +141,19 @@
},
"console": "internalConsole"
},
{
"name": "Benchmark",
"type": "coreclr",
"request": "launch",
"program": "${workspaceRoot}/osu.Game.Benchmarks/bin/Release/netcoreapp3.1/osu.Game.Benchmarks.dll",
"args": [
"--filter",
"*"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build benchmarks",
"console": "internalConsole"
},
{
"name": "Cake: Debug Script",
"type": "coreclr",

22
.vscode/tasks.json vendored
View File

@ -2,7 +2,8 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [{
"tasks": [
{
"label": "Build osu! (Debug)",
"type": "shell",
"command": "dotnet",
@ -78,7 +79,8 @@
],
"group": "build",
"problemMatcher": "$msCompile"
}, {
},
{
"label": "Build tournament tests (Release)",
"type": "shell",
"command": "dotnet",
@ -94,6 +96,22 @@
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build benchmarks",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Benchmarks",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Restore (netcoreapp3.1)",
"type": "shell",

View File

@ -40,7 +40,7 @@
<RepositoryUrl>https://github.com/ppy/osu</RepositoryUrl>
<PackageReleaseNotes>Automated release.</PackageReleaseNotes>
<Company>ppy Pty Ltd</Company>
<Copyright>Copyright (c) 2019 ppy Pty Ltd</Copyright>
<Copyright>Copyright (c) 2020 ppy Pty Ltd</Copyright>
<PackageTags>osu game</PackageTags>
</PropertyGroup>
</Project>

View File

@ -1,7 +1,7 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.1)
CFPropertyList (3.0.2)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
atomos (0.1.3)
@ -18,8 +18,8 @@ GEM
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.5)
emoji_regex (1.0.1)
excon (0.67.0)
faraday (0.15.4)
excon (0.71.1)
faraday (0.17.3)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
@ -27,7 +27,7 @@ GEM
faraday_middleware (0.13.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.7)
fastlane (2.133.0)
fastlane (2.140.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
@ -36,13 +36,13 @@ GEM
commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 2.0)
excon (>= 0.45.0, < 1.0.0)
faraday (< 0.16.0)
excon (>= 0.71.0, < 1.0.0)
faraday (~> 0.17)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (< 0.16.0)
faraday_middleware (~> 0.13.1)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.21.2, < 0.24.0)
google-api-client (>= 0.29.2, < 0.37.0)
google-cloud-storage (>= 1.15.0, < 2.0.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
@ -61,56 +61,58 @@ GEM
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.8.1, < 2.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-clean_testflight_testers (0.3.0)
fastlane-plugin-souyuz (0.8.1)
souyuz (>= 0.8.1)
fastlane-plugin-souyuz (0.9.1)
souyuz (= 0.9.1)
fastlane-plugin-xamarin (0.6.3)
gh_inspector (1.1.3)
google-api-client (0.23.9)
google-api-client (0.36.4)
addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.5, < 0.7.0)
googleauth (~> 0.9)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
mini_mime (~> 1.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
signet (~> 0.9)
google-cloud-core (1.3.1)
signet (~> 0.12)
google-cloud-core (1.5.0)
google-cloud-env (~> 1.0)
google-cloud-env (1.2.1)
google-cloud-errors (~> 1.0)
google-cloud-env (1.3.0)
faraday (~> 0.11)
google-cloud-storage (1.16.0)
google-cloud-errors (1.0.0)
google-cloud-storage (1.25.1)
addressable (~> 2.5)
digest-crc (~> 0.4)
google-api-client (~> 0.23)
google-api-client (~> 0.33)
google-cloud-core (~> 1.2)
googleauth (>= 0.6.2, < 0.10.0)
googleauth (0.6.7)
googleauth (~> 0.9)
mini_mime (~> 1.0)
googleauth (0.10.0)
faraday (~> 0.12)
jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.7)
signet (~> 0.12)
highline (1.7.10)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
json (2.2.0)
json (2.3.0)
jwt (2.1.0)
memoist (0.16.0)
mime-types (3.3)
mime-types-data (~> 3.2015)
mime-types-data (3.2019.1009)
mini_magick (4.9.5)
memoist (0.16.2)
mini_magick (4.10.1)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
multi_json (1.13.1)
multi_json (1.14.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
nanaimo (0.2.6)
naturally (2.2.0)
nokogiri (1.10.4)
nokogiri (1.10.7)
mini_portile2 (~> 2.4.0)
os (1.0.1)
plist (3.5.0)
@ -128,12 +130,12 @@ GEM
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.6)
simctl (1.6.7)
CFPropertyList
naturally
slack-notifier (2.3.2)
souyuz (0.8.1)
fastlane (>= 2.29.0)
souyuz (0.9.1)
fastlane (>= 1.103.0)
highline (~> 1.7)
nokogiri (~> 1.7)
terminal-notifier (2.0.0)
@ -141,15 +143,15 @@ GEM
unicode-display_width (~> 1.1, >= 1.1.1)
tty-cursor (0.7.0)
tty-screen (0.7.0)
tty-spinner (0.9.1)
tty-spinner (0.9.2)
tty-cursor (~> 0.7)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.6)
unicode-display_width (1.6.0)
unicode-display_width (1.6.1)
word_wrap (1.0.0)
xcodeproj (1.12.0)
xcodeproj (1.14.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)

View File

@ -1,4 +1,4 @@
Copyright (c) 2019 ppy Pty Ltd <contact@ppy.sh>.
Copyright (c) 2020 ppy Pty Ltd <contact@ppy.sh>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -53,7 +53,7 @@
<Reference Include="Java.Interop" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1215.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1219.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.122.0" />
</ItemGroup>
</Project>

View File

@ -3,6 +3,7 @@
"path": "osu.sln",
"projects": [
"osu.Desktop\\osu.Desktop.csproj",
"osu.Game.Benchmarks\\osu.Game.Benchmarks.csproj",
"osu.Game.Rulesets.Catch.Tests\\osu.Game.Rulesets.Catch.Tests.csproj",
"osu.Game.Rulesets.Catch\\osu.Game.Rulesets.Catch.csproj",
"osu.Game.Rulesets.Mania.Tests\\osu.Game.Rulesets.Mania.Tests.csproj",

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Text;
using DiscordRPC;
using DiscordRPC.Message;
using osu.Framework.Allocation;
@ -43,6 +45,10 @@ namespace osu.Desktop
};
client.OnReady += onReady;
// safety measure for now, until we performance test / improve backoff for failed connections.
client.OnConnectionFailed += (_, __) => client.Deinitialize();
client.OnError += (_, e) => Logger.Log($"An error occurred with Discord RPC Client: {e.Code} {e.Message}", LoggingTarget.Network);
(user = provider.LocalUser.GetBoundCopy()).BindValueChanged(u =>
@ -69,6 +75,9 @@ namespace osu.Desktop
private void updateStatus()
{
if (!client.IsInitialized)
return;
if (status.Value is UserStatusOffline)
{
client.ClearPresence();
@ -77,8 +86,8 @@ namespace osu.Desktop
if (status.Value is UserStatusOnline && activity.Value != null)
{
presence.State = activity.Value.Status;
presence.Details = getDetails(activity.Value);
presence.State = truncate(activity.Value.Status);
presence.Details = truncate(getDetails(activity.Value));
}
else
{
@ -96,6 +105,27 @@ namespace osu.Desktop
client.SetPresence(presence);
}
private static readonly int ellipsis_length = Encoding.UTF8.GetByteCount(new[] { '…' });
private string truncate(string str)
{
if (Encoding.UTF8.GetByteCount(str) <= 128)
return str;
ReadOnlyMemory<char> strMem = str.AsMemory();
do
{
strMem = strMem[..^1];
} while (Encoding.UTF8.GetByteCount(strMem.Span) + ellipsis_length > 128);
return string.Create(strMem.Length + 1, strMem, (span, mem) =>
{
mem.Span.CopyTo(span);
span[^1] = '…';
});
}
private string getDetails(UserActivity activity)
{
switch (activity)

View File

@ -22,8 +22,9 @@ namespace osu.Desktop
{
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;
bool useSdl = args.Contains("--sdl");
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true, useSdl: useSdl))
{
host.ExceptionThrown += handleException;

View File

@ -12,7 +12,7 @@
<description>click the circles. to the beat.</description>
<summary>click the circles.</summary>
<releaseNotes>testing</releaseNotes>
<copyright>Copyright (c) 2019 ppy Pty Ltd</copyright>
<copyright>Copyright (c) 2020 ppy Pty Ltd</copyright>
<language>en-AU</language>
</metadata>
<files>

View File

@ -0,0 +1,37 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.IO;
using BenchmarkDotNet.Attributes;
using osu.Framework.IO.Stores;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
using osu.Game.IO.Archives;
using osu.Game.Resources;
namespace osu.Game.Benchmarks
{
public class BenchmarkBeatmapParsing : BenchmarkTest
{
private readonly MemoryStream beatmapStream = new MemoryStream();
public override void SetUp()
{
using (var resources = new DllResourceStore(OsuResources.ResourceAssembly))
using (var archive = resources.GetStream("Beatmaps/241526 Soleily - Renatus.osz"))
using (var reader = new ZipArchiveReader(archive))
reader.GetStream("Soleily - Renatus (Gamu) [Insane].osu").CopyTo(beatmapStream);
}
[Benchmark]
public Beatmap BenchmarkBundledBeatmap()
{
beatmapStream.Seek(0, SeekOrigin.Begin);
var reader = new LineBufferedReader(beatmapStream); // no disposal
var decoder = Decoder.GetDecoder<Beatmap>(reader);
return decoder.Decode(reader);
}
}
}

View File

@ -0,0 +1,23 @@
// 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 BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using NUnit.Framework;
namespace osu.Game.Benchmarks
{
[TestFixture]
[MemoryDiagnoser]
public abstract class BenchmarkTest
{
[GlobalSetup]
[OneTimeSetUp]
public virtual void SetUp()
{
}
[Test]
public void RunBenchmark() => BenchmarkRunner.Run(GetType());
}
}

View File

@ -0,0 +1,17 @@
// 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 BenchmarkDotNet.Running;
namespace osu.Game.Benchmarks
{
public static class Program
{
public static void Main(string[] args)
{
BenchmarkSwitcher
.FromAssembly(typeof(Program).Assembly)
.Run(args);
}
}
}

View File

@ -0,0 +1,8 @@
{
"profiles": {
"All Benchmarks": {
"commandName": "Project",
"commandLineArgs": "--filter *"
}
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup>
</Project>

View File

@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using NUnit.Framework;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;

View File

@ -4,7 +4,7 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using System.Collections.Generic;
using System;
using System.Linq;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects;
@ -14,12 +14,12 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{
public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
{
public CatchBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
public CatchBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
: base(beatmap, ruleset)
{
}
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition);
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
{

View File

@ -24,13 +24,14 @@ using System;
namespace osu.Game.Rulesets.Catch
{
public class CatchRuleset : Ruleset
public class CatchRuleset : Ruleset, ILegacyRuleset
{
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 ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap, this);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
public const string SHORT_NAME = "fruits";
@ -106,6 +107,12 @@ namespace osu.Game.Rulesets.Catch
new CatchModFlashlight(),
};
case ModType.Conversion:
return new Mod[]
{
new CatchModDifficultyAdjust(),
};
case ModType.Automation:
return new Mod[]
{
@ -128,13 +135,15 @@ namespace osu.Game.Rulesets.Catch
public override string ShortName => SHORT_NAME;
public override string PlayingVerb => "Catching fruit";
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetCatch };
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(this, beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new CatchPerformanceCalculator(this, beatmap, score);
public override int? LegacyID => 2;
public int LegacyID => 2;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new CatchReplayFrame();
}

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 0;
case HitResult.Perfect:
return 0.008;
return 0.01;
}
}

View File

@ -18,17 +18,5 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 30;
}
}
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
default:
return base.HealthIncreaseFor(result);
case HitResult.Perfect:
return 0.007;
}
}
}
}

View File

@ -23,18 +23,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
}
}
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
default:
return -0.02;
case HitResult.Perfect:
return 0.01;
}
}
/// <summary>
/// Whether fruit on the platter should explode or drop.
/// Note that this is only checked if the owning object is also <see cref="IHasComboInformation.LastInCombo" />

View File

@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 0;
case HitResult.Perfect:
return 0.004;
return 0.02;
}
}
}

View File

@ -0,0 +1,49 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Mods
{
public class CatchModDifficultyAdjust : ModDifficultyAdjust
{
[SettingSource("Fruit Size", "Override a beatmap's set CS.")]
public BindableNumber<float> CircleSize { get; } = new BindableFloat
{
Precision = 0.1f,
MinValue = 1,
MaxValue = 10,
Default = 5,
Value = 5,
};
[SettingSource("Approach Rate", "Override a beatmap's set AR.")]
public BindableNumber<float> ApproachRate { get; } = new BindableFloat
{
Precision = 0.1f,
MinValue = 1,
MaxValue = 10,
Default = 5,
Value = 5,
};
protected override void TransferSettings(BeatmapDifficulty difficulty)
{
base.TransferSettings(difficulty);
TransferSetting(CircleSize, difficulty.CircleSize);
TransferSetting(ApproachRate, difficulty.ApproachRate);
}
protected override void ApplySettings(BeatmapDifficulty difficulty)
{
base.ApplySettings(difficulty);
difficulty.CircleSize = CircleSize.Value;
difficulty.ApproachRate = ApproachRate.Value;
}
}
}

View File

@ -36,7 +36,10 @@ namespace osu.Game.Rulesets.Catch.Mods
//disable keyboard controls
public bool OnPressed(CatchAction action) => true;
public bool OnReleased(CatchAction action) => true;
public void OnReleased(CatchAction action)
{
}
protected override bool OnMouseMove(MouseMoveEvent e)
{

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using osuTK;
using osuTK.Graphics;

View File

@ -3,7 +3,7 @@
using System;
using System.Linq;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.Catch.Beatmaps;

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Input.StateChanges;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Replays;
using osu.Game.Rulesets.Replays;

View File

@ -1,40 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Scoring
{
public class CatchScoreProcessor : ScoreProcessor
{
public CatchScoreProcessor(IBeatmap beatmap)
: base(beatmap)
{
}
private float hpDrainRate;
protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
}
protected override double HealthAdjustmentFactorFor(JudgementResult result)
{
switch (result.Type)
{
case HitResult.Miss:
return hpDrainRate;
default:
return 10.2 - hpDrainRate; // Award less HP as drain rate is increased
}
}
public override HitWindows CreateHitWindows() => new CatchHitWindows();
}
}

View File

@ -7,7 +7,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
@ -103,7 +103,9 @@ namespace osu.Game.Rulesets.Catch.UI
MovableCatcher.X = state.CatcherX.Value;
}
public bool OnReleased(CatchAction action) => false;
public void OnReleased(CatchAction action)
{
}
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
@ -341,24 +343,22 @@ namespace osu.Game.Rulesets.Catch.UI
return false;
}
public bool OnReleased(CatchAction action)
public void OnReleased(CatchAction action)
{
switch (action)
{
case CatchAction.MoveLeft:
currentDirection++;
return true;
break;
case CatchAction.MoveRight:
currentDirection--;
return true;
break;
case CatchAction.Dash:
Dashing = false;
return true;
break;
}
return false;
}
/// <summary>

View File

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;

View File

@ -0,0 +1,46 @@
// 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.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
public abstract class SkinnableTestScene : OsuGridTestScene
{
private Skin defaultSkin;
protected SkinnableTestScene()
: base(1, 2)
{
}
[BackgroundDependencyLoader]
private void load(AudioManager audio, SkinManager skinManager)
{
defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);
}
public void SetContents(Func<Drawable> creationFunction)
{
Cell(0).Child = createProvider(null, creationFunction);
Cell(1).Child = createProvider(defaultSkin, creationFunction);
}
private Drawable createProvider(Skin skin, Func<Drawable> creationFunction)
{
var mainProvider = new SkinProvidingContainer(skin);
return mainProvider
.WithChild(new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider))
{
Child = creationFunction()
});
}
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Tests
{
public class TestSceneDrawableJudgement : SkinnableTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(DrawableJudgement),
typeof(DrawableManiaJudgement)
};
public TestSceneDrawableJudgement()
{
foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1))
{
AddStep("Show " + result.GetDescription(), () => SetContents(() =>
new DrawableManiaJudgement(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}));
}
}
}
}

View File

@ -0,0 +1,314 @@
// 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 NUnit.Framework;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Replays;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
public class TestSceneHoldNoteInput : RateAdjustedBeatmapTestScene
{
private const double time_before_head = 250;
private const double time_head = 1500;
private const double time_during_hold_1 = 2500;
private const double time_tail = 4000;
private const double time_after_tail = 5250;
private List<JudgementResult> judgementResults;
private bool allJudgedFired;
/// <summary>
/// -----[ ]-----
/// o o
/// </summary>
[Test]
public void TestNoInput()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_before_head),
new ManiaReplayFrame(time_after_tail),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss);
assertTailJudgement(HitResult.Miss);
assertNoteJudgement(HitResult.Perfect);
}
/// <summary>
/// -----[ ]-----
/// x o
/// </summary>
[Test]
public void TestPressTooEarlyAndReleaseAfterTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
new ManiaReplayFrame(time_after_tail, ManiaAction.Key1),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// x o
/// </summary>
[Test]
public void TestPressTooEarlyAndReleaseAtTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
new ManiaReplayFrame(time_tail),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// xo x o
/// </summary>
[Test]
public void TestPressTooEarlyThenPressAtStartAndReleaseAfterTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
new ManiaReplayFrame(time_before_head + 10),
new ManiaReplayFrame(time_head, ManiaAction.Key1),
new ManiaReplayFrame(time_after_tail),
});
assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// xo x o
/// </summary>
[Test]
public void TestPressTooEarlyThenPressAtStartAndReleaseAtTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
new ManiaReplayFrame(time_before_head + 10),
new ManiaReplayFrame(time_head, ManiaAction.Key1),
new ManiaReplayFrame(time_tail),
});
assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Perfect);
}
/// <summary>
/// -----[ ]-----
/// xo o
/// </summary>
[Test]
public void TestPressAtStartAndBreak()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_head, ManiaAction.Key1),
new ManiaReplayFrame(time_head + 10),
new ManiaReplayFrame(time_after_tail),
});
assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Miss);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// xo x o
/// </summary>
[Test]
public void TestPressAtStartThenBreakThenRepressAndReleaseAfterTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_head, ManiaAction.Key1),
new ManiaReplayFrame(time_head + 10),
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
new ManiaReplayFrame(time_after_tail),
});
assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// xo x o o
/// </summary>
[Test]
public void TestPressAtStartThenBreakThenRepressAndReleaseAtTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_head, ManiaAction.Key1),
new ManiaReplayFrame(time_head + 10),
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
new ManiaReplayFrame(time_tail),
});
assertHeadJudgement(HitResult.Perfect);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Meh);
}
/// <summary>
/// -----[ ]-----
/// x o
/// </summary>
[Test]
public void TestPressDuringNoteAndReleaseAfterTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
new ManiaReplayFrame(time_after_tail),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Miss);
}
/// <summary>
/// -----[ ]-----
/// x o o
/// </summary>
[Test]
public void TestPressDuringNoteAndReleaseAtTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
new ManiaReplayFrame(time_tail),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Perfect);
assertTailJudgement(HitResult.Meh);
}
/// <summary>
/// -----[ ]-----
/// xo o
/// </summary>
[Test]
public void TestPressAndReleaseAtTail()
{
performTest(new List<ReplayFrame>
{
new ManiaReplayFrame(time_tail, ManiaAction.Key1),
new ManiaReplayFrame(time_tail + 10),
});
assertHeadJudgement(HitResult.Miss);
assertTickJudgement(HitResult.Miss);
assertTailJudgement(HitResult.Meh);
}
private void assertHeadJudgement(HitResult result)
=> AddAssert($"head judged as {result}", () => judgementResults[0].Type == result);
private void assertTailJudgement(HitResult result)
=> AddAssert($"tail judged as {result}", () => judgementResults[^2].Type == result);
private void assertNoteJudgement(HitResult result)
=> AddAssert($"hold note judged as {result}", () => judgementResults[^1].Type == result);
private void assertTickJudgement(HitResult result)
=> AddAssert($"tick judged as {result}", () => judgementResults[6].Type == result); // arbitrary tick
private ScoreAccessibleReplayPlayer currentPlayer;
private void performTest(List<ReplayFrame> frames)
{
AddStep("load player", () =>
{
Beatmap.Value = CreateWorkingBeatmap(new Beatmap<ManiaHitObject>
{
HitObjects =
{
new HoldNote
{
StartTime = time_head,
Duration = time_tail - time_head,
Column = 0,
}
},
BeatmapInfo =
{
BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 4 },
Ruleset = new ManiaRuleset().RulesetInfo
},
});
Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f });
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
p.OnLoadComplete += _ =>
{
p.ScoreProcessor.NewJudgement += result =>
{
if (currentPlayer == p) judgementResults.Add(result);
};
p.ScoreProcessor.AllJudged += () =>
{
if (currentPlayer == p) allJudgedFired = true;
};
};
LoadScreen(currentPlayer = p);
allJudgedFired = false;
judgementResults = new List<JudgementResult>();
});
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
AddUntilStep("Wait for all judged", () => allJudgedFired);
}
private class ScoreAccessibleReplayPlayer : ReplayPlayer
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
protected override bool PauseOnFocusLost => false;
public ScoreAccessibleReplayPlayer(Score score)
: base(score, false, false)
{
}
}
}
}

View File

@ -0,0 +1,15 @@
// 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.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
public class TestScenePlayer : PlayerTestScene
{
public TestScenePlayer()
: base(new ManiaRuleset())
{
}
}
}

View File

@ -5,7 +5,7 @@ using osu.Game.Rulesets.Mania.Objects;
using System;
using System.Linq;
using System.Collections.Generic;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// </summary>
private const int max_notes_for_density = 7;
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
public int TargetColumns;
public bool Dual;
public readonly bool IsForCurrentRuleset;
@ -37,10 +35,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
private ManiaBeatmap beatmap;
public ManiaBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
public ManiaBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
: base(beatmap, ruleset)
{
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(new ManiaRuleset().RulesetInfo);
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
var roundedCircleSize = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize);
var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
@ -69,6 +67,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
}
}
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition || h is ManiaHitObject);
protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original)
{
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;

View File

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.MathUtils;

View File

@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
// Todo: This shouldn't exist, mania should not reference the drawable hitobject directly.
if (DrawableObject.IsLoaded)
{
DrawableNote note = position == HoldNotePosition.Start ? DrawableObject.Head : DrawableObject.Tail;
DrawableNote note = position == HoldNotePosition.Start ? (DrawableNote)DrawableObject.Head : DrawableObject.Tail;
Anchor = note.Anchor;
Origin = note.Origin;

View File

@ -54,10 +54,10 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
return true;
}
protected override bool OnMouseUp(MouseUpEvent e)
protected override void OnMouseUp(MouseUpEvent e)
{
EndPlacement();
return base.OnMouseUp(e);
base.OnMouseUp(e);
}
public override void UpdatePosition(Vector2 screenSpacePosition)

View File

@ -55,14 +55,12 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
return base.OnMouseDown(e);
}
protected override bool OnDrag(DragEvent e)
protected override void OnDrag(DragEvent e)
{
var result = base.OnDrag(e);
base.OnDrag(e);
ScreenSpaceDragPosition = e.ScreenSpaceMousePosition;
DragPosition = DrawableObject.ToLocalSpace(e.ScreenSpaceMousePosition);
return result;
}
public override void Show()

View File

@ -0,0 +1,34 @@
// 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 osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Screens.Edit.Compose.Components;
namespace osu.Game.Rulesets.Mania.Edit
{
public class ManiaBlueprintContainer : ComposeBlueprintContainer
{
public ManiaBlueprintContainer(IEnumerable<DrawableHitObject> drawableHitObjects)
: base(drawableHitObjects)
{
}
public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableNote note:
return new NoteSelectionBlueprint(note);
case DrawableHoldNote holdNote:
return new HoldNoteSelectionBlueprint(holdNote);
}
return base.CreateBlueprintFor(hitObject);
}
}
}

View File

@ -5,11 +5,8 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
@ -52,26 +49,12 @@ namespace osu.Game.Rulesets.Mania.Edit
return drawableRuleset;
}
protected override ComposeBlueprintContainer CreateBlueprintContainer() => new ManiaBlueprintContainer(drawableRuleset.Playfield.AllHitObjects);
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
{
new NoteCompositionTool(),
new HoldNoteCompositionTool()
};
public override SelectionHandler CreateSelectionHandler() => new ManiaSelectionHandler();
public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableNote note:
return new NoteSelectionBlueprint(note);
case DrawableHoldNote holdNote:
return new HoldNoteSelectionBlueprint(holdNote);
}
return base.CreateBlueprintFor(hitObject);
}
}
}

View File

@ -15,11 +15,11 @@ namespace osu.Game.Rulesets.Mania.Judgements
{
switch (result)
{
case HitResult.Miss:
default:
return 0;
default:
return 0.040;
case HitResult.Perfect:
return 0.01;
}
}
}

View File

@ -29,32 +29,5 @@ namespace osu.Game.Rulesets.Mania.Judgements
return 300;
}
}
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
case HitResult.Miss:
return -0.125;
case HitResult.Meh:
return 0.005;
case HitResult.Ok:
return 0.010;
case HitResult.Good:
return 0.035;
case HitResult.Great:
return 0.055;
case HitResult.Perfect:
return 0.065;
default:
return 0;
}
}
}
}

View File

@ -26,18 +26,20 @@ using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Difficulty;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mania.Skinning;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mania
{
public class ManiaRuleset : Ruleset
public class ManiaRuleset : Ruleset, ILegacyRuleset
{
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 ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
@ -45,6 +47,8 @@ namespace osu.Game.Rulesets.Mania
public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this);
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new ManiaLegacySkinTransformer(source);
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
@ -151,6 +155,7 @@ namespace osu.Game.Rulesets.Mania
new ManiaModRandom(),
new ManiaModDualStages(),
new ManiaModMirror(),
new ManiaModDifficultyAdjust(),
};
case ModType.Automation:
@ -174,11 +179,13 @@ namespace osu.Game.Rulesets.Mania
public override string ShortName => SHORT_NAME;
public override string PlayingVerb => "Smashing keys";
public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.RulesetMania };
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(this, beatmap);
public override int? LegacyID => 3;
public int LegacyID => 3;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame();

View File

@ -3,12 +3,9 @@
using osu.Game.Rulesets.Mods;
namespace osu.Game.Online.API.Requests.Responses
namespace osu.Game.Rulesets.Mania.Mods
{
public class APIMod : IMod
public class ManiaModDifficultyAdjust : ModDifficultyAdjust
{
public string Acronym { get; set; }
public bool Equals(IMod other) => Acronym == other?.Acronym;
}
}

View File

@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string Name => "Fade In";
public override string Acronym => "FI";
public override IconUsage Icon => OsuIcon.ModHidden;
public override IconUsage? Icon => OsuIcon.ModHidden;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => @"Keys appear out of nowhere!";
public override double ScoreMultiplier => 1;

View File

@ -4,7 +4,7 @@
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Beatmaps;
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Random";
public override string Acronym => "RD";
public override ModType Type => ModType.Conversion;
public override IconUsage Icon => OsuIcon.Dice;
public override IconUsage? Icon => OsuIcon.Dice;
public override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 1;

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
@ -21,11 +20,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
public override bool DisplayResult => false;
public DrawableNote Head => headContainer.Child;
public DrawableNote Tail => tailContainer.Child;
public DrawableHoldNoteHead Head => headContainer.Child;
public DrawableHoldNoteTail Tail => tailContainer.Child;
private readonly Container<DrawableHeadNote> headContainer;
private readonly Container<DrawableTailNote> tailContainer;
private readonly Container<DrawableHoldNoteHead> headContainer;
private readonly Container<DrawableHoldNoteTail> tailContainer;
private readonly Container<DrawableHoldNoteTick> tickContainer;
private readonly BodyPiece bodyPiece;
@ -33,12 +32,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// <summary>
/// Time at which the user started holding this hold note. Null if the user is not holding this hold note.
/// </summary>
private double? holdStartTime;
public double? HoldStartTime { get; private set; }
/// <summary>
/// Whether the hold note has been released too early and shouldn't give full score for the release.
/// </summary>
private bool hasBroken;
public bool HasBroken { get; private set; }
public DrawableHoldNote(HoldNote hitObject)
: base(hitObject)
@ -49,8 +48,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
bodyPiece = new BodyPiece { RelativeSizeAxes = Axes.X },
tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both },
headContainer = new Container<DrawableHeadNote> { RelativeSizeAxes = Axes.Both },
tailContainer = new Container<DrawableTailNote> { RelativeSizeAxes = Axes.Both },
headContainer = new Container<DrawableHoldNoteHead> { RelativeSizeAxes = Axes.Both },
tailContainer = new Container<DrawableHoldNoteTail> { RelativeSizeAxes = Axes.Both },
});
AccentColour.BindValueChanged(colour =>
@ -65,11 +64,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
switch (hitObject)
{
case DrawableHeadNote head:
case DrawableHoldNoteHead head:
headContainer.Child = head;
break;
case DrawableTailNote tail:
case DrawableHoldNoteTail tail:
tailContainer.Child = tail;
break;
@ -92,7 +91,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
switch (hitObject)
{
case TailNote _:
return new DrawableTailNote(this)
return new DrawableHoldNoteTail(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@ -100,7 +99,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
};
case Note _:
return new DrawableHeadNote(this)
return new DrawableHoldNoteHead(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@ -110,7 +109,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
case HoldNoteTick tick:
return new DrawableHoldNoteTick(tick)
{
HoldStartTime = () => holdStartTime,
HoldStartTime = () => HoldStartTime,
AccentColour = { BindTarget = AccentColour }
};
}
@ -125,12 +124,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (Tail.AllJudged)
ApplyResult(r => r.Type = HitResult.Perfect);
}
protected override void Update()
{
base.Update();
@ -146,146 +139,62 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.UpdateStateTransforms(state);
}
protected void BeginHold()
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
holdStartTime = Time.Current;
bodyPiece.Hitting = true;
}
if (Tail.AllJudged)
ApplyResult(r => r.Type = HitResult.Perfect);
protected void EndHold()
{
holdStartTime = null;
bodyPiece.Hitting = false;
if (Tail.Result.Type == HitResult.Miss)
HasBroken = true;
}
public bool OnPressed(ManiaAction action)
{
// Make sure the action happened within the body of the hold note
if (Time.Current < HitObject.StartTime || Time.Current > HitObject.EndTime)
if (AllJudged)
return false;
if (action != Action.Value)
return false;
// The user has pressed during the body of the hold note, after the head note and its hit windows have passed
// and within the limited range of the above if-statement. This state will be managed by the head note if the
// user has pressed during the hit windows of the head note.
BeginHold();
beginHoldAt(Time.Current - Head.HitObject.StartTime);
Head.UpdateResult();
return true;
}
public bool OnReleased(ManiaAction action)
private void beginHoldAt(double timeOffset)
{
// Make sure that the user started holding the key during the hold note
if (!holdStartTime.HasValue)
return false;
if (timeOffset < -Head.HitObject.HitWindows.WindowFor(HitResult.Miss))
return;
HoldStartTime = Time.Current;
bodyPiece.Hitting = true;
}
public void OnReleased(ManiaAction action)
{
if (AllJudged)
return;
if (action != Action.Value)
return false;
return;
EndHold();
// Make sure a hold was started
if (HoldStartTime == null)
return;
Tail.UpdateResult();
endHold();
// If the key has been released too early, the user should not receive full score for the release
if (!Tail.IsHit)
hasBroken = true;
return true;
HasBroken = true;
}
/// <summary>
/// The head note of a hold.
/// </summary>
private class DrawableHeadNote : DrawableNote
private void endHold()
{
private readonly DrawableHoldNote holdNote;
public DrawableHeadNote(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Head)
{
this.holdNote = holdNote;
}
public override bool OnPressed(ManiaAction action)
{
if (!base.OnPressed(action))
return false;
// If the key has been released too early, the user should not receive full score for the release
if (Result.Type == HitResult.Miss)
holdNote.hasBroken = true;
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
// The body doesn't handle these early early hits, so we have to explicitly set the holding state here
holdNote.BeginHold();
return true;
}
}
/// <summary>
/// The tail note of a hold.
/// </summary>
private class DrawableTailNote : DrawableNote
{
/// <summary>
/// Lenience of release hit windows. This is to make cases where the hold note release
/// is timed alongside presses of other hit objects less awkward.
/// Todo: This shouldn't exist for non-LegacyBeatmapDecoder beatmaps
/// </summary>
private const double release_window_lenience = 1.5;
private readonly DrawableHoldNote holdNote;
public DrawableTailNote(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Tail)
{
this.holdNote = holdNote;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
Debug.Assert(HitObject.HitWindows != null);
// Factor in the release lenience
timeOffset /= release_window_lenience;
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss);
return;
}
var result = HitObject.HitWindows.ResultFor(timeOffset);
if (result == HitResult.None)
return;
ApplyResult(r =>
{
if (holdNote.hasBroken && (result == HitResult.Perfect || result == HitResult.Perfect))
result = HitResult.Good;
r.Type = result;
});
}
public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down
public override bool OnReleased(ManiaAction action)
{
// Make sure that the user started holding the key during the hold note
if (!holdNote.holdStartTime.HasValue)
return false;
if (action != Action.Value)
return false;
UpdateResult(true);
// Handled by the hold note, which will set holding = false
return false;
}
HoldStartTime = null;
bodyPiece.Hitting = false;
}
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
/// <summary>
/// The head of a <see cref="DrawableHoldNote"/>.
/// </summary>
public class DrawableHoldNoteHead : DrawableNote
{
public DrawableHoldNoteHead(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Head)
{
}
public void UpdateResult() => base.UpdateResult(true);
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
public override void OnReleased(ManiaAction action)
{
}
}
}

View File

@ -0,0 +1,66 @@
// 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.Diagnostics;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
/// <summary>
/// The tail of a <see cref="DrawableHoldNote"/>.
/// </summary>
public class DrawableHoldNoteTail : DrawableNote
{
/// <summary>
/// Lenience of release hit windows. This is to make cases where the hold note release
/// is timed alongside presses of other hit objects less awkward.
/// Todo: This shouldn't exist for non-LegacyBeatmapDecoder beatmaps
/// </summary>
private const double release_window_lenience = 1.5;
private readonly DrawableHoldNote holdNote;
public DrawableHoldNoteTail(DrawableHoldNote holdNote)
: base(holdNote.HitObject.Tail)
{
this.holdNote = holdNote;
}
public void UpdateResult() => base.UpdateResult(true);
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
Debug.Assert(HitObject.HitWindows != null);
// Factor in the release lenience
timeOffset /= release_window_lenience;
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss);
return;
}
var result = HitObject.HitWindows.ResultFor(timeOffset);
if (result == HitResult.None)
return;
ApplyResult(r =>
{
// If the head wasn't hit or the hold note was broken, cap the max score to Meh.
if (result > HitResult.Meh && (!holdNote.Head.IsHit || holdNote.HasBroken))
result = HitResult.Meh;
r.Type = result;
});
}
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
public override void OnReleased(ManiaAction action)
{
}
}
}

View File

@ -77,6 +77,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return UpdateResult(true);
}
public virtual bool OnReleased(ManiaAction action) => false;
public virtual void OnReleased(ManiaAction action)
{
}
}
}

View File

@ -1,68 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
public class GlowPiece : CompositeDrawable, IHasAccentColour
{
private const float glow_alpha = 0.7f;
private const float glow_radius = 5;
public GlowPiece()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
InternalChild = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
};
}
protected override void LoadComplete()
{
base.LoadComplete();
updateGlow();
}
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
updateGlow();
}
}
private void updateGlow()
{
if (!IsLoaded)
return;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = AccentColour.Opacity(glow_alpha),
Radius = glow_radius,
Hollow = true
};
}
}
}

View File

@ -1,85 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
{
public class LaneGlowPiece : CompositeDrawable, IHasAccentColour
{
private const float total_height = 100;
private const float glow_height = 50;
private const float glow_alpha = 0.4f;
private const float edge_alpha = 0.3f;
public LaneGlowPiece()
{
BypassAutoSizeAxes = Axes.Both;
RelativeSizeAxes = Axes.X;
Height = total_height;
InternalChildren = new[]
{
new Container
{
Name = "Left edge",
RelativeSizeAxes = Axes.Y,
Width = 1,
Children = createGradient(edge_alpha)
},
new Container
{
Name = "Right edge",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = 1,
Children = createGradient(edge_alpha)
},
new Container
{
Name = "Glow",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = glow_height,
Children = createGradient(glow_alpha)
}
};
}
private Drawable[] createGradient(float alpha) => new Drawable[]
{
new Box
{
Name = "Top",
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Blending = BlendingParameters.Additive,
Colour = ColourInfo.GradientVertical(Color4.Transparent, Color4.White.Opacity(alpha))
},
new Box
{
Name = "Bottom",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
Blending = BlendingParameters.Additive,
Colour = ColourInfo.GradientVertical(Color4.White.Opacity(alpha), Color4.Transparent)
}
};
public Color4 AccentColour
{
get => Colour;
set => Colour = value;
}
}
}

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Replays
public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
// We don't need to fully convert, just create the converter
var converter = new ManiaBeatmapConverter(beatmap);
var converter = new ManiaBeatmapConverter(beatmap, new ManiaRuleset());
// NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling
// elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage.

View File

@ -1,87 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Scoring
{
internal class ManiaScoreProcessor : ScoreProcessor
{
/// <summary>
/// The hit HP multiplier at OD = 0.
/// </summary>
private const double hp_multiplier_min = 0.75;
/// <summary>
/// The hit HP multiplier at OD = 0.
/// </summary>
private const double hp_multiplier_mid = 0.85;
/// <summary>
/// The hit HP multiplier at OD = 0.
/// </summary>
private const double hp_multiplier_max = 1;
/// <summary>
/// The MISS HP multiplier at OD = 0.
/// </summary>
private const double hp_multiplier_miss_min = 0.5;
/// <summary>
/// The MISS HP multiplier at OD = 5.
/// </summary>
private const double hp_multiplier_miss_mid = 0.75;
/// <summary>
/// The MISS HP multiplier at OD = 10.
/// </summary>
private const double hp_multiplier_miss_max = 1;
/// <summary>
/// The MISS HP multiplier. This is multiplied to the miss hp increase.
/// </summary>
private double hpMissMultiplier = 1;
/// <summary>
/// The HIT HP multiplier. This is multiplied to hit hp increases.
/// </summary>
private double hpMultiplier = 1;
public ManiaScoreProcessor(IBeatmap beatmap)
: base(beatmap)
{
}
protected override void ApplyBeatmap(IBeatmap beatmap)
{
base.ApplyBeatmap(beatmap);
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty;
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
}
protected override void SimulateAutoplay(IBeatmap beatmap)
{
while (true)
{
base.SimulateAutoplay(beatmap);
if (!HasFailed)
break;
hpMultiplier *= 1.01;
hpMissMultiplier *= 0.98;
Reset(false);
}
}
protected override double HealthAdjustmentFactorFor(JudgementResult result)
=> result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier;
public override HitWindows CreateHitWindows() => new ManiaHitWindows();
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Audio;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Skinning
{
public class ManiaLegacySkinTransformer : ISkin
{
private readonly ISkin source;
public ManiaLegacySkinTransformer(ISkin source)
{
this.source = source;
}
public Drawable GetDrawableComponent(ISkinComponent component)
{
switch (component)
{
case GameplaySkinComponent<HitResult> resultComponent:
return getResult(resultComponent);
}
return null;
}
private Drawable getResult(GameplaySkinComponent<HitResult> resultComponent)
{
switch (resultComponent.Component)
{
case HitResult.Miss:
return this.GetAnimation("mania-hit0", true, true);
case HitResult.Meh:
return this.GetAnimation("mania-hit50", true, true);
case HitResult.Ok:
return this.GetAnimation("mania-hit100", true, true);
case HitResult.Good:
return this.GetAnimation("mania-hit200", true, true);
case HitResult.Great:
return this.GetAnimation("mania-hit300", true, true);
case HitResult.Perfect:
return this.GetAnimation("mania-hit300g", true, true);
}
return null;
}
public Texture GetTexture(string componentName) => source.GetTexture(componentName);
public SampleChannel GetSample(ISampleInfo sample) => source.GetSample(sample);
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) =>
source.GetConfig<TLookup, TValue>(lookup);
}
}

View File

@ -191,7 +191,9 @@ namespace osu.Game.Rulesets.Mania.UI
return true;
}
public bool OnReleased(ManiaAction action) => false;
public void OnReleased(ManiaAction action)
{
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
// This probably shouldn't exist as is, but the columns in the stage are separated by a 1px border

View File

@ -98,11 +98,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
return false;
}
public bool OnReleased(ManiaAction action)
public void OnReleased(ManiaAction action)
{
if (action == this.action.Value)
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
return false;
}
}
}

View File

@ -18,30 +18,17 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{
public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour
{
private const float hit_target_bar_height = 2;
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly Container hitTargetLine;
private readonly Drawable hitTargetBar;
private readonly Drawable hitTarget;
public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
{
InternalChildren = new[]
{
hitTargetBar = new Box
hitTarget = new DefaultHitTarget
{
RelativeSizeAxes = Axes.X,
Height = NotePiece.NOTE_HEIGHT,
Alpha = 0.6f,
Colour = Color4.Black
},
hitTargetLine = new Container
{
RelativeSizeAxes = Axes.X,
Height = hit_target_bar_height,
Masking = true,
Child = new Box { RelativeSizeAxes = Axes.Both }
},
hitObjectContainer
};
@ -55,17 +42,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
{
Anchor anchor = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
hitTargetBar.Anchor = hitTargetBar.Origin = anchor;
hitTargetLine.Anchor = hitTargetLine.Origin = anchor;
hitTarget.Anchor = hitTarget.Origin = anchor;
}, true);
}
protected override void LoadComplete()
{
base.LoadComplete();
updateColours();
}
private Color4 accentColour;
public Color4 AccentColour
@ -78,21 +58,88 @@ namespace osu.Game.Rulesets.Mania.UI.Components
accentColour = value;
updateColours();
if (hitTarget is IHasAccentColour colouredHitTarget)
colouredHitTarget.AccentColour = accentColour;
}
}
private void updateColours()
private class DefaultHitTarget : CompositeDrawable, IHasAccentColour
{
if (!IsLoaded)
return;
private const float hit_target_bar_height = 2;
hitTargetLine.EdgeEffect = new EdgeEffectParameters
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly Container hitTargetLine;
private readonly Drawable hitTargetBar;
public DefaultHitTarget()
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Opacity(0.5f),
};
InternalChildren = new[]
{
hitTargetBar = new Box
{
RelativeSizeAxes = Axes.X,
Height = NotePiece.NOTE_HEIGHT,
Alpha = 0.6f,
Colour = Color4.Black
},
hitTargetLine = new Container
{
RelativeSizeAxes = Axes.X,
Height = hit_target_bar_height,
Masking = true,
Child = new Box { RelativeSizeAxes = Axes.Both }
},
};
}
[BackgroundDependencyLoader]
private void load(IScrollingInfo scrollingInfo)
{
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(dir =>
{
Anchor anchor = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
hitTargetBar.Anchor = hitTargetBar.Origin = anchor;
hitTargetLine.Anchor = hitTargetLine.Origin = anchor;
}, true);
}
protected override void LoadComplete()
{
base.LoadComplete();
updateColours();
}
private Color4 accentColour;
public Color4 AccentColour
{
get => accentColour;
set
{
if (accentColour == value)
return;
accentColour = value;
updateColours();
}
}
private void updateColours()
{
if (!IsLoaded)
return;
hitTargetLine.EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = accentColour.Opacity(0.5f),
};
}
}
}
}

View File

@ -115,11 +115,10 @@ namespace osu.Game.Rulesets.Mania.UI.Components
return false;
}
public bool OnReleased(ManiaAction action)
public void OnReleased(ManiaAction action)
{
if (action == this.action.Value)
keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
return false;
}
}
}

View File

@ -5,7 +5,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osuTK;
using osuTK.Graphics;

View File

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;

View File

@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Tests
[BackgroundDependencyLoader]
private void load(AudioManager audio, SkinManager skinManager)
{
var dllStore = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll");
var dllStore = new DllResourceStore(typeof(SkinnableTestScene).Assembly);
metricsSkin = new TestLegacySkin(new SkinInfo(), new NamespacedResourceStore<byte[]>(dllStore, "Resources/metrics_skin"), audio, true);
defaultSkin = skinManager.GetSkin(DefaultLegacySkin.Info);

View File

@ -0,0 +1,155 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Graphics;
using osu.Framework.IO.Stores;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestSceneLegacyBeatmapSkin : OsuTestScene
{
[Resolved]
private AudioManager audio { get; set; }
[TestCase(true)]
[TestCase(false)]
public void TestBeatmapComboColours(bool customSkinColoursPresent)
{
ExposedPlayer player = null;
AddStep("load coloured beatmap", () => player = loadBeatmap(customSkinColoursPresent, true));
AddUntilStep("wait for player", () => player.IsLoaded);
AddAssert("is beatmap skin colours", () => player.UsableComboColours.SequenceEqual(TestBeatmapSkin.Colours));
}
[Test]
public void TestBeatmapNoComboColours()
{
ExposedPlayer player = null;
AddStep("load no-colour beatmap", () => player = loadBeatmap(false, false));
AddUntilStep("wait for player", () => player.IsLoaded);
AddAssert("is default user skin colours", () => player.UsableComboColours.SequenceEqual(SkinConfiguration.DefaultComboColours));
}
[Test]
public void TestBeatmapNoComboColoursSkinOverride()
{
ExposedPlayer player = null;
AddStep("load custom-skin colour", () => player = loadBeatmap(true, false));
AddUntilStep("wait for player", () => player.IsLoaded);
AddAssert("is custom user skin colours", () => player.UsableComboColours.SequenceEqual(TestSkin.Colours));
}
private ExposedPlayer loadBeatmap(bool userHasCustomColours, bool beatmapHasColours)
{
ExposedPlayer player;
Beatmap.Value = new CustomSkinWorkingBeatmap(audio, beatmapHasColours);
Child = new OsuScreenStack(player = new ExposedPlayer(userHasCustomColours)) { RelativeSizeAxes = Axes.Both };
return player;
}
private class ExposedPlayer : Player
{
private readonly bool userHasCustomColours;
public ExposedPlayer(bool userHasCustomColours)
: base(false, false)
{
this.userHasCustomColours = userHasCustomColours;
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<ISkinSource>(new TestSkin(userHasCustomColours));
return dependencies;
}
public IReadOnlyList<Color4> UsableComboColours =>
GameplayClockContainer.ChildrenOfType<BeatmapSkinProvidingContainer>()
.First()
.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value;
}
private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
{
private readonly bool hasColours;
public CustomSkinWorkingBeatmap(AudioManager audio, bool hasColours)
: base(new Beatmap
{
BeatmapInfo =
{
BeatmapSet = new BeatmapSetInfo(),
Ruleset = new OsuRuleset().RulesetInfo,
},
HitObjects = { new HitCircle { Position = new Vector2(256, 192) } }
}, null, null, audio)
{
this.hasColours = hasColours;
}
protected override ISkin GetSkin() => new TestBeatmapSkin(BeatmapInfo, hasColours);
}
private class TestBeatmapSkin : LegacyBeatmapSkin
{
public static Color4[] Colours { get; } =
{
new Color4(50, 100, 150, 255),
new Color4(40, 80, 120, 255),
};
public TestBeatmapSkin(BeatmapInfo beatmap, bool hasColours)
: base(beatmap, new ResourceStore<byte[]>(), null)
{
if (hasColours)
Configuration.AddComboColours(Colours);
}
}
private class TestSkin : LegacySkin, ISkinSource
{
public static Color4[] Colours { get; } =
{
new Color4(150, 100, 50, 255),
new Color4(20, 20, 20, 255),
};
public TestSkin(bool hasCustomColours)
: base(new SkinInfo(), null, null, string.Empty)
{
if (hasCustomColours)
Configuration.AddComboColours(Colours);
}
public event Action SourceChanged
{
add { }
remove { }
}
}
}
}

View File

@ -9,7 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Beatmaps;
@ -33,8 +33,8 @@ namespace osu.Game.Rulesets.Osu.Tests
typeof(CircularDistanceSnapGrid)
};
[Cached(typeof(IEditorBeatmap))]
private readonly EditorBeatmap<OsuHitObject> editorBeatmap;
[Cached(typeof(EditorBeatmap))]
private readonly EditorBeatmap editorBeatmap;
[Cached]
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public TestSceneOsuDistanceSnapGrid()
{
editorBeatmap = new EditorBeatmap<OsuHitObject>(new OsuBeatmap());
editorBeatmap = new EditorBeatmap(new OsuBeatmap());
}
[SetUp]

View File

@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;

View File

@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects;

View File

@ -4,7 +4,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Framework.Testing;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Tests
EndTime = 6000,
},
// placeholder object to avoid hitting the results screen
new HitObject
new HitCircle
{
StartTime = 99999,
}

View File

@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">

View File

@ -7,7 +7,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects.Types;
using System;
using System.Linq;
using osu.Game.Rulesets.Osu.UI;
using osu.Framework.Extensions.IEnumerableExtensions;
@ -15,12 +15,12 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
{
public class OsuBeatmapConverter : BeatmapConverter<OsuHitObject>
{
public OsuBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
public OsuBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
: base(beatmap, ruleset)
{
}
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasPosition) };
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasPosition);
protected override IEnumerable<OsuHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap)
{

View File

@ -135,13 +135,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
return false;
}
protected override bool OnMouseUp(MouseUpEvent e) => RequestSelection != null;
protected override bool OnClick(ClickEvent e) => RequestSelection != null;
protected override bool OnDragStart(DragStartEvent e) => e.Button == MouseButton.Left;
protected override bool OnDrag(DragEvent e)
protected override void OnDrag(DragEvent e)
{
if (ControlPoint == slider.Path.ControlPoints[0])
{
@ -158,12 +156,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
}
else
ControlPoint.Position.Value += e.Delta;
return true;
}
protected override bool OnDragEnd(DragEndEvent e) => true;
/// <summary>
/// Updates the state of the circular control point marker.
/// </summary>

View File

@ -108,7 +108,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
return false;
}
public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete;
public void OnReleased(PlatformAction action)
{
}
private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e)
{

View File

@ -106,11 +106,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
return true;
}
protected override bool OnMouseUp(MouseUpEvent e)
protected override void OnMouseUp(MouseUpEvent e)
{
if (state == PlacementState.Body && e.Button == MouseButton.Right)
endCurve();
return base.OnMouseUp(e);
base.OnMouseUp(e);
}
protected override bool OnDoubleClick(DoubleClickEvent e)

View File

@ -90,19 +90,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
protected override bool OnDragStart(DragStartEvent e) => placementControlPointIndex != null;
protected override bool OnDrag(DragEvent e)
protected override void OnDrag(DragEvent e)
{
Debug.Assert(placementControlPointIndex != null);
HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position.Value = e.MousePosition - HitObject.Position;
return true;
}
protected override bool OnDragEnd(DragEndEvent e)
protected override void OnDragEnd(DragEndEvent e)
{
placementControlPointIndex = null;
return true;
}
private BindableList<PathControlPoint> controlPoints => HitObject.Path.ControlPoints;

View File

@ -0,0 +1,41 @@
// 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 osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Screens.Edit.Compose.Components;
namespace osu.Game.Rulesets.Osu.Edit
{
public class OsuBlueprintContainer : ComposeBlueprintContainer
{
public OsuBlueprintContainer(IEnumerable<DrawableHitObject> drawableHitObjects)
: base(drawableHitObjects)
{
}
protected override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableHitCircle circle:
return new HitCircleSelectionBlueprint(circle);
case DrawableSlider slider:
return new SliderSelectionBlueprint(slider);
case DrawableSpinner spinner:
return new SpinnerSelectionBlueprint(spinner);
}
return base.CreateBlueprintFor(hitObject);
}
}
}

View File

@ -9,12 +9,7 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Compose.Components;
@ -37,24 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit
new SpinnerCompositionTool()
};
public override SelectionHandler CreateSelectionHandler() => new OsuSelectionHandler();
public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableHitCircle circle:
return new HitCircleSelectionBlueprint(circle);
case DrawableSlider slider:
return new SliderSelectionBlueprint(slider);
case DrawableSpinner spinner:
return new SpinnerSelectionBlueprint(spinner);
}
return base.CreateBlueprintFor(hitObject);
}
protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(HitObjects);
protected override DistanceSnapGrid CreateDistanceSnapGrid(IEnumerable<HitObject> selectedHitObjects)
{
@ -91,10 +69,10 @@ namespace osu.Game.Rulesets.Osu.Edit
if (sourceIndex == -1)
return null;
OsuHitObject sourceObject = EditorBeatmap.HitObjects[sourceIndex];
HitObject sourceObject = EditorBeatmap.HitObjects[sourceIndex];
int targetIndex = sourceIndex + targetOffset;
OsuHitObject targetObject = null;
HitObject targetObject = null;
// Keep advancing the target object while its start time falls before the end time of the source object
while (true)
@ -111,7 +89,7 @@ namespace osu.Game.Rulesets.Osu.Edit
targetIndex++;
}
return new OsuDistanceSnapGrid(sourceObject, targetObject);
return new OsuDistanceSnapGrid((OsuHitObject)sourceObject, (OsuHitObject)targetObject);
}
}
}

View File

@ -27,22 +27,5 @@ namespace osu.Game.Rulesets.Osu.Judgements
return 300;
}
}
protected override double HealthIncreaseFor(HitResult result)
{
switch (result)
{
case HitResult.Miss:
return -0.02;
case HitResult.Meh:
case HitResult.Good:
case HitResult.Great:
return 0.01;
default:
return 0;
}
}
}
}

View File

@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Autopilot";
public override string Acronym => "AP";
public override IconUsage Icon => OsuIcon.ModAutopilot;
public override IconUsage? Icon => OsuIcon.ModAutopilot;
public override ModType Type => ModType.Automation;
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 1;

View File

@ -18,13 +18,13 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModBlinds : Mod, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToScoreProcessor
public class OsuModBlinds : Mod, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToHealthProcessor
{
public override string Name => "Blinds";
public override string Description => "Play with blinds on your screen.";
public override string Acronym => "BL";
public override IconUsage Icon => FontAwesome.Solid.Adjust;
public override IconUsage? Icon => FontAwesome.Solid.Adjust;
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => false;
@ -37,9 +37,9 @@ namespace osu.Game.Rulesets.Osu.Mods
drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield.HitObjectContainer, drawableRuleset.Beatmap));
}
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
{
scoreProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); };
healthProcessor.Health.ValueChanged += health => { blinds.AnimateClosedness((float)health.NewValue); };
}
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;

View File

@ -5,13 +5,13 @@ using osu.Framework.Graphics.Sprites;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModDeflate : OsuModeObjectScaleTween
public class OsuModDeflate : OsuModObjectScaleTween
{
public override string Name => "Deflate";
public override string Acronym => "DF";
public override IconUsage Icon => FontAwesome.Solid.CompressArrowsAlt;
public override IconUsage? Icon => FontAwesome.Solid.CompressArrowsAlt;
public override string Description => "Hit them at the right size!";

View File

@ -0,0 +1,49 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModDifficultyAdjust : ModDifficultyAdjust
{
[SettingSource("Circle Size", "Override a beatmap's set CS.")]
public BindableNumber<float> CircleSize { get; } = new BindableFloat
{
Precision = 0.1f,
MinValue = 1,
MaxValue = 10,
Default = 5,
Value = 5,
};
[SettingSource("Approach Rate", "Override a beatmap's set AR.")]
public BindableNumber<float> ApproachRate { get; } = new BindableFloat
{
Precision = 0.1f,
MinValue = 1,
MaxValue = 10,
Default = 5,
Value = 5,
};
protected override void TransferSettings(BeatmapDifficulty difficulty)
{
base.TransferSettings(difficulty);
TransferSetting(CircleSize, difficulty.CircleSize);
TransferSetting(ApproachRate, difficulty.ApproachRate);
}
protected override void ApplySettings(BeatmapDifficulty difficulty)
{
base.ApplySettings(difficulty);
difficulty.CircleSize = CircleSize.Value;
difficulty.ApproachRate = ApproachRate.Value;
}
}
}

View File

@ -8,7 +8,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;

View File

@ -5,13 +5,13 @@ using osu.Framework.Graphics.Sprites;
namespace osu.Game.Rulesets.Osu.Mods
{
internal class OsuModGrow : OsuModeObjectScaleTween
internal class OsuModGrow : OsuModObjectScaleTween
{
public override string Name => "Grow";
public override string Acronym => "GR";
public override IconUsage Icon => FontAwesome.Solid.ArrowsAltV;
public override IconUsage? Icon => FontAwesome.Solid.ArrowsAltV;
public override string Description => "Hit them at the right size!";

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
/// <summary>
/// Adjusts the size of hit objects during their fade in animation.
/// </summary>
public abstract class OsuModeObjectScaleTween : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
public abstract class OsuModObjectScaleTween : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
{
public override ModType Type => ModType.Fun;

View File

@ -20,13 +20,13 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Spin In";
public override string Acronym => "SI";
public override IconUsage Icon => FontAwesome.Solid.Undo;
public override IconUsage? Icon => FontAwesome.Solid.Undo;
public override ModType Type => ModType.Fun;
public override string Description => "Circles spin in. No approach circles.";
public override double ScoreMultiplier => 1;
// todo: this mod should be able to be compatible with hidden with a bit of further implementation.
public override Type[] IncompatibleMods => new[] { typeof(OsuModeObjectScaleTween), typeof(OsuModHidden), typeof(OsuModTraceable) };
public override Type[] IncompatibleMods => new[] { typeof(OsuModObjectScaleTween), typeof(OsuModHidden), typeof(OsuModTraceable) };
private const int rotate_offset = 360;
private const float rotate_starting_width = 2;

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Spun Out";
public override string Acronym => "SO";
public override IconUsage Icon => OsuIcon.ModSpunout;
public override IconUsage? Icon => OsuIcon.ModSpunout;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => @"Spinners will be automatically completed.";
public override double ScoreMultiplier => 0.9;

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Name => "Target";
public override string Acronym => "TP";
public override ModType Type => ModType.Conversion;
public override IconUsage Icon => OsuIcon.ModTarget;
public override IconUsage? Icon => OsuIcon.ModTarget;
public override string Description => @"Practice keeping up with the beat of the song.";
public override double ScoreMultiplier => 1;
}

View File

@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Bindables;
using System.Collections.Generic;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
@ -19,12 +18,11 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Traceable";
public override string Acronym => "TC";
public override IconUsage Icon => FontAwesome.Brands.SnapchatGhost;
public override ModType Type => ModType.Fun;
public override string Description => "Put your faith in the approach circles...";
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModeObjectScaleTween) };
public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween) };
private Bindable<bool> increaseFirstObjectVisibility = new Bindable<bool>();
public void ReadFromConfig(OsuConfigManager config)

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Transform";
public override string Acronym => "TR";
public override IconUsage Icon => FontAwesome.Solid.ArrowsAlt;
public override IconUsage? Icon => FontAwesome.Solid.ArrowsAlt;
public override ModType Type => ModType.Fun;
public override string Description => "Everything rotates. EVERYTHING.";
public override double ScoreMultiplier => 1;

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Wiggle";
public override string Acronym => "WG";
public override IconUsage Icon => FontAwesome.Solid.Certificate;
public override IconUsage? Icon => FontAwesome.Solid.Certificate;
public override ModType Type => ModType.Fun;
public override string Description => "They just won't stay still...";
public override double ScoreMultiplier => 1;

View File

@ -205,7 +205,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return false;
}
public bool OnReleased(OsuAction action) => false;
public void OnReleased(OsuAction action)
{
}
}
}
}

View File

@ -6,13 +6,11 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Scoring;
using osuTK;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@ -23,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private double animDuration;
private readonly SkinnableDrawable scaleContainer;
private readonly Drawable scaleContainer;
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
: base(repeatPoint)
@ -36,16 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Blending = BlendingParameters.Additive;
Origin = Anchor.Centre;
InternalChild = scaleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon
{
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.Solid.ChevronRight,
Size = new Vector2(0.35f)
}, confineMode: ConfineMode.NoScaling)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
InternalChild = scaleContainer = new ReverseArrowPiece();
}
private readonly IBindable<float> scaleBindable = new Bindable<float>();
@ -65,11 +54,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdateInitialTransforms()
{
animDuration = Math.Min(150, repeatPoint.SpanDuration / 2);
animDuration = Math.Min(300, repeatPoint.SpanDuration);
this.Animate(
d => d.FadeIn(animDuration),
d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 4, Easing.OutElasticHalf)
d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 2, Easing.OutElasticHalf)
);
}
@ -88,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
break;
case ArmedState.Hit:
this.FadeOut(animDuration, Easing.OutQuint)
this.FadeOut(animDuration, Easing.Out)
.ScaleTo(Scale * 1.5f, animDuration, Easing.Out);
break;
}
@ -121,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
break;
}
float aimRotation = MathHelper.RadiansToDegrees(MathF.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
float aimRotation = MathUtils.RadiansToDegrees(MathF.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
while (Math.Abs(aimRotation - Rotation) > 180)
aimRotation += aimRotation < Rotation ? 360 : -360;

View File

@ -0,0 +1,43 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osuTK;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class ReverseArrowPiece : BeatSyncedContainer
{
public ReverseArrowPiece()
{
Divisor = 2;
MinimumBeatLength = 200;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Blending = BlendingParameters.Additive;
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ReverseArrow), _ => new SpriteIcon
{
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.Solid.ChevronRight,
Size = new Vector2(0.35f)
})
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes) =>
Child.ScaleTo(1.3f).ScaleTo(1f, timingPoint.BeatLength, Easing.Out);
}
}

View File

@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private void setRange(double p0, double p1)
{
if (p0 > p1)
MathHelper.Swap(ref p0, ref p1);
(p0, p1) = (p1, p0);
if (SnakedStart == p0 && SnakedEnd == p1) return;

View File

@ -8,6 +8,7 @@ using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Utils;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@ -93,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
base.Update();
var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2));
var thisAngle = -MathUtils.RadiansToDegrees(MathF.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2));
bool validAndTracking = tracking && spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current;

View File

@ -6,7 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;

View File

@ -32,13 +32,13 @@ using System;
namespace osu.Game.Rulesets.Osu
{
public class OsuRuleset : Ruleset
public class OsuRuleset : Ruleset, ILegacyRuleset
{
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 ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap, this);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);
@ -130,6 +130,7 @@ namespace osu.Game.Rulesets.Osu
return new Mod[]
{
new OsuModTarget(),
new OsuModDifficultyAdjust(),
};
case ModType.Automation:
@ -174,11 +175,13 @@ namespace osu.Game.Rulesets.Osu
public override string ShortName => SHORT_NAME;
public override string PlayingVerb => "Clicking circles";
public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this);
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new OsuLegacySkinTransformer(source);
public override int? LegacyID => 0;
public int LegacyID => 0;
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame();

Some files were not shown because too many files have changed in this diff Show More