1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 13:22:55 +08:00

Merge branch 'master' into spun-out

This commit is contained in:
杜Nate 2020-02-08 13:54:39 +08:00 committed by GitHub
commit 8d480c794a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
493 changed files with 8980 additions and 4433 deletions

View File

@ -135,7 +135,7 @@ csharp_preferred_modifier_order = public,private,protected,internal,new,abstract
csharp_style_expression_bodied_accessors = true:warning
csharp_style_expression_bodied_constructors = false:none
csharp_style_expression_bodied_indexers = true:warning
csharp_style_expression_bodied_methods = true:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = true:warning
csharp_style_expression_bodied_properties = true:warning
csharp_style_expression_bodied_local_functions = true:silent

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

@ -111,7 +111,6 @@ platform :ios do
souyuz(
platform: "ios",
build_target: "osu_iOS",
plist_path: "../osu.iOS/Info.plist"
)
end

View File

@ -54,6 +54,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1230.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.111.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.207.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

@ -75,6 +75,9 @@ namespace osu.Desktop
private void updateStatus()
{
if (!client.IsInitialized)
return;
if (status.Value is UserStatusOffline)
{
client.ClearPresence();

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

@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
<PackageReference Include="DiscordRichPresence" Version="1.0.121" />
<PackageReference Include="DiscordRichPresence" Version="1.0.147" />
</ItemGroup>
<ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" />

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

@ -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

@ -36,7 +36,11 @@ namespace osu.Game.Rulesets.Catch.Objects
}
}
public double EndTime => StartTime + Duration;
public double EndTime
{
get => StartTime + Duration;
set => Duration = value - StartTime;
}
public double Duration { get; set; }
}

View File

@ -110,7 +110,11 @@ namespace osu.Game.Rulesets.Catch.Objects
}
}
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
public double EndTime
{
get => StartTime + this.SpanCount() * Path.Distance / Velocity;
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
}
public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;

View File

