1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-14 06:42:34 +08:00

Compare commits

..

2 Commits

271 changed files with 2436 additions and 3844 deletions
+1 -3
View File
@@ -24,9 +24,7 @@ Clone the repository including submodules
Build and run
- Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included)
- From command line using `dotnet run --project osu.Desktop`. When building for non-development purposes, add `-c Release` to gain higher performance.
Note: If you run from command line under linux, you will need to prefix the output folder to your `LD_LIBRARY_PATH`. See `.vscode/launch.json` for an example
- From command line using `dotnet run --project osu.Desktop --framework netcoreapp2.1`
If you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`). Visual Studio Code users must run `Restore` task from debug tab before attempt to build.
+4 -2
View File
@@ -9,10 +9,12 @@ install:
- cmd: git submodule update --init --recursive --depth=5
- cmd: choco install resharper-clt -y
- cmd: choco install nvika -y
- cmd: dotnet tool install CodeFileSanity --version 0.0.16 --global
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.5/CodeFileSanity.exe
before_build:
- cmd: CodeFileSanity
- cmd: CodeFileSanity.exe
- cmd: nuget restore -verbosity quiet
environment:
TargetFramework: net471
build:
project: osu.sln
parallel: true
+5 -3
View File
@@ -16,15 +16,17 @@ build_script:
- cd osu-deploy
- nuget restore -verbosity quiet
- msbuild osu.Desktop.Deploy.csproj
- cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\netcoreapp2.1\osu.Desktop.Deploy.dll.config
- dotnet bin/Debug/netcoreapp2.1/osu.Desktop.Deploy.dll %code_signing_password% %APPVEYOR_REPO_TAG_NAME%
- cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\net471\osu.Desktop.Deploy.exe.config
- cd bin\Debug\net471\
- osu.Desktop.Deploy.exe %code_signing_password% %APPVEYOR_REPO_TAG_NAME%
environment:
TargetFramework: net471
decode_secret:
secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY=
code_signing_password:
secure: 34tLNqvjmmZEi97MLKfrnQ==
artifacts:
- path: 'osu-deploy/releases/*'
- path: 'Releases\*'
deploy:
- provider: Environment
name: github
+5 -5
View File
@@ -13,7 +13,6 @@ using osu.Game;
using OpenTK.Input;
using Microsoft.Win32;
using osu.Desktop.Updater;
using osu.Framework;
using osu.Framework.Platform.Windows;
namespace osu.Desktop
@@ -52,10 +51,11 @@ namespace osu.Desktop
v.State = Visibility.Visible;
});
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
Add(new SquirrelUpdateManager());
else
Add(new SimpleUpdateManager());
#if NET_FRAMEWORK
Add(new SquirrelUpdateManager());
#else
Add(new SimpleUpdateManager());
#endif
}
}
+12 -22
View File
@@ -4,14 +4,14 @@
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework;
using osu.Framework.Development;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.IPC;
#if NET_FRAMEWORK
using System.Runtime;
#endif
namespace osu.Desktop
{
public static class Program
@@ -19,13 +19,13 @@ namespace osu.Desktop
[STAThread]
public static int Main(string[] args)
{
useMultiCoreJit();
// Back up the cwd before DesktopGameHost changes it
var cwd = Environment.CurrentDirectory;
using (DesktopGameHost host = Host.GetSuitableHost(@"osu", true))
{
host.ExceptionThrown += handleException;
if (!host.IsPrimaryInstance)
{
var importer = new ArchiveImportIPCChannel(host);
@@ -52,23 +52,13 @@ namespace osu.Desktop
}
}
private static int allowableExceptions = DebugUtils.IsDebugBuild ? 0 : 1;
/// <summary>
/// Allow a maximum of one unhandled exception, per second of execution.
/// </summary>
/// <param name="arg"></param>
/// <returns></returns>
private static bool handleException(Exception arg)
private static void useMultiCoreJit()
{
bool continueExecution = Interlocked.Decrement(ref allowableExceptions) >= 0;
Logger.Log($"Unhandled exception has been {(continueExecution ? $"allowed with {allowableExceptions} more allowable exceptions" : "denied")} .");
// restore the stock of allowable exceptions after a short delay.
Task.Delay(1000).ContinueWith(_ => Interlocked.Increment(ref allowableExceptions));
return continueExecution;
#if NET_FRAMEWORK
var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles"));
ProfileOptimization.SetProfileRoot(directory.FullName);
ProfileOptimization.StartProfile("Startup.Profile");
#endif
}
}
}
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
#if NET_FRAMEWORK
using System;
using System.Threading.Tasks;
using osu.Framework.Allocation;
@@ -161,3 +162,4 @@ namespace osu.Desktop.Updater
}
}
}
#endif
+8 -6
View File
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.Game.props" />
<PropertyGroup Label="Project">
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFrameworks>net471;netcoreapp2.1</TargetFrameworks>
<OutputType>WinExe</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -13,6 +13,9 @@
<Version>0.0.0</Version>
<FileVersion>0.0.0</FileVersion>
</PropertyGroup>
<PropertyGroup Label="Defines">
<DefineConstants Condition="'$(TargetFramework)' == 'net471'">$(DefineConstants);NET_FRAMEWORK</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<StartupObject>osu.Desktop.Program</StartupObject>
</PropertyGroup>
@@ -26,12 +29,11 @@
<PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" />
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
<PackageReference Include="ppy.squirrel.windows" Version="1.8.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.1" />
<PackageReference Include="squirrel.windows" Version="1.8.0" Condition="'$(TargetFramework)' == 'net471'" />
</ItemGroup>
<ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" />
</ItemGroup>
</Project>
</Project>
+1 -1
View File
@@ -3,7 +3,7 @@
<metadata>
<id>osulazer</id>
<version>0.0.0</version>
<title>osu!lazer</title>
<title>osulazer</title>
<authors>ppy Pty Ltd</authors>
<owners>Dean Herbert</owners>
<projectUrl>https://osu.ppy.sh/</projectUrl>
+32 -4
View File
@@ -2,7 +2,35 @@
"version": "0.2.0",
"configurations": [
{
"name": "VisualTests (Debug)",
"name": "VisualTests (Debug, net471)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Catch.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Release, net471)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Catch.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -10,12 +38,12 @@
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
"preLaunchTask": "Build (Debug, dotnet)",
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Release)",
"name": "VisualTests (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -23,7 +51,7 @@
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
"preLaunchTask": "Build (Release, dotnet)",
"env": {},
"console": "internalConsole"
}
+46 -6
View File
@@ -4,13 +4,12 @@
"version": "2.0.0",
"tasks": [
{
"label": "Build (Debug)",
"label": "Build (Debug, msbuild)",
"type": "shell",
"command": "dotnet",
"command": "msbuild",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Catch.Tests.csproj",
"/p:TargetFramework=net471",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -19,13 +18,45 @@
"problemMatcher": "$msCompile"
},
{
"label": "Build (Release)",
"label": "Build (Release, msbuild)",
"type": "shell",
"command": "msbuild",
"args": [
"osu.Game.Rulesets.Catch.Tests.csproj",
"/p:Configuration=Release",
"/p:TargetFramework=net471",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build (Debug, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Catch.Tests.csproj",
"/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build (Release, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Catch.Tests.csproj",
"/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -35,7 +66,16 @@
"problemMatcher": "$msCompile"
},
{
"label": "Restore",
"label": "Restore (net471)",
"type": "shell",
"command": "nuget",
"args": [
"restore"
],
"problemMatcher": []
},
{
"label": "Restore (netcoreapp2.1)",
"type": "shell",
"command": "dotnet",
"args": [
@@ -1,14 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFrameworks>netcoreapp2.1;net471</TargetFrameworks>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
@@ -41,7 +41,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
RepeatCount = curveData.RepeatCount,
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset ?? 0
};
}
@@ -52,8 +51,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
StartTime = obj.StartTime,
Samples = obj.Samples,
Duration = endTime.Duration,
NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
NewCombo = comboData?.NewCombo ?? false
};
}
else
@@ -63,7 +61,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
StartTime = obj.StartTime,
Samples = obj.Samples,
NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH
};
}
+4 -2
View File
@@ -93,11 +93,13 @@ namespace osu.Game.Rulesets.Catch
new CatchModHidden(),
new CatchModFlashlight(),
};
case ModType.Automation:
case ModType.Special:
return new Mod[]
{
new MultiMod(new CatchModAutoplay(), new ModCinema()),
new CatchModRelax(),
null,
null,
new MultiMod(new CatchModAutoplay(), new ModCinema()),
};
default:
return new Mod[] { };
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Judgements
@@ -10,6 +9,8 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
public override bool AffectsCombo => false;
public override bool ShouldExplode => true;
protected override int NumericResultFor(HitResult result)
{
switch (result)
@@ -31,7 +32,5 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 8;
}
}
public override bool ShouldExplodeFor(JudgementResult result) => true;
}
}
@@ -23,10 +23,21 @@ namespace osu.Game.Rulesets.Catch.Judgements
}
/// <summary>
/// Retrieves the numeric health increase of a <see cref="HitResult"/>.
/// The base health increase for the result achieved.
/// </summary>
/// <param name="result">The <see cref="HitResult"/> to find the numeric health increase for.</param>
/// <returns>The numeric health increase of <paramref name="result"/>.</returns>
public float HealthIncrease => HealthIncreaseFor(Result);
/// <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" />
/// </summary>
public virtual bool ShouldExplode => IsHit;
/// <summary>
/// Convert a <see cref="HitResult"/> to a base health increase.
/// </summary>
/// <param name="result">The value to convert.</param>
/// <returns>The base health increase.</returns>
protected virtual float HealthIncreaseFor(HitResult result)
{
switch (result)
@@ -37,18 +48,5 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 10.2f;
}
}
/// <summary>
/// Retrieves the numeric health increase of a <see cref="JudgementResult"/>.
/// </summary>
/// <param name="result">The <see cref="JudgementResult"/> to find the numeric health increase for.</param>
/// <returns>The numeric health increase of <paramref name="result"/>.</returns>
public float HealthIncreaseFor(JudgementResult result) => HealthIncreaseFor(result.Type);
/// <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" />
/// </summary>
public virtual bool ShouldExplodeFor(JudgementResult result) => result.IsHit;
}
}
@@ -1,15 +1,10 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Catch.Objects
{
public class Banana : Fruit
{
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
public override Judgement CreateJudgement() => new CatchBananaJudgement();
}
}
@@ -20,8 +20,6 @@ namespace osu.Game.Rulesets.Catch.Objects
public virtual bool NewCombo { get; set; }
public int ComboOffset { get; set; }
public int IndexInCurrentCombo { get; set; }
public int ComboIndex { get; set; }
@@ -1,6 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Catch.Judgements;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableBanana : DrawableFruit
@@ -9,5 +11,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
: base(h)
{
}
protected override CatchJudgement CreateJudgement() => new CatchBananaJudgement();
}
}
@@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AddNested(getVisualRepresentation?.Invoke(b));
}
protected override bool ProvidesJudgement => false;
protected override void AddNested(DrawableHitObject h)
{
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
@@ -5,6 +5,7 @@ using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
@@ -52,14 +53,20 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
public Func<CatchHitObject, bool> CheckPosition;
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (CheckPosition == null) return;
if (timeOffset >= 0 && Result != null)
ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss);
if (timeOffset >= 0)
{
var judgement = CreateJudgement();
judgement.Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss;
AddJudgement(judgement);
}
}
protected virtual CatchJudgement CreateJudgement() => new CatchJudgement();
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
@@ -6,6 +6,7 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Catch.Judgements;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
@@ -23,6 +24,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Masking = false;
}
protected override CatchJudgement CreateJudgement() => new CatchDropletJudgement();
[BackgroundDependencyLoader]
private void load()
{
@@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AddNested(getVisualRepresentation?.Invoke(o));
}
protected override bool ProvidesJudgement => false;
protected override void AddNested(DrawableHitObject h)
{
var catchObject = (DrawableCatchHitObject)h;
@@ -2,15 +2,18 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Game.Rulesets.Catch.Judgements;
namespace osu.Game.Rulesets.Catch.Objects.Drawable
{
public class DrawableTinyDroplet : DrawableDroplet
{
public DrawableTinyDroplet(TinyDroplet h)
public DrawableTinyDroplet(Droplet h)
: base(h)
{
Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 8;
}
protected override CatchJudgement CreateJudgement() => new CatchTinyDropletJudgement();
}
}
@@ -1,13 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Catch.Objects
{
public class Droplet : CatchHitObject
{
public override Judgement CreateJudgement() => new CatchDropletJudgement();
}
}
-4
View File
@@ -1,13 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Catch.Objects
{
public class Fruit : CatchHitObject
{
public override Judgement CreateJudgement() => new CatchJudgement();
}
}
@@ -1,13 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Catch.Objects
{
public class TinyDroplet : Droplet
{
public override Judgement CreateJudgement() => new CatchTinyDropletJudgement();
}
}
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects;
@@ -20,28 +21,55 @@ namespace osu.Game.Rulesets.Catch.Scoring
private float hpDrainRate;
protected override void ApplyBeatmap(Beatmap<CatchHitObject> beatmap)
protected override void SimulateAutoplay(Beatmap<CatchHitObject> beatmap)
{
base.ApplyBeatmap(beatmap);
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
foreach (var obj in beatmap.HitObjects)
{
switch (obj)
{
case JuiceStream stream:
foreach (var nestedObject in stream.NestedHitObjects)
switch (nestedObject)
{
case TinyDroplet _:
AddJudgement(new CatchTinyDropletJudgement { Result = HitResult.Perfect });
break;
case Droplet _:
AddJudgement(new CatchDropletJudgement { Result = HitResult.Perfect });
break;
case Fruit _:
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
break;
}
break;
case BananaShower shower:
foreach (var _ in shower.NestedHitObjects.Cast<CatchHitObject>())
AddJudgement(new CatchBananaJudgement { Result = HitResult.Perfect });
break;
case Fruit _:
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
break;
}
}
}
private const double harshness = 0.01;
protected override void ApplyResult(JudgementResult result)
protected override void OnNewJudgement(Judgement judgement)
{
base.ApplyResult(result);
base.OnNewJudgement(judgement);
if (result.Type == HitResult.Miss)
if (judgement.Result == HitResult.Miss)
{
if (!result.Judgement.IsBonus)
if (!judgement.IsBonus)
Health.Value -= hpDrainRate * (harshness * 2);
return;
}
if (result.Judgement is CatchJudgement catchJudgement)
Health.Value += Math.Max(catchJudgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
if (judgement is CatchJudgement catchJudgement)
Health.Value += Math.Max(catchJudgement.HealthIncrease - hpDrainRate, 0) * harshness;
}
}
}
+2 -3
View File
@@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.UI
public override void Add(DrawableHitObject h)
{
h.OnNewResult += onNewResult;
h.OnJudgement += onJudgement;
base.Add(h);
@@ -67,7 +67,6 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.CheckPosition = CheckIfWeCanCatch;
}
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
=> catcherArea.OnResult((DrawableCatchHitObject)judgedObject, result);
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement);
}
}
+11 -9
View File
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.UI
private DrawableCatchHitObject lastPlateableFruit;
public void OnResult(DrawableCatchHitObject fruit, JudgementResult result)
public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
{
void runAfterLoaded(Action action)
{
@@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.UI
lastPlateableFruit.OnLoadComplete = _ => action();
}
if (result.IsHit && fruit.CanBePlated)
if (judgement.IsHit && fruit.CanBePlated)
{
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
@@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.UI
if (fruit.HitObject.LastInCombo)
{
if (((CatchJudgement)result.Judgement).ShouldExplodeFor(result))
if (((CatchJudgement)judgement).ShouldExplode)
runAfterLoaded(() => MovableCatcher.Explode());
else
MovableCatcher.Drop();
@@ -407,7 +407,9 @@ namespace osu.Game.Rulesets.Catch.UI
/// </summary>
public void Explode()
{
foreach (var f in caughtFruit.ToArray())
var fruit = caughtFruit.ToArray();
foreach (var f in fruit)
Explode(f);
}
@@ -420,15 +422,15 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.Anchor = Anchor.TopLeft;
fruit.Position = caughtFruit.ToSpaceOfOtherDrawable(fruit.DrawPosition, ExplodingFruitTarget);
if (!caughtFruit.Remove(fruit))
// we may have already been removed by a previous operation (due to the weird OnLoadComplete scheduling).
// this avoids a crash on potentially attempting to Add a fruit to ExplodingFruitTarget twice.
return;
caughtFruit.Remove(fruit);
ExplodingFruitTarget.Add(fruit);
}
fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine).Then().MoveToY(fruit.Y + 50, 500, Easing.InSine);
fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine)
.Then()
.MoveToY(fruit.Y + 50, 500, Easing.InSine);
fruit.MoveToX(fruit.X + originalX * 6, 1000);
fruit.FadeOut(750);
+32 -4
View File
@@ -2,7 +2,35 @@
"version": "0.2.0",
"configurations": [
{
"name": "VisualTests (Debug)",
"name": "VisualTests (Debug, net471)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Mania.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Release, net471)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Mania.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -10,12 +38,12 @@
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
"preLaunchTask": "Build (Debug, dotnet)",
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Release)",
"name": "VisualTests (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -23,7 +51,7 @@
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
"preLaunchTask": "Build (Release, dotnet)",
"env": {},
"console": "internalConsole"
}
+46 -6
View File
@@ -4,13 +4,12 @@
"version": "2.0.0",
"tasks": [
{
"label": "Build (Debug)",
"label": "Build (Debug, msbuild)",
"type": "shell",
"command": "dotnet",
"command": "msbuild",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Mania.Tests.csproj",
"/p:TargetFramework=net471",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -19,13 +18,45 @@
"problemMatcher": "$msCompile"
},
{
"label": "Build (Release)",
"label": "Build (Release, msbuild)",
"type": "shell",
"command": "msbuild",
"args": [
"osu.Game.Rulesets.Mania.Tests.csproj",
"/p:Configuration=Release",
"/p:TargetFramework=net471",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build (Debug, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Mania.Tests.csproj",
"/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build (Release, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Mania.Tests.csproj",
"/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -35,7 +66,16 @@
"problemMatcher": "$msCompile"
},
{
"label": "Restore",
"label": "Restore (net471)",
"type": "shell",
"command": "nuget",
"args": [
"restore"
],
"problemMatcher": []
},
{
"label": "Restore (netcoreapp2.1)",
"type": "shell",
"command": "dotnet",
"args": [
@@ -14,20 +14,24 @@ namespace osu.Game.Rulesets.Mania.Tests
/// </summary>
public class ScrollingTestContainer : Container
{
[Cached(Type = typeof(IScrollingInfo))]
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
private readonly ScrollingDirection direction;
public ScrollingTestContainer(ScrollingDirection direction)
{
scrollingInfo.Direction.Value = direction;
this.direction = direction;
}
public void Flip() => scrollingInfo.Direction.Value = scrollingInfo.Direction.Value == ScrollingDirection.Up ? ScrollingDirection.Down : ScrollingDirection.Up;
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<IScrollingInfo>(new ScrollingInfo { Direction = { Value = direction }});
return dependencies;
}
public class TestScrollingInfo : IScrollingInfo
{
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
private class ScrollingInfo : IScrollingInfo
{
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
}
}
}
@@ -1,32 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Mania.Tests
{
[TestFixture]
public class TestCaseEditor : EditorTestCase
{
private readonly Bindable<ManiaScrollingDirection> direction = new Bindable<ManiaScrollingDirection>();
public TestCaseEditor()
: base(new ManiaRuleset())
{
AddStep("upwards scroll", () => direction.Value = ManiaScrollingDirection.Up);
AddStep("downwards scroll", () => direction.Value = ManiaScrollingDirection.Down);
}
[BackgroundDependencyLoader]
private void load(RulesetConfigCache configCache)
{
var config = (ManiaConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
config.BindWith(ManiaSetting.ScrollDirection, direction);
}
}
}
+11 -22
View File
@@ -46,20 +46,15 @@ namespace osu.Game.Rulesets.Mania.Tests
Spacing = new Vector2(20),
Children = new[]
{
createNoteDisplay(ScrollingDirection.Down, 1, out var note1),
createNoteDisplay(ScrollingDirection.Up, 2, out var note2),
createHoldNoteDisplay(ScrollingDirection.Down, 1, out var holdNote1),
createHoldNoteDisplay(ScrollingDirection.Up, 2, out var holdNote2),
createNoteDisplay(ScrollingDirection.Down),
createNoteDisplay(ScrollingDirection.Up),
createHoldNoteDisplay(ScrollingDirection.Down),
createHoldNoteDisplay(ScrollingDirection.Up),
}
};
AddAssert("note 1 facing downwards", () => verifyAnchors(note1, Anchor.y2));
AddAssert("note 2 facing upwards", () => verifyAnchors(note2, Anchor.y0));
AddAssert("hold note 1 facing downwards", () => verifyAnchors(holdNote1, Anchor.y2));
AddAssert("hold note 2 facing upwards", () => verifyAnchors(holdNote2, Anchor.y0));
}
private Drawable createNoteDisplay(ScrollingDirection direction, int identifier, out DrawableNote hitObject)
private Drawable createNoteDisplay(ScrollingDirection direction)
{
var note = new Note { StartTime = 999999999 };
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
@@ -67,24 +62,24 @@ namespace osu.Game.Rulesets.Mania.Tests
return new ScrollingTestContainer(direction)
{
AutoSizeAxes = Axes.Both,
Child = new NoteContainer(direction, $"note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}")
Child = new NoteContainer(direction, $"note, scrolling {direction.ToString().ToLowerInvariant()}")
{
Child = hitObject = new DrawableNote(note) { AccentColour = Color4.OrangeRed }
Child = new DrawableNote(note) { AccentColour = Color4.OrangeRed }
}
};
}
private Drawable createHoldNoteDisplay(ScrollingDirection direction, int identifier, out DrawableHoldNote hitObject)
private Drawable createHoldNoteDisplay(ScrollingDirection direction)
{
var note = new HoldNote { StartTime = 999999999, Duration = 5000 };
var note = new HoldNote { StartTime = 999999999, Duration = 1000 };
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
return new ScrollingTestContainer(direction)
{
AutoSizeAxes = Axes.Both,
Child = new NoteContainer(direction, $"hold note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}")
Child = new NoteContainer(direction, $"hold note, scrolling {direction.ToString().ToLowerInvariant()}")
{
Child = hitObject = new DrawableHoldNote(note)
Child = new DrawableHoldNote(note)
{
RelativeSizeAxes = Axes.Both,
AccentColour = Color4.OrangeRed,
@@ -93,12 +88,6 @@ namespace osu.Game.Rulesets.Mania.Tests
};
}
private bool verifyAnchors(DrawableHitObject hitObject, Anchor expectedAnchor)
=> hitObject.Anchor.HasFlag(expectedAnchor) && hitObject.Origin.HasFlag(expectedAnchor);
private bool verifyAnchors(DrawableHoldNote holdNote, Anchor expectedAnchor)
=> verifyAnchors((DrawableHitObject)holdNote, expectedAnchor) && holdNote.NestedHitObjects.All(n => verifyAnchors(n, expectedAnchor));
private class NoteContainer : Container
{
private readonly Container content;
+2 -19
View File
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@@ -25,8 +24,6 @@ namespace osu.Game.Rulesets.Mania.Tests
private readonly List<ManiaStage> stages = new List<ManiaStage>();
private FillFlowContainer<ScrollingTestContainer> fill;
public TestCaseStage()
: base(columns)
{
@@ -35,7 +32,7 @@ namespace osu.Game.Rulesets.Mania.Tests
[BackgroundDependencyLoader]
private void load()
{
Child = fill = new FillFlowContainer<ScrollingTestContainer>
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
@@ -57,22 +54,8 @@ namespace osu.Game.Rulesets.Mania.Tests
AddStep("hold note", createHoldNote);
AddStep("minor bar line", () => createBarLine(false));
AddStep("major bar line", () => createBarLine(true));
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[0], Anchor.TopCentre));
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[1], Anchor.BottomCentre));
AddStep("flip direction", () =>
{
foreach (var c in fill.Children)
c.Flip();
});
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[0], Anchor.BottomCentre));
AddAssert("check note anchors", () => notesInStageAreAnchored(stages[1], Anchor.TopCentre));
}
private bool notesInStageAreAnchored(ManiaStage stage, Anchor anchor) => stage.Columns.SelectMany(c => c.AllHitObjects).All(o => o.Anchor == anchor);
private void createNote()
{
foreach (var stage in stages)
@@ -118,7 +101,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}
}
private ScrollingTestContainer createStage(ScrollingDirection direction, ManiaAction action)
private Drawable createStage(ScrollingDirection direction, ManiaAction action)
{
var specialAction = ManiaAction.Special1;
@@ -1,14 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFrameworks>netcoreapp2.1;net471</TargetFrameworks>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
@@ -173,18 +173,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern();
int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects;
int nextColumn = GetRandomColumn();
int nextColumn = Random.Next(RandomStart, TotalColumns);
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
{
// Find available column
nextColumn = FindAvailableColumn(nextColumn, pattern, PreviousPattern);
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(pattern, nextColumn, startTime, EndTime);
}
// This is can't be combined with the above loop due to RNG
for (int i = 0; i < noteCount - usableColumns; i++)
{
nextColumn = FindAvailableColumn(nextColumn, pattern);
while (pattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(pattern, nextColumn, startTime, EndTime);
}
@@ -209,13 +210,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
{
while (PreviousPattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, TotalColumns);
}
int lastColumn = nextColumn;
for (int i = 0; i < noteCount; i++)
{
addToPattern(pattern, nextColumn, startTime, startTime);
nextColumn = FindAvailableColumn(nextColumn, validation: c => c != lastColumn);
while (nextColumn == lastColumn)
nextColumn = Random.Next(RandomStart, TotalColumns);
lastColumn = nextColumn;
startTime += SegmentDuration;
}
@@ -307,7 +313,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (TotalColumns > 2)
addToPattern(pattern, nextColumn, startTime, startTime);
nextColumn = GetRandomColumn();
nextColumn = Random.Next(RandomStart, TotalColumns);
startTime += SegmentDuration;
}
@@ -386,11 +392,16 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
{
while (PreviousPattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, TotalColumns);
}
for (int i = 0; i < columnRepeat; i++)
{
nextColumn = FindAvailableColumn(nextColumn, pattern);
while (pattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(pattern, nextColumn, startTime, EndTime);
startTime += SegmentDuration;
}
@@ -415,12 +426,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
holdColumn = FindAvailableColumn(holdColumn, PreviousPattern);
{
while (PreviousPattern.ColumnHasObject(holdColumn))
holdColumn = Random.Next(RandomStart, TotalColumns);
}
// Create the hold note
addToPattern(pattern, holdColumn, startTime, EndTime);
int nextColumn = GetRandomColumn();
int nextColumn = Random.Next(RandomStart, TotalColumns);
int noteCount;
if (ConversionDifficulty > 6.5)
noteCount = GetRandomNoteCount(0.63, 0);
@@ -441,7 +455,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
for (int j = 0; j < noteCount; j++)
{
nextColumn = FindAvailableColumn(nextColumn, validation: c => c != holdColumn, patterns: rowPattern);
while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn)
nextColumn = Random.Next(RandomStart, TotalColumns);
addToPattern(rowPattern, nextColumn, startTime, startTime);
}
}
@@ -39,17 +39,32 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
addToPattern(pattern, 0, generateHold);
break;
case 8:
addToPattern(pattern, FindAvailableColumn(GetRandomColumn(), PreviousPattern), generateHold);
addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold);
break;
default:
if (TotalColumns > 0)
addToPattern(pattern, GetRandomColumn(), generateHold);
addToPattern(pattern, getNextRandomColumn(0), generateHold);
break;
}
return pattern;
}
/// <summary>
/// Picks a random column after a column.
/// </summary>
/// <param name="start">The starting column.</param>
/// <returns>A random column after <paramref name="start"/>.</returns>
private int getNextRandomColumn(int start)
{
int nextColumn = Random.Next(start, TotalColumns);
while (PreviousPattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(start, TotalColumns);
return nextColumn;
}
/// <summary>
/// Constructs and adds a note to a pattern.
/// </summary>
@@ -25,6 +25,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
PatternType lastStair, IBeatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));
if (density < 0) throw new ArgumentOutOfRangeException(nameof(density));
StairType = lastStair;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
@@ -231,27 +234,22 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i < noteCount; i++)
{
nextColumn = allowStacking
? FindAvailableColumn(nextColumn, nextColumn: getNextColumn, patterns: pattern)
: FindAvailableColumn(nextColumn, nextColumn: getNextColumn, patterns: new[] { pattern, PreviousPattern });
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking)
{
if (convertType.HasFlag(PatternType.Gathered))
{
nextColumn++;
if (nextColumn == TotalColumns)
nextColumn = RandomStart;
}
else
nextColumn = Random.Next(RandomStart, TotalColumns);
}
addToPattern(pattern, nextColumn);
}
return pattern;
int getNextColumn(int last)
{
if (convertType.HasFlag(PatternType.Gathered))
{
last++;
if (last == TotalColumns)
last = RandomStart;
}
else
last = GetRandomColumn();
return last;
}
}
/// <summary>
@@ -288,19 +286,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3)
{
if (convertType.HasFlag(PatternType.ForceNotStack))
return generateRandomPattern(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
var pattern = new Pattern();
bool addToCentre;
int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre);
int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2;
int nextColumn = GetRandomColumn(upperBound: columnLimit);
int nextColumn = Random.Next(RandomStart, columnLimit);
for (int i = 0; i < noteCount; i++)
{
nextColumn = FindAvailableColumn(nextColumn, upperBound: columnLimit, patterns: pattern);
while (pattern.ColumnHasObject(nextColumn))
nextColumn = Random.Next(RandomStart, columnLimit);
// Add normal note
addToPattern(pattern, nextColumn);
@@ -372,6 +368,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
addToCentre = false;
if (convertType.HasFlag(PatternType.ForceNotStack))
return getRandomNoteCount(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
switch (TotalColumns)
{
case 2:
@@ -3,7 +3,6 @@
using System;
using System.Linq;
using JetBrains.Annotations;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects;
@@ -91,7 +90,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
}
private double? conversionDifficulty;
/// <summary>
/// A difficulty factor used for various conversion methods from osu!stable.
/// </summary>
@@ -118,82 +116,5 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
return conversionDifficulty.Value;
}
}
/// <summary>
/// Finds a new column in which a <see cref="HitObject"/> can be placed.
/// This uses <see cref="GetRandomColumn"/> to pick the next candidate column.
/// </summary>
/// <param name="initialColumn">The initial column to test. This may be returned if it is already a valid column.</param>
/// <param name="patterns">A list of patterns for which the validity of a column should be checked against.
/// A column is not a valid candidate if a <see cref="HitObject"/> occupies the same column in any of the patterns.</param>
/// <returns>A column for which there are no <see cref="HitObject"/>s in any of <paramref name="patterns"/> occupying the same column.</returns>
/// <exception cref="NotEnoughColumnsException">If there are no valid candidate columns.</exception>
protected int FindAvailableColumn(int initialColumn, params Pattern[] patterns)
=> FindAvailableColumn(initialColumn, null, patterns: patterns);
/// <summary>
/// Finds a new column in which a <see cref="HitObject"/> can be placed.
/// </summary>
/// <param name="initialColumn">The initial column to test. This may be returned if it is already a valid column.</param>
/// <param name="nextColumn">A function to retrieve the next column. If null, a randomisation scheme will be used.</param>
/// <param name="validation">A function to perform additional validation checks to determine if a column is a valid candidate for a <see cref="HitObject"/>.</param>
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
/// <param name="upperBound">The maximum column index. If null, <see cref="PatternGenerator.TotalColumns"/> is used.</param>
/// <param name="patterns">A list of patterns for which the validity of a column should be checked against.
/// A column is not a valid candidate if a <see cref="HitObject"/> occupies the same column in any of the patterns.</param>
/// <returns>A column which has passed the <paramref name="validation"/> check and for which there are no
/// <see cref="HitObject"/>s in any of <paramref name="patterns"/> occupying the same column.</returns>
/// <exception cref="NotEnoughColumnsException">If there are no valid candidate columns.</exception>
protected int FindAvailableColumn(int initialColumn, int? lowerBound = null, int? upperBound = null, Func<int, int> nextColumn = null, [InstantHandle] Func<int, bool> validation = null,
params Pattern[] patterns)
{
lowerBound = lowerBound ?? RandomStart;
upperBound = upperBound ?? TotalColumns;
nextColumn = nextColumn ?? (_ => GetRandomColumn(lowerBound, upperBound));
// Check for the initial column
if (isValid(initialColumn))
return initialColumn;
// Ensure that we have at least one free column, so that an endless loop is avoided
bool hasValidColumns = false;
for (int i = lowerBound.Value; i < upperBound.Value; i++)
{
hasValidColumns = isValid(i);
if (hasValidColumns)
break;
}
if (!hasValidColumns)
throw new NotEnoughColumnsException();
// Iterate until a valid column is found. This is a random iteration in the default case.
do
{
initialColumn = nextColumn(initialColumn);
} while (!isValid(initialColumn));
return initialColumn;
bool isValid(int column) => validation?.Invoke(column) != false && !patterns.Any(p => p.ColumnHasObject(column));
}
/// <summary>
/// Returns a random column index in the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
/// </summary>
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
/// <param name="upperBound">The maximum column index. If null, <see cref="PatternGenerator.TotalColumns"/> is used.</param>
protected int GetRandomColumn(int? lowerBound = null, int? upperBound = null) => Random.Next(lowerBound ?? RandomStart, upperBound ?? TotalColumns);
/// <summary>
/// Occurs when mania conversion is stuck in an infinite loop unable to find columns to place new hitobjects in.
/// </summary>
public class NotEnoughColumnsException : Exception
{
public NotEnoughColumnsException()
: base("There were not enough columns to complete conversion.")
{
}
}
}
}
@@ -1,84 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI.Scrolling;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
{
public class HoldNoteMask : HitObjectMask
{
public new DrawableHoldNote HitObject => (DrawableHoldNote)base.HitObject;
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private readonly BodyPiece body;
public HoldNoteMask(DrawableHoldNote hold)
: base(hold)
{
InternalChildren = new Drawable[]
{
new HoldNoteNoteMask(hold.Head),
new HoldNoteNoteMask(hold.Tail),
body = new BodyPiece
{
AccentColour = Color4.Transparent
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, IScrollingInfo scrollingInfo)
{
body.BorderColour = colours.Yellow;
direction.BindTo(scrollingInfo.Direction);
}
protected override void Update()
{
base.Update();
Size = HitObject.DrawSize + new Vector2(0, HitObject.Tail.DrawHeight);
Position = Parent.ToLocalSpace(HitObject.ScreenSpaceDrawQuad.TopLeft);
// This is a side-effect of not matching the hitobject's anchors/origins, which is kinda hard to do
// When scrolling upwards our origin is already at the top of the head note (which is the intended location),
// but when scrolling downwards our origin is at the _bottom_ of the tail note (where we need to be at the _top_ of the tail note)
if (direction.Value == ScrollingDirection.Down)
Y -= HitObject.Tail.DrawHeight;
}
private class HoldNoteNoteMask : NoteMask
{
public HoldNoteNoteMask(DrawableNote note)
: base(note)
{
Select();
}
protected override void Update()
{
base.Update();
Anchor = HitObject.Anchor;
Origin = HitObject.Origin;
Position = HitObject.DrawPosition;
}
// Todo: This is temporary, since the note masks don't do anything special yet. In the future they will handle input.
public override bool HandleMouseInput => false;
}
}
}
@@ -1,39 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
{
public class NoteMask : HitObjectMask
{
public NoteMask(DrawableNote note)
: base(note)
{
Scale = note.Scale;
CornerRadius = 5;
Masking = true;
AddInternal(new NotePiece());
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Colour = colours.Yellow;
}
protected override void Update()
{
base.Update();
Size = HitObject.DrawSize;
Position = Parent.ToLocalSpace(HitObject.ScreenSpaceDrawQuad.TopLeft);
}
}
}
@@ -1,17 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.UI;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Mania.Edit
{
public class ManiaEditPlayfield : ManiaPlayfield
{
public ManiaEditPlayfield(List<StageDefinition> stages)
: base(stages)
{
}
}
}
@@ -1,27 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Edit
{
public class ManiaEditRulesetContainer : ManiaRulesetContainer
{
public ManiaEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
protected override Playfield CreatePlayfield() => new ManiaEditPlayfield(Beatmap.Stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
protected override Vector2 PlayfieldArea => Vector2.One;
}
}
@@ -1,56 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Edit
{
public class ManiaHitObjectComposer : HitObjectComposer
{
protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
public ManiaHitObjectComposer(Ruleset ruleset)
: base(ruleset)
{
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs<IScrollingInfo>(new ManiaScrollingInfo(Config));
return dependencies;
}
protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new ManiaEditRulesetContainer(ruleset, beatmap);
protected override IReadOnlyList<ICompositionTool> CompositionTools => new ICompositionTool[]
{
new HitObjectCompositionTool<Note>("Note"),
new HitObjectCompositionTool<HoldNote>("Hold"),
};
public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableNote note:
return new NoteMask(note);
case DrawableHoldNote holdNote:
return new HoldNoteMask(holdNote);
}
return base.CreateMaskFor(hitObject);
}
}
}
@@ -0,0 +1,27 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Judgements
{
public class HoldNoteTailJudgement : ManiaJudgement
{
/// <summary>
/// Whether the hold note has been released too early and shouldn't give full score for the release.
/// </summary>
public bool HasBroken;
protected override int NumericResultFor(HitResult result)
{
switch (result)
{
default:
return base.NumericResultFor(result);
case HitResult.Great:
case HitResult.Perfect:
return base.NumericResultFor(HasBroken ? HitResult.Good : result);
}
}
}
}
+1 -9
View File
@@ -19,11 +19,9 @@ using osu.Game.Configuration;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Difficulty;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania
@@ -34,8 +32,6 @@ namespace osu.Game.Rulesets.Mania
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => new ManiaPerformanceCalculator(this, beatmap, score);
public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this);
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
@@ -124,7 +120,7 @@ namespace osu.Game.Rulesets.Mania
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()),
new ManiaModFlashlight(),
};
case ModType.Conversion:
case ModType.Special:
return new Mod[]
{
new MultiMod(new ManiaModKey4(),
@@ -139,10 +135,6 @@ namespace osu.Game.Rulesets.Mania
new ManiaModRandom(),
new ManiaModDualStages(),
new ManiaModMirror(),
};
case ModType.Automation:
return new Mod[]
{
new MultiMod(new ManiaModAutoplay(), new ModCinema()),
};
default:
@@ -13,7 +13,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string ShortenedName => Name;
public abstract int KeyCount { get; }
public override ModType Type => ModType.Conversion;
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true;
@@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Dual Stages";
public override string ShortenedName => "DS";
public override string Description => @"Double the stages, double the fun!";
public override ModType Type => ModType.Conversion;
public override double ScoreMultiplier => 1;
private bool isForCurrentRuleset;
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string Name => "Mirror";
public override string ShortenedName => "MR";
public override ModType Type => ModType.Conversion;
public override ModType Type => ModType.Special;
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
@@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Mania.Mods
{
public override string Name => "Random";
public override string ShortenedName => "RD";
public override ModType Type => ModType.Conversion;
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
public override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 1;
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
@@ -18,10 +19,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary>
public class DrawableHoldNote : DrawableManiaHitObject<HoldNote>, IKeyBindingHandler<ManiaAction>
{
public override bool DisplayResult => false;
public override bool DisplayJudgement => false;
public readonly DrawableNote Head;
public readonly DrawableNote Tail;
private readonly DrawableNote head;
private readonly DrawableNote tail;
private readonly BodyPiece bodyPiece;
@@ -56,12 +57,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
HoldStartTime = () => holdStartTime
})
},
Head = new DrawableHeadNote(this)
head = new DrawableHeadNote(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
Tail = new DrawableTailNote(this)
tail = new DrawableTailNote(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
@@ -71,8 +72,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
foreach (var tick in tickContainer)
AddNested(tick);
AddNested(Head);
AddNested(Tail);
AddNested(head);
AddNested(tail);
}
protected override void OnDirectionChanged(ScrollingDirection direction)
@@ -90,16 +91,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.AccentColour = value;
bodyPiece.AccentColour = value;
Head.AccentColour = value;
Tail.AccentColour = value;
head.AccentColour = value;
tail.AccentColour = value;
tickContainer.ForEach(t => t.AccentColour = value);
}
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (Tail.AllJudged)
ApplyResult(r => r.Type = HitResult.Perfect);
if (tail.AllJudged)
AddJudgement(new HoldNoteJudgement { Result = HitResult.Perfect });
}
protected override void Update()
@@ -107,20 +108,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.Update();
// Make the body piece not lie under the head note
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * Head.Height / 2;
bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
}
protected void BeginHold()
{
holdStartTime = Time.Current;
bodyPiece.Hitting = true;
}
protected void EndHold()
{
holdStartTime = null;
bodyPiece.Hitting = false;
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * head.Height / 2;
bodyPiece.Height = DrawHeight - head.Height / 2 + tail.Height / 2;
}
public bool OnPressed(ManiaAction action)
@@ -135,7 +124,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// 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();
holdStartTime = Time.Current;
return true;
}
@@ -148,10 +138,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != Action.Value)
return false;
EndHold();
holdStartTime = null;
// If the key has been released too early, the user should not receive full score for the release
if (!Tail.IsHit)
if (!tail.IsHit)
hasBroken = true;
return true;
@@ -176,12 +166,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
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)
if (Judgements.Any(j => j.Result == 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();
holdNote.holdStartTime = Time.Current;
return true;
}
@@ -207,7 +197,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
this.holdNote = holdNote;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
// Factor in the release lenience
timeOffset /= release_window_lenience;
@@ -215,7 +205,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss);
{
AddJudgement(new HoldNoteTailJudgement
{
Result = HitResult.Miss,
HasBroken = holdNote.hasBroken
});
}
return;
}
@@ -224,12 +220,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (result == HitResult.None)
return;
ApplyResult(r =>
AddJudgement(new HoldNoteTailJudgement
{
if (holdNote.hasBroken && (result == HitResult.Perfect || result == HitResult.Perfect))
result = HitResult.Good;
r.Type = result;
Result = result,
HasBroken = holdNote.hasBroken
});
}
@@ -244,7 +238,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != Action.Value)
return false;
UpdateResult(true);
UpdateJudgement(true);
// Handled by the hold note, which will set holding = false
return false;
@@ -7,6 +7,7 @@ using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Scoring;
@@ -71,17 +72,29 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
return;
if (Time.Current < HitObject.StartTime)
return;
var startTime = HoldStartTime?.Invoke();
if (HoldStartTime?.Invoke() > HitObject.StartTime)
return;
if (startTime == null || startTime > HitObject.StartTime)
ApplyResult(r => r.Type = HitResult.Miss);
else
ApplyResult(r => r.Type = HitResult.Perfect);
AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
}
protected override void Update()
{
if (AllJudged)
return;
if (HoldStartTime?.Invoke() == null)
return;
UpdateJudgement(true);
}
}
}
@@ -6,6 +6,7 @@ using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
@@ -55,12 +56,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss);
AddJudgement(new ManiaJudgement { Result = HitResult.Miss });
return;
}
@@ -68,7 +69,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (result == HitResult.None)
return;
ApplyResult(r => r.Type = result);
AddJudgement(new ManiaJudgement { Result = result });
}
public virtual bool OnPressed(ManiaAction action)
@@ -76,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != Action.Value)
return false;
return UpdateResult(true);
return UpdateJudgement(true);
}
public virtual bool OnReleased(ManiaAction action) => false;
@@ -32,7 +32,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
background = new Box { RelativeSizeAxes = Axes.Both },
foreground = new BufferedContainer
{
Blending = BlendingMode.Additive,
RelativeSizeAxes = Axes.Both,
CacheDrawnFrameBuffer = true,
Children = new Drawable[]
@@ -74,7 +73,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
private Color4 accentColour;
public Color4 AccentColour
{
get { return accentColour; }
@@ -88,16 +86,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
}
public bool Hitting
{
get { return hitting; }
set
{
hitting = value;
updateAccentColour();
}
}
private Cached subtractionCache = new Cached();
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
@@ -130,26 +118,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
}
private bool hitting;
private void updateAccentColour()
{
if (!IsLoaded)
return;
foreground.Colour = AccentColour.Opacity(0.5f);
background.Colour = AccentColour.Opacity(0.7f);
const float animation_length = 50;
foreground.ClearTransforms(false, nameof(foreground.Colour));
if (hitting)
{
// wait for the next sync point
double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2);
using (foreground.BeginDelayedSequence(synchronisedOffset))
foreground.FadeColour(AccentColour.Lighten(0.2f), animation_length).Then().FadeColour(foreground.Colour, animation_length).Loop();
}
foreground.Colour = AccentColour.Opacity(0.9f);
background.Colour = AccentColour.Opacity(0.6f);
subtractionCache.Invalidate();
}
+4 -8
View File
@@ -3,8 +3,6 @@
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Mania.Objects
@@ -57,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Objects
/// <summary>
/// The tail note of the hold.
/// </summary>
public readonly TailNote Tail = new TailNote();
public readonly Note Tail = new Note();
/// <summary>
/// The time between ticks of this hold.
@@ -70,6 +68,9 @@ namespace osu.Game.Rulesets.Mania.Objects
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
Head.ApplyDefaults(controlPointInfo, difficulty);
Tail.ApplyDefaults(controlPointInfo, difficulty);
}
protected override void CreateNestedHitObjects()
@@ -77,9 +78,6 @@ namespace osu.Game.Rulesets.Mania.Objects
base.CreateNestedHitObjects();
createTicks();
AddNested(Head);
AddNested(Tail);
}
private void createTicks()
@@ -96,7 +94,5 @@ namespace osu.Game.Rulesets.Mania.Objects
});
}
}
public override Judgement CreateJudgement() => new HoldNoteJudgement();
}
}
@@ -1,9 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.Objects
{
/// <summary>
@@ -11,6 +8,5 @@ namespace osu.Game.Rulesets.Mania.Objects
/// </summary>
public class HoldNoteTick : ManiaHitObject
{
public override Judgement CreateJudgement() => new HoldNoteTickJudgement();
}
}
-4
View File
@@ -1,9 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.Objects
{
/// <summary>
@@ -11,6 +8,5 @@ namespace osu.Game.Rulesets.Mania.Objects
/// </summary>
public class Note : ManiaHitObject
{
public override Judgement CreateJudgement() => new ManiaJudgement();
}
}
@@ -1,13 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
namespace osu.Game.Rulesets.Mania.Objects
{
public class TailNote : Note
{
public override Judgement CreateJudgement() => new ManiaJudgement();
}
}
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
@@ -96,20 +97,31 @@ namespace osu.Game.Rulesets.Mania.Scoring
{
}
protected override void ApplyBeatmap(Beatmap<ManiaHitObject> beatmap)
protected override void SimulateAutoplay(Beatmap<ManiaHitObject> 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(Beatmap<ManiaHitObject> beatmap)
{
while (true)
{
base.SimulateAutoplay(beatmap);
foreach (var obj in beatmap.HitObjects)
{
var holdNote = obj as HoldNote;
if (holdNote != null)
{
// Head
AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
// Ticks
int tickCount = holdNote.NestedHitObjects.OfType<HoldNoteTick>().Count();
for (int i = 0; i < tickCount; i++)
AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
}
AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
}
if (!HasFailed)
break;
@@ -121,20 +133,20 @@ namespace osu.Game.Rulesets.Mania.Scoring
}
}
protected override void ApplyResult(JudgementResult result)
protected override void OnNewJudgement(Judgement judgement)
{
base.ApplyResult(result);
base.OnNewJudgement(judgement);
bool isTick = result.Judgement is HoldNoteTickJudgement;
bool isTick = judgement is HoldNoteTickJudgement;
if (isTick)
{
if (result.IsHit)
if (judgement.IsHit)
Health.Value += hpMultiplier * hp_increase_tick;
}
else
{
switch (result.Type)
switch (judgement.Result)
{
case HitResult.Miss:
Health.Value += hpMissMultiplier * hp_increase_miss;
+3 -3
View File
@@ -131,14 +131,14 @@ namespace osu.Game.Rulesets.Mania.UI
public override void Add(DrawableHitObject hitObject)
{
hitObject.AccentColour = AccentColour;
hitObject.OnNewResult += OnNewResult;
hitObject.OnJudgement += OnJudgement;
HitObjects.Add(hitObject);
}
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements)
if (!judgement.IsHit || !judgedObject.DisplayJudgement || !DisplayJudgements)
return;
explosionContainer.Add(new HitExplosion(judgedObject)
@@ -8,10 +8,10 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
public class DrawableManiaJudgement : DrawableJudgement
internal class DrawableManiaJudgement : DrawableJudgement
{
public DrawableManiaJudgement(JudgementResult result, DrawableHitObject judgedObject)
: base(result, judgedObject)
public DrawableManiaJudgement(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement, judgedObject)
{
}
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.UI
this.FadeInFromZero(50, Easing.OutQuint);
if (Result.IsHit)
if (Judgement.IsHit)
{
this.ScaleTo(0.8f);
this.ScaleTo(1, 250, Easing.OutElastic);
+7 -5
View File
@@ -1,20 +1,22 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects;
using osu.Framework.Graphics.Containers;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Configuration;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaPlayfield : ManiaScrollingPlayfield
{
public List<Column> Columns => stages.SelectMany(x => x.Columns).ToList();
private readonly List<ManiaStage> stages = new List<ManiaStage>();
public ManiaPlayfield(List<StageDefinition> stageDefinitions)
@@ -26,11 +28,11 @@ namespace osu.Game.Rulesets.Mania.UI
throw new ArgumentException("Can't have zero or fewer stages.");
GridContainer playfieldGrid;
AddInternal(playfieldGrid = new GridContainer
InternalChild = playfieldGrid = new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[] { new Drawable[stageDefinitions.Count] }
});
};
var normalColumnAction = ManiaAction.Key1;
var specialColumnAction = ManiaAction.Special1;
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
@@ -34,7 +35,8 @@ namespace osu.Game.Rulesets.Mania.UI
public IEnumerable<BarLine> BarLines;
protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
private ScrollingInfo scrollingInfo;
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
@@ -71,6 +73,9 @@ namespace osu.Game.Rulesets.Mania.UI
private void load()
{
BarLines.ForEach(Playfield.Add);
((ManiaConfigManager)Config).BindWith(ManiaSetting.ScrollDirection, configDirection);
configDirection.BindValueChanged(d => scrollingInfo.Direction.Value = (ScrollingDirection)d, true);
}
private DependencyContainer dependencies;
@@ -78,14 +83,11 @@ namespace osu.Game.Rulesets.Mania.UI
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
if (dependencies.Get<ManiaScrollingInfo>() == null)
dependencies.CacheAs<IScrollingInfo>(new ManiaScrollingInfo(Config));
dependencies.CacheAs<IScrollingInfo>(scrollingInfo = new ScrollingInfo());
return dependencies;
}
protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -113,5 +115,11 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
private class ScrollingInfo : IScrollingInfo
{
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
}
}
}
@@ -1,23 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaScrollingInfo : IScrollingInfo
{
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
public ManiaScrollingInfo(ManiaConfigManager config)
{
config.BindWith(ManiaSetting.ScrollDirection, configDirection);
configDirection.BindValueChanged(v => Direction.Value = (ScrollingDirection)v, true);
}
}
}
+5 -5
View File
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.UI
/// <summary>
/// A collection of <see cref="Column"/>s.
/// </summary>
public class ManiaStage : ManiaScrollingPlayfield
internal class ManiaStage : ManiaScrollingPlayfield
{
public const float HIT_TARGET_POSITION = 50;
@@ -156,18 +156,18 @@ namespace osu.Game.Rulesets.Mania.UI
var maniaObject = (ManiaHitObject)h.HitObject;
int columnIndex = maniaObject.Column - firstColumnIndex;
Columns.ElementAt(columnIndex).Add(h);
h.OnNewResult += OnNewResult;
h.OnJudgement += OnJudgement;
}
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
if (!judgedObject.DisplayResult || !DisplayJudgements)
if (!judgedObject.DisplayJudgement || !DisplayJudgements)
return;
judgements.Clear();
judgements.Add(new DrawableManiaJudgement(result, judgedObject)
judgements.Add(new DrawableManiaJudgement(judgement, judgedObject)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
+32 -4
View File
@@ -2,7 +2,35 @@
"version": "0.2.0",
"configurations": [
{
"name": "VisualTests (Debug)",
"name": "VisualTests (Debug, net471)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/bin/Debug/net471/osu.Game.Rulesets.Osu.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug, msbuild)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Release, net471)",
"windows": {
"type": "clr"
},
"type": "mono",
"request": "launch",
"program": "${workspaceRoot}/bin/Release/net471/osu.Game.Rulesets.Osu.Tests.exe",
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release, msbuild)",
"runtimeExecutable": null,
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Debug, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -10,12 +38,12 @@
"${workspaceRoot}/bin/Debug/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
"preLaunchTask": "Build (Debug, dotnet)",
"env": {},
"console": "internalConsole"
},
{
"name": "VisualTests (Release)",
"name": "VisualTests (Release, netcoreapp2.1)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
@@ -23,7 +51,7 @@
"${workspaceRoot}/bin/Release/netcoreapp2.1/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",
"preLaunchTask": "Build (Release, dotnet)",
"env": {},
"console": "internalConsole"
}
+46 -6
View File
@@ -4,13 +4,12 @@
"version": "2.0.0",
"tasks": [
{
"label": "Build (Debug)",
"label": "Build (Debug, msbuild)",
"type": "shell",
"command": "dotnet",
"command": "msbuild",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Osu.Tests.csproj",
"/p:TargetFramework=net471",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
@@ -19,13 +18,45 @@
"problemMatcher": "$msCompile"
},
{
"label": "Build (Release)",
"label": "Build (Release, msbuild)",
"type": "shell",
"command": "msbuild",
"args": [
"osu.Game.Rulesets.Osu.Tests.csproj",
"/p:Configuration=Release",
"/p:TargetFramework=net471",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build (Debug, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Osu.Tests.csproj",
"/p:TargetFramework=netcoreapp2.1",
"/p:GenerateFullPaths=true",
"/m",
"/verbosity:m"
],
"group": "build",
"problemMatcher": "$msCompile"
},
{
"label": "Build (Release, dotnet)",
"type": "shell",
"command": "dotnet",
"args": [
"build",
"--no-restore",
"osu.Game.Rulesets.Osu.Tests.csproj",
"/p:TargetFramework=netcoreapp2.1",
"/p:Configuration=Release",
"/p:GenerateFullPaths=true",
"/m",
@@ -35,7 +66,16 @@
"problemMatcher": "$msCompile"
},
{
"label": "Restore",
"label": "Restore (net471)",
"type": "shell",
"command": "nuget",
"args": [
"restore"
],
"problemMatcher": []
},
{
"label": "Restore (netcoreapp2.1)",
"type": "shell",
"command": "dotnet",
"args": [
@@ -1,64 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class StackingTest
{
[Test]
public void TestStacking()
{
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(beatmap_data)))
using (var reader = new StreamReader(stream))
{
var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo);
var objects = converted.HitObjects.ToList();
// The last hitobject triggers the stacking
for (int i = 0; i < objects.Count - 1; i++)
Assert.AreEqual(0, ((OsuHitObject)objects[i]).StackHeight);
}
}
private const string beatmap_data = @"
osu file format v14
[General]
StackLeniency: 0.2
[Difficulty]
ApproachRate:9.2
SliderMultiplier:1
SliderTickRate:0.5
[TimingPoints]
217871,6400,4,2,1,20,1,0
217871,-800,4,2,1,20,0,0
218071,-787.5,4,2,1,20,0,0
218271,-775,4,2,1,20,0,0
218471,-762.5,4,2,1,20,0,0
218671,-750,4,2,1,20,0,0
240271,-10,4,2,0,5,0,0
[HitObjects]
311,185,217871,6,0,L|318:158,1,25
311,185,218071,2,0,L|335:170,1,25
311,185,218271,2,0,L|338:192,1,25
311,185,218471,2,0,L|325:209,1,25
311,185,218671,2,0,L|304:212,1,25
311,185,240271,5,0,0:0:0:0:
";
}
}
@@ -5,10 +5,12 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
using OpenTK;
using osu.Game.Rulesets.Osu.Judgements;
using System.Collections.Generic;
using System;
using osu.Game.Rulesets.Mods;
@@ -85,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}
}
protected class TestDrawableHitCircle : DrawableHitCircle
private class TestDrawableHitCircle : DrawableHitCircle
{
private readonly bool auto;
@@ -94,17 +96,19 @@ namespace osu.Game.Rulesets.Osu.Tests
this.auto = auto;
}
public void TriggerJudgement() => UpdateResult(true);
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (auto && !userTriggered && timeOffset > 0)
{
// force success
ApplyResult(r => r.Type = HitResult.Great);
AddJudgement(new OsuJudgement
{
Result = HitResult.Great
});
State.Value = ArmedState.Hit;
}
else
base.CheckForResult(userTriggered, timeOffset);
base.CheckForJudgements(userTriggered, timeOffset);
}
}
}
@@ -1,23 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestCaseShaking : TestCaseHitCircle
{
public override void Add(Drawable drawable)
{
base.Add(drawable);
if (drawable is TestDrawableHitCircle hitObject)
{
Scheduler.AddDelayed(() => hitObject.TriggerJudgement(),
hitObject.HitObject.StartTime - (hitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current);
}
}
}
}
@@ -304,13 +304,13 @@ namespace osu.Game.Rulesets.Osu.Tests
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>())
mod.ApplyToDrawableHitObjects(new[] { drawable });
drawable.OnNewResult += onNewResult;
drawable.OnJudgement += onJudgement;
Add(drawable);
}
private float judgementOffsetDirection = 1;
private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
{
var osuObject = judgedObject as DrawableOsuHitObject;
if (osuObject == null)
@@ -321,8 +321,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = result.IsHit ? "Hit!" : "Miss!",
Colour = result.IsHit ? Color4.Green : Color4.Red,
Text = judgement.IsHit ? "Hit!" : "Miss!",
Colour = judgement.IsHit ? Color4.Green : Color4.Red,
TextSize = 30,
Position = osuObject.HitObject.StackedEndPosition + judgementOffsetDirection * new Vector2(0, 45)
});
@@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests
this.auto = auto;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1)
{
@@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Tests
auto = false;
}
base.CheckForResult(userTriggered, timeOffset);
base.CheckForJudgements(userTriggered, timeOffset);
}
}
}
@@ -1,14 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFrameworks>netcoreapp2.1;net471</TargetFrameworks>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
@@ -12,7 +12,7 @@ using osu.Game.Rulesets.Osu.UI;
namespace osu.Game.Rulesets.Osu.Beatmaps
{
public class OsuBeatmapConverter : BeatmapConverter<OsuHitObject>
internal class OsuBeatmapConverter : BeatmapConverter<OsuHitObject>
{
public OsuBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
@@ -42,7 +42,6 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
RepeatCount = curveData.RepeatCount,
Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset
};
}
@@ -53,9 +52,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
StartTime = original.StartTime,
Samples = original.Samples,
EndTime = endTimeData.EndTime,
Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2,
NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2
};
}
else
@@ -65,8 +62,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
StartTime = original.StartTime,
Samples = original.Samples,
Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
NewCombo = comboData?.NewCombo ?? false
};
}
}
@@ -8,16 +8,16 @@ using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Beatmaps
{
public class OsuBeatmapProcessor : BeatmapProcessor
internal class OsuBeatmapProcessor : BeatmapProcessor
{
public OsuBeatmapProcessor(IBeatmap beatmap)
: base(beatmap)
{
}
public override void PostProcess()
public override void PreProcess()
{
base.PostProcess();
base.PreProcess();
applyStacking((Beatmap<OsuHitObject>)Beatmap);
}
@@ -1,17 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Rulesets.Osu.Judgements
{
public enum ComboResult
{
[Description(@"")]
None,
[Description(@"Good")]
Good,
[Description(@"Amazing")]
Perfect
}
}
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Judgements
@@ -24,5 +25,7 @@ namespace osu.Game.Rulesets.Osu.Judgements
return 300;
}
}
public ComboResult Combo;
}
}
@@ -1,17 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
namespace osu.Game.Rulesets.Osu.Judgements
{
public class OsuJudgementResult : JudgementResult
{
public ComboResult ComboType;
public OsuJudgementResult(Judgement judgement)
: base(judgement)
{
}
}
}
@@ -12,7 +12,6 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Name => "Autopilot";
public override string ShortenedName => "AP";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_autopilot;
public override ModType Type => ModType.Automation;
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) };
+1 -76
View File
@@ -2,89 +2,14 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Input.States;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.UI;
using static osu.Game.Input.Handlers.ReplayInputHandler;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModRelax : ModRelax, IApplicableFailOverride, IUpdatableByPlayfield, IApplicableToRulesetContainer<OsuHitObject>
public class OsuModRelax : ModRelax
{
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
public bool AllowFail => false;
public void Update(Playfield playfield)
{
bool requiresHold = false;
bool requiresHit = false;
const float relax_leniency = 3;
foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
{
if (!(drawable is DrawableOsuHitObject osuHit))
continue;
double time = osuHit.Clock.CurrentTime;
double relativetime = time - osuHit.HitObject.StartTime;
if (time < osuHit.HitObject.StartTime - relax_leniency) continue;
if (osuHit.HitObject is IHasEndTime hasEnd && time > hasEnd.EndTime || osuHit.IsHit)
continue;
requiresHit |= osuHit is DrawableHitCircle && osuHit.IsHovered && osuHit.HitObject.HitWindows.CanBeHit(relativetime);
requiresHold |= osuHit is DrawableSlider slider && (slider.Ball.IsHovered || osuHit.IsHovered) || osuHit is DrawableSpinner;
}
if (requiresHit)
{
addAction(false);
addAction(true);
}
addAction(requiresHold);
}
private bool wasHit;
private bool wasLeft;
private OsuInputManager osuInputManager;
private void addAction(bool hitting)
{
if (wasHit == hitting)
return;
wasHit = hitting;
var state = new ReplayState<OsuAction>
{
PressedActions = new List<OsuAction>()
};
if (hitting)
{
state.PressedActions.Add(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
wasLeft = !wasLeft;
}
osuInputManager.HandleCustomInput(new InputState(), state);
}
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)
{
// grab the input manager for future use.
osuInputManager = (OsuInputManager)rulesetContainer.KeyBindingInputManager;
osuInputManager.AllowUserPresses = false;
}
}
}
@@ -12,7 +12,6 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Name => "Spun Out";
public override string ShortenedName => "SO";
public override FontAwesome Icon => FontAwesome.fa_osu_mod_spunout;
public override ModType Type => ModType.DifficultyReduction;
public override string Description => @"Spinners will be automatically completed.";
public override double ScoreMultiplier => 0.9;
public override bool Ranked => true;
@@ -10,7 +10,6 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Target";
public override string ShortenedName => "TP";
public override ModType Type => ModType.Conversion;
public override FontAwesome Icon => FontAwesome.fa_osu_mod_target;
public override string Description => @"Practice keeping up with the beat of the song.";
public override double ScoreMultiplier => 1;
@@ -7,7 +7,6 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
@@ -21,26 +20,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
{
Origin = Anchor.Centre;
Child = new SkinnableDrawable("Play/osu/followpoint", _ => new Container
Masking = true;
AutoSizeAxes = Axes.Both;
CornerRadius = width / 2;
EdgeEffect = new EdgeEffectParameters
{
Masking = true,
AutoSizeAxes = Axes.Both,
CornerRadius = width / 2,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = Color4.White.Opacity(0.2f),
Radius = 4,
},
Child = new Box
Type = EdgeEffectType.Glow,
Colour = Color4.White.Opacity(0.2f),
Radius = 4,
};
Children = new Drawable[]
{
new Box
{
Size = new Vector2(width),
Blending = BlendingMode.Additive,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Alpha = 0.5f,
}
}, restrictSize: false);
},
};
}
}
}
@@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
Vector2 distanceVector = endPosition - startPosition;
int distance = (int)distanceVector.Length;
float rotation = (float)(Math.Atan2(distanceVector.Y, distanceVector.X) * (180 / Math.PI));
float rotation = (float)Math.Atan2(distanceVector.Y, distanceVector.X);
double duration = endTime - startTime;
for (int d = (int)(PointDistance * 1.5); d < distance - PointDistance; d += PointDistance)
@@ -6,6 +6,7 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using OpenTK.Graphics;
@@ -39,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (AllJudged)
return false;
UpdateResult(true);
UpdateJudgement(true);
return true;
},
},
@@ -76,24 +77,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = HitResult.Miss);
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
return;
}
var result = HitObject.HitWindows.ResultFor(timeOffset);
if (result == HitResult.None)
{
Shake(Math.Abs(timeOffset) - HitObject.HitWindows.HalfWindowFor(HitResult.Miss));
return;
}
ApplyResult(r => r.Type = result);
AddJudgement(new OsuJudgement
{
Result = result,
});
}
protected override void UpdatePreemptState()
@@ -2,37 +2,27 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.ComponentModel;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
using System.Linq;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using OpenTK.Graphics;
using osu.Game.Graphics.Containers;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject>
{
public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Clock?.CurrentTime >= HitObject.StartTime - HitObject.TimePreempt;
private readonly ShakeContainer shakeContainer;
public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - HitObject.TimePreempt;
protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject)
{
base.AddInternal(shakeContainer = new ShakeContainer { RelativeSizeAxes = Axes.Both });
Alpha = 0;
}
// Forward all internal management to shakeContainer.
// This is a bit ugly but we don't have the concept of InternalContent so it'll have to do for now. (https://github.com/ppy/osu-framework/issues/1690)
protected override void AddInternal(Drawable drawable) => shakeContainer.Add(drawable);
protected override void ClearInternal(bool disposeChildren = true) => shakeContainer.Clear(disposeChildren);
protected override bool RemoveInternal(Drawable drawable) => shakeContainer.Remove(drawable);
protected sealed override void UpdateState(ArmedState state)
{
double transformTime = HitObject.StartTime - HitObject.TimePreempt;
@@ -44,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
UpdatePreemptState();
var judgementOffset = Math.Min(HitObject.HitWindows.HalfWindowFor(HitResult.Miss), Result?.TimeOffset ?? 0);
var judgementOffset = Math.Min(HitObject.HitWindows.HalfWindowFor(HitResult.Miss), Judgements.FirstOrDefault()?.TimeOffset ?? 0);
using (BeginDelayedSequence(HitObject.TimePreempt + judgementOffset, true))
UpdateCurrentState(state);
@@ -67,19 +57,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// Todo: At some point we need to move these to DrawableHitObject after ensuring that all other Rulesets apply
// transforms in the same way and don't rely on them not being cleared
public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
{
}
public override void ApplyTransformsAt(double time, bool propagateChildren = false)
{
}
public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { }
public override void ApplyTransformsAt(double time, bool propagateChildren = false) { }
private OsuInputManager osuActionInputManager;
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
}
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
public enum ComboResult
{
[Description(@"")]
None,
[Description(@"Good")]
Good,
[Description(@"Amazing")]
Perfect
}
}
@@ -11,14 +11,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuJudgement : DrawableJudgement
{
public DrawableOsuJudgement(JudgementResult result, DrawableHitObject judgedObject)
: base(result, judgedObject)
public DrawableOsuJudgement(Judgement judgement, DrawableHitObject judgedObject)
: base(judgement, judgedObject)
{
}
protected override void LoadComplete()
{
if (Result.Type != HitResult.Miss)
if (Judgement.Result != HitResult.Miss)
JudgementText?.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint);
base.LoadComplete();
@@ -8,8 +8,8 @@ using osu.Framework.MathUtils;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -33,18 +33,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
InternalChildren = new Drawable[]
{
new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon
new SpriteIcon
{
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.fa_chevron_right
}, restrictSize: false)
}
};
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (repeatPoint.StartTime <= Time.Current)
ApplyResult(r => r.Type = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss);
AddJudgement(new OsuJudgement { Result = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss });
}
protected override void UpdatePreemptState()
@@ -74,8 +74,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
private bool hasRotation;
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
{
bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0;
@@ -89,33 +87,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
int searchStart = isRepeatAtEnd ? curve.Count - 1 : 0;
int direction = isRepeatAtEnd ? -1 : 1;
Vector2 aimRotationVector = Vector2.Zero;
// find the next vector2 in the curve which is not equal to our current position to infer a rotation.
for (int i = searchStart; i >= 0 && i < curve.Count; i += direction)
{
if (Precision.AlmostEquals(curve[i], Position))
continue;
aimRotationVector = curve[i];
Rotation = MathHelper.RadiansToDegrees((float)Math.Atan2(curve[i].Y - Position.Y, curve[i].X - Position.X));
break;
}
float aimRotation = MathHelper.RadiansToDegrees((float)Math.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
while (Math.Abs(aimRotation - Rotation) > 180)
aimRotation += aimRotation < Rotation ? 360 : -360;
if (!hasRotation)
{
Rotation = aimRotation;
hasRotation = true;
}
else
{
// If we're already snaking, interpolate to smooth out sharp curves (linear sliders, mainly).
Rotation = Interpolation.ValueAt(MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 100), Rotation, aimRotation, 0, 50, Easing.OutQuint);
}
}
}
}
@@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Configuration;
using osu.Game.Rulesets.Scoring;
using OpenTK.Graphics;
@@ -44,17 +45,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
},
ticks = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
repeatPoints = new Container<DrawableRepeatPoint> { RelativeSizeAxes = Axes.Both },
Ball = new SliderBall(s, this)
Ball = new SliderBall(s)
{
BypassAutoSizeAxes = Axes.Both,
Scale = new Vector2(s.Scale),
AlwaysPresent = true,
Alpha = 0
},
HeadCircle = new DrawableSliderHead(s, s.HeadCircle)
{
OnShake = Shake
},
HeadCircle = new DrawableSliderHead(s, s.HeadCircle),
TailCircle = new DrawableSliderTail(s, s.TailCircle)
};
@@ -134,27 +132,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (userTriggered || Time.Current < slider.EndTime)
return;
ApplyResult(r =>
if (!userTriggered && Time.Current >= slider.EndTime)
{
var judgementsCount = NestedHitObjects.Count();
var judgementsHit = NestedHitObjects.Count(h => h.IsHit);
var hitFraction = (double)judgementsHit / judgementsCount;
if (hitFraction == 1 && HeadCircle.Result.Type == HitResult.Great)
r.Type = HitResult.Great;
else if (hitFraction >= 0.5 && HeadCircle.Result.Type >= HitResult.Good)
r.Type = HitResult.Good;
if (hitFraction == 1 && HeadCircle.Judgements.Any(j => j.Result == HitResult.Great))
AddJudgement(new OsuJudgement { Result = HitResult.Great });
else if (hitFraction >= 0.5 && HeadCircle.Judgements.Any(j => j.Result >= HitResult.Good))
AddJudgement(new OsuJudgement { Result = HitResult.Good });
else if (hitFraction > 0)
r.Type = HitResult.Meh;
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
else
r.Type = HitResult.Miss;
});
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
}
}
protected override void UpdateCurrentState(ArmedState state)
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
@@ -29,9 +28,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!IsHit)
Position = slider.CurvePositionAt(completionProgress);
}
public Action<double> OnShake;
protected override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength);
}
}
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
@@ -11,11 +12,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
/// <summary>
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
/// </summary>
public override bool DisplayResult => false;
public override bool DisplayJudgement => false;
public bool Tracking { get; set; }
public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
public DrawableSliderTail(Slider slider, HitCircle hitCircle)
: base(hitCircle)
{
Origin = Anchor.Centre;
@@ -28,10 +29,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Position = HitObject.Position - slider.Position;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered && timeOffset >= 0)
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
AddJudgement(new OsuSliderTailJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
}
}
}
@@ -6,9 +6,8 @@ using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
@@ -18,39 +17,35 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public bool Tracking { get; set; }
public override bool DisplayResult => false;
public override bool DisplayJudgement => false;
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{
Size = new Vector2(16) * sliderTick.Scale;
Masking = true;
CornerRadius = Size.X / 2;
Origin = Anchor.Centre;
BorderThickness = 2;
BorderColour = Color4.White;
InternalChildren = new Drawable[]
{
new SkinnableDrawable("Play/osu/sliderscorepoint", _ => new Container
new Box
{
Masking = true,
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
CornerRadius = Size.X / 2,
BorderThickness = 2,
BorderColour = Color4.White,
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = AccentColour,
Alpha = 0.3f,
}
}, restrictSize: false)
Colour = AccentColour,
Alpha = 0.3f,
}
};
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
AddJudgement(new OsuJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
}
protected override void UpdatePreemptState()
@@ -11,6 +11,7 @@ using OpenTK.Graphics;
using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Screens.Ranking;
using osu.Game.Rulesets.Scoring;
@@ -116,7 +117,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1);
protected override void CheckForResult(bool userTriggered, double timeOffset)
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (Time.Current < HitObject.StartTime) return;
@@ -135,20 +136,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
glow.FadeColour(completeColour, duration);
}
if (userTriggered || Time.Current < Spinner.EndTime)
return;
ApplyResult(r =>
if (!userTriggered && Time.Current >= Spinner.EndTime)
{
if (Progress >= 1)
r.Type = HitResult.Great;
AddJudgement(new OsuJudgement { Result = HitResult.Great });
else if (Progress > .9)
r.Type = HitResult.Good;
AddJudgement(new OsuJudgement { Result = HitResult.Good });
else if (Progress > .75)
r.Type = HitResult.Meh;
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
else if (Time.Current >= Spinner.EndTime)
r.Type = HitResult.Miss;
});
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
}
}
[BackgroundDependencyLoader]
@@ -8,8 +8,8 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.EventArgs;
using osu.Framework.Input.States;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@@ -18,7 +18,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private const float width = 128;
private Color4 accentColour = Color4.Black;
/// <summary>
/// The colour that is used for the slider ball.
/// </summary>
@@ -28,48 +27,35 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
set
{
accentColour = value;
if (drawableBall != null)
drawableBall.Colour = value;
if (ball != null)
ball.Colour = value;
}
}
private readonly Slider slider;
public readonly Drawable FollowCircle;
private Drawable drawableBall;
private readonly DrawableSlider drawableSlider;
public readonly Box FollowCircle;
private readonly Box ball;
public SliderBall(Slider slider, DrawableSlider drawableSlider = null)
public SliderBall(Slider slider)
{
this.drawableSlider = drawableSlider;
this.slider = slider;
Masking = true;
AutoSizeAxes = Axes.Both;
Blending = BlendingMode.Additive;
Origin = Anchor.Centre;
BorderThickness = 10;
BorderColour = Color4.Orange;
Children = new[]
Children = new Drawable[]
{
FollowCircle = new Container
FollowCircle = new Box
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Colour = Color4.Orange,
Width = width,
Height = width,
Alpha = 0,
Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = 5,
BorderColour = Color4.Orange,
Blending = BlendingMode.Additive,
Child = new Box
{
Colour = Color4.Orange,
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f,
}
}),
},
new CircularContainer
{
@@ -77,26 +63,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
AutoSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
BorderThickness = 10,
BorderColour = Color4.White,
Alpha = 1,
Child = new Container
Children = new[]
{
Width = width,
Height = width,
// TODO: support skin filename animation (sliderb0, sliderb1...)
Child = new SkinnableDrawable("Play/osu/sliderb", _ => new CircularContainer
ball = new Box
{
Masking = true,
RelativeSizeAxes = Axes.Both,
BorderThickness = 10,
BorderColour = Color4.White,
Alpha = 1,
Child = drawableBall = new Box
{
Colour = AccentColour,
RelativeSizeAxes = Axes.Both,
Alpha = 0.4f,
}
}),
Colour = AccentColour,
Alpha = 0.4f,
Width = width,
Height = width,
},
}
}
};
@@ -122,6 +100,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return base.OnMouseMove(state);
}
// If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position.
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos);
public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
{
// Consider the case of rewinding - children's transforms are handled internally, so propagating down
@@ -130,7 +111,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
private bool tracking;
public bool Tracking
{
get { return tracking; }
@@ -140,8 +120,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
return;
tracking = value;
FollowCircle.ScaleTo(tracking ? 2f : 1, 300, Easing.OutQuint);
FollowCircle.FadeTo(tracking ? 1f : 0, 300, Easing.OutQuint);
FollowCircle.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
FollowCircle.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint);
}
}
@@ -156,8 +136,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
// Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
Tracking = canCurrentlyTrack
&& lastState != null
&& ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
&& (drawableSlider?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
&& base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
&& ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
}
}
@@ -7,15 +7,14 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.OpenGL.Textures;
using osu.Framework.Graphics.Lines;
using osu.Framework.Graphics.Textures;
using OpenTK;
using OpenTK.Graphics.ES30;
using OpenTK.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@@ -44,7 +43,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public double? SnakedEnd { get; private set; }
private Color4 accentColour = Color4.White;
/// <summary>
/// Used to colour the path.
/// </summary>
@@ -63,7 +61,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
private Color4 borderColour = Color4.White;
/// <summary>
/// Used to colour the path border.
/// </summary>
@@ -88,7 +85,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private Vector2 topLeftOffset;
private readonly Slider slider;
public SliderBody(Slider s)
{
slider = s;
@@ -143,7 +139,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
var texture = new Texture(textureWidth, 1);
//initialise background
var raw = new Image<Rgba32>(textureWidth, 1);
var raw = new RawTexture(textureWidth, 1);
var bytes = raw.Data;
const float aa_portion = 0.02f;
const float border_portion = 0.128f;
@@ -158,13 +155,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
if (progress <= border_portion)
{
raw[i, 0] = new Rgba32(BorderColour.R, BorderColour.G, BorderColour.B, Math.Min(progress / aa_portion, 1) * BorderColour.A);
bytes[i * 4] = (byte)(BorderColour.R * 255);
bytes[i * 4 + 1] = (byte)(BorderColour.G * 255);
bytes[i * 4 + 2] = (byte)(BorderColour.B * 255);
bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (BorderColour.A * 255));
}
else
{
progress -= border_portion;
raw[i, 0] = new Rgba32(AccentColour.R, AccentColour.G, AccentColour.B,
(opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * AccentColour.A);
bytes[i * 4] = (byte)(AccentColour.R * 255);
bytes[i * 4 + 1] = (byte)(AccentColour.G * 255);
bytes[i * 4 + 2] = (byte)(AccentColour.B * 255);
bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (AccentColour.A * 255));
}
}
@@ -1,13 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
public class HitCircle : OsuHitObject
{
public override Judgement CreateJudgement() => new OsuJudgement();
}
}
@@ -54,8 +54,6 @@ namespace osu.Game.Rulesets.Osu.Objects
public virtual bool NewCombo { get; set; }
public int ComboOffset { get; set; }
public virtual int IndexInCurrentCombo { get; set; }
public virtual int ComboIndex { get; set; }
@@ -4,8 +4,6 @@
using System;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -18,15 +16,10 @@ namespace osu.Game.Rulesets.Osu.Objects
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
// Out preempt should be one span early to give the user ample warning.
TimePreempt += SpanDuration;
// We want to show the first RepeatPoint as the TimePreempt dictates but on short (and possibly fast) sliders
// we may need to cut down this time on following RepeatPoints to only show up to two RepeatPoints at any given time.
if (RepeatIndex > 0)
TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
}
public override Judgement CreateJudgement() => new OsuJudgement();
}
}
+2 -6
View File
@@ -10,8 +10,6 @@ using System.Linq;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -96,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public double TickDistance;
public HitCircle HeadCircle;
public SliderTailCircle TailCircle;
public HitCircle TailCircle;
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
@@ -135,7 +133,7 @@ namespace osu.Game.Rulesets.Osu.Objects
ComboIndex = ComboIndex,
};
TailCircle = new SliderTailCircle(this)
TailCircle = new SliderCircle(this)
{
StartTime = EndTime,
Position = EndPosition,
@@ -213,7 +211,5 @@ namespace osu.Game.Rulesets.Osu.Objects
});
}
}
public override Judgement CreateJudgement() => new OsuJudgement();
}
}

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