mirror of
https://github.com/ppy/osu.git
synced 2026-05-18 02:49:53 +08:00
Compare commits
1121 Commits
Vendored
+42
-35
@@ -2,63 +2,70 @@
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"command": "msbuild",
|
||||
"type": "shell",
|
||||
"suppressTaskName": true,
|
||||
"args": [
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/property:DebugType=portable",
|
||||
"/verbosity:minimal",
|
||||
"/m" //parallel compiling support.
|
||||
],
|
||||
"tasks": [{
|
||||
"taskName": "Build (Debug)",
|
||||
"label": "Build (Debug)",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/p:DebugType=portable",
|
||||
"/m",
|
||||
"/v:m"
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$msCompile"
|
||||
]
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"taskName": "Build (Release)",
|
||||
"label": "Build (Release)",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"/p:Configuration=Release",
|
||||
"/p:DebugType=portable",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
"/v:m"
|
||||
],
|
||||
"group": "build",
|
||||
"args": [
|
||||
"/property:Configuration=Release"
|
||||
],
|
||||
"problemMatcher": [
|
||||
"$msCompile"
|
||||
]
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"taskName": "Clean (Debug)",
|
||||
"label": "Clean (Debug)",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"/target:Clean"
|
||||
"/p:DebugType=portable",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/m",
|
||||
"/t:Clean",
|
||||
"/v:m"
|
||||
],
|
||||
"problemMatcher": [
|
||||
"$msCompile"
|
||||
]
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"taskName": "Clean (Release)",
|
||||
"label": "Clean (Release)",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"/target:Clean",
|
||||
"/property:Configuration=Release"
|
||||
"/p:Configuration=Release",
|
||||
"/p:GenerateFullPaths=true",
|
||||
"/p:DebugType=portable",
|
||||
"/m",
|
||||
"/t:Clean",
|
||||
"/v:m"
|
||||
],
|
||||
"problemMatcher": [
|
||||
"$msCompile"
|
||||
]
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"taskName": "Clean All",
|
||||
"label": "Clean All",
|
||||
"dependsOn": [
|
||||
"Clean (Debug)",
|
||||
"Clean (Release)"
|
||||
],
|
||||
"problemMatcher": [
|
||||
"$msCompile"
|
||||
]
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -15,7 +15,9 @@ This is still heavily under development and is not intended for end-user use. Th
|
||||
|
||||
We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted.
|
||||
|
||||
Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu-framework/issues).
|
||||
Please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**.
|
||||
|
||||
Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues).
|
||||
|
||||
Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible.
|
||||
|
||||
|
||||
+3
-3
@@ -9,17 +9,17 @@ cache:
|
||||
- inspectcode -> appveyor.yml
|
||||
- packages -> **\packages.config
|
||||
install:
|
||||
- cmd: git submodule update --init --recursive
|
||||
- cmd: git submodule update --init --recursive --depth=5
|
||||
- cmd: choco install resharper-clt -y
|
||||
- cmd: choco install nvika -y
|
||||
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.3/CodeFileSanity.exe
|
||||
before_build:
|
||||
- cmd: CodeFileSanity.exe
|
||||
- cmd: nuget restore
|
||||
- cmd: nuget restore -verbosity quiet
|
||||
build:
|
||||
project: osu.sln
|
||||
parallel: true
|
||||
verbosity: minimal
|
||||
after_build:
|
||||
- cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln
|
||||
- cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
|
||||
- cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
|
||||
+1
-1
Submodule osu-framework updated: 5986f21268...f4fde31f8c
+1
-1
Submodule osu-resources updated: a4418111f8...4287ee8043
@@ -13,7 +13,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
|
||||
<add key="ProjectName" value="osu.Desktop" />
|
||||
<add key="NuSpecName" value="osu.Desktop\osu.nuspec" />
|
||||
<add key="SolutionName" value="osu" />
|
||||
<add key="TargetName" value="Client\osu.Desktop" />
|
||||
<add key="TargetName" value="osu.Desktop" />
|
||||
<add key="PackageName" value="osulazer" />
|
||||
<add key="IconName" value="lazer.ico" />
|
||||
<add key="CodeSigningCertificate" value="" />
|
||||
|
||||
@@ -145,6 +145,8 @@ namespace osu.Desktop.Deploy
|
||||
/// </summary>
|
||||
private static void checkReleaseFiles()
|
||||
{
|
||||
if (!canGitHub) return;
|
||||
|
||||
var releaseLines = getReleaseLines();
|
||||
|
||||
//ensure we have all files necessary
|
||||
@@ -157,6 +159,8 @@ namespace osu.Desktop.Deploy
|
||||
|
||||
private static void pruneReleases()
|
||||
{
|
||||
if (!canGitHub) return;
|
||||
|
||||
write("Pruning RELEASES...");
|
||||
|
||||
var releaseLines = getReleaseLines().ToList();
|
||||
@@ -190,7 +194,7 @@ namespace osu.Desktop.Deploy
|
||||
|
||||
private static void uploadBuild(string version)
|
||||
{
|
||||
if (string.IsNullOrEmpty(GitHubAccessToken) || string.IsNullOrEmpty(codeSigningCertPath))
|
||||
if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate))
|
||||
return;
|
||||
|
||||
write("Publishing to GitHub...");
|
||||
@@ -228,8 +232,12 @@ namespace osu.Desktop.Deploy
|
||||
|
||||
private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage);
|
||||
|
||||
private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken);
|
||||
|
||||
private static void checkGitHubReleases()
|
||||
{
|
||||
if (!canGitHub) return;
|
||||
|
||||
write("Checking GitHub releases...");
|
||||
var req = new JsonWebRequest<List<GitHubRelease>>($"{GitHubApiEndpoint}");
|
||||
req.AuthenticatedBlockingPerform();
|
||||
@@ -390,7 +398,7 @@ namespace osu.Desktop.Deploy
|
||||
|
||||
public static void AuthenticatedBlockingPerform(this WebRequest r)
|
||||
{
|
||||
r.Headers.Add("Authorization", $"token {GitHubAccessToken}");
|
||||
r.AddHeader("Authorization", $"token {GitHubAccessToken}");
|
||||
r.Perform();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="..\osu.Game.props" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
@@ -22,7 +22,6 @@
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<LangVersion>6</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@@ -102,9 +101,6 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\osu.licenseheader">
|
||||
<Link>osu.licenseheader</Link>
|
||||
</None>
|
||||
<None Include="App.config">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
|
||||
@@ -18,9 +18,12 @@ namespace osu.Desktop
|
||||
{
|
||||
internal class OsuGameDesktop : OsuGame
|
||||
{
|
||||
private readonly bool noVersionOverlay;
|
||||
|
||||
public OsuGameDesktop(string[] args = null)
|
||||
: base(args)
|
||||
{
|
||||
noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false;
|
||||
}
|
||||
|
||||
public override Storage GetStorageForStableInstall()
|
||||
@@ -79,11 +82,14 @@ namespace osu.Desktop
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
|
||||
if (!noVersionOverlay)
|
||||
{
|
||||
Add(v);
|
||||
v.State = Visibility.Visible;
|
||||
});
|
||||
LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v =>
|
||||
{
|
||||
Add(v);
|
||||
v.State = Visibility.Visible;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void SetHost(GameHost host)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Development;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -198,10 +197,9 @@ namespace osu.Desktop.Overlays
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
catch (Exception)
|
||||
{
|
||||
//likely have no internet connection.
|
||||
//we'll ignore this and retry later.
|
||||
// we'll ignore this and retry later. can be triggered by no internet connection or thread abortion.
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -233,7 +231,8 @@ namespace osu.Desktop.Overlays
|
||||
Text = @"Update ready to install. Click to restart!",
|
||||
Activated = () =>
|
||||
{
|
||||
UpdateManager.RestartAppWhenExited();
|
||||
// Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here
|
||||
UpdateManager.RestartAppWhenExited().Wait();
|
||||
game.GracefullyExit();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
|
||||
<Import Project="..\osu.Game.props" />
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{419659FD-72EA-4678-9EB8-B22A746CED70}</ProjectGuid>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@@ -62,7 +63,6 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<Commandlineparameters>
|
||||
</Commandlineparameters>
|
||||
<LangVersion>6</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>none</DebugType>
|
||||
@@ -98,7 +98,6 @@
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<LangVersion>6</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<StartArguments>--tests</StartArguments>
|
||||
</PropertyGroup>
|
||||
@@ -174,9 +173,6 @@
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\osu.licenseheader">
|
||||
<Link>osu.licenseheader</Link>
|
||||
</None>
|
||||
<None Include="app.config" />
|
||||
<None Include="OpenTK.dll.config" />
|
||||
<None Include="osu!.res" />
|
||||
|
||||
@@ -11,14 +11,14 @@ using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
{
|
||||
internal class CatchBeatmapConverter : BeatmapConverter<CatchBaseHit>
|
||||
internal class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
|
||||
{
|
||||
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
|
||||
|
||||
protected override IEnumerable<CatchBaseHit> ConvertHitObject(HitObject obj, Beatmap beatmap)
|
||||
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap)
|
||||
{
|
||||
var curveData = obj as IHasCurve;
|
||||
var positionData = obj as IHasPosition;
|
||||
var positionData = obj as IHasXPosition;
|
||||
var comboData = obj as IHasCombo;
|
||||
|
||||
if (positionData == null)
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
{
|
||||
internal class CatchBeatmapProcessor : BeatmapProcessor<CatchBaseHit>
|
||||
internal class CatchBeatmapProcessor : BeatmapProcessor<CatchHitObject>
|
||||
{
|
||||
public override void PostProcess(Beatmap<CatchBaseHit> beatmap)
|
||||
public override void PostProcess(Beatmap<CatchHitObject> beatmap)
|
||||
{
|
||||
if (beatmap.ComboColors.Count == 0)
|
||||
return;
|
||||
@@ -16,7 +22,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
int comboIndex = 0;
|
||||
int colourIndex = 0;
|
||||
|
||||
CatchBaseHit lastObj = null;
|
||||
CatchHitObject lastObj = null;
|
||||
|
||||
initialiseHyperDash(beatmap.HitObjects);
|
||||
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
@@ -34,5 +42,49 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
lastObj = obj;
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseHyperDash(List<CatchHitObject> objects)
|
||||
{
|
||||
// todo: add difficulty adjust.
|
||||
double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2;
|
||||
|
||||
int lastDirection = 0;
|
||||
double lastExcess = halfCatcherWidth;
|
||||
|
||||
int objCount = objects.Count;
|
||||
|
||||
for (int i = 0; i < objCount - 1; i++)
|
||||
{
|
||||
CatchHitObject currentObject = objects[i];
|
||||
|
||||
// not needed?
|
||||
// if (currentObject is TinyDroplet) continue;
|
||||
|
||||
CatchHitObject nextObject = objects[i + 1];
|
||||
|
||||
// while (nextObject is TinyDroplet)
|
||||
// {
|
||||
// if (++i == objCount - 1) break;
|
||||
// nextObject = objects[i + 1];
|
||||
// }
|
||||
|
||||
int thisDirection = nextObject.X > currentObject.X ? 1 : -1;
|
||||
double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4;
|
||||
double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth);
|
||||
|
||||
if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext)
|
||||
{
|
||||
currentObject.HyperDashTarget = nextObject;
|
||||
lastExcess = halfCatcherWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
//currentObject.DistanceToHyperDash = timeToNext - distanceToNext;
|
||||
lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth);
|
||||
}
|
||||
|
||||
lastDirection = thisDirection;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,17 +8,14 @@ using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch
|
||||
{
|
||||
public class CatchDifficultyCalculator : DifficultyCalculator<CatchBaseHit>
|
||||
public class CatchDifficultyCalculator : DifficultyCalculator<CatchHitObject>
|
||||
{
|
||||
public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
|
||||
|
||||
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
|
||||
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,9 +93,11 @@ namespace osu.Game.Rulesets.Catch
|
||||
|
||||
public override string Description => "osu!catch";
|
||||
|
||||
public override string ShortName => "fruits";
|
||||
|
||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new CatchDifficultyCalculator(beatmap);
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
|
||||
|
||||
public override int LegacyID => 2;
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public abstract class CatchBaseHit : HitObject, IHasXPosition, IHasCombo
|
||||
{
|
||||
public float X { get; set; }
|
||||
|
||||
public Color4 ComboColour { get; set; } = Color4.Gray;
|
||||
public int ComboIndex { get; set; }
|
||||
|
||||
public virtual bool NewCombo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The next fruit starts a new combo. Used for explodey.
|
||||
/// </summary>
|
||||
public virtual bool LastInCombo { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2007-2017 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.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public abstract class CatchHitObject : HitObject, IHasXPosition, IHasCombo
|
||||
{
|
||||
public const double OBJECT_RADIUS = 44;
|
||||
|
||||
public float X { get; set; }
|
||||
|
||||
public Color4 ComboColour { get; set; } = Color4.Gray;
|
||||
public int ComboIndex { get; set; }
|
||||
|
||||
public virtual bool NewCombo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The next fruit starts a new combo. Used for explodey.
|
||||
/// </summary>
|
||||
public virtual bool LastInCombo { get; set; }
|
||||
|
||||
public float Scale { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this fruit can initiate a hyperdash.
|
||||
/// </summary>
|
||||
public bool HyperDash => HyperDashTarget != null;
|
||||
|
||||
/// <summary>
|
||||
/// The target fruit if we are to initiate a hyperdash.
|
||||
/// </summary>
|
||||
public CatchHitObject HyperDashTarget;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
|
||||
Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,12 @@ using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
public abstract class DrawableCatchHitObject<TObject> : DrawableCatchHitObject
|
||||
where TObject : CatchBaseHit
|
||||
where TObject : CatchHitObject
|
||||
{
|
||||
public new TObject HitObject;
|
||||
|
||||
@@ -17,12 +18,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
: base(hitObject)
|
||||
{
|
||||
HitObject = hitObject;
|
||||
|
||||
Scale = new Vector2(HitObject.Scale);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class DrawableCatchHitObject : DrawableScrollingHitObject<CatchBaseHit>
|
||||
public abstract class DrawableCatchHitObject : DrawableScrollingHitObject<CatchHitObject>
|
||||
{
|
||||
protected DrawableCatchHitObject(CatchBaseHit hitObject)
|
||||
protected DrawableCatchHitObject(CatchHitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
RelativePositionAxes = Axes.Both;
|
||||
@@ -30,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
Y = (float)HitObject.StartTime;
|
||||
}
|
||||
|
||||
public Func<CatchBaseHit, bool> CheckPosition;
|
||||
public Func<CatchHitObject, bool> CheckPosition;
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
@@ -70,6 +71,20 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (HitObject.HyperDash)
|
||||
{
|
||||
Add(new Pulp
|
||||
{
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AccentColour = Color4.Red,
|
||||
Blending = BlendingMode.Additive,
|
||||
Alpha = 0.5f,
|
||||
Scale = new Vector2(2)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
|
||||
};
|
||||
|
||||
foreach (CatchBaseHit tick in s.Ticks)
|
||||
foreach (CatchHitObject tick in s.Ticks)
|
||||
{
|
||||
TinyDroplet tiny = tick as TinyDroplet;
|
||||
if (tiny != null)
|
||||
@@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AddNested(DrawableHitObject<CatchBaseHit> h)
|
||||
protected override void AddNested(DrawableHitObject<CatchHitObject> h)
|
||||
{
|
||||
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
|
||||
dropletContainer.Add(h);
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
|
||||
{
|
||||
public class Pulp : Circle, IHasAccentColour
|
||||
{
|
||||
public const float PULP_SIZE = 20;
|
||||
public const float PULP_SIZE = (float)CatchHitObject.OBJECT_RADIUS / 2.2f;
|
||||
|
||||
public Pulp()
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public class Droplet : CatchBaseHit
|
||||
public class Droplet : CatchHitObject
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public class Fruit : CatchBaseHit
|
||||
public class Fruit : CatchHitObject
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ using osu.Framework.Lists;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public class JuiceStream : CatchBaseHit, IHasCurve
|
||||
public class JuiceStream : CatchHitObject, IHasCurve
|
||||
{
|
||||
/// <summary>
|
||||
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
||||
@@ -42,11 +42,11 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
||||
}
|
||||
|
||||
public IEnumerable<CatchBaseHit> Ticks
|
||||
public IEnumerable<CatchHitObject> Ticks
|
||||
{
|
||||
get
|
||||
{
|
||||
SortedList<CatchBaseHit> ticks = new SortedList<CatchBaseHit>((a, b) => a.StartTime.CompareTo(b.StartTime));
|
||||
SortedList<CatchHitObject> ticks = new SortedList<CatchHitObject>((a, b) => a.StartTime.CompareTo(b.StartTime));
|
||||
|
||||
if (TickDistance == 0)
|
||||
return ticks;
|
||||
|
||||
@@ -10,14 +10,14 @@ using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Scoring
|
||||
{
|
||||
internal class CatchScoreProcessor : ScoreProcessor<CatchBaseHit>
|
||||
internal class CatchScoreProcessor : ScoreProcessor<CatchHitObject>
|
||||
{
|
||||
public CatchScoreProcessor(RulesetContainer<CatchBaseHit> rulesetContainer)
|
||||
public CatchScoreProcessor(RulesetContainer<CatchHitObject> rulesetContainer)
|
||||
: base(rulesetContainer)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SimulateAutoplay(Beatmap<CatchBaseHit> beatmap)
|
||||
protected override void SimulateAutoplay(Beatmap<CatchHitObject> beatmap)
|
||||
{
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
|
||||
@@ -11,16 +11,26 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseCatchStacker() : base(typeof(CatchRuleset))
|
||||
public TestCaseCatchStacker()
|
||||
: base(typeof(CatchRuleset))
|
||||
{
|
||||
}
|
||||
|
||||
protected override Beatmap CreateBeatmap()
|
||||
{
|
||||
var beatmap = new Beatmap();
|
||||
var beatmap = new Beatmap
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 6,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
beatmap.HitObjects.Add(new Fruit { X = 0.5f, StartTime = i * 100, NewCombo = i % 8 == 0 });
|
||||
for (int i = 0; i < 512; i++)
|
||||
beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 });
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
internal class TestCaseCatcher : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(Catcher),
|
||||
};
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new CatchInputManager(rulesets.GetRuleset(2))
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new Catcher
|
||||
{
|
||||
RelativePositionAxes = Axes.Both,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Size = new Vector2(1, 0.2f),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseCatcherArea : OsuTestCase
|
||||
{
|
||||
private RulesetInfo catchRuleset;
|
||||
private TestCatcherArea catcherArea;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(CatcherArea),
|
||||
};
|
||||
|
||||
public TestCaseCatcherArea()
|
||||
{
|
||||
AddSliderStep<float>("CircleSize", 0, 8, 5, createCatcher);
|
||||
AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t));
|
||||
}
|
||||
|
||||
private void createCatcher(float size)
|
||||
{
|
||||
Child = new CatchInputManager(catchRuleset)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.BottomLeft
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
catchRuleset = rulesets.GetRuleset(2);
|
||||
}
|
||||
|
||||
private class TestCatcherArea : CatcherArea
|
||||
{
|
||||
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
|
||||
: base(beatmapDifficulty)
|
||||
{
|
||||
}
|
||||
|
||||
public void ToggleHyperDash(bool status) => MovableCatcher.HyperDashModifier = status ? 2 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseHyperdash()
|
||||
: base(typeof(CatchRuleset))
|
||||
{
|
||||
}
|
||||
|
||||
protected override Beatmap CreateBeatmap()
|
||||
{
|
||||
var beatmap = new Beatmap();
|
||||
|
||||
for (int i = 0; i < 512; i++)
|
||||
if (i % 5 < 3)
|
||||
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
: base(new CatchRuleset(new RulesetInfo()))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawable;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@@ -15,15 +15,14 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatchPlayfield : ScrollingPlayfield
|
||||
{
|
||||
public static readonly float BASE_WIDTH = 512;
|
||||
public const float BASE_WIDTH = 512;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container<Drawable> content;
|
||||
|
||||
private readonly Container catcherContainer;
|
||||
private readonly Catcher catcher;
|
||||
private readonly CatcherArea catcherArea;
|
||||
|
||||
public CatchPlayfield()
|
||||
public CatchPlayfield(BeatmapDifficulty difficulty)
|
||||
: base(Axes.Y)
|
||||
{
|
||||
Container explodingFruitContainer;
|
||||
@@ -43,30 +42,16 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
catcherContainer = new Container
|
||||
catcherArea = new CatcherArea(difficulty)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
ExplodingFruitTarget = explodingFruitContainer,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Height = 180,
|
||||
Child = catcher = new Catcher
|
||||
{
|
||||
ExplodingFruitTarget = explodingFruitContainer,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Origin = Anchor.TopCentre,
|
||||
X = 0.5f,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
catcher.Size = new Vector2(catcherContainer.DrawSize.Y);
|
||||
}
|
||||
|
||||
public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X / DrawSize.X / 2;
|
||||
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
|
||||
|
||||
public override void Add(DrawableHitObject h)
|
||||
{
|
||||
@@ -88,7 +73,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
(judgedObject.Parent as Container<DrawableHitObject>)?.Remove(judgedObject);
|
||||
(judgedObject.Parent as Container)?.Remove(judgedObject);
|
||||
|
||||
catcher.Add(judgedObject, screenPosition);
|
||||
catcherArea.Add(judgedObject, screenPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchBaseHit>
|
||||
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchHitObject>
|
||||
{
|
||||
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||
@@ -22,15 +22,15 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
||||
|
||||
protected override BeatmapProcessor<CatchBaseHit> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
|
||||
protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
|
||||
|
||||
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
|
||||
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter() => new CatchBeatmapConverter();
|
||||
|
||||
protected override Playfield CreatePlayfield() => new CatchPlayfield();
|
||||
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
|
||||
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
|
||||
|
||||
protected override DrawableHitObject<CatchBaseHit> GetVisualRepresentation(CatchBaseHit h)
|
||||
protected override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h)
|
||||
{
|
||||
var fruit = h as Fruit;
|
||||
if (fruit != null)
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
||||
{
|
||||
private Texture texture;
|
||||
|
||||
private Container<DrawableHitObject> caughtFruit;
|
||||
|
||||
public Container ExplodingFruitTarget;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
createCatcherSprite(),
|
||||
caughtFruit = new Container<DrawableHitObject>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int currentDirection;
|
||||
|
||||
private bool dashing;
|
||||
|
||||
protected bool Dashing
|
||||
{
|
||||
get { return dashing; }
|
||||
set
|
||||
{
|
||||
if (value == dashing) return;
|
||||
|
||||
dashing = value;
|
||||
|
||||
if (dashing)
|
||||
Schedule(addAdditiveSprite);
|
||||
}
|
||||
}
|
||||
|
||||
private void addAdditiveSprite()
|
||||
{
|
||||
if (!dashing) return;
|
||||
|
||||
var additive = createCatcherSprite();
|
||||
|
||||
additive.RelativePositionAxes = Axes.Both;
|
||||
additive.Blending = BlendingMode.Additive;
|
||||
additive.Position = Position;
|
||||
additive.Scale = Scale;
|
||||
|
||||
((Container)Parent).Add(additive);
|
||||
|
||||
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
|
||||
|
||||
Scheduler.AddDelayed(addAdditiveSprite, 50);
|
||||
}
|
||||
|
||||
private Sprite createCatcherSprite() => new Sprite
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
FillMode = FillMode.Fit,
|
||||
Texture = texture,
|
||||
OriginPosition = new Vector2(DrawWidth / 2, 10) //temporary until the sprite is aligned correctly.
|
||||
};
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection--;
|
||||
return true;
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection++;
|
||||
return true;
|
||||
case CatchAction.Dash:
|
||||
Dashing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection++;
|
||||
return true;
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection--;
|
||||
return true;
|
||||
case CatchAction.Dash:
|
||||
Dashing = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
|
||||
/// </summary>
|
||||
private const double base_speed = 1.0 / 512;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (currentDirection == 0) return;
|
||||
|
||||
double dashModifier = Dashing ? 1 : 0.5;
|
||||
|
||||
Scale = new Vector2(Math.Sign(currentDirection), 1);
|
||||
X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * base_speed * dashModifier, 0, 1);
|
||||
}
|
||||
|
||||
public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
|
||||
{
|
||||
fruit.RelativePositionAxes = Axes.None;
|
||||
fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0);
|
||||
|
||||
fruit.Anchor = Anchor.TopCentre;
|
||||
fruit.Origin = Anchor.BottomCentre;
|
||||
fruit.Scale *= 0.7f;
|
||||
fruit.LifetimeEnd = double.MaxValue;
|
||||
|
||||
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
|
||||
|
||||
while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
|
||||
{
|
||||
fruit.X += RNG.Next(-5, 5);
|
||||
fruit.Y -= RNG.Next(0, 5);
|
||||
}
|
||||
|
||||
caughtFruit.Add(fruit);
|
||||
|
||||
if (((CatchBaseHit)fruit.HitObject).LastInCombo)
|
||||
explode();
|
||||
}
|
||||
|
||||
private void explode()
|
||||
{
|
||||
var fruit = caughtFruit.ToArray();
|
||||
|
||||
foreach (var f in fruit)
|
||||
{
|
||||
var originalX = f.X * Scale.X;
|
||||
|
||||
if (ExplodingFruitTarget != null)
|
||||
{
|
||||
f.Anchor = Anchor.TopLeft;
|
||||
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
||||
|
||||
caughtFruit.Remove(f);
|
||||
|
||||
ExplodingFruitTarget.Add(f);
|
||||
}
|
||||
|
||||
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
|
||||
.Then()
|
||||
.MoveToY(f.Y + 50, 500, Easing.InSine);
|
||||
|
||||
f.MoveToX(f.X + originalX * 6, 1000);
|
||||
f.FadeOut(750);
|
||||
|
||||
f.Expire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatcherArea : Container
|
||||
{
|
||||
public const float CATCHER_SIZE = 172;
|
||||
|
||||
protected readonly Catcher MovableCatcher;
|
||||
|
||||
public Container ExplodingFruitTarget
|
||||
{
|
||||
set { MovableCatcher.ExplodingFruitTarget = value; }
|
||||
}
|
||||
|
||||
public CatcherArea(BeatmapDifficulty difficulty = null)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = CATCHER_SIZE;
|
||||
Child = MovableCatcher = new Catcher(difficulty)
|
||||
{
|
||||
AdditiveTarget = this,
|
||||
};
|
||||
}
|
||||
|
||||
public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
|
||||
{
|
||||
fruit.RelativePositionAxes = Axes.None;
|
||||
fruit.Position = new Vector2(MovableCatcher.ToLocalSpace(absolutePosition).X - MovableCatcher.DrawSize.X / 2, 0);
|
||||
|
||||
fruit.Anchor = Anchor.TopCentre;
|
||||
fruit.Origin = Anchor.BottomCentre;
|
||||
fruit.Scale *= 0.7f;
|
||||
fruit.LifetimeEnd = double.MaxValue;
|
||||
|
||||
MovableCatcher.Add(fruit);
|
||||
}
|
||||
|
||||
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
|
||||
|
||||
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
||||
{
|
||||
private Texture texture;
|
||||
|
||||
private Container<DrawableHitObject> caughtFruit;
|
||||
|
||||
public Container ExplodingFruitTarget;
|
||||
|
||||
public Container AdditiveTarget;
|
||||
|
||||
public Catcher(BeatmapDifficulty difficulty = null)
|
||||
{
|
||||
RelativePositionAxes = Axes.X;
|
||||
X = 0.5f;
|
||||
|
||||
Origin = Anchor.TopCentre;
|
||||
Anchor = Anchor.TopLeft;
|
||||
|
||||
Size = new Vector2(CATCHER_SIZE);
|
||||
if (difficulty != null)
|
||||
Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
texture = textures.Get(@"Play/Catch/fruit-catcher-idle");
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
createCatcherSprite(),
|
||||
caughtFruit = new Container<DrawableHitObject>
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int currentDirection;
|
||||
|
||||
private bool dashing;
|
||||
|
||||
protected bool Dashing
|
||||
{
|
||||
get { return dashing; }
|
||||
set
|
||||
{
|
||||
if (value == dashing) return;
|
||||
|
||||
dashing = value;
|
||||
|
||||
Trail |= dashing;
|
||||
}
|
||||
}
|
||||
|
||||
private bool trail;
|
||||
|
||||
/// <summary>
|
||||
/// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
|
||||
/// </summary>
|
||||
protected bool Trail
|
||||
{
|
||||
get { return trail; }
|
||||
set
|
||||
{
|
||||
if (value == trail) return;
|
||||
|
||||
trail = value;
|
||||
|
||||
if (Trail)
|
||||
beginTrail();
|
||||
}
|
||||
}
|
||||
|
||||
private void beginTrail()
|
||||
{
|
||||
Trail &= dashing || HyperDashing;
|
||||
Trail &= AdditiveTarget != null;
|
||||
|
||||
if (!Trail) return;
|
||||
|
||||
var additive = createCatcherSprite();
|
||||
|
||||
additive.Anchor = Anchor;
|
||||
additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly.
|
||||
additive.Position = Position;
|
||||
additive.Scale = Scale;
|
||||
additive.Colour = HyperDashing ? Color4.Red : Color4.White;
|
||||
additive.RelativePositionAxes = RelativePositionAxes;
|
||||
additive.Blending = BlendingMode.Additive;
|
||||
|
||||
AdditiveTarget.Add(additive);
|
||||
|
||||
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
|
||||
|
||||
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
|
||||
}
|
||||
|
||||
private Sprite createCatcherSprite() => new Sprite
|
||||
{
|
||||
Size = new Vector2(CATCHER_SIZE),
|
||||
FillMode = FillMode.Fill,
|
||||
Texture = texture,
|
||||
OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly.
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Add a caught fruit to the catcher's stack.
|
||||
/// </summary>
|
||||
/// <param name="fruit">The fruit that was caught.</param>
|
||||
public void Add(DrawableHitObject fruit)
|
||||
{
|
||||
float distance = fruit.DrawSize.X / 2 * fruit.Scale.X;
|
||||
|
||||
while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance))
|
||||
{
|
||||
fruit.X += RNG.Next(-5, 5);
|
||||
fruit.Y -= RNG.Next(0, 5);
|
||||
}
|
||||
|
||||
caughtFruit.Add(fruit);
|
||||
|
||||
var catchObject = (CatchHitObject)fruit.HitObject;
|
||||
|
||||
if (catchObject.LastInCombo)
|
||||
explode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Let the catcher attempt to catch a fruit.
|
||||
/// </summary>
|
||||
/// <param name="fruit">The fruit to catch.</param>
|
||||
/// <returns>Whether the catch is possible.</returns>
|
||||
public bool AttemptCatch(CatchHitObject fruit)
|
||||
{
|
||||
const double relative_catcher_width = CATCHER_SIZE / 2;
|
||||
|
||||
// this stuff wil disappear once we move fruit to non-relative coordinate space in the future.
|
||||
var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH;
|
||||
var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
var validCatch =
|
||||
catchObjectPosition >= catcherPosition - relative_catcher_width / 2 &&
|
||||
catchObjectPosition <= catcherPosition + relative_catcher_width / 2;
|
||||
|
||||
if (validCatch && fruit.HyperDash)
|
||||
{
|
||||
HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED;
|
||||
HyperDashDirection = fruit.HyperDashTarget.X - fruit.X;
|
||||
}
|
||||
else
|
||||
HyperDashModifier = 1;
|
||||
|
||||
return validCatch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether we are hypderdashing or not.
|
||||
/// </summary>
|
||||
public bool HyperDashing => hyperDashModifier != 1;
|
||||
|
||||
private double hyperDashModifier = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The direction in which hyperdash is allowed. 0 allows both directions.
|
||||
/// </summary>
|
||||
public double HyperDashDirection;
|
||||
|
||||
/// <summary>
|
||||
/// The speed modifier resultant from hyperdash. Will trigger hyperdash when not equal to 1.
|
||||
/// </summary>
|
||||
public double HyperDashModifier
|
||||
{
|
||||
get { return hyperDashModifier; }
|
||||
set
|
||||
{
|
||||
if (value == hyperDashModifier) return;
|
||||
hyperDashModifier = value;
|
||||
|
||||
const float transition_length = 180;
|
||||
|
||||
if (HyperDashing)
|
||||
{
|
||||
this.FadeColour(Color4.OrangeRed, transition_length, Easing.OutQuint);
|
||||
this.FadeTo(0.2f, transition_length, Easing.OutQuint);
|
||||
Trail = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
HyperDashDirection = 0;
|
||||
this.FadeColour(Color4.White, transition_length, Easing.OutQuint);
|
||||
this.FadeTo(1, transition_length, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection--;
|
||||
return true;
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection++;
|
||||
return true;
|
||||
case CatchAction.Dash:
|
||||
Dashing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(CatchAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection++;
|
||||
return true;
|
||||
case CatchAction.MoveRight:
|
||||
currentDirection--;
|
||||
return true;
|
||||
case CatchAction.Dash:
|
||||
Dashing = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable.
|
||||
/// </summary>
|
||||
public const double BASE_SPEED = 1.0 / 512;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (currentDirection == 0) return;
|
||||
|
||||
var direction = Math.Sign(currentDirection);
|
||||
|
||||
double dashModifier = Dashing ? 1 : 0.5;
|
||||
|
||||
if (hyperDashModifier != 1 && (HyperDashDirection == 0 || direction == Math.Sign(HyperDashDirection)))
|
||||
dashModifier = hyperDashModifier;
|
||||
|
||||
Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y);
|
||||
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
|
||||
}
|
||||
|
||||
private void explode()
|
||||
{
|
||||
var fruit = caughtFruit.ToArray();
|
||||
|
||||
foreach (var f in fruit)
|
||||
{
|
||||
var originalX = f.X * Scale.X;
|
||||
|
||||
if (ExplodingFruitTarget != null)
|
||||
{
|
||||
f.Anchor = Anchor.TopLeft;
|
||||
f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget);
|
||||
|
||||
caughtFruit.Remove(f);
|
||||
|
||||
ExplodingFruitTarget.Add(f);
|
||||
}
|
||||
|
||||
f.MoveToY(f.Y - 50, 250, Easing.OutSine)
|
||||
.Then()
|
||||
.MoveToY(f.Y + 50, 500, Easing.InSine);
|
||||
|
||||
f.MoveToX(f.X + originalX * 6, 1000);
|
||||
f.FadeOut(750);
|
||||
|
||||
f.Expire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="..\osu.Game.props" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
@@ -21,7 +21,6 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<LangVersion>6</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
@@ -57,25 +56,24 @@
|
||||
<Compile Include="Objects\JuiceStream.cs" />
|
||||
<Compile Include="Scoring\CatchScoreProcessor.cs" />
|
||||
<Compile Include="Judgements\CatchJudgement.cs" />
|
||||
<Compile Include="Objects\CatchBaseHit.cs" />
|
||||
<Compile Include="Objects\CatchHitObject.cs" />
|
||||
<Compile Include="Objects\Drawable\DrawableFruit.cs" />
|
||||
<Compile Include="Objects\Droplet.cs" />
|
||||
<Compile Include="Objects\Fruit.cs" />
|
||||
<Compile Include="Objects\TinyDroplet.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Tests\TestCaseCatcher.cs" />
|
||||
<Compile Include="Tests\TestCaseCatcherArea.cs" />
|
||||
<Compile Include="Tests\TestCaseCatchStacker.cs" />
|
||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||
<Compile Include="Tests\TestCaseCatchPlayer.cs" />
|
||||
<Compile Include="UI\Catcher.cs" />
|
||||
<Compile Include="Tests\TestCaseHyperdash.cs" />
|
||||
<Compile Include="UI\CatcherArea.cs" />
|
||||
<Compile Include="UI\CatchRulesetContainer.cs" />
|
||||
<Compile Include="UI\CatchPlayfield.cs" />
|
||||
<Compile Include="CatchRuleset.cs" />
|
||||
<Compile Include="Mods\CatchMod.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\osu.licenseheader">
|
||||
<Link>osu.licenseheader</Link>
|
||||
</None>
|
||||
<None Include="app.config" />
|
||||
<None Include="OpenTK.dll.config" />
|
||||
<None Include="packages.config" />
|
||||
@@ -92,6 +90,9 @@
|
||||
<Private>True</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -138,8 +138,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
Pattern newPattern = conversion.Generate();
|
||||
lastPattern = newPattern;
|
||||
|
||||
var stairPatternGenerator = (HitObjectPatternGenerator)conversion;
|
||||
lastStair = stairPatternGenerator.StairType;
|
||||
var stairPatternGenerator = conversion as HitObjectPatternGenerator;
|
||||
lastStair = stairPatternGenerator?.StairType ?? lastStair;
|
||||
|
||||
return newPattern.HitObjects;
|
||||
}
|
||||
|
||||
@@ -16,11 +16,8 @@ namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
}
|
||||
|
||||
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
|
||||
|
||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.BaseDifficulty.CircleSize)));
|
||||
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,9 +105,11 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
public override string Description => "osu!mania";
|
||||
|
||||
public override string ShortName => "mania";
|
||||
|
||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap);
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap);
|
||||
|
||||
public override int LegacyID => 3;
|
||||
|
||||
|
||||
@@ -86,18 +86,17 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
|
||||
}
|
||||
|
||||
public class ManiaModRandom : Mod, IApplicableMod<ManiaHitObject>
|
||||
public class ManiaModRandom : Mod, IApplicableToRulesetContainer<ManiaHitObject>
|
||||
{
|
||||
public override string Name => "Random";
|
||||
public override string ShortenedName => "RD";
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
|
||||
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
|
||||
public override string Description => @"Shuffle around the notes!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
|
||||
{
|
||||
int availableColumns = ((ManiaRulesetContainer)rulesetContainer).AvailableColumns;
|
||||
|
||||
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
|
||||
|
||||
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
|
||||
@@ -177,21 +176,10 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
|
||||
public class ManiaModAutoplay : ModAutoplay<ManiaHitObject>
|
||||
{
|
||||
private int availableColumns;
|
||||
|
||||
public override void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
|
||||
{
|
||||
// Todo: This shouldn't be done, we should be getting a ManiaBeatmap which should store AvailableColumns
|
||||
// But this is dependent on a _lot_ of refactoring
|
||||
var maniaRulesetContainer = (ManiaRulesetContainer)rulesetContainer;
|
||||
availableColumns = maniaRulesetContainer.AvailableColumns;
|
||||
|
||||
base.ApplyToRulesetContainer(rulesetContainer);
|
||||
}
|
||||
protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap) => new Score
|
||||
{
|
||||
User = new User { Username = "osu!topus!" },
|
||||
Replay = new ManiaAutoGenerator(beatmap, availableColumns).Generate(),
|
||||
Replay = new ManiaAutoGenerator(beatmap).Generate(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
protected override void UpdateState(ArmedState state)
|
||||
{
|
||||
switch (State)
|
||||
switch (State.Value)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
AccentColour = Color4.Green;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
@@ -13,15 +13,11 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
{
|
||||
internal class ManiaAutoGenerator : AutoGenerator<ManiaHitObject>
|
||||
{
|
||||
private const double release_delay = 20;
|
||||
public const double RELEASE_DELAY = 20;
|
||||
|
||||
private readonly int availableColumns;
|
||||
|
||||
public ManiaAutoGenerator(Beatmap<ManiaHitObject> beatmap, int availableColumns)
|
||||
public ManiaAutoGenerator(Beatmap<ManiaHitObject> beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
this.availableColumns = availableColumns;
|
||||
|
||||
Replay = new Replay { User = new User { Username = @"Autoplay" } };
|
||||
}
|
||||
|
||||
@@ -30,104 +26,52 @@ namespace osu.Game.Rulesets.Mania.Replays
|
||||
public override Replay Generate()
|
||||
{
|
||||
// Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled
|
||||
Replay.Frames.Add(new ReplayFrame(-100000, null, null, ReplayButtonState.None));
|
||||
Replay.Frames.Add(new ManiaReplayFrame(-100000, 0));
|
||||
|
||||
double[] holdEndTimes = new double[availableColumns];
|
||||
for (int i = 0; i < availableColumns; i++)
|
||||
holdEndTimes[i] = double.NegativeInfinity;
|
||||
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
|
||||
|
||||
// Notes are handled row-by-row
|
||||
foreach (var objGroup in Beatmap.HitObjects.GroupBy(h => h.StartTime))
|
||||
int activeColumns = 0;
|
||||
foreach (var group in pointGroups)
|
||||
{
|
||||
double groupTime = objGroup.Key;
|
||||
|
||||
int activeColumns = 0;
|
||||
|
||||
// Get the previously held-down active columns
|
||||
for (int i = 0; i < availableColumns; i++)
|
||||
foreach (var point in group)
|
||||
{
|
||||
if (holdEndTimes[i] > groupTime)
|
||||
activeColumns |= 1 << i;
|
||||
if (point is HitPoint)
|
||||
activeColumns |= 1 << point.Column;
|
||||
if (point is ReleasePoint)
|
||||
activeColumns ^= 1 << point.Column;
|
||||
}
|
||||
|
||||
// Add on the group columns, keeping track of the held notes for the next rows
|
||||
foreach (var obj in objGroup)
|
||||
{
|
||||
var holdNote = obj as HoldNote;
|
||||
if (holdNote != null)
|
||||
holdEndTimes[obj.Column] = Math.Max(holdEndTimes[obj.Column], holdNote.EndTime);
|
||||
|
||||
activeColumns |= 1 << obj.Column;
|
||||
}
|
||||
|
||||
Replay.Frames.Add(new ReplayFrame(groupTime, activeColumns, null, ReplayButtonState.None));
|
||||
|
||||
// Add the release frames. We can't do this with the loop above because we need activeColumns to be fully populated
|
||||
foreach (var obj in objGroup.GroupBy(h => (h as IHasEndTime)?.EndTime ?? h.StartTime + release_delay).OrderBy(h => h.Key))
|
||||
{
|
||||
var groupEndTime = obj.Key;
|
||||
|
||||
int activeColumnsAtEnd = 0;
|
||||
for (int i = 0; i < availableColumns; i++)
|
||||
{
|
||||
if (holdEndTimes[i] > groupEndTime)
|
||||
activeColumnsAtEnd |= 1 << i;
|
||||
}
|
||||
|
||||
Replay.Frames.Add(new ReplayFrame(groupEndTime, activeColumnsAtEnd, 0, ReplayButtonState.None));
|
||||
}
|
||||
Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, activeColumns));
|
||||
}
|
||||
|
||||
Replay.Frames = Replay.Frames
|
||||
// Pick the maximum activeColumns for all frames at the same time
|
||||
.GroupBy(f => f.Time)
|
||||
.Select(g => new ReplayFrame(g.First().Time, maxMouseX(g), 0, ReplayButtonState.None))
|
||||
// The addition of release frames above maybe result in unordered frames, but we need them ordered
|
||||
.OrderBy(f => f.Time)
|
||||
.ToList();
|
||||
|
||||
return Replay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the maximum <see cref="ReplayFrame.MouseX"/> by count of bits from a grouping of <see cref="ReplayFrame"/>s.
|
||||
/// </summary>
|
||||
/// <param name="group">The <see cref="ReplayFrame"/> grouping to search.</param>
|
||||
/// <returns>The maximum <see cref="ReplayFrame.MouseX"/> by count of bits.</returns>
|
||||
private float maxMouseX(IGrouping<double, ReplayFrame> group)
|
||||
private IEnumerable<IActionPoint> generateActionPoints()
|
||||
{
|
||||
int currentCount = -1;
|
||||
int currentMax = 0;
|
||||
|
||||
foreach (var val in group)
|
||||
foreach (var obj in Beatmap.HitObjects)
|
||||
{
|
||||
int newCount = countBits((int)(val.MouseX ?? 0));
|
||||
if (newCount > currentCount)
|
||||
{
|
||||
currentCount = newCount;
|
||||
currentMax = (int)(val.MouseX ?? 0);
|
||||
}
|
||||
yield return new HitPoint { Time = obj.StartTime, Column = obj.Column };
|
||||
yield return new ReleasePoint { Time = ((obj as IHasEndTime)?.EndTime ?? obj.StartTime) + RELEASE_DELAY, Column = obj.Column };
|
||||
}
|
||||
|
||||
return currentMax;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Counts the number of bits set in a value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to count.</param>
|
||||
/// <returns>The number of set bits.</returns>
|
||||
private int countBits(int value)
|
||||
private interface IActionPoint
|
||||
{
|
||||
int count = 0;
|
||||
while (value > 0)
|
||||
{
|
||||
if ((value & 1) > 0)
|
||||
count++;
|
||||
value >>= 1;
|
||||
}
|
||||
double Time { get; set; }
|
||||
int Column { get; set; }
|
||||
}
|
||||
|
||||
return count;
|
||||
private struct HitPoint : IActionPoint
|
||||
{
|
||||
public double Time { get; set; }
|
||||
public int Column { get; set; }
|
||||
}
|
||||
|
||||
private struct ReleasePoint : IActionPoint
|
||||
{
|
||||
public double Time { get; set; }
|
||||
public int Column { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,29 +2,37 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Input;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Replays
|
||||
{
|
||||
internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler
|
||||
{
|
||||
public ManiaFramedReplayInputHandler(Replay replay)
|
||||
private readonly ManiaRulesetContainer container;
|
||||
|
||||
public ManiaFramedReplayInputHandler(Replay replay, ManiaRulesetContainer container)
|
||||
: base(replay)
|
||||
{
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
private ManiaPlayfield playfield;
|
||||
public override List<InputState> GetPendingStates()
|
||||
{
|
||||
var actions = new List<ManiaAction>();
|
||||
|
||||
int activeColumns = (int)(CurrentFrame.MouseX ?? 0);
|
||||
if (playfield == null)
|
||||
playfield = (ManiaPlayfield)container.Playfield;
|
||||
|
||||
int activeColumns = (int)(CurrentFrame.MouseX ?? 0);
|
||||
int counter = 0;
|
||||
while (activeColumns > 0)
|
||||
{
|
||||
if ((activeColumns & 1) > 0)
|
||||
actions.Add(ManiaAction.Key1 + counter);
|
||||
actions.Add(playfield.Columns.ElementAt(counter).Action);
|
||||
counter++;
|
||||
activeColumns >>= 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Replays
|
||||
{
|
||||
public class ManiaReplayFrame : ReplayFrame
|
||||
{
|
||||
public override bool IsImportant => MouseX > 0;
|
||||
|
||||
public ManiaReplayFrame(double time, int activeColumns)
|
||||
: base(time, activeColumns, null, ReplayButtonState.None)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseAutoGeneration : OsuTestCase
|
||||
{
|
||||
[Test]
|
||||
public void TestSingleNote()
|
||||
{
|
||||
// | |
|
||||
// | - |
|
||||
// | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSingleHoldNote()
|
||||
{
|
||||
// | |
|
||||
// | * |
|
||||
// | * |
|
||||
// | * |
|
||||
// | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSingleNoteChord()
|
||||
{
|
||||
// | | |
|
||||
// | - | - |
|
||||
// | | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 });
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||
Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHoldNoteChord()
|
||||
{
|
||||
// | | |
|
||||
// | * | * |
|
||||
// | * | * |
|
||||
// | * | * |
|
||||
// | | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 });
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames");
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time");
|
||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time");
|
||||
Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSingleNoteStair()
|
||||
{
|
||||
// | | |
|
||||
// | | - |
|
||||
// | - | |
|
||||
// | | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 1000 });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 });
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
||||
Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time");
|
||||
Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time");
|
||||
Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
|
||||
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 1 has not been released");
|
||||
Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 2 has not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHoldNoteStair()
|
||||
{
|
||||
// | | |
|
||||
// | | * |
|
||||
// | * | * |
|
||||
// | * | * |
|
||||
// | * | |
|
||||
// | | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 });
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 });
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames");
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time");
|
||||
Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time");
|
||||
Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time");
|
||||
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
|
||||
Assert.AreEqual(3, generated.Frames[2].MouseX, "Keys 1 and 2 have not been pressed");
|
||||
Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 1 has not been released");
|
||||
Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHoldNoteWithReleasePress()
|
||||
{
|
||||
// | | |
|
||||
// | * | - |
|
||||
// | * | |
|
||||
// | * | |
|
||||
// | | |
|
||||
|
||||
var beatmap = new Beatmap<ManiaHitObject>();
|
||||
beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY });
|
||||
beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 });
|
||||
|
||||
var generated = new ManiaAutoGenerator(beatmap).Generate();
|
||||
|
||||
Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames");
|
||||
Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time");
|
||||
Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time");
|
||||
Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time");
|
||||
Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed");
|
||||
Assert.AreEqual(2, generated.Frames[2].MouseX, "Key 1 has not been released or key 2 has not been pressed");
|
||||
Assert.AreEqual(0, generated.Frames[3].MouseX, "Keys 1 and 2 have not been released");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
internal class TestCaseManiaHitObjects : OsuTestCase
|
||||
public class TestCaseManiaHitObjects : OsuTestCase
|
||||
{
|
||||
public TestCaseManiaHitObjects()
|
||||
{
|
||||
|
||||
@@ -21,13 +21,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
internal class TestCaseManiaPlayfield : OsuTestCase
|
||||
public class TestCaseManiaPlayfield : OsuTestCase
|
||||
{
|
||||
private const double start_time = 500;
|
||||
private const double duration = 500;
|
||||
|
||||
public override string Description => @"Mania playfield";
|
||||
|
||||
protected override double TimePerAction => 200;
|
||||
|
||||
private RulesetInfo maniaRuleset;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
: base(new ManiaRuleset(new RulesetInfo()))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,8 +57,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500).Expire();
|
||||
this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500);
|
||||
inner.FadeOut(250);
|
||||
|
||||
Expire(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic);
|
||||
|
||||
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
|
||||
protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="..\osu.Game.props" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
@@ -21,7 +21,6 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<LangVersion>6</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
@@ -72,6 +71,7 @@
|
||||
<Compile Include="Objects\Types\IHasColumn.cs" />
|
||||
<Compile Include="Replays\ManiaAutoGenerator.cs" />
|
||||
<Compile Include="Replays\ManiaFramedReplayInputHandler.cs" />
|
||||
<Compile Include="Replays\ManiaReplayFrame.cs" />
|
||||
<Compile Include="Scoring\ManiaScoreProcessor.cs" />
|
||||
<Compile Include="Objects\BarLine.cs" />
|
||||
<Compile Include="Objects\HoldNote.cs" />
|
||||
@@ -80,8 +80,10 @@
|
||||
<Compile Include="Objects\Note.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ManiaInputManager.cs" />
|
||||
<Compile Include="Tests\TestCaseAutoGeneration.cs" />
|
||||
<Compile Include="Tests\TestCaseManiaHitObjects.cs" />
|
||||
<Compile Include="Tests\TestCaseManiaPlayfield.cs" />
|
||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||
<Compile Include="Timing\GravityScrollingContainer.cs" />
|
||||
<Compile Include="Timing\ScrollingAlgorithm.cs" />
|
||||
<Compile Include="UI\Column.cs" />
|
||||
@@ -96,9 +98,6 @@
|
||||
<Compile Include="Timing\ManiaSpeedAdjustmentContainer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\osu.licenseheader">
|
||||
<Link>osu.licenseheader</Link>
|
||||
</None>
|
||||
<None Include="app.config" />
|
||||
<None Include="OpenTK.dll.config" />
|
||||
<None Include="packages.config" />
|
||||
@@ -115,6 +114,9 @@
|
||||
<Private>True</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public class OsuEditPlayfield : OsuPlayfield
|
||||
{
|
||||
protected override CursorContainer CreateCursor() => null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2007-2017 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.Osu.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public class OsuEditRulesetContainer : OsuRulesetContainer
|
||||
{
|
||||
public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
|
||||
: base(ruleset, beatmap, isForCurrentRuleset)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Playfield CreatePlayfield() => new OsuEditPlayfield();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Tools;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public class OsuHitObjectComposer : HitObjectComposer
|
||||
{
|
||||
public OsuHitObjectComposer(Ruleset ruleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
}
|
||||
|
||||
protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new OsuEditRulesetContainer(ruleset, beatmap, true);
|
||||
|
||||
protected override IReadOnlyList<ICompositionTool> CompositionTools => new ICompositionTool[]
|
||||
{
|
||||
new HitObjectCompositionTool<HitCircle>(),
|
||||
new HitObjectCompositionTool<Slider>(),
|
||||
new HitObjectCompositionTool<Spinner>()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ using osu.Game.Rulesets.Osu.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@@ -33,22 +32,29 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
}
|
||||
|
||||
public class OsuModHardRock : ModHardRock, IApplicableMod<OsuHitObject>
|
||||
public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject>
|
||||
{
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
public override bool Ranked => true;
|
||||
|
||||
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)
|
||||
public void ApplyToHitObject(OsuHitObject hitObject)
|
||||
{
|
||||
hitObject.Position = new Vector2(hitObject.Position.X, OsuPlayfield.BASE_SIZE.Y - hitObject.Y);
|
||||
|
||||
var slider = hitObject as Slider;
|
||||
if (slider == null)
|
||||
return;
|
||||
|
||||
var newControlPoints = new List<Vector2>();
|
||||
slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
|
||||
|
||||
slider.ControlPoints = newControlPoints;
|
||||
slider.Curve?.Calculate(); // Recalculate the slider curve
|
||||
}
|
||||
|
||||
public void ApplyToHitObjects(RulesetContainer<OsuHitObject> rulesetContainer)
|
||||
{
|
||||
rulesetContainer.Objects.OfType<OsuHitObject>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Y));
|
||||
rulesetContainer.Objects.OfType<Slider>().ForEach(s =>
|
||||
{
|
||||
var newControlPoints = new List<Vector2>();
|
||||
s.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
|
||||
|
||||
s.ControlPoints = newControlPoints;
|
||||
s.Curve?.Calculate(); // Recalculate the slider curve
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
{
|
||||
private const float width = 8;
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
public FollowPoint()
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
@@ -52,9 +52,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
|
||||
private void update()
|
||||
{
|
||||
Clear();
|
||||
|
||||
if (hitObjects == null)
|
||||
return;
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
},
|
||||
ApproachCircle = new ApproachCircle
|
||||
{
|
||||
Alpha = 0,
|
||||
Scale = new Vector2(4),
|
||||
Colour = AccentColour,
|
||||
}
|
||||
};
|
||||
@@ -82,21 +84,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
});
|
||||
}
|
||||
|
||||
protected override void UpdateInitialState()
|
||||
{
|
||||
base.UpdateInitialState();
|
||||
|
||||
// sane defaults
|
||||
ring.Show();
|
||||
circle.Show();
|
||||
number.Show();
|
||||
glow.Show();
|
||||
|
||||
ApproachCircle.Hide();
|
||||
ApproachCircle.ScaleTo(new Vector2(4));
|
||||
explode.Hide();
|
||||
}
|
||||
|
||||
protected override void UpdatePreemptState()
|
||||
{
|
||||
base.UpdatePreemptState();
|
||||
|
||||
@@ -23,12 +23,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
protected sealed override void UpdateState(ArmedState state)
|
||||
{
|
||||
FinishTransforms();
|
||||
double transformTime = HitObject.StartTime - TIME_PREEMPT;
|
||||
|
||||
using (BeginAbsoluteSequence(HitObject.StartTime - TIME_PREEMPT, true))
|
||||
base.ApplyTransformsAt(transformTime, true);
|
||||
base.ClearTransformsAfter(transformTime, true);
|
||||
|
||||
using (BeginAbsoluteSequence(transformTime, true))
|
||||
{
|
||||
UpdateInitialState();
|
||||
|
||||
UpdatePreemptState();
|
||||
|
||||
using (BeginDelayedSequence(TIME_PREEMPT + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true))
|
||||
@@ -36,11 +37,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void UpdateInitialState()
|
||||
{
|
||||
Hide();
|
||||
}
|
||||
|
||||
protected virtual void UpdatePreemptState()
|
||||
{
|
||||
this.FadeIn(TIME_FADEIN);
|
||||
@@ -50,6 +46,11 @@ 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) { }
|
||||
|
||||
private OsuInputManager osuActionInputManager;
|
||||
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
|
||||
}
|
||||
|
||||
@@ -18,9 +18,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
public double FadeInTime;
|
||||
public double FadeOutTime;
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) : base(repeatPoint)
|
||||
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
|
||||
: base(repeatPoint)
|
||||
{
|
||||
this.repeatPoint = repeatPoint;
|
||||
this.drawableSlider = drawableSlider;
|
||||
@@ -28,6 +27,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Blending = BlendingMode.Additive;
|
||||
Origin = Anchor.Centre;
|
||||
Scale = new Vector2(0.5f);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@@ -51,12 +51,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
var animIn = Math.Min(150, repeatPoint.StartTime - FadeInTime);
|
||||
|
||||
this.Animate(
|
||||
d => d.FadeIn(animIn),
|
||||
d => d.ScaleTo(0.5f).ScaleTo(1.2f, animIn)
|
||||
).Then(
|
||||
d => d.ScaleTo(1, 150, Easing.Out)
|
||||
);
|
||||
this.FadeIn(animIn).ScaleTo(1.2f, animIn)
|
||||
.Then()
|
||||
.ScaleTo(1, 150, Easing.Out);
|
||||
}
|
||||
|
||||
protected override void UpdateCurrentState(ArmedState state)
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@@ -43,7 +44,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
ball = new SliderBall(s)
|
||||
{
|
||||
Scale = new Vector2(s.Scale),
|
||||
AccentColour = AccentColour
|
||||
AccentColour = AccentColour,
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0
|
||||
},
|
||||
initialCircle = new DrawableHitCircle(new HitCircle
|
||||
{
|
||||
@@ -148,16 +151,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateInitialState()
|
||||
{
|
||||
base.UpdateInitialState();
|
||||
body.Alpha = 1;
|
||||
|
||||
//we need to be present to handle input events. note that we still don't get enough events (we don't get a position if the mouse hasn't moved since the slider appeared).
|
||||
ball.AlwaysPresent = true;
|
||||
ball.Alpha = 0;
|
||||
}
|
||||
|
||||
protected override void UpdateCurrentState(ArmedState state)
|
||||
{
|
||||
ball.FadeIn();
|
||||
@@ -173,6 +166,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
|
||||
public Drawable ProxiedLayer => initialCircle.ApproachCircle;
|
||||
|
||||
public override Vector2 SelectionPoint => ToScreenSpace(body.Position);
|
||||
public override Quad SelectionQuad => body.PathDrawQuad;
|
||||
}
|
||||
|
||||
internal interface ISliderProgress
|
||||
|
||||
@@ -20,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
public bool Tracking;
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
|
||||
public override bool DisplayJudgement => false;
|
||||
|
||||
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
|
||||
|
||||
@@ -101,14 +101,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
// 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 ClearTransforms(bool propagateChildren = false, string targetMember = null)
|
||||
{
|
||||
// Consider the case of rewinding - children's transforms are handled internally, so propagating down
|
||||
// any further will cause weirdness with the Tracking bool below. Let's not propagate further at this point.
|
||||
base.ClearTransforms(false, targetMember);
|
||||
}
|
||||
|
||||
private bool tracking;
|
||||
public bool Tracking
|
||||
{
|
||||
get { return tracking; }
|
||||
private set
|
||||
{
|
||||
if (value == tracking) return;
|
||||
|
||||
if (value == tracking)
|
||||
return;
|
||||
tracking = value;
|
||||
|
||||
follow.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
|
||||
@@ -123,8 +130,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
base.Update();
|
||||
|
||||
// Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
|
||||
if (Time.Current < slider.EndTime)
|
||||
Tracking = canCurrentlyTrack && lastState != null && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
|
||||
Tracking = canCurrentlyTrack
|
||||
&& lastState != null
|
||||
&& base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
|
||||
&& ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
|
||||
}
|
||||
|
||||
public void UpdateProgress(double progress, int repeat)
|
||||
|
||||
@@ -14,6 +14,7 @@ using osu.Game.Configuration;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics.ES30;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
@@ -49,6 +50,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
}
|
||||
}
|
||||
|
||||
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
|
||||
|
||||
private int textureWidth => (int)PathWidth * 2;
|
||||
|
||||
private readonly Slider slider;
|
||||
@@ -182,4 +185,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
SetRange(start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@@ -61,6 +62,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
public void SetRotation(float currentRotation)
|
||||
{
|
||||
// If we've gone back in time, it's fine to work with a fresh set of records for now
|
||||
if (records.Count > 0 && Time.Current < records.Last().Time)
|
||||
records.Clear();
|
||||
|
||||
if (records.Count > 0)
|
||||
{
|
||||
var record = records.Peek();
|
||||
|
||||
@@ -45,6 +45,18 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
set { Curve.Distance = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The position of the cursor at the point of completion of this <see cref="Slider"/> if it was hit
|
||||
/// with as few movements as possible. This is set and used by difficulty calculation.
|
||||
/// </summary>
|
||||
internal Vector2? LazyEndPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The distance travelled by the cursor upon completion of this <see cref="Slider"/> if it was hit
|
||||
/// with as few movements as possible. This is set and used by difficulty calculation.
|
||||
/// </summary>
|
||||
internal float LazyTravelDistance;
|
||||
|
||||
public List<SampleInfoList> RepeatSamples { get; set; } = new List<SampleInfoList>();
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
|
||||
@@ -16,19 +17,25 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
|
||||
private const int section_length = 400;
|
||||
private const double difficulty_multiplier = 0.0675;
|
||||
|
||||
public OsuDifficultyCalculator(Beatmap beatmap) : base(beatmap)
|
||||
public OsuDifficultyCalculator(Beatmap beatmap)
|
||||
: base(beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
public OsuDifficultyCalculator(Beatmap beatmap, Mod[] mods)
|
||||
: base(beatmap, mods)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void PreprocessHitObjects()
|
||||
{
|
||||
foreach (OsuHitObject h in Objects)
|
||||
foreach (OsuHitObject h in Beatmap.HitObjects)
|
||||
(h as Slider)?.Curve?.Calculate();
|
||||
}
|
||||
|
||||
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
|
||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||
{
|
||||
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Objects);
|
||||
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects, TimeRate);
|
||||
Skill[] skills =
|
||||
{
|
||||
new Aim(),
|
||||
@@ -60,13 +67,13 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
|
||||
|
||||
if (categoryDifficulty != null)
|
||||
{
|
||||
categoryDifficulty.Add("Aim", aimRating.ToString("0.00"));
|
||||
categoryDifficulty.Add("Speed", speedRating.ToString("0.00"));
|
||||
categoryDifficulty.Add("Aim", aimRating);
|
||||
categoryDifficulty.Add("Speed", speedRating);
|
||||
}
|
||||
|
||||
return starRating;
|
||||
}
|
||||
|
||||
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
|
||||
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter(Beatmap beatmap) => new OsuBeatmapConverter();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
/// Creates an enumerator, which preprocesses a list of <see cref="OsuHitObject"/>s recieved as input, wrapping them as
|
||||
/// <see cref="OsuDifficultyHitObject"/> which contains extra data required for difficulty calculation.
|
||||
/// </summary>
|
||||
public OsuDifficultyBeatmap(List<OsuHitObject> objects)
|
||||
public OsuDifficultyBeatmap(List<OsuHitObject> objects, double timeRate)
|
||||
{
|
||||
// Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
|
||||
// This should probably happen before the objects reach the difficulty calculator.
|
||||
objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime));
|
||||
difficultyObjects = createDifficultyObjectEnumerator(objects);
|
||||
difficultyObjects = createDifficultyObjectEnumerator(objects, timeRate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
private IEnumerator<OsuDifficultyHitObject> createDifficultyObjectEnumerator(List<OsuHitObject> objects)
|
||||
private IEnumerator<OsuDifficultyHitObject> createDifficultyObjectEnumerator(List<OsuHitObject> objects, double timeRate)
|
||||
{
|
||||
// We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object.
|
||||
OsuHitObject[] triangle = new OsuHitObject[3];
|
||||
@@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
triangle[1] = triangle[0];
|
||||
triangle[0] = objects[i];
|
||||
|
||||
yield return new OsuDifficultyHitObject(triangle);
|
||||
yield return new OsuDifficultyHitObject(triangle, timeRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
@@ -33,13 +35,17 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
|
||||
private const int normalized_radius = 52;
|
||||
|
||||
private readonly double timeRate;
|
||||
|
||||
private readonly OsuHitObject[] t;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the object calculating extra data required for difficulty calculation.
|
||||
/// </summary>
|
||||
public OsuDifficultyHitObject(OsuHitObject[] triangle)
|
||||
public OsuDifficultyHitObject(OsuHitObject[] triangle, double timeRate)
|
||||
{
|
||||
this.timeRate = timeRate;
|
||||
|
||||
t = triangle;
|
||||
BaseObject = t[0];
|
||||
setDistances();
|
||||
@@ -57,14 +63,54 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
scalingFactor *= 1 + smallCircleBonus;
|
||||
}
|
||||
|
||||
Distance = (t[0].StackedPosition - t[1].StackedPosition).Length * scalingFactor;
|
||||
Vector2 lastCursorPosition = t[1].StackedPosition;
|
||||
float lastTravelDistance = 0;
|
||||
|
||||
var lastSlider = t[1] as Slider;
|
||||
if (lastSlider != null)
|
||||
{
|
||||
computeSliderCursorPosition(lastSlider);
|
||||
lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition;
|
||||
lastTravelDistance = lastSlider.LazyTravelDistance;
|
||||
}
|
||||
|
||||
Distance = (lastTravelDistance + (BaseObject.StackedPosition - lastCursorPosition).Length) * scalingFactor;
|
||||
}
|
||||
|
||||
private void setTimingValues()
|
||||
{
|
||||
// Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure.
|
||||
DeltaTime = Math.Max(40, t[0].StartTime - t[1].StartTime);
|
||||
DeltaTime = Math.Max(40, (t[0].StartTime - t[1].StartTime) / timeRate);
|
||||
TimeUntilHit = 450; // BaseObject.PreEmpt;
|
||||
}
|
||||
|
||||
private void computeSliderCursorPosition(Slider slider)
|
||||
{
|
||||
if (slider.LazyEndPosition != null)
|
||||
return;
|
||||
slider.LazyEndPosition = slider.StackedPosition;
|
||||
|
||||
float approxFollowCircleRadius = (float)(slider.Radius * 3);
|
||||
var computeVertex = new Action<double>(t =>
|
||||
{
|
||||
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
|
||||
var diff = slider.PositionAt(t) - slider.LazyEndPosition.Value;
|
||||
float dist = diff.Length;
|
||||
|
||||
if (dist > approxFollowCircleRadius)
|
||||
{
|
||||
// The cursor would be outside the follow circle, we need to move it
|
||||
diff.Normalize(); // Obtain direction of diff
|
||||
dist -= approxFollowCircleRadius;
|
||||
slider.LazyEndPosition += diff * dist;
|
||||
slider.LazyTravelDistance += dist;
|
||||
}
|
||||
});
|
||||
|
||||
var scoringTimes = slider.Ticks.Select(t => t.StartTime).Concat(slider.RepeatPoints.Select(r => r.StartTime)).OrderBy(t => t);
|
||||
foreach (var time in scoringTimes)
|
||||
computeVertex(time);
|
||||
computeVertex(slider.EndTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@@ -9,6 +10,8 @@ namespace osu.Game.Rulesets.Osu
|
||||
{
|
||||
public class OsuInputManager : RulesetInputManager<OsuAction>
|
||||
{
|
||||
public IEnumerable<OsuAction> PressedActions => KeyBindingContainer.PressedActions;
|
||||
|
||||
public OsuInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.OsuDifficulty;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@@ -14,6 +13,12 @@ using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Osu.Scoring;
|
||||
using osu.Game.Rulesets.Osu.Edit;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu
|
||||
{
|
||||
@@ -29,21 +34,35 @@ namespace osu.Game.Rulesets.Osu
|
||||
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
|
||||
};
|
||||
|
||||
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
|
||||
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap)
|
||||
{
|
||||
IEnumerable<HitObject> hitObjects = beatmap.Beatmap.HitObjects;
|
||||
IEnumerable<HitObject> circles = hitObjects.Where(c => !(c is IHasEndTime));
|
||||
IEnumerable<HitObject> sliders = hitObjects.Where(s => s is IHasCurve);
|
||||
IEnumerable<HitObject> spinners = hitObjects.Where(s => s is IHasEndTime && !(s is IHasCurve));
|
||||
|
||||
return new[]
|
||||
{
|
||||
new BeatmapStatistic
|
||||
{
|
||||
Name = @"Circle count",
|
||||
Content = beatmap.Beatmap.HitObjects.Count(h => h is HitCircle).ToString(),
|
||||
Icon = FontAwesome.fa_dot_circle_o
|
||||
},
|
||||
new BeatmapStatistic
|
||||
{
|
||||
Name = @"Slider count",
|
||||
Content = beatmap.Beatmap.HitObjects.Count(h => h is Slider).ToString(),
|
||||
Icon = FontAwesome.fa_circle_o
|
||||
}
|
||||
};
|
||||
new BeatmapStatistic
|
||||
{
|
||||
Name = @"Circle Count",
|
||||
Content = circles.Count().ToString(),
|
||||
Icon = FontAwesome.fa_circle_o
|
||||
},
|
||||
new BeatmapStatistic
|
||||
{
|
||||
Name = @"Slider Count",
|
||||
Content = sliders.Count().ToString(),
|
||||
Icon = FontAwesome.fa_circle
|
||||
},
|
||||
new BeatmapStatistic
|
||||
{
|
||||
Name = @"Spinner Count",
|
||||
Content = spinners.Count().ToString(),
|
||||
Icon = FontAwesome.fa_circle
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override IEnumerable<Mod> GetModsFor(ModType type)
|
||||
{
|
||||
@@ -112,10 +131,16 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o };
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap);
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods);
|
||||
|
||||
public override PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score);
|
||||
|
||||
public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this);
|
||||
|
||||
public override string Description => "osu!";
|
||||
|
||||
public override string ShortName => "osu";
|
||||
|
||||
public override SettingsSubsection CreateSettings() => new OsuSettings();
|
||||
|
||||
public override int LegacyID => 0;
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Scoring
|
||||
{
|
||||
public class OsuPerformanceCalculator : PerformanceCalculator<OsuHitObject>
|
||||
{
|
||||
private readonly int countHitCircles;
|
||||
private readonly int beatmapMaxCombo;
|
||||
|
||||
private Mod[] mods;
|
||||
private double realApproachRate;
|
||||
private double accuracy;
|
||||
private int scoreMaxCombo;
|
||||
private int count300;
|
||||
private int count100;
|
||||
private int count50;
|
||||
private int countMiss;
|
||||
|
||||
public OsuPerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score)
|
||||
: base(ruleset, beatmap, score)
|
||||
{
|
||||
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
|
||||
|
||||
beatmapMaxCombo = Beatmap.HitObjects.Count;
|
||||
beatmapMaxCombo += Beatmap.HitObjects.OfType<Slider>().Sum(s => s.RepeatCount + s.Ticks.Count());
|
||||
}
|
||||
|
||||
public override double Calculate(Dictionary<string, double> categoryRatings = null)
|
||||
{
|
||||
mods = Score.Mods;
|
||||
accuracy = Score.Accuracy;
|
||||
scoreMaxCombo = Score.MaxCombo;
|
||||
count300 = Convert.ToInt32(Score.Statistics["300"]);
|
||||
count100 = Convert.ToInt32(Score.Statistics["100"]);
|
||||
count50 = Convert.ToInt32(Score.Statistics["50"]);
|
||||
countMiss = Convert.ToInt32(Score.Statistics["x"]);
|
||||
|
||||
// Don't count scores made with supposedly unranked mods
|
||||
if (mods.Any(m => !m.Ranked))
|
||||
return 0;
|
||||
|
||||
// Todo: In the future we should apply changes to PreEmpt/AR at an OsuHitObject/BaseDifficulty level, but this is done
|
||||
// locally for now as doing so would modify animations and other things unexpectedly
|
||||
// DO NOT MODIFY THIS
|
||||
double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate;
|
||||
if (mods.Any(m => m is OsuModHardRock))
|
||||
ar = Math.Min(10, ar * 1.4);
|
||||
if (mods.Any(m => m is OsuModEasy))
|
||||
ar = Math.Max(0, ar / 2);
|
||||
double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450);
|
||||
realApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5;
|
||||
|
||||
// Custom multipliers for NoFail and SpunOut.
|
||||
double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
|
||||
|
||||
if (mods.Any(m => m is OsuModNoFail))
|
||||
multiplier *= 0.90f;
|
||||
|
||||
if (mods.Any(m => m is OsuModSpunOut))
|
||||
multiplier *= 0.95f;
|
||||
|
||||
double aimValue = computeAimValue();
|
||||
double speedValue = computeSpeedValue();
|
||||
double accuracyValue = computeAccuracyValue();
|
||||
double totalValue =
|
||||
Math.Pow(
|
||||
Math.Pow(aimValue, 1.1f) +
|
||||
Math.Pow(speedValue, 1.1f) +
|
||||
Math.Pow(accuracyValue, 1.1f), 1.0f / 1.1f
|
||||
) * multiplier;
|
||||
|
||||
if (categoryRatings != null)
|
||||
{
|
||||
categoryRatings.Add("Aim", aimValue);
|
||||
categoryRatings.Add("Speed", speedValue);
|
||||
categoryRatings.Add("Accuracy", accuracyValue);
|
||||
}
|
||||
|
||||
return totalValue;
|
||||
}
|
||||
|
||||
private double computeAimValue()
|
||||
{
|
||||
double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Aim"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
|
||||
|
||||
// Longer maps are worth more
|
||||
double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
|
||||
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
|
||||
|
||||
aimValue *= lengthBonus;
|
||||
|
||||
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
||||
aimValue *= Math.Pow(0.97f, countMiss);
|
||||
|
||||
// Combo scaling
|
||||
if (beatmapMaxCombo > 0)
|
||||
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
|
||||
|
||||
double approachRateFactor = 1.0f;
|
||||
if (realApproachRate > 10.33f)
|
||||
approachRateFactor += 0.45f * (realApproachRate - 10.33f);
|
||||
else if (realApproachRate < 8.0f)
|
||||
{
|
||||
// HD is worth more with lower ar!
|
||||
if (mods.Any(h => h is OsuModHidden))
|
||||
approachRateFactor += 0.02f * (8.0f - realApproachRate);
|
||||
else
|
||||
approachRateFactor += 0.01f * (8.0f - realApproachRate);
|
||||
}
|
||||
|
||||
aimValue *= approachRateFactor;
|
||||
|
||||
if (mods.Any(h => h is OsuModHidden))
|
||||
aimValue *= 1.18f;
|
||||
|
||||
if (mods.Any(h => h is OsuModFlashlight))
|
||||
{
|
||||
// Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps.
|
||||
aimValue *= 1.45f * lengthBonus;
|
||||
}
|
||||
|
||||
// Scale the aim value with accuracy _slightly_
|
||||
aimValue *= 0.5f + accuracy / 2.0f;
|
||||
// It is important to also consider accuracy difficulty when doing that
|
||||
aimValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500;
|
||||
|
||||
return aimValue;
|
||||
}
|
||||
|
||||
private double computeSpeedValue()
|
||||
{
|
||||
double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Speed"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f;
|
||||
|
||||
// Longer maps are worth more
|
||||
speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) +
|
||||
(totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f);
|
||||
|
||||
// Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available
|
||||
speedValue *= Math.Pow(0.97f, countMiss);
|
||||
|
||||
// Combo scaling
|
||||
if (beatmapMaxCombo > 0)
|
||||
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
|
||||
|
||||
// Scale the speed value with accuracy _slightly_
|
||||
speedValue *= 0.5f + accuracy / 2.0f;
|
||||
// It is important to also consider accuracy difficulty when doing that
|
||||
speedValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500;
|
||||
|
||||
return speedValue;
|
||||
}
|
||||
|
||||
private double computeAccuracyValue()
|
||||
{
|
||||
// This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window
|
||||
double betterAccuracyPercentage;
|
||||
int amountHitObjectsWithAccuracy = countHitCircles;
|
||||
|
||||
if (amountHitObjectsWithAccuracy > 0)
|
||||
betterAccuracyPercentage = ((count300 - (totalHits - amountHitObjectsWithAccuracy)) * 6 + count100 * 2 + count50) / (amountHitObjectsWithAccuracy * 6);
|
||||
else
|
||||
betterAccuracyPercentage = 0;
|
||||
|
||||
// It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points
|
||||
if (betterAccuracyPercentage < 0)
|
||||
betterAccuracyPercentage = 0;
|
||||
|
||||
// Lots of arbitrary values from testing.
|
||||
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
||||
double accuracyValue = Math.Pow(1.52163f, Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f;
|
||||
|
||||
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
||||
accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
|
||||
|
||||
if (mods.Any(m => m is OsuModHidden))
|
||||
accuracyValue *= 1.02f;
|
||||
if (mods.Any(m => m is OsuModFlashlight))
|
||||
accuracyValue *= 1.02f;
|
||||
|
||||
return accuracyValue;
|
||||
}
|
||||
|
||||
private double totalHits => count300 + count100 + count50 + countMiss;
|
||||
private double totalSuccessfulHits => count300 + count100 + count50;
|
||||
|
||||
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
internal class TestCaseHitObjects : OsuTestCase
|
||||
public class TestCaseHitObjects : OsuTestCase
|
||||
{
|
||||
private FramedClock framedClock;
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
h.Depth = depth++;
|
||||
|
||||
if (auto)
|
||||
h.State = ArmedState.Hit;
|
||||
h.State.Value = ArmedState.Hit;
|
||||
|
||||
playfieldContainer.Add(h);
|
||||
var proxyable = h as IDrawableHitObjectWithProxiedApproach;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
: base(new OsuRuleset(new RulesetInfo()))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
{
|
||||
internal class CursorTrail : Drawable
|
||||
{
|
||||
public override bool HandleInput => true;
|
||||
|
||||
private int currentIndex;
|
||||
|
||||
private Shader shader;
|
||||
|
||||
@@ -13,6 +13,7 @@ using System.Linq;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
@@ -30,6 +31,9 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Parent == null)
|
||||
return Vector2.Zero;
|
||||
|
||||
var parentSize = Parent.DrawSize;
|
||||
var aspectSize = parentSize.X * 0.75f < parentSize.Y ? new Vector2(parentSize.X, parentSize.X * 0.75f) : new Vector2(parentSize.Y * 4f / 3f, parentSize.Y);
|
||||
|
||||
@@ -65,7 +69,10 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
AddInternal(new GameplayCursor());
|
||||
|
||||
var cursor = CreateCursor();
|
||||
if (cursor != null)
|
||||
AddInternal(cursor);
|
||||
}
|
||||
|
||||
public override void Add(DrawableHitObject h)
|
||||
@@ -102,5 +109,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
|
||||
judgementLayer.Add(explosion);
|
||||
}
|
||||
|
||||
protected virtual CursorContainer CreateCursor() => new GameplayCursor();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="..\osu.Game.props" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
@@ -22,7 +22,6 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<LangVersion>6</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
@@ -49,6 +48,9 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Beatmaps\OsuBeatmapConverter.cs" />
|
||||
<Compile Include="Beatmaps\OsuBeatmapProcessor.cs" />
|
||||
<Compile Include="Edit\OsuEditPlayfield.cs" />
|
||||
<Compile Include="Edit\OsuEditRulesetContainer.cs" />
|
||||
<Compile Include="Edit\OsuHitObjectComposer.cs" />
|
||||
<Compile Include="Objects\Drawables\DrawableOsuHitObject.cs" />
|
||||
<Compile Include="Objects\Drawables\Connections\ConnectionRenderer.cs" />
|
||||
<Compile Include="Objects\Drawables\Connections\FollowPointRenderer.cs" />
|
||||
@@ -85,9 +87,11 @@
|
||||
<Compile Include="OsuInputManager.cs" />
|
||||
<Compile Include="Replays\OsuReplayInputHandler.cs" />
|
||||
<Compile Include="Tests\TestCaseHitObjects.cs" />
|
||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||
<Compile Include="UI\Cursor\CursorTrail.cs" />
|
||||
<Compile Include="UI\Cursor\GameplayCursor.cs" />
|
||||
<Compile Include="UI\OsuSettings.cs" />
|
||||
<Compile Include="Scoring\OsuPerformanceCalculator.cs" />
|
||||
<Compile Include="Scoring\OsuScoreProcessor.cs" />
|
||||
<Compile Include="UI\OsuRulesetContainer.cs" />
|
||||
<Compile Include="UI\OsuPlayfield.cs" />
|
||||
@@ -103,9 +107,6 @@
|
||||
<Compile Include="Replays\OsuAutoGeneratorBase.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\osu.licenseheader">
|
||||
<Link>osu.licenseheader</Link>
|
||||
</None>
|
||||
<None Include="app.config" />
|
||||
<None Include="OpenTK.dll.config" />
|
||||
<None Include="packages.config" />
|
||||
@@ -122,7 +123,9 @@
|
||||
<Private>True</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Judgements
|
||||
{
|
||||
public class TaikoStrongHitJudgement : TaikoJudgement
|
||||
@@ -11,9 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements
|
||||
|
||||
public TaikoStrongHitJudgement()
|
||||
{
|
||||
base.Result = HitResult.Perfect;
|
||||
Final = true;
|
||||
}
|
||||
|
||||
public new HitResult Result => base.Result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
/// </summary>
|
||||
protected abstract TaikoAction[] HitActions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether a second hit is allowed to be processed. This occurs once this hit object has been hit successfully.
|
||||
/// </summary>
|
||||
protected bool SecondHitAllowed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the last key pressed is a valid hit key.
|
||||
/// </summary>
|
||||
@@ -45,7 +50,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
if (!validKeyPressed)
|
||||
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
|
||||
else if (hitOffset < HitObject.HitWindowGood)
|
||||
AddJudgement(new TaikoJudgement { Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good });
|
||||
{
|
||||
AddJudgement(new TaikoJudgement
|
||||
{
|
||||
Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good,
|
||||
Final = !HitObject.IsStrong
|
||||
});
|
||||
|
||||
SecondHitAllowed = true;
|
||||
}
|
||||
else
|
||||
AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
|
||||
}
|
||||
@@ -72,7 +85,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime;
|
||||
using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true))
|
||||
{
|
||||
switch (State)
|
||||
switch (State.Value)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
this.Delay(HitObject.HitWindowMiss).Expire();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
@@ -24,27 +25,25 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
}
|
||||
|
||||
private bool processedSecondHit;
|
||||
public override bool AllJudged => processedSecondHit && base.AllJudged;
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (!base.AllJudged)
|
||||
if (!SecondHitAllowed)
|
||||
{
|
||||
base.CheckForJudgements(userTriggered, timeOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset > second_hit_window)
|
||||
AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Miss });
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get here, we're assured that the key pressed is the correct secondary key
|
||||
|
||||
if (Math.Abs(firstHitTime - Time.Current) < second_hit_window)
|
||||
{
|
||||
AddJudgement(new TaikoStrongHitJudgement());
|
||||
processedSecondHit = true;
|
||||
}
|
||||
AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Great });
|
||||
}
|
||||
|
||||
public override bool OnReleased(TaikoAction action)
|
||||
@@ -56,8 +55,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
|
||||
public override bool OnPressed(TaikoAction action)
|
||||
{
|
||||
if (AllJudged)
|
||||
return false;
|
||||
|
||||
// Check if we've handled the first key
|
||||
if (!base.AllJudged)
|
||||
if (!SecondHitAllowed)
|
||||
{
|
||||
// First key hasn't been handled yet, attempt to handle it
|
||||
bool handled = base.OnPressed(action);
|
||||
@@ -72,10 +74,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
return handled;
|
||||
}
|
||||
|
||||
// If we've already hit the second key, don't handle this object any further
|
||||
if (processedSecondHit)
|
||||
return false;
|
||||
|
||||
// Don't handle represses of the first key
|
||||
if (firstHitAction == action)
|
||||
return false;
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
||||
/// <summary>
|
||||
/// Taiko fails at the end of the map if the player has not half-filled their HP bar.
|
||||
/// </summary>
|
||||
protected override bool FailCondition => Hits == MaxHits && Health.Value <= 0.5;
|
||||
protected override bool DefaultFailCondition => Hits == MaxHits && Health.Value <= 0.5;
|
||||
|
||||
private double hpIncreaseTick;
|
||||
private double hpIncreaseGreat;
|
||||
|
||||
@@ -5,7 +5,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Taiko.Beatmaps;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko
|
||||
@@ -36,12 +35,12 @@ namespace osu.Game.Rulesets.Taiko
|
||||
{
|
||||
}
|
||||
|
||||
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
|
||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||
{
|
||||
// Fill our custom DifficultyHitObject class, that carries additional information
|
||||
difficultyHitObjects.Clear();
|
||||
|
||||
foreach (var hitObject in Objects)
|
||||
foreach (var hitObject in Beatmap.HitObjects)
|
||||
difficultyHitObjects.Add(new TaikoHitObjectDifficulty(hitObject));
|
||||
|
||||
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
||||
@@ -53,8 +52,8 @@ namespace osu.Game.Rulesets.Taiko
|
||||
|
||||
if (categoryDifficulty != null)
|
||||
{
|
||||
categoryDifficulty.Add("Strain", starRating.ToString("0.00", CultureInfo.InvariantCulture));
|
||||
categoryDifficulty.Add("Hit window 300", (35 /*HitObjectManager.HitWindow300*/ / TimeRate).ToString("0.00", CultureInfo.InvariantCulture));
|
||||
categoryDifficulty.Add("Strain", starRating);
|
||||
categoryDifficulty.Add("Hit window 300", 35 /*HitObjectManager.HitWindow300*/ / TimeRate);
|
||||
}
|
||||
|
||||
return starRating;
|
||||
@@ -134,6 +133,6 @@ namespace osu.Game.Rulesets.Taiko
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(true);
|
||||
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter(Beatmap beatmap) => new TaikoBeatmapConverter(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,9 +95,11 @@ namespace osu.Game.Rulesets.Taiko
|
||||
|
||||
public override string Description => "osu!taiko";
|
||||
|
||||
public override string ShortName => "taiko";
|
||||
|
||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new TaikoDifficultyCalculator(beatmap);
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap);
|
||||
|
||||
public override int LegacyID => 1;
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints
|
||||
{
|
||||
public TestCasePerformancePoints()
|
||||
: base(new TaikoRuleset(new RulesetInfo()))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,11 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
internal class TestCaseTaikoPlayfield : OsuTestCase
|
||||
public class TestCaseTaikoPlayfield : OsuTestCase
|
||||
{
|
||||
private const double default_duration = 1000;
|
||||
private const float scroll_time = 1000;
|
||||
|
||||
public override string Description => "Taiko playfield";
|
||||
|
||||
protected override double TimePerAction => default_duration * 2;
|
||||
|
||||
private readonly Random rng = new Random(1337);
|
||||
|
||||
@@ -244,7 +244,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
if (judgedObject.X >= -0.05f && judgedObject is DrawableHit)
|
||||
{
|
||||
// If we're far enough away from the left stage, we should bring outselves in front of it
|
||||
topLevelHitContainer.Add(judgedObject.CreateProxy());
|
||||
// Todo: The following try-catch is temporary for replay rewinding support
|
||||
try
|
||||
{
|
||||
topLevelHitContainer.Add(judgedObject.CreateProxy());
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="..\osu.Game.props" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
@@ -21,7 +21,6 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<LangVersion>6</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
@@ -83,6 +82,7 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Scoring\TaikoScoreProcessor.cs" />
|
||||
<Compile Include="TaikoInputManager.cs" />
|
||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||
<Compile Include="Tests\TestCaseTaikoPlayfield.cs" />
|
||||
<Compile Include="UI\HitTarget.cs" />
|
||||
<Compile Include="UI\InputDrum.cs" />
|
||||
@@ -95,9 +95,6 @@
|
||||
<Compile Include="Mods\TaikoMod.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\osu.licenseheader">
|
||||
<Link>osu.licenseheader</Link>
|
||||
</None>
|
||||
<None Include="app.config" />
|
||||
<None Include="OpenTK.dll.config" />
|
||||
<None Include="packages.config" />
|
||||
@@ -114,6 +111,9 @@
|
||||
<Private>True</Private>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Tests.Resources;
|
||||
using System.Linq;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
[TestFixture]
|
||||
public class LegacyBeatmapDecoderTest
|
||||
{
|
||||
[Test]
|
||||
public void TestDecodeBeatmapGeneral()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var beatmap = decoder.DecodeBeatmap(stream);
|
||||
var beatmapInfo = beatmap.BeatmapInfo;
|
||||
var metadata = beatmap.Metadata;
|
||||
|
||||
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile);
|
||||
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
|
||||
Assert.AreEqual(164471, metadata.PreviewTime);
|
||||
Assert.IsFalse(beatmapInfo.Countdown);
|
||||
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
|
||||
Assert.IsTrue(beatmapInfo.RulesetID == 0);
|
||||
Assert.IsFalse(beatmapInfo.LetterboxInBreaks);
|
||||
Assert.IsFalse(beatmapInfo.SpecialStyle);
|
||||
Assert.IsFalse(beatmapInfo.WidescreenStoryboard);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeBeatmapEditor()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var beatmapInfo = decoder.DecodeBeatmap(stream).BeatmapInfo;
|
||||
|
||||
int[] expectedBookmarks =
|
||||
{
|
||||
11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351,
|
||||
95901, 106450, 116999, 119637, 130186, 140735, 151285,
|
||||
161834, 164471, 175020, 185570, 196119, 206669, 209306
|
||||
};
|
||||
Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length);
|
||||
for (int i = 0; i < expectedBookmarks.Length; i++)
|
||||
Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]);
|
||||
Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing);
|
||||
Assert.AreEqual(4, beatmapInfo.BeatDivisor);
|
||||
Assert.AreEqual(4, beatmapInfo.GridSize);
|
||||
Assert.AreEqual(2, beatmapInfo.TimelineZoom);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeBeatmapMetadata()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var beatmap = decoder.DecodeBeatmap(stream);
|
||||
var beatmapInfo = beatmap.BeatmapInfo;
|
||||
var metadata = beatmap.Metadata;
|
||||
|
||||
Assert.AreEqual("Renatus", metadata.Title);
|
||||
Assert.AreEqual("Renatus", metadata.TitleUnicode);
|
||||
Assert.AreEqual("Soleily", metadata.Artist);
|
||||
Assert.AreEqual("Soleily", metadata.ArtistUnicode);
|
||||
Assert.AreEqual("Gamu", metadata.AuthorString);
|
||||
Assert.AreEqual("Insane", beatmapInfo.Version);
|
||||
Assert.AreEqual(string.Empty, metadata.Source);
|
||||
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", metadata.Tags);
|
||||
Assert.AreEqual(557821, beatmapInfo.OnlineBeatmapID);
|
||||
Assert.AreEqual(241526, metadata.OnlineBeatmapSetID);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeBeatmapDifficulty()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var difficulty = decoder.DecodeBeatmap(stream).BeatmapInfo.BaseDifficulty;
|
||||
|
||||
Assert.AreEqual(6.5f, difficulty.DrainRate);
|
||||
Assert.AreEqual(4, difficulty.CircleSize);
|
||||
Assert.AreEqual(8, difficulty.OverallDifficulty);
|
||||
Assert.AreEqual(9, difficulty.ApproachRate);
|
||||
Assert.AreEqual(1.8f, difficulty.SliderMultiplier);
|
||||
Assert.AreEqual(2, difficulty.SliderTickRate);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeBeatmapEvents()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var beatmap = decoder.DecodeBeatmap(stream);
|
||||
var metadata = beatmap.Metadata;
|
||||
var breakPoint = beatmap.Breaks[0];
|
||||
|
||||
Assert.AreEqual("machinetop_background.jpg", metadata.BackgroundFile);
|
||||
Assert.AreEqual(122474, breakPoint.StartTime);
|
||||
Assert.AreEqual(140135, breakPoint.EndTime);
|
||||
Assert.IsTrue(breakPoint.HasEffect);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeBeatmapTimingPoints()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var beatmap = decoder.DecodeBeatmap(stream);
|
||||
var controlPoints = beatmap.ControlPointInfo;
|
||||
|
||||
Assert.AreEqual(4, controlPoints.TimingPoints.Count);
|
||||
var timingPoint = controlPoints.TimingPoints[0];
|
||||
Assert.AreEqual(956, timingPoint.Time);
|
||||
Assert.AreEqual(329.67032967033d, timingPoint.BeatLength);
|
||||
Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature);
|
||||
|
||||
Assert.AreEqual(5, controlPoints.DifficultyPoints.Count);
|
||||
var difficultyPoint = controlPoints.DifficultyPoints[0];
|
||||
Assert.AreEqual(116999, difficultyPoint.Time);
|
||||
Assert.AreEqual(0.75000000000000189d, difficultyPoint.SpeedMultiplier);
|
||||
|
||||
Assert.AreEqual(34, controlPoints.SoundPoints.Count);
|
||||
var soundPoint = controlPoints.SoundPoints[0];
|
||||
Assert.AreEqual(956, soundPoint.Time);
|
||||
Assert.AreEqual("soft", soundPoint.SampleBank);
|
||||
Assert.AreEqual(60, soundPoint.SampleVolume);
|
||||
|
||||
Assert.AreEqual(8, controlPoints.EffectPoints.Count);
|
||||
var effectPoint = controlPoints.EffectPoints[0];
|
||||
Assert.AreEqual(53703, effectPoint.Time);
|
||||
Assert.IsTrue(effectPoint.KiaiMode);
|
||||
Assert.IsFalse(effectPoint.OmitFirstBarLine);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeBeatmapColors()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var comboColors = decoder.DecodeBeatmap(stream).ComboColors;
|
||||
|
||||
Color4[] expectedColors =
|
||||
{
|
||||
new Color4(142, 199, 255, 255),
|
||||
new Color4(255, 128, 128, 255),
|
||||
new Color4(128, 255, 255, 255),
|
||||
new Color4(128, 255, 128, 255),
|
||||
new Color4(255, 187, 255, 255),
|
||||
new Color4(255, 177, 140, 255),
|
||||
};
|
||||
Assert.AreEqual(expectedColors.Length, comboColors.Count);
|
||||
for (int i = 0; i < expectedColors.Length; i++)
|
||||
Assert.AreEqual(expectedColors[i], comboColors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeBeatmapHitObjects()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var hitObjects = decoder.DecodeBeatmap(stream).HitObjects;
|
||||
|
||||
var curveData = hitObjects[0] as IHasCurve;
|
||||
var positionData = hitObjects[0] as IHasPosition;
|
||||
|
||||
Assert.IsNotNull(positionData);
|
||||
Assert.IsNotNull(curveData);
|
||||
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
|
||||
Assert.AreEqual(956, hitObjects[0].StartTime);
|
||||
Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
|
||||
|
||||
positionData = hitObjects[1] as IHasPosition;
|
||||
|
||||
Assert.IsNotNull(positionData);
|
||||
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
|
||||
Assert.AreEqual(1285, hitObjects[1].StartTime);
|
||||
Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2007-2017 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 NUnit.Framework;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Storyboards;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
[TestFixture]
|
||||
public class LegacyStoryboardDecoderTest
|
||||
{
|
||||
[Test]
|
||||
public void TestDecodeStoryboardEvents()
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
{
|
||||
var storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(stream);
|
||||
|
||||
Assert.IsTrue(storyboard.HasDrawable);
|
||||
Assert.AreEqual(4, storyboard.Layers.Count());
|
||||
|
||||
StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3);
|
||||
Assert.IsNotNull(background);
|
||||
Assert.AreEqual(16, background.Elements.Count());
|
||||
Assert.IsTrue(background.EnabledWhenFailing);
|
||||
Assert.IsTrue(background.EnabledWhenPassing);
|
||||
Assert.AreEqual("Background", background.Name);
|
||||
|
||||
StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2);
|
||||
Assert.IsNotNull(fail);
|
||||
Assert.AreEqual(0, fail.Elements.Count());
|
||||
Assert.IsTrue(fail.EnabledWhenFailing);
|
||||
Assert.IsFalse(fail.EnabledWhenPassing);
|
||||
Assert.AreEqual("Fail", fail.Name);
|
||||
|
||||
StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1);
|
||||
Assert.IsNotNull(pass);
|
||||
Assert.AreEqual(0, pass.Elements.Count());
|
||||
Assert.IsFalse(pass.EnabledWhenFailing);
|
||||
Assert.IsTrue(pass.EnabledWhenPassing);
|
||||
Assert.AreEqual("Pass", pass.Name);
|
||||
|
||||
StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0);
|
||||
Assert.IsNotNull(foreground);
|
||||
Assert.AreEqual(151, foreground.Elements.Count());
|
||||
Assert.IsTrue(foreground.EnabledWhenFailing);
|
||||
Assert.IsTrue(foreground.EnabledWhenPassing);
|
||||
Assert.AreEqual("Foreground", foreground.Name);
|
||||
|
||||
int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite));
|
||||
int animationCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardAnimation));
|
||||
int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSample));
|
||||
|
||||
Assert.AreEqual(15, spriteCount);
|
||||
Assert.AreEqual(1, animationCount);
|
||||
Assert.AreEqual(0, sampleCount);
|
||||
Assert.AreEqual(background.Elements.Count(), spriteCount + animationCount + sampleCount);
|
||||
|
||||
var sprite = background.Elements.ElementAt(0) as StoryboardSprite;
|
||||
Assert.NotNull(sprite);
|
||||
Assert.IsTrue(sprite.HasCommands);
|
||||
Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition);
|
||||
Assert.IsTrue(sprite.IsDrawable);
|
||||
Assert.AreEqual(Anchor.Centre, sprite.Origin);
|
||||
Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path);
|
||||
|
||||
var animation = background.Elements.ElementAt(12) as StoryboardAnimation;
|
||||
Assert.NotNull(animation);
|
||||
Assert.AreEqual(141175, animation.EndTime);
|
||||
Assert.AreEqual(10, animation.FrameCount);
|
||||
Assert.AreEqual(30, animation.FrameDelay);
|
||||
Assert.IsTrue(animation.HasCommands);
|
||||
Assert.AreEqual(new Vector2(320, 240), animation.InitialPosition);
|
||||
Assert.IsTrue(animation.IsDrawable);
|
||||
Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType);
|
||||
Assert.AreEqual(Anchor.Centre, animation.Origin);
|
||||
Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path);
|
||||
Assert.AreEqual(78993, animation.StartTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Tests.Resources;
|
||||
using System.Linq;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
[TestFixture]
|
||||
public class OsuLegacyDecoderTest
|
||||
{
|
||||
[Test]
|
||||
public void TestDecodeMetadata()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream));
|
||||
var meta = beatmap.BeatmapInfo.Metadata;
|
||||
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
|
||||
Assert.AreEqual("Soleily", meta.Artist);
|
||||
Assert.AreEqual("Soleily", meta.ArtistUnicode);
|
||||
Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile);
|
||||
Assert.AreEqual("Gamu", meta.AuthorString);
|
||||
Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile);
|
||||
Assert.AreEqual(164471, meta.PreviewTime);
|
||||
Assert.AreEqual(string.Empty, meta.Source);
|
||||
Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags);
|
||||
Assert.AreEqual("Renatus", meta.Title);
|
||||
Assert.AreEqual("Renatus", meta.TitleUnicode);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeGeneral()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmapInfo = decoder.Decode(new StreamReader(stream)).BeatmapInfo;
|
||||
Assert.AreEqual(0, beatmapInfo.AudioLeadIn);
|
||||
Assert.AreEqual(false, beatmapInfo.Countdown);
|
||||
Assert.AreEqual(0.7f, beatmapInfo.StackLeniency);
|
||||
Assert.AreEqual(false, beatmapInfo.SpecialStyle);
|
||||
Assert.IsTrue(beatmapInfo.RulesetID == 0);
|
||||
Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks);
|
||||
Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeEditor()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream)).BeatmapInfo;
|
||||
int[] expectedBookmarks =
|
||||
{
|
||||
11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351,
|
||||
95901, 106450, 116999, 119637, 130186, 140735, 151285,
|
||||
161834, 164471, 175020, 185570, 196119, 206669, 209306
|
||||
};
|
||||
Assert.AreEqual(expectedBookmarks.Length, beatmap.Bookmarks.Length);
|
||||
for (int i = 0; i < expectedBookmarks.Length; i++)
|
||||
Assert.AreEqual(expectedBookmarks[i], beatmap.Bookmarks[i]);
|
||||
Assert.AreEqual(1.8, beatmap.DistanceSpacing);
|
||||
Assert.AreEqual(4, beatmap.BeatDivisor);
|
||||
Assert.AreEqual(4, beatmap.GridSize);
|
||||
Assert.AreEqual(2, beatmap.TimelineZoom);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeDifficulty()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream));
|
||||
var difficulty = beatmap.BeatmapInfo.BaseDifficulty;
|
||||
Assert.AreEqual(6.5f, difficulty.DrainRate);
|
||||
Assert.AreEqual(4, difficulty.CircleSize);
|
||||
Assert.AreEqual(8, difficulty.OverallDifficulty);
|
||||
Assert.AreEqual(9, difficulty.ApproachRate);
|
||||
Assert.AreEqual(1.8f, difficulty.SliderMultiplier);
|
||||
Assert.AreEqual(2, difficulty.SliderTickRate);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeColors()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream));
|
||||
Color4[] expected =
|
||||
{
|
||||
new Color4(142, 199, 255, 255),
|
||||
new Color4(255, 128, 128, 255),
|
||||
new Color4(128, 255, 255, 255),
|
||||
new Color4(128, 255, 128, 255),
|
||||
new Color4(255, 187, 255, 255),
|
||||
new Color4(255, 177, 140, 255),
|
||||
};
|
||||
Assert.AreEqual(expected.Length, beatmap.ComboColors.Count);
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
Assert.AreEqual(expected[i], beatmap.ComboColors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeHitObjects()
|
||||
{
|
||||
var decoder = new OsuLegacyDecoder();
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream));
|
||||
|
||||
var curveData = beatmap.HitObjects[0] as IHasCurve;
|
||||
var positionData = beatmap.HitObjects[0] as IHasPosition;
|
||||
|
||||
Assert.IsNotNull(positionData);
|
||||
Assert.IsNotNull(curveData);
|
||||
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
|
||||
Assert.AreEqual(956, beatmap.HitObjects[0].StartTime);
|
||||
Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
|
||||
|
||||
positionData = beatmap.HitObjects[1] as IHasPosition;
|
||||
|
||||
Assert.IsNotNull(positionData);
|
||||
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
|
||||
Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime);
|
||||
Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
|
||||
BeatmapMetadata meta;
|
||||
using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu")))
|
||||
meta = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata;
|
||||
meta = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata;
|
||||
|
||||
Assert.AreEqual(241526, meta.OnlineBeatmapSetID);
|
||||
Assert.AreEqual("Soleily", meta.Artist);
|
||||
|
||||
+1997
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,5 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseAllPlayers : TestCasePlayer
|
||||
{
|
||||
public override string Description => @"Showing everything to play the game.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,8 @@ using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseBeatSyncedContainer : OsuTestCase
|
||||
public class TestCaseBeatSyncedContainer : OsuTestCase
|
||||
{
|
||||
public override string Description => @"Tests beat synced containers.";
|
||||
|
||||
private readonly MusicController mc;
|
||||
|
||||
public TestCaseBeatSyncedContainer()
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Carousel;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseBeatmapCarousel : OsuTestCase
|
||||
{
|
||||
private TestBeatmapCarousel carousel;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(CarouselItem),
|
||||
typeof(CarouselGroup),
|
||||
typeof(CarouselGroupEagerSelect),
|
||||
typeof(CarouselBeatmap),
|
||||
typeof(CarouselBeatmapSet),
|
||||
|
||||
typeof(DrawableCarouselItem),
|
||||
typeof(CarouselItemState),
|
||||
|
||||
typeof(DrawableCarouselBeatmap),
|
||||
typeof(DrawableCarouselBeatmapSet),
|
||||
};
|
||||
|
||||
|
||||
private readonly Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>();
|
||||
|
||||
private BeatmapInfo currentSelection;
|
||||
|
||||
private const int set_count = 5;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Add(carousel = new TestBeatmapCarousel
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
});
|
||||
|
||||
List<BeatmapSetInfo> beatmapSets = new List<BeatmapSetInfo>();
|
||||
|
||||
for (int i = 1; i <= set_count; i++)
|
||||
beatmapSets.Add(createTestBeatmapSet(i));
|
||||
|
||||
carousel.SelectionChanged = s => currentSelection = s;
|
||||
|
||||
AddStep("Load Beatmaps", () => { carousel.BeatmapSets = beatmapSets; });
|
||||
|
||||
AddUntilStep(() => carousel.BeatmapSets.Any(), "Wait for load");
|
||||
|
||||
testTraversal();
|
||||
testFiltering();
|
||||
testRandom();
|
||||
testAddRemove();
|
||||
testSorting();
|
||||
|
||||
testRemoveAll();
|
||||
}
|
||||
|
||||
private void ensureRandomFetchSuccess() =>
|
||||
AddAssert("ensure prev random fetch worked", () => selectedSets.Peek() == carousel.SelectedBeatmapSet);
|
||||
|
||||
private void checkSelected(int set, int? diff = null) =>
|
||||
AddAssert($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () =>
|
||||
{
|
||||
if (diff != null)
|
||||
return carousel.SelectedBeatmap == carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First();
|
||||
|
||||
return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmap);
|
||||
});
|
||||
|
||||
private void setSelected(int set, int diff) =>
|
||||
AddStep($"select set{set} diff{diff}", () =>
|
||||
carousel.SelectBeatmap(carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff - 1).First()));
|
||||
|
||||
private void advanceSelection(bool diff, int direction = 1, int count = 1)
|
||||
{
|
||||
if (count == 1)
|
||||
AddStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () =>
|
||||
carousel.SelectNext(direction, !diff));
|
||||
else
|
||||
{
|
||||
AddRepeatStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () =>
|
||||
carousel.SelectNext(direction, !diff), count);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkVisibleItemCount(bool diff, int count) =>
|
||||
AddAssert($"{count} {(diff ? "diffs" : "sets")} visible", () =>
|
||||
carousel.Items.Count(s => (diff ? s.Item is CarouselBeatmap : s.Item is CarouselBeatmapSet) && s.Item.Visible) == count);
|
||||
|
||||
private void nextRandom() =>
|
||||
AddStep("select random next", () =>
|
||||
{
|
||||
carousel.RandomAlgorithm.Value = RandomSelectAlgorithm.RandomPermutation;
|
||||
|
||||
if (!selectedSets.Any() && carousel.SelectedBeatmap != null)
|
||||
selectedSets.Push(carousel.SelectedBeatmapSet);
|
||||
|
||||
carousel.SelectNextRandom();
|
||||
selectedSets.Push(carousel.SelectedBeatmapSet);
|
||||
});
|
||||
|
||||
private void ensureRandomDidntRepeat() =>
|
||||
AddAssert("ensure no repeats", () => selectedSets.Distinct().Count() == selectedSets.Count);
|
||||
|
||||
private void prevRandom() => AddStep("select random last", () =>
|
||||
{
|
||||
carousel.SelectPreviousRandom();
|
||||
selectedSets.Pop();
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Test keyboard traversal
|
||||
/// </summary>
|
||||
private void testTraversal()
|
||||
{
|
||||
advanceSelection(direction: 1, diff: false);
|
||||
checkSelected(1, 1);
|
||||
|
||||
advanceSelection(direction: 1, diff: true);
|
||||
checkSelected(1, 2);
|
||||
|
||||
advanceSelection(direction: -1, diff: false);
|
||||
checkSelected(set_count, 1);
|
||||
|
||||
advanceSelection(direction: -1, diff: true);
|
||||
checkSelected(set_count - 1, 3);
|
||||
|
||||
advanceSelection(diff: false);
|
||||
advanceSelection(diff: false);
|
||||
checkSelected(1, 2);
|
||||
|
||||
advanceSelection(direction: -1, diff: true);
|
||||
advanceSelection(direction: -1, diff: true);
|
||||
checkSelected(set_count, 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test filtering
|
||||
/// </summary>
|
||||
private void testFiltering()
|
||||
{
|
||||
// basic filtering
|
||||
|
||||
setSelected(1, 1);
|
||||
|
||||
AddStep("Filter", () => carousel.Filter(new FilterCriteria { SearchText = "set #3!" }, false));
|
||||
checkVisibleItemCount(diff: false, count: 1);
|
||||
checkVisibleItemCount(diff: true, count: 3);
|
||||
checkSelected(3, 1);
|
||||
|
||||
advanceSelection(diff: true, count: 4);
|
||||
checkSelected(3, 2);
|
||||
|
||||
AddStep("Un-filter (debounce)", () => carousel.Filter(new FilterCriteria()));
|
||||
AddUntilStep(() => !carousel.PendingFilterTask, "Wait for debounce");
|
||||
checkVisibleItemCount(diff: false, count: set_count);
|
||||
checkVisibleItemCount(diff: true, count: 3);
|
||||
|
||||
// test filtering some difficulties (and keeping current beatmap set selected).
|
||||
|
||||
setSelected(1, 2);
|
||||
AddStep("Filter some difficulties", () => carousel.Filter(new FilterCriteria { SearchText = "Normal" }, false));
|
||||
checkSelected(1, 1);
|
||||
|
||||
AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false));
|
||||
checkSelected(1, 1);
|
||||
|
||||
AddStep("Filter all", () => carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false));
|
||||
|
||||
checkVisibleItemCount(false, 0);
|
||||
checkVisibleItemCount(true, 0);
|
||||
AddAssert("Selection is null", () => currentSelection == null);
|
||||
|
||||
AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false));
|
||||
|
||||
AddAssert("Selection is non-null", () => currentSelection != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test random non-repeating algorithm
|
||||
/// </summary>
|
||||
private void testRandom()
|
||||
{
|
||||
setSelected(1, 1);
|
||||
|
||||
nextRandom();
|
||||
ensureRandomDidntRepeat();
|
||||
nextRandom();
|
||||
ensureRandomDidntRepeat();
|
||||
nextRandom();
|
||||
ensureRandomDidntRepeat();
|
||||
|
||||
prevRandom();
|
||||
ensureRandomFetchSuccess();
|
||||
prevRandom();
|
||||
ensureRandomFetchSuccess();
|
||||
|
||||
nextRandom();
|
||||
ensureRandomDidntRepeat();
|
||||
nextRandom();
|
||||
ensureRandomDidntRepeat();
|
||||
|
||||
nextRandom();
|
||||
AddAssert("ensure repeat", () => selectedSets.Contains(carousel.SelectedBeatmapSet));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test adding and removing beatmap sets
|
||||
/// </summary>
|
||||
private void testAddRemove()
|
||||
{
|
||||
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 1)));
|
||||
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 2)));
|
||||
|
||||
checkVisibleItemCount(false, set_count + 2);
|
||||
|
||||
AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 2)));
|
||||
|
||||
checkVisibleItemCount(false, set_count + 1);
|
||||
|
||||
setSelected(set_count + 1, 1);
|
||||
|
||||
AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 1)));
|
||||
|
||||
checkVisibleItemCount(false, set_count);
|
||||
|
||||
checkSelected(set_count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test sorting
|
||||
/// </summary>
|
||||
private void testSorting()
|
||||
{
|
||||
AddStep("Sort by author", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Author }, false));
|
||||
AddAssert("Check zzzzz is at bottom", () => carousel.BeatmapSets.Last().Metadata.AuthorString == "zzzzz");
|
||||
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
|
||||
AddAssert($"Check #{set_count} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Title.EndsWith($"#{set_count}!"));
|
||||
}
|
||||
|
||||
private void testRemoveAll()
|
||||
{
|
||||
setSelected(2, 1);
|
||||
AddAssert("Selection is non-null", () => currentSelection != null);
|
||||
|
||||
AddStep("Remove selected", () => carousel.RemoveBeatmapSet(carousel.SelectedBeatmapSet));
|
||||
checkSelected(2);
|
||||
|
||||
AddStep("Remove first", () => carousel.RemoveBeatmapSet(carousel.BeatmapSets.First()));
|
||||
AddStep("Remove first", () => carousel.RemoveBeatmapSet(carousel.BeatmapSets.First()));
|
||||
checkSelected(1);
|
||||
|
||||
AddUntilStep(() =>
|
||||
{
|
||||
if (!carousel.BeatmapSets.Any()) return true;
|
||||
|
||||
carousel.RemoveBeatmapSet(carousel.BeatmapSets.Last());
|
||||
return false;
|
||||
}, "Remove all");
|
||||
|
||||
AddAssert("Selection is null", () => currentSelection == null);
|
||||
}
|
||||
|
||||
|
||||
private BeatmapSetInfo createTestBeatmapSet(int i)
|
||||
{
|
||||
return new BeatmapSetInfo
|
||||
{
|
||||
ID = i,
|
||||
OnlineBeatmapSetID = i,
|
||||
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
OnlineBeatmapSetID = i,
|
||||
// Create random metadata, then we can check if sorting works based on these
|
||||
Artist = $"peppy{i.ToString().PadLeft(6, '0')}",
|
||||
Title = $"test set #{i}!",
|
||||
AuthorString = string.Concat(Enumerable.Repeat((char)('z' - Math.Min(25, i - 1)), 5))
|
||||
},
|
||||
Beatmaps = new List<BeatmapInfo>(new[]
|
||||
{
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineBeatmapID = i * 10,
|
||||
Path = "normal.osu",
|
||||
Version = "Normal",
|
||||
StarDifficulty = 2,
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
OverallDifficulty = 3.5f,
|
||||
}
|
||||
},
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineBeatmapID = i * 10 + 1,
|
||||
Path = "hard.osu",
|
||||
Version = "Hard",
|
||||
StarDifficulty = 5,
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
OverallDifficulty = 5,
|
||||
}
|
||||
},
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineBeatmapID = i * 10 + 2,
|
||||
Path = "insane.osu",
|
||||
Version = "Insane",
|
||||
StarDifficulty = 6,
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
OverallDifficulty = 7,
|
||||
}
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
private class TestBeatmapCarousel : BeatmapCarousel
|
||||
{
|
||||
public new List<DrawableCarouselItem> Items => base.Items;
|
||||
|
||||
public bool PendingFilterTask => FilterTask != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,9 @@ using OpenTK;
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[TestFixture]
|
||||
internal class TestCaseBeatmapDetailArea : OsuTestCase
|
||||
[System.ComponentModel.Description("PlaySongSelect leaderboard/details area")]
|
||||
public class TestCaseBeatmapDetailArea : OsuTestCase
|
||||
{
|
||||
public override string Description => @"Beatmap details in song select";
|
||||
|
||||
public TestCaseBeatmapDetailArea()
|
||||
{
|
||||
Add(new BeatmapDetailArea
|
||||
@@ -23,4 +22,4 @@ namespace osu.Game.Tests.Visual
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
@@ -8,10 +9,9 @@ using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseBeatmapDetails : OsuTestCase
|
||||
[Description("PlaySongSelect beatmap details")]
|
||||
public class TestCaseBeatmapDetails : OsuTestCase
|
||||
{
|
||||
public override string Description => "BeatmapDetails tab of BeatmapDetailArea";
|
||||
|
||||
public TestCaseBeatmapDetails()
|
||||
{
|
||||
BeatmapDetails details;
|
||||
@@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 5.3f,
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
@@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual
|
||||
StarDifficulty = 4.8f,
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Select;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseBeatmapInfoWedge : OsuTestCase
|
||||
{
|
||||
private BeatmapManager beatmaps;
|
||||
private readonly Random random;
|
||||
private readonly BeatmapInfoWedge infoWedge;
|
||||
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
public TestCaseBeatmapInfoWedge()
|
||||
{
|
||||
random = new Random(0123);
|
||||
|
||||
Add(infoWedge = new BeatmapInfoWedge
|
||||
{
|
||||
Size = new Vector2(0.5f, 245),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Top = 20,
|
||||
},
|
||||
});
|
||||
|
||||
AddStep("show", () =>
|
||||
{
|
||||
Content.FadeInFromZero(250);
|
||||
infoWedge.State = Visibility.Visible;
|
||||
infoWedge.UpdateBeatmap(beatmap);
|
||||
});
|
||||
AddStep("hide", () =>
|
||||
{
|
||||
infoWedge.State = Visibility.Hidden;
|
||||
Content.FadeOut(100);
|
||||
});
|
||||
AddStep("random beatmap", randomBeatmap);
|
||||
AddStep("null beatmap", () => infoWedge.UpdateBeatmap(beatmap.Default));
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game, BeatmapManager beatmaps)
|
||||
{
|
||||
this.beatmaps = beatmaps;
|
||||
beatmap.BindTo(game.Beatmap);
|
||||
}
|
||||
|
||||
private void randomBeatmap()
|
||||
{
|
||||
var sets = beatmaps.GetAllUsableBeatmapSets();
|
||||
if (sets.Count == 0)
|
||||
return;
|
||||
|
||||
var b = sets[random.Next(0, sets.Count)].Beatmaps[0];
|
||||
beatmap.Value = beatmaps.GetWorkingBeatmap(b);
|
||||
infoWedge.UpdateBeatmap(beatmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.ComponentModel;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Screens.Select.Options;
|
||||
using OpenTK.Graphics;
|
||||
@@ -8,10 +9,9 @@ using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseBeatmapOptionsOverlay : OsuTestCase
|
||||
[Description("bottom beatmap details")]
|
||||
public class TestCaseBeatmapOptionsOverlay : OsuTestCase
|
||||
{
|
||||
public override string Description => @"Beatmap options in song select";
|
||||
|
||||
public TestCaseBeatmapOptionsOverlay()
|
||||
{
|
||||
var overlay = new BeatmapOptionsOverlay();
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
// Copyright (c) 2007-2017 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.Framework.Graphics.Shapes;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.BeatmapSet.Scores;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Users;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[System.ComponentModel.Description("in BeatmapOverlay")]
|
||||
public class TestCaseBeatmapScoresContainer : OsuTestCase
|
||||
{
|
||||
private readonly IEnumerable<OnlineScore> scores;
|
||||
private readonly IEnumerable<OnlineScore> anotherScores;
|
||||
private readonly OnlineScore topScore;
|
||||
private readonly Box background;
|
||||
|
||||
public TestCaseBeatmapScoresContainer()
|
||||
{
|
||||
Container container;
|
||||
ScoresContainer scoresContainer;
|
||||
|
||||
Child = container = new Container
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.8f,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new Box { RelativeSizeAxes = Axes.Both },
|
||||
scoresContainer = new ScoresContainer(),
|
||||
}
|
||||
};
|
||||
|
||||
AddStep("scores pack 1", () => scoresContainer.Scores = scores);
|
||||
AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores);
|
||||
AddStep("only top score", () => scoresContainer.Scores = new[] { topScore });
|
||||
AddStep("remove scores", scoresContainer.CleanAllScores);
|
||||
AddStep("turn on loading", () => scoresContainer.IsLoading = true);
|
||||
AddStep("turn off loading", () => scoresContainer.IsLoading = false);
|
||||
AddStep("resize to big", () => container.ResizeWidthTo(1, 300));
|
||||
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
|
||||
|
||||
scores = new[]
|
||||
{
|
||||
new OnlineScore
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 6602580,
|
||||
Username = @"waaiiru",
|
||||
Country = new Country
|
||||
{
|
||||
FullName = @"Spain",
|
||||
FlagName = @"ES",
|
||||
},
|
||||
},
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
new OsuModHidden(),
|
||||
new OsuModFlashlight(),
|
||||
new OsuModHardRock(),
|
||||
},
|
||||
Rank = ScoreRank.XH,
|
||||
TotalScore = 1234567890,
|
||||
Accuracy = 1,
|
||||
},
|
||||
new OnlineScore
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 4608074,
|
||||
Username = @"Skycries",
|
||||
Country = new Country
|
||||
{
|
||||
FullName = @"Brazil",
|
||||
FlagName = @"BR",
|
||||
},
|
||||
},
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
new OsuModHidden(),
|
||||
new OsuModFlashlight(),
|
||||
},
|
||||
Rank = ScoreRank.S,
|
||||
TotalScore = 1234789,
|
||||
Accuracy = 0.9997,
|
||||
},
|
||||
new OnlineScore
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 1014222,
|
||||
Username = @"eLy",
|
||||
Country = new Country
|
||||
{
|
||||
FullName = @"Japan",
|
||||
FlagName = @"JP",
|
||||
},
|
||||
},
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
new OsuModHidden(),
|
||||
},
|
||||
Rank = ScoreRank.B,
|
||||
TotalScore = 12345678,
|
||||
Accuracy = 0.9854,
|
||||
},
|
||||
new OnlineScore
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 1541390,
|
||||
Username = @"Toukai",
|
||||
Country = new Country
|
||||
{
|
||||
FullName = @"Canada",
|
||||
FlagName = @"CA",
|
||||
},
|
||||
},
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
},
|
||||
Rank = ScoreRank.C,
|
||||
TotalScore = 1234567,
|
||||
Accuracy = 0.8765,
|
||||
},
|
||||
new OnlineScore
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 7151382,
|
||||
Username = @"Mayuri Hana",
|
||||
Country = new Country
|
||||
{
|
||||
FullName = @"Thailand",
|
||||
FlagName = @"TH",
|
||||
},
|
||||
},
|
||||
Rank = ScoreRank.F,
|
||||
TotalScore = 123456,
|
||||
Accuracy = 0.6543,
|
||||
},
|
||||
};
|
||||
foreach(var s in scores)
|
||||
{
|
||||
s.Statistics.Add("300", RNG.Next(2000));
|
||||
s.Statistics.Add("100", RNG.Next(2000));
|
||||
s.Statistics.Add("50", RNG.Next(2000));
|
||||
}
|
||||
|
||||
anotherScores = new[]
|
||||
{
|
||||
new OnlineScore
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 4608074,
|
||||
Username = @"Skycries",
|
||||
Country = new Country
|
||||
{
|
||||
FullName = @"Brazil",
|
||||
FlagName = @"BR",
|
||||
},
|
||||
},
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
new OsuModHidden(),
|
||||
new OsuModFlashlight(),
|
||||
},
|
||||
Rank = ScoreRank.S,
|
||||
TotalScore = 1234789,
|
||||
Accuracy = 0.9997,
|
||||
},
|
||||
new OnlineScore
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 6602580,
|
||||
Username = @"waaiiru",
|
||||
Country = new Country
|
||||
{
|
||||
FullName = @"Spain",
|
||||
FlagName = @"ES",
|
||||
},
|
||||
},
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
new OsuModHidden(),
|
||||
new OsuModFlashlight(),
|
||||
new OsuModHardRock(),
|
||||
},
|
||||
Rank = ScoreRank.XH,
|
||||
TotalScore = 1234567890,
|
||||
Accuracy = 1,
|
||||
},
|
||||
new OnlineScore
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 7151382,
|
||||
Username = @"Mayuri Hana",
|
||||
Country = new Country
|
||||
{
|
||||
FullName = @"Thailand",
|
||||
FlagName = @"TH",
|
||||
},
|
||||
},
|
||||
Rank = ScoreRank.F,
|
||||
TotalScore = 123456,
|
||||
Accuracy = 0.6543,
|
||||
},
|
||||
new OnlineScore
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 1014222,
|
||||
Username = @"eLy",
|
||||
Country = new Country
|
||||
{
|
||||
FullName = @"Japan",
|
||||
FlagName = @"JP",
|
||||
},
|
||||
},
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
new OsuModHidden(),
|
||||
},
|
||||
Rank = ScoreRank.B,
|
||||
TotalScore = 12345678,
|
||||
Accuracy = 0.9854,
|
||||
},
|
||||
new OnlineScore
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 1541390,
|
||||
Username = @"Toukai",
|
||||
Country = new Country
|
||||
{
|
||||
FullName = @"Canada",
|
||||
FlagName = @"CA",
|
||||
},
|
||||
},
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
},
|
||||
Rank = ScoreRank.C,
|
||||
TotalScore = 1234567,
|
||||
Accuracy = 0.8765,
|
||||
},
|
||||
};
|
||||
foreach (var s in anotherScores)
|
||||
{
|
||||
s.Statistics.Add("300", RNG.Next(2000));
|
||||
s.Statistics.Add("100", RNG.Next(2000));
|
||||
s.Statistics.Add("50", RNG.Next(2000));
|
||||
}
|
||||
|
||||
topScore = new OnlineScore
|
||||
{
|
||||
User = new User
|
||||
{
|
||||
Id = 2705430,
|
||||
Username = @"Mooha",
|
||||
Country = new Country
|
||||
{
|
||||
FullName = @"France",
|
||||
FlagName = @"FR",
|
||||
},
|
||||
},
|
||||
Mods = new Mod[]
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
new OsuModFlashlight(),
|
||||
new OsuModHardRock(),
|
||||
},
|
||||
Rank = ScoreRank.B,
|
||||
TotalScore = 987654321,
|
||||
Accuracy = 0.8487,
|
||||
};
|
||||
topScore.Statistics.Add("300", RNG.Next(2000));
|
||||
topScore.Statistics.Add("100", RNG.Next(2000));
|
||||
topScore.Statistics.Add("50", RNG.Next(2000));
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
background.Colour = colours.Gray2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,8 @@ using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseBeatmapSetOverlay : OsuTestCase
|
||||
public class TestCaseBeatmapSetOverlay : OsuTestCase
|
||||
{
|
||||
public override string Description => @"view online beatmap sets";
|
||||
|
||||
private readonly BeatmapSetOverlay overlay;
|
||||
|
||||
public TestCaseBeatmapSetOverlay()
|
||||
@@ -53,6 +51,7 @@ namespace osu.Game.Tests.Visual
|
||||
Submitted = new DateTime(2016, 2, 10),
|
||||
Ranked = new DateTime(2016, 6, 19),
|
||||
BPM = 236,
|
||||
HasVideo = true,
|
||||
Covers = new BeatmapSetOnlineCovers
|
||||
{
|
||||
Cover = @"https://assets.ppy.sh/beatmaps/415886/covers/cover.jpg?1465651778",
|
||||
@@ -75,7 +74,6 @@ namespace osu.Game.Tests.Visual
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 115000,
|
||||
HasVideo = false,
|
||||
CircleCount = 265,
|
||||
SliderCount = 71,
|
||||
PlayCount = 47906,
|
||||
@@ -83,7 +81,7 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
@@ -103,7 +101,6 @@ namespace osu.Game.Tests.Visual
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 118000,
|
||||
HasVideo = true,
|
||||
CircleCount = 592,
|
||||
SliderCount = 62,
|
||||
PlayCount = 162021,
|
||||
@@ -111,7 +108,7 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
@@ -131,7 +128,6 @@ namespace osu.Game.Tests.Visual
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 118000,
|
||||
HasVideo = false,
|
||||
CircleCount = 1042,
|
||||
SliderCount = 79,
|
||||
PlayCount = 225178,
|
||||
@@ -139,7 +135,7 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
@@ -159,7 +155,6 @@ namespace osu.Game.Tests.Visual
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 118000,
|
||||
HasVideo = false,
|
||||
CircleCount = 1352,
|
||||
SliderCount = 69,
|
||||
PlayCount = 131545,
|
||||
@@ -167,7 +162,7 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
@@ -187,7 +182,6 @@ namespace osu.Game.Tests.Visual
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 118000,
|
||||
HasVideo = false,
|
||||
CircleCount = 1730,
|
||||
SliderCount = 115,
|
||||
PlayCount = 117673,
|
||||
@@ -195,7 +189,7 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
@@ -227,6 +221,7 @@ namespace osu.Game.Tests.Visual
|
||||
Submitted = new DateTime(2016, 6, 11),
|
||||
Ranked = new DateTime(2016, 7, 12),
|
||||
BPM = 160,
|
||||
HasVideo = false,
|
||||
Covers = new BeatmapSetOnlineCovers
|
||||
{
|
||||
Cover = @"https://assets.ppy.sh/beatmaps/625493/covers/cover.jpg?1499167472",
|
||||
@@ -249,7 +244,6 @@ namespace osu.Game.Tests.Visual
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 193000,
|
||||
HasVideo = false,
|
||||
CircleCount = 262,
|
||||
SliderCount = 0,
|
||||
PlayCount = 3952,
|
||||
@@ -257,7 +251,7 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
@@ -277,7 +271,6 @@ namespace osu.Game.Tests.Visual
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 193000,
|
||||
HasVideo = false,
|
||||
CircleCount = 464,
|
||||
SliderCount = 0,
|
||||
PlayCount = 4833,
|
||||
@@ -285,7 +278,7 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
@@ -305,7 +298,6 @@ namespace osu.Game.Tests.Visual
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 193000,
|
||||
HasVideo = false,
|
||||
CircleCount = 712,
|
||||
SliderCount = 0,
|
||||
PlayCount = 4405,
|
||||
@@ -313,7 +305,7 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
@@ -333,7 +325,6 @@ namespace osu.Game.Tests.Visual
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 193000,
|
||||
HasVideo = false,
|
||||
CircleCount = 943,
|
||||
SliderCount = 0,
|
||||
PlayCount = 3950,
|
||||
@@ -341,7 +332,7 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
@@ -361,7 +352,6 @@ namespace osu.Game.Tests.Visual
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 193000,
|
||||
HasVideo = false,
|
||||
CircleCount = 1068,
|
||||
SliderCount = 0,
|
||||
PlayCount = 5856,
|
||||
@@ -369,7 +359,7 @@ namespace osu.Game.Tests.Visual
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 10),
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user