@ -20,7 +20,9 @@ namespace osu.Game.Rulesets.Catch.UI
internal readonly CatcherArea CatcherArea;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || CatcherArea.ReceivePositionalInputAt(screenSpacePos);
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) =>
// only check the X position; handle all vertical space.
base.ReceivePositionalInputAt(new Vector2(screenSpacePos.X, ScreenSpaceDrawQuad.Centre.Y));
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation)
{

View File

@ -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

@ -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

@ -13,7 +13,7 @@ using osuTK;
namespace osu.Game.Rulesets.Mania.Edit.Blueprints
{
public class ManiaSelectionBlueprint : SelectionBlueprint
public class ManiaSelectionBlueprint : OverlaySelectionBlueprint
{
public Vector2 ScreenSpaceDragPosition { get; private set; }
public Vector2 DragPosition { get; private set; }
@ -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 OverlaySelectionBlueprint 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

@ -5,6 +5,7 @@ using System;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Timing;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI;
@ -70,10 +71,12 @@ namespace osu.Game.Rulesets.Mania.Edit
// When scrolling downwards the anchor position is at the bottom of the screen, however the movement event assumes the anchor is at the top of the screen.
// This causes the delta to assume a positive hitobject position, and which can be corrected for by subtracting the parent height.
if (scrollingInfo.Direction.Value == ScrollingDirection.Down)
delta -= moveEvent.Blueprint.DrawableObject.Parent.DrawHeight;
delta -= moveEvent.Blueprint.Parent.DrawHeight; // todo: probably wrong
foreach (var b in SelectedBlueprints)
foreach (var selectionBlueprint in SelectedBlueprints)
{
var b = (OverlaySelectionBlueprint)selectionBlueprint;
var hitObject = b.DrawableObject;
var objectParent = (HitObjectContainer)hitObject.Parent;

View File

@ -7,7 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Edit.Masks
{
public abstract class ManiaSelectionBlueprint : SelectionBlueprint
public abstract class ManiaSelectionBlueprint : OverlaySelectionBlueprint
{
protected ManiaSelectionBlueprint(DrawableHitObject drawableObject)
: base(drawableObject)

View File

@ -237,19 +237,19 @@ namespace osu.Game.Rulesets.Mania
{
LeftKeys = new[]
{
InputKey.Number1,
InputKey.Number2,
InputKey.Number3,
InputKey.Number4,
InputKey.Q,
InputKey.W,
InputKey.E,
InputKey.R,
},
RightKeys = new[]
{
InputKey.Z,
InputKey.X,
InputKey.C,
InputKey.V
InputKey.V,
InputKey.B
},
SpecialKey = InputKey.Tilde,
SpecialKey = InputKey.S,
SpecialAction = ManiaAction.Special1,
NormalActionStart = ManiaAction.Key1
}.GenerateKeyBindingsFor(keys, out var nextNormal);
@ -265,12 +265,12 @@ namespace osu.Game.Rulesets.Mania
},
RightKeys = new[]
{
InputKey.O,
InputKey.P,
InputKey.BracketLeft,
InputKey.BracketRight
InputKey.K,
InputKey.L,
InputKey.Semicolon,
InputKey.Quote
},
SpecialKey = InputKey.BackSlash,
SpecialKey = InputKey.I,
SpecialAction = ManiaAction.Special2,
NormalActionStart = nextNormal
}.GenerateKeyBindingsFor(keys, out _);

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

@ -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

@ -171,17 +171,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
bodyPiece.Hitting = true;
}
public bool OnReleased(ManiaAction action)
public void OnReleased(ManiaAction action)
{
if (AllJudged)
return false;
return;
if (action != Action.Value)
return false;
return;
// Make sure a hold was started
if (HoldStartTime == null)
return false;
return;
Tail.UpdateResult();
endHold();
@ -189,8 +189,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// 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;
}
private void endHold()

View File

@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
public override bool OnReleased(ManiaAction action) => false; // Handled by the hold note
public override void OnReleased(ManiaAction action)
{
}
}
}

View File

@ -59,6 +59,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
public override bool OnReleased(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

@ -15,7 +15,11 @@ namespace osu.Game.Rulesets.Mania.Objects
/// </summary>
public class HoldNote : ManiaHitObject, IHasEndTime
{
public double EndTime => StartTime + Duration;
public double EndTime
{
get => StartTime + Duration;
set => Duration = value - StartTime;
}
private double duration;

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,8 +18,6 @@ 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 Drawable hitTarget;
@ -67,6 +65,8 @@ namespace osu.Game.Rulesets.Mania.UI.Components
private class DefaultHitTarget : CompositeDrawable, IHasAccentColour
{
private const float hit_target_bar_height = 2;
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly Container hitTargetLine;

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

@ -7,12 +7,10 @@ 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;
@ -21,7 +19,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestSceneLegacyBeatmapSkin : OsuTestScene
public class TestSceneLegacyBeatmapSkin : ScreenTestScene
{
[Resolved]
private AudioManager audio { get; set; }
@ -65,7 +63,8 @@ namespace osu.Game.Rulesets.Osu.Tests
ExposedPlayer player;
Beatmap.Value = new CustomSkinWorkingBeatmap(audio, beatmapHasColours);
Child = new OsuScreenStack(player = new ExposedPlayer(userHasCustomColours)) { RelativeSizeAxes = Axes.Both };
LoadScreen(player = new ExposedPlayer(userHasCustomColours));
return player;
}
@ -90,7 +89,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public IReadOnlyList<Color4> UsableComboColours =>
GameplayClockContainer.ChildrenOfType<BeatmapSkinProvidingContainer>()
.First()
.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value;
.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value;
}
private class CustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap

View File

@ -44,6 +44,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private const double time_during_slide_2 = 3000;
private const double time_during_slide_3 = 3500;
private const double time_during_slide_4 = 3800;
private const double time_slider_end = 4000;
private List<JudgementResult> judgementResults;
private bool allJudgedFired;
@ -284,6 +285,48 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("Tracking acquired", assertMidSliderJudgements);
}
/// <summary>
/// Scenario:
/// - Press a key on the slider head
/// - While holding the key, move cursor close to the edge of tracking area
/// - Keep the cursor on the edge of tracking area until the slider ends
/// Expected Result:
/// A passing test case will have the slider track the cursor throughout the whole test.
/// </summary>
[Test]
public void TestTrackingAreaEdge()
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
new OsuReplayFrame { Position = new Vector2(0, OsuHitObject.OBJECT_RADIUS * 1.19f), Actions = { OsuAction.LeftButton }, Time = time_slider_start + 250 },
new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.199f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
});
AddAssert("Tracking kept", assertGreatJudge);
}
/// <summary>
/// Scenario:
/// - Press a key on the slider head
/// - While holding the key, move cursor just outside the tracking area
/// - Keep the cursor just outside the tracking area until the slider ends
/// Expected Result:
/// A passing test case will have the slider drop the tracking on frame 2.
/// </summary>
[Test]
public void TestTrackingAreaOutsideEdge()
{
performTest(new List<ReplayFrame>
{
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_slider_start },
new OsuReplayFrame { Position = new Vector2(0, OsuHitObject.OBJECT_RADIUS * 1.21f), Actions = { OsuAction.LeftButton }, Time = time_slider_start + 250 },
new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.201f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
});
AddAssert("Tracking dropped", assertMidSliderJudgementFail);
}
private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great;
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;
@ -294,6 +337,8 @@ namespace osu.Game.Rulesets.Osu.Tests
private ScoreAccessibleReplayPlayer currentPlayer;
private const float slider_path_length = 25;
private void performTest(List<ReplayFrame> frames)
{
AddStep("load player", () =>
@ -309,8 +354,8 @@ namespace osu.Game.Rulesets.Osu.Tests
Path = new SliderPath(PathType.PerfectCurve, new[]
{
Vector2.Zero,
new Vector2(25, 0),
}, 25),
new Vector2(slider_path_length, 0),
}, slider_path_length),
}
},
BeatmapInfo =

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.16.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">

View File

@ -7,10 +7,10 @@ using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints
{
public abstract class OsuSelectionBlueprint<T> : SelectionBlueprint
public abstract class OsuSelectionBlueprint<T> : OverlaySelectionBlueprint
where T : OsuHitObject
{
protected T HitObject => (T)DrawableObject.HitObject;
protected new T HitObject => (T)DrawableObject.HitObject;
protected OsuSelectionBlueprint(DrawableHitObject drawableObject)
: base(drawableObject)

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

@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
: base(slider)
{
this.position = position;
InternalChild = CirclePiece = new HitCirclePiece();
Select();

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;
@ -173,7 +170,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)),
};
public override Vector2 SelectionPoint => HeadBlueprint.SelectionPoint;
public override Vector2 SelectionPoint => ((DrawableSlider)DrawableObject).HeadCircle.ScreenSpaceDrawQuad.Centre;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos);

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Edit;
@ -8,6 +9,7 @@ using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
using osuTK;
using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
{
@ -29,22 +31,31 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
{
base.Update();
if (isPlacingEnd)
HitObject.EndTime = Math.Max(HitObject.StartTime, EditorClock.CurrentTime);
piece.UpdateFrom(HitObject);
}
protected override bool OnClick(ClickEvent e)
protected override bool OnMouseDown(MouseDownEvent e)
{
if (isPlacingEnd)
{
if (e.Button != MouseButton.Right)
return false;
HitObject.EndTime = EditorClock.CurrentTime;
EndPlacement();
}
else
{
isPlacingEnd = true;
piece.FadeTo(1f, 150, Easing.OutQuint);
if (e.Button != MouseButton.Left)
return false;
BeginPlacement();
piece.FadeTo(1f, 150, Easing.OutQuint);
isPlacingEnd = true;
}
return true;

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 OverlaySelectionBlueprint 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,27 +32,13 @@ 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)
{
if (BlueprintContainer.CurrentTool is SpinnerCompositionTool)
return null;
var objects = selectedHitObjects.ToList();
if (objects.Count == 0)
@ -111,6 +92,9 @@ namespace osu.Game.Rulesets.Osu.Edit
targetIndex++;
}
if (sourceObject is Spinner)
return null;
return new OsuDistanceSnapGrid((OsuHitObject)sourceObject, (OsuHitObject)targetObject);
}
}

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

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods
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;

View File

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods
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

@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods
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

@ -20,7 +20,7 @@ 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;

View File

@ -17,7 +17,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,7 +18,6 @@ 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;

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

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
/// <summary>
/// The start time of <see cref="Start"/>.
/// </summary>
public readonly Bindable<double> StartTime = new Bindable<double>();
public readonly Bindable<double> StartTime = new BindableDouble();
/// <summary>
/// The <see cref="DrawableOsuHitObject"/> which <see cref="FollowPoint"/>s will exit from.

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
private readonly IBindable<float> scaleBindable = new Bindable<float>();
private readonly IBindable<float> scaleBindable = new BindableFloat();
public OsuAction? HitAction => HitArea.HitAction;
@ -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.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,19 +34,10 @@ 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>();
private readonly IBindable<float> scaleBindable = new BindableFloat();
[BackgroundDependencyLoader]
private void load()
@ -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;
}

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
private readonly IBindable<float> scaleBindable = new Bindable<float>();
private readonly IBindable<float> scaleBindable = new BindableFloat();
public DrawableSlider(Slider s)
: base(s)

View File

@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
};
}
private readonly IBindable<float> scaleBindable = new Bindable<float>();
private readonly IBindable<float> scaleBindable = new BindableFloat();
[BackgroundDependencyLoader]
private void load()

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

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public Func<OsuAction?> GetInitialHitAction;
private readonly Slider slider;
public readonly Drawable FollowCircle;
private readonly Drawable followCircle;
private readonly DrawableSlider drawableSlider;
public SliderBall(Slider slider, DrawableSlider drawableSlider = null)
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Children = new[]
{
FollowCircle = new FollowCircleContainer
followCircle = new FollowCircleContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
@ -95,8 +95,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
tracking = value;
FollowCircle.ScaleTo(tracking ? 2f : 1, 300, Easing.OutQuint);
FollowCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
followCircle.ScaleTo(tracking ? 2.4f : 1f, 300, Easing.OutQuint);
followCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
}
}
@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
// in valid time range
Time.Current >= slider.StartTime && Time.Current < slider.EndTime &&
// in valid position range
lastScreenSpaceMousePosition.HasValue && FollowCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
// valid action
(actions?.Any(isValidTrackingAction) ?? false);
}

View File

@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public double Radius => OBJECT_RADIUS * Scale;
public readonly Bindable<float> ScaleBindable = new Bindable<float>(1);
public readonly Bindable<float> ScaleBindable = new BindableFloat(1);
public float Scale
{

View File

@ -18,7 +18,12 @@ namespace osu.Game.Rulesets.Osu.Objects
{
public class Slider : OsuHitObject, IHasCurve
{
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
public double EndTime
{
get => StartTime + this.SpanCount() * Path.Distance / Velocity;
set => throw new System.NotSupportedException($"Adjust via {nameof(RepeatCount)} instead"); // can be implemented if/when needed.
}
public double Duration => EndTime - StartTime;
private readonly Cached<Vector2> endPositionCache = new Cached<Vector2>();
@ -81,7 +86,7 @@ namespace osu.Game.Rulesets.Osu.Objects
set
{
repeatCount = value;
endPositionCache.Invalidate();
updateNestedPositions();
}
}

View File

@ -13,8 +13,13 @@ namespace osu.Game.Rulesets.Osu.Objects
{
public class Spinner : OsuHitObject, IHasEndTime
{
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
public double EndTime
{
get => StartTime + Duration;
set => Duration = value - StartTime;
}
public double Duration { get; set; }
/// <summary>
/// Number of spins required to finish the spinner without miss.

View File

@ -46,17 +46,20 @@ namespace osu.Game.Rulesets.Osu.Skinning
switch (osuComponent.Component)
{
case OsuSkinComponents.FollowPoint:
return this.GetAnimation(component.LookupName, true, false);
return this.GetAnimation(component.LookupName, true, false, true);
case OsuSkinComponents.SliderFollowCircle:
var followCircle = this.GetAnimation("sliderfollowcircle", true, true);
var followCircle = this.GetAnimation("sliderfollowcircle", true, true, true);
if (followCircle != null)
// follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x
followCircle.Scale *= 0.5f;
return followCircle;
case OsuSkinComponents.SliderBall:
var sliderBallContent = this.GetAnimation("sliderb", true, true, "");
var sliderBallContent = this.GetAnimation("sliderb", true, true, animationSeparator: "");
// todo: slider ball has a custom frame delay based on velocity
// Math.Max((150 / Velocity) * GameBase.SIXTY_FRAME_TIME, GameBase.SIXTY_FRAME_TIME);
if (sliderBallContent != null)
{

View File

@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
autoCursorScale = config.GetBindable<bool>(OsuSetting.AutoCursorSize);
autoCursorScale.ValueChanged += _ => calculateScale();
CursorScale = new Bindable<float>();
CursorScale = new BindableFloat();
CursorScale.ValueChanged += e => ActiveCursor.Scale = cursorTrail.Scale = new Vector2(e.NewValue);
calculateScale();
@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
return false;
}
public bool OnReleased(OsuAction action)
public void OnReleased(OsuAction action)
{
switch (action)
{
@ -120,8 +120,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
updateExpandedState();
break;
}
return false;
}
public override bool HandlePositionalInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.

View File

@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.UI
{
Add(localCursorContainer = new OsuCursorContainer());
localCursorScale = new Bindable<float>();
localCursorScale = new BindableFloat();
localCursorScale.BindTo(localCursorContainer.CursorScale);
localCursorScale.BindValueChanged(scale => cursorScaleContainer.Scale = new Vector2(scale.NewValue), true);
}
@ -107,7 +107,9 @@ namespace osu.Game.Rulesets.Osu.UI
return false;
}
public bool OnReleased(OsuAction action) => false;
public void OnReleased(OsuAction action)
{
}
public void Appear() => Schedule(() =>
{

View File

@ -77,11 +77,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return result;
}
public override bool OnReleased(TaikoAction action)
public override void OnReleased(TaikoAction action)
{
if (action == HitAction)
HitAction = null;
return base.OnReleased(action);
base.OnReleased(action);
}
protected override void Update()

View File

@ -77,7 +77,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public Drawable CreateProxiedContent() => proxiedContent.CreateProxy();
public abstract bool OnPressed(TaikoAction action);
public virtual bool OnReleased(TaikoAction action) => false;
public virtual void OnReleased(TaikoAction action)
{
}
public override double LifetimeStart
{

View File

@ -18,7 +18,11 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// </summary>
private const float base_distance = 100;
public double EndTime => StartTime + Duration;
public double EndTime
{
get => StartTime + Duration;
set => Duration = value - StartTime;
}
public double Duration { get; set; }

View File

@ -11,7 +11,11 @@ namespace osu.Game.Rulesets.Taiko.Objects
{
public class Swell : TaikoHitObject, IHasEndTime
{
public double EndTime => StartTime + Duration;
public double EndTime
{
get => StartTime + Duration;
set => Duration = value - StartTime;
}
public double Duration { get; set; }

View File

@ -187,7 +187,9 @@ namespace osu.Game.Rulesets.Taiko.UI
return false;
}
public bool OnReleased(TaikoAction action) => false;
public void OnReleased(TaikoAction action)
{
}
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
@ -13,7 +14,9 @@ using osu.Game.IPC;
using osu.Framework.Allocation;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Resources;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
@ -552,6 +555,83 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
[Test]
public async Task TestUpdateBeatmapInfo()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapInfo)))
{
try
{
var osu = loadOsu(host);
var manager = osu.Dependencies.Get<BeatmapManager>();
var temp = TestResources.GetTestBeatmapForImport();
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
// Update via the beatmap, not the beatmap info, to ensure correct linking
BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0];
Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap;
beatmapToUpdate.BeatmapInfo.Version = "updated";
manager.Update(setToUpdate);
BeatmapInfo updatedInfo = manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID);
Assert.That(updatedInfo.Version, Is.EqualTo("updated"));
}
finally
{
host.Exit();
}
}
}
[Test]
public async Task TestUpdateBeatmapFile()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestUpdateBeatmapFile)))
{
try
{
var osu = loadOsu(host);
var manager = osu.Dependencies.Get<BeatmapManager>();
var temp = TestResources.GetTestBeatmapForImport();
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
BeatmapSetInfo setToUpdate = manager.GetAllUsableBeatmapSets()[0];
Beatmap beatmapToUpdate = (Beatmap)manager.GetWorkingBeatmap(setToUpdate.Beatmaps.First(b => b.RulesetID == 0)).Beatmap;
BeatmapSetFileInfo fileToUpdate = setToUpdate.Files.First(f => beatmapToUpdate.BeatmapInfo.Path.Contains(f.Filename));
using (var stream = new MemoryStream())
{
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
{
beatmapToUpdate.HitObjects.Clear();
beatmapToUpdate.HitObjects.Add(new HitCircle { StartTime = 5000 });
new LegacyBeatmapEncoder(beatmapToUpdate).Encode(writer);
}
stream.Seek(0, SeekOrigin.Begin);
manager.UpdateFile(setToUpdate, fileToUpdate, stream);
}
// Check that the old file reference has been removed
Assert.That(manager.QueryBeatmapSet(s => s.ID == setToUpdate.ID).Files.All(f => f.ID != fileToUpdate.ID));
// Check that the new file is referenced correctly by attempting a retrieval
Beatmap updatedBeatmap = (Beatmap)manager.GetWorkingBeatmap(manager.QueryBeatmap(b => b.ID == beatmapToUpdate.BeatmapInfo.ID)).Beatmap;
Assert.That(updatedBeatmap.HitObjects.Count, Is.EqualTo(1));
Assert.That(updatedBeatmap.HitObjects[0].StartTime, Is.EqualTo(5000));
}
finally
{
host.Exit();
}
}
}
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
{
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);

View File

@ -3,8 +3,11 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Edit;
@ -19,12 +22,34 @@ namespace osu.Game.Tests.Editor
private TestHitObjectComposer composer;
[Cached(typeof(EditorBeatmap))]
private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap());
[Cached(typeof(IBeatSnapProvider))]
private readonly EditorBeatmap editorBeatmap;
protected override Container<Drawable> Content { get; }
public TestSceneHitObjectComposerDistanceSnapping()
{
base.Content.Add(new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
editorBeatmap = new EditorBeatmap(new OsuBeatmap()),
Content = new Container
{
RelativeSizeAxes = Axes.Both,
}
},
});
}
[SetUp]
public void Setup() => Schedule(() =>
{
Child = composer = new TestHitObjectComposer();
Children = new Drawable[]
{
composer = new TestHitObjectComposer()
};
BeatDivisor.Value = 1;
@ -111,17 +136,19 @@ namespace osu.Game.Tests.Editor
[Test]
public void TestGetSnappedDurationFromDistance()
{
assertSnappedDuration(50, 0);
assertSnappedDuration(0, 0);
assertSnappedDuration(50, 1000);
assertSnappedDuration(100, 1000);
assertSnappedDuration(150, 1000);
assertSnappedDuration(150, 2000);
assertSnappedDuration(200, 2000);
assertSnappedDuration(250, 2000);
assertSnappedDuration(250, 3000);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDuration(0, 0);
assertSnappedDuration(50, 0);
assertSnappedDuration(100, 0);
assertSnappedDuration(150, 0);
assertSnappedDuration(100, 1000);
assertSnappedDuration(150, 1000);
assertSnappedDuration(200, 1000);
assertSnappedDuration(250, 1000);
@ -132,8 +159,8 @@ namespace osu.Game.Tests.Editor
});
assertSnappedDuration(50, 0);
assertSnappedDuration(100, 0);
assertSnappedDuration(150, 0);
assertSnappedDuration(100, 500);
assertSnappedDuration(150, 500);
assertSnappedDuration(200, 500);
assertSnappedDuration(250, 500);
assertSnappedDuration(400, 1000);
@ -142,17 +169,17 @@ namespace osu.Game.Tests.Editor
[Test]
public void GetSnappedDistanceFromDistance()
{
assertSnappedDistance(50, 0);
assertSnappedDistance(50, 100);
assertSnappedDistance(100, 100);
assertSnappedDistance(150, 100);
assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
assertSnappedDistance(250, 300);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDistance(50, 0);
assertSnappedDistance(100, 0);
assertSnappedDistance(150, 0);
assertSnappedDistance(100, 200);
assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
@ -163,8 +190,8 @@ namespace osu.Game.Tests.Editor
});
assertSnappedDistance(50, 0);
assertSnappedDistance(100, 0);
assertSnappedDistance(150, 0);
assertSnappedDistance(100, 200);
assertSnappedDistance(150, 200);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
assertSnappedDistance(400, 400);

View File

@ -126,10 +126,10 @@ namespace osu.Game.Tests.Gameplay
{
switch (lookup)
{
case GlobalSkinConfiguration global:
case GlobalSkinColours global:
switch (global)
{
case GlobalSkinConfiguration.ComboColours:
case GlobalSkinColours.ComboColours:
return SkinUtils.As<TValue>(new Bindable<IReadOnlyList<Color4>>(ComboColours));
}

View File

@ -0,0 +1,82 @@
// 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 Newtonsoft.Json;
using NUnit.Framework;
using osu.Framework.Bindables;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Tests.Online
{
[TestFixture]
public class TestAPIModSerialization
{
[Test]
public void TestAcronymIsPreserved()
{
var apiMod = new APIMod(new TestMod());
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
Assert.That(deserialized.Acronym, Is.EqualTo(apiMod.Acronym));
}
[Test]
public void TestRawSettingIsPreserved()
{
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
Assert.That(deserialized.Settings, Contains.Key("test_setting").With.ContainValue(2.0));
}
[Test]
public void TestConvertedModHasCorrectSetting()
{
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
var converted = (TestMod)deserialized.ToMod(new TestRuleset());
Assert.That(converted.TestSetting.Value, Is.EqualTo(2));
}
private class TestRuleset : Ruleset
{
public override IEnumerable<Mod> GetModsFor(ModType type) => new[] { new TestMod() };
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new System.NotImplementedException();
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException();
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new System.NotImplementedException();
public override string Description { get; } = string.Empty;
public override string ShortName { get; } = string.Empty;
}
private class TestMod : Mod
{
public override string Name => "Test Mod";
public override string Acronym => "TM";
public override double ScoreMultiplier => 1;
[SettingSource("Test")]
public BindableNumber<double> TestSetting { get; } = new BindableDouble
{
MinValue = 0,
MaxValue = 10,
Default = 5,
Precision = 0.01,
};
}
}
}

View File

@ -18,12 +18,21 @@ namespace osu.Game.Tests.ScrollAlgorithms
}
[Test]
public void TestDisplayStartTime()
public void TestPointDisplayStartTime()
{
Assert.AreEqual(-8000, algorithm.GetDisplayStartTime(2000, 10000));
Assert.AreEqual(-3000, algorithm.GetDisplayStartTime(2000, 5000));
Assert.AreEqual(2000, algorithm.GetDisplayStartTime(7000, 5000));
Assert.AreEqual(7000, algorithm.GetDisplayStartTime(17000, 10000));
Assert.AreEqual(-8000, algorithm.GetDisplayStartTime(2000, 0, 10000, 1));
Assert.AreEqual(-3000, algorithm.GetDisplayStartTime(2000, 0, 5000, 1));
Assert.AreEqual(2000, algorithm.GetDisplayStartTime(7000, 0, 5000, 1));
Assert.AreEqual(7000, algorithm.GetDisplayStartTime(17000, 0, 10000, 1));
}
[Test]
public void TestObjectDisplayStartTime()
{
Assert.AreEqual(900, algorithm.GetDisplayStartTime(2000, 50, 1000, 500)); // 2000 - (1 + 50 / 500) * 1000
Assert.AreEqual(8900, algorithm.GetDisplayStartTime(10000, 50, 1000, 500)); // 10000 - (1 + 50 / 500) * 1000
Assert.AreEqual(13500, algorithm.GetDisplayStartTime(15000, 250, 1000, 500)); // 15000 - (1 + 250 / 500) * 1000
Assert.AreEqual(19000, algorithm.GetDisplayStartTime(25000, 100, 5000, 500)); // 25000 - (1 + 100 / 500) * 5000
}
[Test]

View File

@ -27,11 +27,22 @@ namespace osu.Game.Tests.ScrollAlgorithms
}
[Test]
public void TestDisplayStartTime()
public void TestPointDisplayStartTime()
{
Assert.AreEqual(1000, algorithm.GetDisplayStartTime(2000, 1000)); // Like constant
Assert.AreEqual(10000, algorithm.GetDisplayStartTime(10500, 1000)); // 10500 - (1000 * 0.5)
Assert.AreEqual(20000, algorithm.GetDisplayStartTime(22000, 1000)); // 23000 - (1000 / 0.5)
Assert.AreEqual(1000, algorithm.GetDisplayStartTime(2000, 0, 1000, 1)); // Like constant
Assert.AreEqual(10000, algorithm.GetDisplayStartTime(10500, 0, 1000, 1)); // 10500 - (1000 * 0.5)
Assert.AreEqual(20000, algorithm.GetDisplayStartTime(22000, 0, 1000, 1)); // 23000 - (1000 / 0.5)
}
[Test]
public void TestObjectDisplayStartTime()
{
Assert.AreEqual(900, algorithm.GetDisplayStartTime(2000, 50, 1000, 500)); // 2000 - (1 + 50 / 500) * 1000 / 1
Assert.AreEqual(9450, algorithm.GetDisplayStartTime(10000, 50, 1000, 500)); // 10000 - (1 + 50 / 500) * 1000 / 2
Assert.AreEqual(14250, algorithm.GetDisplayStartTime(15000, 250, 1000, 500)); // 15000 - (1 + 250 / 500) * 1000 / 2
Assert.AreEqual(16500, algorithm.GetDisplayStartTime(18000, 250, 2000, 500)); // 18000 - (1 + 250 / 500) * 2000 / 2
Assert.AreEqual(17800, algorithm.GetDisplayStartTime(20000, 50, 1000, 500)); // 20000 - (1 + 50 / 500) * 1000 / 0.5
Assert.AreEqual(19800, algorithm.GetDisplayStartTime(22000, 50, 1000, 500)); // 22000 - (1 + 50 / 500) * 1000 / 0.5
}
[Test]

View File

@ -95,7 +95,7 @@ namespace osu.Game.Tests.Skins
[Test]
public void TestGlobalLookup()
{
AddAssert("Check combo colours", () => requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.Count > 0);
AddAssert("Check combo colours", () => requester.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value?.Count > 0);
}
[Test]
@ -121,7 +121,7 @@ namespace osu.Game.Tests.Skins
public void TestEmptyComboColours()
{
AddAssert("Check retrieved combo colours is skin default colours", () =>
requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.SequenceEqual(SkinConfiguration.DefaultComboColours) ?? false);
requester.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value?.SequenceEqual(SkinConfiguration.DefaultComboColours) ?? false);
}
[Test]
@ -136,7 +136,7 @@ namespace osu.Game.Tests.Skins
AddStep("Disallow default colours fallback in source2", () => source2.Configuration.AllowDefaultComboColoursFallback = false);
AddAssert("Check retrieved combo colours from source1", () =>
requester.GetConfig<GlobalSkinConfiguration, IReadOnlyList<Color4>>(GlobalSkinConfiguration.ComboColours)?.Value?.SequenceEqual(source1.Configuration.ComboColours) ?? false);
requester.GetConfig<GlobalSkinColours, IReadOnlyList<Color4>>(GlobalSkinColours.ComboColours)?.Value?.SequenceEqual(source1.Configuration.ComboColours) ?? false);
}
[Test]

View File

@ -68,10 +68,10 @@ namespace osu.Game.Tests.Visual.Background
[SetUp]
public virtual void SetUp() => Schedule(() =>
{
Child = new OsuScreenStack(songSelect = new DummySongSelect())
{
RelativeSizeAxes = Axes.Both
};
var stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both };
Child = stack;
stack.Push(songSelect = new DummySongSelect());
});
/// <summary>
@ -277,7 +277,7 @@ namespace osu.Game.Tests.Visual.Background
private void setupUserSettings()
{
AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null);
AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmap != null);
AddStep("Set default user settings", () =>
{
SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray();
@ -302,8 +302,8 @@ namespace osu.Game.Tests.Visual.Background
}
public readonly Bindable<bool> DimEnabled = new Bindable<bool>();
public readonly Bindable<double> DimLevel = new Bindable<double>();
public readonly Bindable<double> BlurLevel = new Bindable<double>();
public readonly Bindable<double> DimLevel = new BindableDouble();
public readonly Bindable<double> BlurLevel = new BindableDouble();
public new BeatmapCarousel Carousel => base.Carousel;

View File

@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Screens.Edit;
@ -14,6 +15,7 @@ namespace osu.Game.Tests.Visual.Editor
public class TestSceneComposeScreen : EditorClockTestScene
{
[Cached(typeof(EditorBeatmap))]
[Cached(typeof(IBeatSnapProvider))]
private readonly EditorBeatmap editorBeatmap =
new EditorBeatmap(new OsuBeatmap
{

View File

@ -85,64 +85,64 @@ namespace osu.Game.Tests.Visual.Editor
{
}
protected override void CreateContent(Vector2 startPosition)
protected override void CreateContent()
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5),
Position = startPosition
Position = StartPosition
});
int beatIndex = 0;
int indexFromPlacement = 0;
for (float s = startPosition.X + DistanceSpacing; s <= DrawWidth && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++)
for (float s = StartPosition.X + DistanceSpacing; s <= DrawWidth && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5, 10),
Position = new Vector2(s, startPosition.Y),
Colour = GetColourForBeatIndex(beatIndex)
Position = new Vector2(s, StartPosition.Y),
Colour = GetColourForIndexFromPlacement(indexFromPlacement)
});
}
beatIndex = 0;
indexFromPlacement = 0;
for (float s = startPosition.X - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++)
for (float s = StartPosition.X - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5, 10),
Position = new Vector2(s, startPosition.Y),
Colour = GetColourForBeatIndex(beatIndex)
Position = new Vector2(s, StartPosition.Y),
Colour = GetColourForIndexFromPlacement(indexFromPlacement)
});
}
beatIndex = 0;
indexFromPlacement = 0;
for (float s = startPosition.Y + DistanceSpacing; s <= DrawHeight && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++)
for (float s = StartPosition.Y + DistanceSpacing; s <= DrawHeight && indexFromPlacement < MaxIntervals; s += DistanceSpacing, indexFromPlacement++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(10, 5),
Position = new Vector2(startPosition.X, s),
Colour = GetColourForBeatIndex(beatIndex)
Position = new Vector2(StartPosition.X, s),
Colour = GetColourForIndexFromPlacement(indexFromPlacement)
});
}
beatIndex = 0;
indexFromPlacement = 0;
for (float s = startPosition.Y - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++)
for (float s = StartPosition.Y - DistanceSpacing; s >= 0 && indexFromPlacement < MaxIntervals; s -= DistanceSpacing, indexFromPlacement++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(10, 5),
Position = new Vector2(startPosition.X, s),
Colour = GetColourForBeatIndex(beatIndex)
Position = new Vector2(StartPosition.X, s),
Colour = GetColourForIndexFromPlacement(indexFromPlacement)
});
}
}

View File

@ -66,6 +66,7 @@ namespace osu.Game.Tests.Visual.Editor
Dependencies.CacheAs<IAdjustableClock>(clock);
Dependencies.CacheAs<IFrameBasedClock>(clock);
Dependencies.CacheAs(editorBeatmap);
Dependencies.CacheAs<IBeatSnapProvider>(editorBeatmap);
Child = new OsuHitObjectComposer(new OsuRuleset());
}

View File

@ -0,0 +1,28 @@
// 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 NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
public class TestSceneTimelineBlueprintContainer : TimelineTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(TimelineHitObjectBlueprint),
};
public override Drawable CreateTestComponent() => new TimelineBlueprintContainer();
protected override void LoadComplete()
{
base.LoadComplete();
Clock.Seek(10000);
}
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
public class TestSceneTimelineTickDisplay : TimelineTestScene
{
public override Drawable CreateTestComponent() => new TimelineTickDisplay();
[BackgroundDependencyLoader]
private void load()
{
BeatDivisor.Value = 4;
Add(new BeatDivisorControl(BeatDivisor)
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Margin = new MarginPadding(30),
Size = new Vector2(90)
});
}
}
}

View File

@ -27,7 +27,12 @@ namespace osu.Game.Tests.Visual.Editor
};
[Cached(typeof(EditorBeatmap))]
private readonly EditorBeatmap editorBeatmap = new EditorBeatmap(new OsuBeatmap());
private readonly EditorBeatmap editorBeatmap;
public TestSceneTimingScreen()
{
editorBeatmap = new EditorBeatmap(new OsuBeatmap());
}
[BackgroundDependencyLoader]
private void load()

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Bindables;
@ -13,7 +12,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Edit;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK;
@ -21,27 +20,33 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
public class TestSceneEditorComposeTimeline : EditorClockTestScene
public abstract class TimelineTestScene : EditorClockTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(TimelineArea),
typeof(TimelineHitObjectDisplay),
typeof(Timeline),
typeof(TimelineButton),
typeof(CentreMarker)
};
protected TimelineArea TimelineArea { get; private set; }
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
Beatmap.Value = new WaveformTestBeatmap(audio);
var editorBeatmap = new EditorBeatmap((Beatmap<HitObject>)Beatmap.Value.Beatmap);
var playable = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset);
Children = new Drawable[]
var editorBeatmap = new EditorBeatmap(playable);
Dependencies.Cache(editorBeatmap);
Dependencies.CacheAs<IBeatSnapProvider>(editorBeatmap);
AddRange(new Drawable[]
{
editorBeatmap,
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
@ -53,17 +58,19 @@ namespace osu.Game.Tests.Visual.Editor
new AudioVisualiser(),
}
},
new TimelineArea
TimelineArea = new TimelineArea
{
Child = new TimelineHitObjectDisplay(editorBeatmap),
Child = CreateTestComponent(),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Size = new Vector2(0.8f, 100)
Size = new Vector2(0.8f, 100),
}
};
});
}
public abstract Drawable CreateTestComponent();
private class AudioVisualiser : CompositeDrawable
{
private readonly Drawable marker;

View File

@ -2,49 +2,66 @@
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Screens;
using osu.Game.Configuration;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
namespace osu.Game.Tests.Visual.Gameplay
{
/// <summary>
/// A base class which runs <see cref="Player"/> test for all available rulesets.
/// Steps to be run for each ruleset should be added via <see cref="AddCheckSteps"/>.
/// </summary>
public abstract class AllPlayersTestScene : RateAdjustedBeatmapTestScene
public abstract class TestSceneAllRulesetPlayers : RateAdjustedBeatmapTestScene
{
protected Player Player;
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
foreach (var r in rulesets.AvailableRulesets)
{
Player p = null;
AddStep(r.Name, () => p = loadPlayerFor(r));
AddUntilStep("player loaded", () =>
{
if (p?.IsLoaded == true)
{
p = null;
return true;
}
return false;
});
AddCheckSteps();
}
OsuConfigManager manager;
Dependencies.Cache(manager = new OsuConfigManager(LocalStorage));
manager.GetBindable<double>(OsuSetting.DimLevel).Value = 1.0;
}
[Test]
public void TestOsu() => runForRuleset(new OsuRuleset().RulesetInfo);
[Test]
public void TestTaiko() => runForRuleset(new TaikoRuleset().RulesetInfo);
[Test]
public void TestCatch() => runForRuleset(new CatchRuleset().RulesetInfo);
[Test]
public void TestMania() => runForRuleset(new ManiaRuleset().RulesetInfo);
private void runForRuleset(RulesetInfo ruleset)
{
Player p = null;
AddStep($"load {ruleset.Name} player", () => p = loadPlayerFor(ruleset));
AddUntilStep("player loaded", () =>
{
if (p?.IsLoaded == true)
{
p = null;
return true;
}
return false;
});
AddCheckSteps();
}
protected abstract void AddCheckSteps();
private Player loadPlayerFor(RulesetInfo rulesetInfo)

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