mirror of
https://github.com/ppy/osu.git
synced 2025-02-09 19:02:58 +08:00
Merge pull request #1741 from smoogipoo/update-branch
Update netstandard with the latest master changes
This commit is contained in:
commit
4e4ffe3b98
77
.vscode/tasks.json
vendored
77
.vscode/tasks.json
vendored
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
36
COMPILING.md
Normal file
36
COMPILING.md
Normal file
@ -0,0 +1,36 @@
|
||||
# Linux
|
||||
### 1. Requirements:
|
||||
Mono >= 5.4.0 (>= 5.8.0 recommended)
|
||||
Please check [here](http://www.mono-project.com/download/) for stable or [here](http://www.mono-project.com/download/alpha/) for an alpha release.
|
||||
NuGet >= 4.4.0
|
||||
msbuild
|
||||
git
|
||||
|
||||
### 2. Cloning project
|
||||
Clone the entire repository with submodules using
|
||||
```
|
||||
git clone https://github.com/ppy/osu --recursive
|
||||
```
|
||||
Then restore NuGet packages from the repository
|
||||
```
|
||||
nuget restore
|
||||
```
|
||||
### 3. Compiling
|
||||
Simply run `msbuild` where `osu.sln` is located, this will create all binaries in `osu/osu.Desktop/bin/Debug`.
|
||||
### 4. Optimizing
|
||||
If you want additional performance you can change build type to Release with
|
||||
```
|
||||
msbuild -p:Configuration=Release
|
||||
```
|
||||
Additionally, mono provides an AOT utility which attempts to precompile binaries. You can utilize that by running
|
||||
```
|
||||
mono --aot ./osu\!.exe
|
||||
```
|
||||
### 5. Troubleshooting
|
||||
You may run into trouble with NuGet versioning, as the one in packaging system is almost always out of date. Simply run
|
||||
```
|
||||
nuget
|
||||
sudo nuget update -self
|
||||
```
|
||||
**Warning** NuGet creates few config files when it's run for the first time.
|
||||
Do not run NuGet as root on the first run or you might run into very peculiar issues.
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="opentk-develop" value="https://www.myget.org/F/opentk-develop" />
|
||||
</packageSources>
|
||||
</configuration>
|
@ -8,8 +8,11 @@ This is still heavily under development and is not intended for end-user use. Th
|
||||
|
||||
# Requirements
|
||||
|
||||
- A desktop platform which can compile .NET 4.5 (tested on macOS, linux and windows). We recommend using [Visual Studio Code](https://code.visualstudio.com/) (all platforms) or [Visual Studio Community Edition](https://www.visualstudio.com/) (windows only), both of which are free.
|
||||
- Make sure you initialise and keep submodules up-to-date.
|
||||
- A desktop platform that can compile .NET 4.6.1. We recommend using [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) (macOS) or [MonoDevelop](http://www.monodevelop.com/download/) (Linux), all of which are free. [Visual Studio Code](https://code.visualstudio.com/) may also be used but requires further setup steps which are not covered here.
|
||||
|
||||
# Getting Started
|
||||
- Clone the repository including submodules (`git clone --recurse-submodules https://github.com/ppy/osu`)
|
||||
- Build in your IDE of choice (recommended IDEs automatically restore nuget packages; if you are using an alternative make sure to `nuget restore`)
|
||||
|
||||
# Contributing
|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 19e81610469455c4531e0a58574d58bca9ae4cc7
|
||||
Subproject commit 15fd489d2b4dc1d71a6b98b3001c84f1e60c7c10
|
@ -1 +1 @@
|
||||
Subproject commit c79d917605fa792a5f046fd70d7aa75e58dc9fbc
|
||||
Subproject commit 4cc3e92d5576c706060e3d1589bd2a81cf20bfdb
|
@ -118,7 +118,7 @@ namespace osu.Desktop.Overlays
|
||||
|
||||
// only show a notification if we've previously saved a version to the config file (ie. not the first run).
|
||||
if (!string.IsNullOrEmpty(lastVersion))
|
||||
Scheduler.AddDelayed(() => notificationOverlay.Post(new UpdateCompleteNotification(version)), 5000);
|
||||
notificationOverlay.Post(new UpdateCompleteNotification(version));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,14 @@
|
||||
// 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
|
||||
{
|
||||
@ -18,6 +24,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
|
||||
CatchHitObject lastObj = null;
|
||||
|
||||
initialiseHyperDash(beatmap.HitObjects);
|
||||
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
if (obj.NewCombo)
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,6 +93,8 @@ 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, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
|
||||
|
@ -27,9 +27,19 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
public float Scale { get; set; } = 1;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
/// <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;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
{
|
||||
|
@ -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)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
@ -25,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
RelativeChildSize = new Vector2(1, (float)HitObject.Duration)
|
||||
};
|
||||
|
||||
foreach (CatchHitObject tick in s.Ticks)
|
||||
foreach (CatchHitObject tick in s.NestedHitObjects.OfType<CatchHitObject>())
|
||||
{
|
||||
TinyDroplet tiny = tick as TinyDroplet;
|
||||
if (tiny != null)
|
||||
|
@ -11,7 +11,6 @@ using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
using osu.Framework.Lists;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
@ -29,9 +28,9 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
public double Velocity;
|
||||
public double TickDistance;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
|
||||
@ -42,92 +41,94 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
||||
}
|
||||
|
||||
public IEnumerable<CatchHitObject> Ticks
|
||||
protected override void CreateNestedHitObjects()
|
||||
{
|
||||
get
|
||||
base.CreateNestedHitObjects();
|
||||
|
||||
createTicks();
|
||||
}
|
||||
|
||||
private void createTicks()
|
||||
{
|
||||
if (TickDistance == 0)
|
||||
return;
|
||||
|
||||
var length = Curve.Distance;
|
||||
var tickDistance = Math.Min(TickDistance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
AddNested(new Fruit
|
||||
{
|
||||
SortedList<CatchHitObject> ticks = new SortedList<CatchHitObject>((a, b) => a.StartTime.CompareTo(b.StartTime));
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = StartTime,
|
||||
X = X
|
||||
});
|
||||
|
||||
if (TickDistance == 0)
|
||||
return ticks;
|
||||
for (var repeat = 0; repeat < RepeatCount; repeat++)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var reversed = repeat % 2 == 1;
|
||||
|
||||
var length = Curve.Distance;
|
||||
var tickDistance = Math.Min(TickDistance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
ticks.Add(new Fruit
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = StartTime,
|
||||
X = X
|
||||
});
|
||||
if (d > length - minDistanceFromEnd)
|
||||
break;
|
||||
|
||||
for (var repeat = 0; repeat < RepeatCount; repeat++)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var reversed = repeat % 2 == 1;
|
||||
var timeProgress = d / length;
|
||||
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
||||
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
var lastTickTime = repeatStartTime + timeProgress * repeatDuration;
|
||||
AddNested(new Droplet
|
||||
{
|
||||
if (d > length - minDistanceFromEnd)
|
||||
break;
|
||||
|
||||
var timeProgress = d / length;
|
||||
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
||||
|
||||
var lastTickTime = repeatStartTime + timeProgress * repeatDuration;
|
||||
ticks.Add(new Droplet
|
||||
{
|
||||
StartTime = lastTickTime,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
double tinyTickInterval = tickDistance / length * repeatDuration;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
|
||||
for (double t = 0; t < repeatDuration; t += tinyTickInterval)
|
||||
{
|
||||
double progress = reversed ? 1 - t / repeatDuration : t / repeatDuration;
|
||||
|
||||
ticks.Add(new TinyDroplet
|
||||
{
|
||||
StartTime = repeatStartTime + t,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
ticks.Add(new Fruit
|
||||
{
|
||||
Samples = Samples,
|
||||
StartTime = lastTickTime,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = repeatStartTime + repeatDuration,
|
||||
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
X = Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
return ticks;
|
||||
double tinyTickInterval = tickDistance / length * repeatDuration;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
|
||||
for (double t = 0; t < repeatDuration; t += tinyTickInterval)
|
||||
{
|
||||
double progress = reversed ? 1 - t / repeatDuration : t / repeatDuration;
|
||||
|
||||
AddNested(new TinyDroplet
|
||||
{
|
||||
StartTime = repeatStartTime + t,
|
||||
ComboColour = ComboColour,
|
||||
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
AddNested(new Fruit
|
||||
{
|
||||
Samples = Samples,
|
||||
ComboColour = ComboColour,
|
||||
StartTime = repeatStartTime + repeatDuration,
|
||||
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
|
||||
|
||||
public float EndX => Curve.PositionAt(ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH;
|
||||
@ -146,7 +147,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
set { Curve.ControlPoints = value; }
|
||||
}
|
||||
|
||||
public List<SampleInfoList> RepeatSamples { get; set; } = new List<SampleInfoList>();
|
||||
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
|
||||
|
||||
public CurveType CurveType
|
||||
{
|
||||
|
@ -1,10 +1,10 @@
|
||||
// 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.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Scoring
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
|
||||
foreach (var unused in stream.Ticks)
|
||||
foreach (var unused in stream.NestedHitObjects.OfType<CatchHitObject>())
|
||||
AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
|
||||
|
||||
continue;
|
||||
|
@ -14,9 +14,10 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
internal class TestCaseCatcherArea : OsuTestCase
|
||||
public class TestCaseCatcherArea : OsuTestCase
|
||||
{
|
||||
private RulesetInfo catchRuleset;
|
||||
private TestCatcherArea catcherArea;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
@ -26,6 +27,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
public TestCaseCatcherArea()
|
||||
{
|
||||
AddSliderStep<float>("CircleSize", 0, 8, 5, createCatcher);
|
||||
AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t));
|
||||
}
|
||||
|
||||
private void createCatcher(float size)
|
||||
@ -33,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
Child = new CatchInputManager(catchRuleset)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new CatcherArea(new BeatmapDifficulty { CircleSize = size })
|
||||
Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size })
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.BottomLeft
|
||||
@ -46,5 +48,15 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
catchRuleset = rulesets.GetRuleset(2);
|
||||
}
|
||||
|
||||
private class TestCatcherArea : CatcherArea
|
||||
{
|
||||
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
|
||||
: base(beatmapDifficulty)
|
||||
{
|
||||
}
|
||||
|
||||
public void ToggleHyperDash(bool status) => MovableCatcher.HyperDashModifier = status ? 2 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs
Normal file
30
osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ 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;
|
||||
@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
};
|
||||
}
|
||||
|
||||
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.CanCatch(obj);
|
||||
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
|
||||
|
||||
public override void Add(DrawableHitObject h)
|
||||
{
|
||||
|
@ -14,6 +14,7 @@ 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
|
||||
{
|
||||
@ -21,18 +22,18 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public const float CATCHER_SIZE = 172;
|
||||
|
||||
private readonly Catcher catcher;
|
||||
protected readonly Catcher MovableCatcher;
|
||||
|
||||
public Container ExplodingFruitTarget
|
||||
{
|
||||
set { catcher.ExplodingFruitTarget = value; }
|
||||
set { MovableCatcher.ExplodingFruitTarget = value; }
|
||||
}
|
||||
|
||||
public CatcherArea(BeatmapDifficulty difficulty = null)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = CATCHER_SIZE;
|
||||
Child = catcher = new Catcher(difficulty)
|
||||
Child = MovableCatcher = new Catcher(difficulty)
|
||||
{
|
||||
AdditiveTarget = this,
|
||||
};
|
||||
@ -41,17 +42,17 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
public void Add(DrawableHitObject fruit, Vector2 absolutePosition)
|
||||
{
|
||||
fruit.RelativePositionAxes = Axes.None;
|
||||
fruit.Position = new Vector2(catcher.ToLocalSpace(absolutePosition).X - catcher.DrawSize.X / 2, 0);
|
||||
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;
|
||||
|
||||
catcher.Add(fruit);
|
||||
MovableCatcher.Add(fruit);
|
||||
}
|
||||
|
||||
public bool CanCatch(CatchHitObject obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X * Math.Abs(catcher.Scale.X) / DrawSize.X / 2;
|
||||
public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj);
|
||||
|
||||
public class Catcher : Container, IKeyBindingHandler<CatchAction>
|
||||
{
|
||||
@ -105,14 +106,35 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
dashing = value;
|
||||
|
||||
if (dashing)
|
||||
Schedule(addAdditiveSprite);
|
||||
Trail |= dashing;
|
||||
}
|
||||
}
|
||||
|
||||
private void addAdditiveSprite()
|
||||
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
|
||||
{
|
||||
if (!dashing || AdditiveTarget == null) return;
|
||||
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();
|
||||
|
||||
@ -120,6 +142,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
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;
|
||||
|
||||
@ -127,7 +150,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire();
|
||||
|
||||
Scheduler.AddDelayed(addAdditiveSprite, 50);
|
||||
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
|
||||
}
|
||||
|
||||
private Sprite createCatcherSprite() => new Sprite
|
||||
@ -138,6 +161,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
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;
|
||||
@ -150,10 +177,80 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
caughtFruit.Add(fruit);
|
||||
|
||||
if (((CatchHitObject)fruit.HitObject).LastInCombo)
|
||||
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)
|
||||
@ -201,10 +298,15 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
if (currentDirection == 0) return;
|
||||
|
||||
var direction = Math.Sign(currentDirection);
|
||||
|
||||
double dashModifier = Dashing ? 1 : 0.5;
|
||||
|
||||
Scale = new Vector2(Math.Abs(Scale.X) * Math.Sign(currentDirection), Scale.Y);
|
||||
X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
|
||||
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()
|
||||
|
@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// </summary>
|
||||
/// <param name="time">The time to retrieve the sample info list from.</param>
|
||||
/// <returns></returns>
|
||||
private SampleInfoList sampleInfoListAt(double time)
|
||||
private List<SampleInfo> sampleInfoListAt(double time)
|
||||
{
|
||||
var curveData = HitObject as IHasCurve;
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// 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.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -435,7 +436,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
/// </summary>
|
||||
/// <param name="time">The time to retrieve the sample info list from.</param>
|
||||
/// <returns></returns>
|
||||
private SampleInfoList sampleInfoListAt(double time)
|
||||
private List<SampleInfo> sampleInfoListAt(double time)
|
||||
{
|
||||
var curveData = HitObject as IHasCurve;
|
||||
|
||||
|
@ -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 osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.MathUtils;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -76,10 +77,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
Duration = endTime - HitObject.StartTime
|
||||
};
|
||||
|
||||
hold.Head.Samples.Add(new SampleInfo
|
||||
{
|
||||
Name = SampleInfo.HIT_NORMAL
|
||||
});
|
||||
if (hold.Head.Samples == null)
|
||||
hold.Head.Samples = new List<SampleInfo>();
|
||||
|
||||
hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL });
|
||||
|
||||
hold.Tail.Samples = HitObject.Samples;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
|
@ -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 osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
@ -24,4 +24,4 @@ namespace osu.Game.Rulesets.Mania.Judgements
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
@ -11,4 +11,4 @@ namespace osu.Game.Rulesets.Mania.Judgements
|
||||
|
||||
protected override int NumericResultFor(HitResult result) => 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Judgements
|
||||
{
|
||||
|
@ -105,6 +105,8 @@ 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, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap);
|
||||
|
@ -105,6 +105,8 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
|
||||
public abstract class ManiaKeyMod : Mod
|
||||
{
|
||||
// TODO: implement using the IApplicable interface. Haven't done so yet because KeyCount isn't even hooked up at the moment.
|
||||
|
||||
public override string ShortenedName => Name;
|
||||
public abstract int KeyCount { get; }
|
||||
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
|
||||
|
@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
@ -77,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var tick in HitObject.Ticks)
|
||||
foreach (var tick in HitObject.NestedHitObjects.OfType<HoldNoteTick>())
|
||||
{
|
||||
var drawableTick = new DrawableHoldNoteTick(tick)
|
||||
{
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
@ -113,4 +114,4 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
UpdateJudgement(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
|
@ -1,7 +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 System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@ -63,9 +62,9 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// </summary>
|
||||
private double tickSpacing = 50;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
|
||||
@ -74,29 +73,27 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
Tail.ApplyDefaults(controlPointInfo, difficulty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The scoring scoring ticks of the hold note.
|
||||
/// </summary>
|
||||
public IEnumerable<HoldNoteTick> Ticks => ticks ?? (ticks = createTicks());
|
||||
private List<HoldNoteTick> ticks;
|
||||
|
||||
private List<HoldNoteTick> createTicks()
|
||||
protected override void CreateNestedHitObjects()
|
||||
{
|
||||
var ret = new List<HoldNoteTick>();
|
||||
base.CreateNestedHitObjects();
|
||||
|
||||
createTicks();
|
||||
}
|
||||
|
||||
private void createTicks()
|
||||
{
|
||||
if (tickSpacing == 0)
|
||||
return ret;
|
||||
return;
|
||||
|
||||
for (double t = StartTime + tickSpacing; t <= EndTime - tickSpacing; t += tickSpacing)
|
||||
{
|
||||
ret.Add(new HoldNoteTick
|
||||
AddNested(new HoldNoteTick
|
||||
{
|
||||
StartTime = t,
|
||||
Column = Column
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -110,9 +107,9 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// </summary>
|
||||
private const double release_window_lenience = 1.5;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
HitWindows *= release_window_lenience;
|
||||
}
|
||||
|
@ -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 Newtonsoft.Json;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
@ -15,11 +16,12 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// <summary>
|
||||
/// The key-press hit window for this note.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public HitWindows HitWindows { get; protected set; } = new HitWindows();
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
HitWindows = new HitWindows(difficulty.OverallDifficulty);
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
@ -116,7 +115,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
|
||||
|
||||
// Ticks
|
||||
int tickCount = holdNote.Ticks.Count();
|
||||
int tickCount = holdNote.NestedHitObjects.OfType<HoldNoteTick>().Count();
|
||||
for (int i = 0; i < tickCount; i++)
|
||||
AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
|
||||
}
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Timing;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Timing;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
@ -21,7 +21,7 @@ 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;
|
||||
|
@ -9,7 +9,6 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
// Generate the bar lines
|
||||
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
|
||||
|
||||
SortedList<TimingControlPoint> timingPoints = Beatmap.ControlPointInfo.TimingPoints;
|
||||
var timingPoints = Beatmap.ControlPointInfo.TimingPoints;
|
||||
var barLines = new List<DrawableBarLine>();
|
||||
|
||||
for (int i = 0; i < timingPoints.Count; i++)
|
||||
|
15
osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs
Normal file
15
osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs
Normal file
@ -0,0 +1,15 @@
|
||||
// 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;
|
||||
|
||||
protected override bool ProxyApproachCircles => false;
|
||||
}
|
||||
}
|
19
osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
Normal file
19
osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
29
osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
Normal file
29
osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
Normal file
@ -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>()
|
||||
};
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Judgements
|
||||
{
|
||||
@ -34,4 +34,4 @@ namespace osu.Game.Rulesets.Osu.Judgements
|
||||
|
||||
public ComboResult Combo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,11 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
@ -23,13 +26,86 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public class OsuModEasy : ModEasy
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class OsuModHidden : ModHidden
|
||||
public class OsuModHidden : ModHidden, IApplicableToDrawableHitObjects
|
||||
{
|
||||
public override string Description => @"Play with no approach circles and fading notes for a slight score advantage.";
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
|
||||
private const double fade_in_duration_multiplier = 0.4;
|
||||
private const double fade_out_duration_multiplier = 0.3;
|
||||
|
||||
private float preEmpt => DrawableOsuHitObject.TIME_PREEMPT;
|
||||
|
||||
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||
{
|
||||
foreach (var d in drawables.OfType<DrawableOsuHitObject>())
|
||||
{
|
||||
d.ApplyCustomUpdateState += ApplyHiddenState;
|
||||
d.FadeInDuration = preEmpt * fade_in_duration_multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
protected void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
|
||||
{
|
||||
if (!(drawable is DrawableOsuHitObject d))
|
||||
return;
|
||||
|
||||
var fadeOutStartTime = d.HitObject.StartTime - preEmpt + d.FadeInDuration;
|
||||
var fadeOutDuration = preEmpt * fade_out_duration_multiplier;
|
||||
|
||||
// new duration from completed fade in to end (before fading out)
|
||||
var longFadeDuration = ((d.HitObject as IHasEndTime)?.EndTime ?? d.HitObject.StartTime) - fadeOutStartTime;
|
||||
|
||||
switch (drawable)
|
||||
{
|
||||
case DrawableHitCircle circle:
|
||||
// we don't want to see the approach circle
|
||||
circle.ApproachCircle.Hide();
|
||||
|
||||
// fade out immediately after fade in.
|
||||
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
|
||||
circle.FadeOut(fadeOutDuration);
|
||||
break;
|
||||
case DrawableSlider slider:
|
||||
using (slider.BeginAbsoluteSequence(fadeOutStartTime, true))
|
||||
{
|
||||
slider.Body.FadeOut(longFadeDuration, Easing.Out);
|
||||
|
||||
// delay a bit less to let the sliderball fade out peacefully instead of having a hard cut
|
||||
using (slider.BeginDelayedSequence(longFadeDuration - fadeOutDuration, true))
|
||||
slider.Ball.FadeOut(fadeOutDuration);
|
||||
}
|
||||
|
||||
break;
|
||||
case DrawableSpinner spinner:
|
||||
// hide elements we don't care about.
|
||||
spinner.Disc.Hide();
|
||||
spinner.Ticks.Hide();
|
||||
spinner.Background.Hide();
|
||||
|
||||
using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true))
|
||||
{
|
||||
spinner.FadeOut(fadeOutDuration);
|
||||
|
||||
// speed up the end sequence accordingly
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
spinner.ScaleTo(spinner.Scale * 1.2f, fadeOutDuration * 2, Easing.Out);
|
||||
break;
|
||||
case ArmedState.Miss:
|
||||
spinner.ScaleTo(spinner.Scale * 0.8f, fadeOutDuration * 2, Easing.In);
|
||||
break;
|
||||
}
|
||||
|
||||
spinner.Expire();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject>
|
||||
@ -51,11 +127,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
slider.ControlPoints = newControlPoints;
|
||||
slider.Curve?.Calculate(); // Recalculate the slider curve
|
||||
}
|
||||
|
||||
public void ApplyToHitObjects(RulesetContainer<OsuHitObject> rulesetContainer)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class OsuModSuddenDeath : ModSuddenDeath
|
||||
@ -96,7 +167,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public class OsuModPerfect : ModPerfect
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class OsuModSpunOut : Mod
|
||||
|
@ -6,8 +6,8 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@ -21,12 +21,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
private readonly NumberPiece number;
|
||||
private readonly GlowPiece glow;
|
||||
|
||||
public DrawableHitCircle(OsuHitObject h) : base(h)
|
||||
public DrawableHitCircle(HitCircle h) : base(h)
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Position = HitObject.StackedPosition;
|
||||
Scale = new Vector2(HitObject.Scale);
|
||||
Scale = new Vector2(h.Scale);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
},
|
||||
number = new NumberPiece
|
||||
{
|
||||
Text = h is Spinner ? "S" : (HitObject.ComboIndex + 1).ToString(),
|
||||
Text = (HitObject.ComboIndex + 1).ToString(),
|
||||
},
|
||||
ring = new RingPiece(),
|
||||
flash = new FlashPiece(),
|
||||
@ -88,25 +88,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
base.UpdatePreemptState();
|
||||
|
||||
ApproachCircle.FadeIn(Math.Min(TIME_FADEIN * 2, TIME_PREEMPT));
|
||||
ApproachCircle.FadeIn(Math.Min(FadeInDuration * 2, TIME_PREEMPT));
|
||||
ApproachCircle.ScaleTo(1.1f, TIME_PREEMPT);
|
||||
}
|
||||
|
||||
protected override void UpdateCurrentState(ArmedState state)
|
||||
{
|
||||
double duration = ((HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime) - HitObject.StartTime;
|
||||
|
||||
glow.Delay(duration).FadeOut(400);
|
||||
glow.FadeOut(400);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
this.Delay(duration + TIME_PREEMPT).FadeOut(TIME_FADEOUT);
|
||||
this.Delay(TIME_PREEMPT).FadeOut(500);
|
||||
|
||||
Expire(true);
|
||||
|
||||
// override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early.
|
||||
LifetimeEnd = HitObject.StartTime + HitObject.HitWindowFor(HitResult.Miss);
|
||||
break;
|
||||
case ArmedState.Miss:
|
||||
ApproachCircle.FadeOut(50);
|
||||
this.FadeOut(TIME_FADEOUT / 5);
|
||||
this.FadeOut(100);
|
||||
Expire();
|
||||
break;
|
||||
case ArmedState.Hit:
|
||||
|
@ -12,7 +12,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public const float TIME_PREEMPT = 600;
|
||||
public const float TIME_FADEIN = 400;
|
||||
public const float TIME_FADEOUT = 500;
|
||||
|
||||
/// <summary>
|
||||
/// The number of milliseconds used to fade in.
|
||||
/// </summary>
|
||||
public virtual double FadeInDuration { get; set; } = TIME_FADEIN;
|
||||
|
||||
public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - TIME_PREEMPT;
|
||||
|
||||
protected DrawableOsuHitObject(OsuHitObject hitObject)
|
||||
: base(hitObject)
|
||||
@ -37,10 +43,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void UpdatePreemptState()
|
||||
{
|
||||
this.FadeIn(TIME_FADEIN);
|
||||
}
|
||||
protected virtual void UpdatePreemptState() => this.FadeIn(FadeInDuration);
|
||||
|
||||
protected virtual void UpdateCurrentState(ArmedState state)
|
||||
{
|
||||
|
@ -2,10 +2,10 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@ -24,4 +24,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
base.LoadComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
using OpenTK;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@ -24,19 +25,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
this.repeatPoint = repeatPoint;
|
||||
this.drawableSlider = drawableSlider;
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Size = new Vector2(32 * repeatPoint.Scale);
|
||||
|
||||
Blending = BlendingMode.Additive;
|
||||
Origin = Anchor.Centre;
|
||||
Scale = new Vector2(0.5f);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new SpriteIcon
|
||||
{
|
||||
Icon = FontAwesome.fa_eercast,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(32),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Icon = FontAwesome.fa_eercast
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@ -16,23 +18,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
private readonly Slider slider;
|
||||
|
||||
private readonly DrawableHitCircle initialCircle;
|
||||
public readonly DrawableHitCircle InitialCircle;
|
||||
|
||||
private readonly List<ISliderProgress> components = new List<ISliderProgress>();
|
||||
|
||||
private readonly Container<DrawableSliderTick> ticks;
|
||||
private readonly Container<DrawableRepeatPoint> repeatPoints;
|
||||
|
||||
private readonly SliderBody body;
|
||||
private readonly SliderBall ball;
|
||||
public readonly SliderBody Body;
|
||||
public readonly SliderBall Ball;
|
||||
|
||||
public DrawableSlider(Slider s) : base(s)
|
||||
public DrawableSlider(Slider s)
|
||||
: base(s)
|
||||
{
|
||||
slider = s;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
body = new SliderBody(s)
|
||||
Body = new SliderBody(s)
|
||||
{
|
||||
AccentColour = AccentColour,
|
||||
Position = s.StackedPosition,
|
||||
@ -40,35 +43,35 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
},
|
||||
ticks = new Container<DrawableSliderTick>(),
|
||||
repeatPoints = new Container<DrawableRepeatPoint>(),
|
||||
ball = new SliderBall(s)
|
||||
Ball = new SliderBall(s)
|
||||
{
|
||||
Scale = new Vector2(s.Scale),
|
||||
AccentColour = AccentColour,
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0
|
||||
},
|
||||
initialCircle = new DrawableHitCircle(new HitCircle
|
||||
InitialCircle = new DrawableHitCircle(new HitCircle
|
||||
{
|
||||
//todo: avoid creating this temporary HitCircle.
|
||||
StartTime = s.StartTime,
|
||||
Position = s.StackedPosition,
|
||||
ComboIndex = s.ComboIndex,
|
||||
Scale = s.Scale,
|
||||
ComboColour = s.ComboColour,
|
||||
Samples = s.Samples,
|
||||
SampleControlPoint = s.SampleControlPoint
|
||||
})
|
||||
};
|
||||
|
||||
components.Add(body);
|
||||
components.Add(ball);
|
||||
components.Add(Body);
|
||||
components.Add(Ball);
|
||||
|
||||
AddNested(initialCircle);
|
||||
AddNested(InitialCircle);
|
||||
|
||||
var repeatDuration = s.Curve.Distance / s.Velocity;
|
||||
foreach (var tick in s.Ticks)
|
||||
foreach (var tick in s.NestedHitObjects.OfType<SliderTick>())
|
||||
{
|
||||
var repeatStartTime = s.StartTime + tick.RepeatIndex * repeatDuration;
|
||||
var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? TIME_FADEIN : TIME_FADEIN / 2);
|
||||
var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? FadeInDuration : FadeInDuration / 2);
|
||||
var fadeOutTime = repeatStartTime + repeatDuration;
|
||||
|
||||
var drawableTick = new DrawableSliderTick(tick)
|
||||
@ -82,10 +85,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
AddNested(drawableTick);
|
||||
}
|
||||
|
||||
foreach (var repeatPoint in s.RepeatPoints)
|
||||
foreach (var repeatPoint in s.NestedHitObjects.OfType<RepeatPoint>())
|
||||
{
|
||||
var repeatStartTime = s.StartTime + repeatPoint.RepeatIndex * repeatDuration;
|
||||
var fadeInTime = repeatStartTime + (repeatPoint.StartTime - repeatStartTime) / 2 - (repeatPoint.RepeatIndex == 0 ? TIME_FADEIN : TIME_FADEIN / 2);
|
||||
var fadeInTime = repeatStartTime + (repeatPoint.StartTime - repeatStartTime) / 2 - (repeatPoint.RepeatIndex == 0 ? FadeInDuration : FadeInDuration / 2);
|
||||
var fadeOutTime = repeatStartTime + repeatDuration;
|
||||
|
||||
var drawableRepeatPoint = new DrawableRepeatPoint(repeatPoint, this)
|
||||
@ -103,11 +106,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
private int currentRepeat;
|
||||
public bool Tracking;
|
||||
|
||||
public override double FadeInDuration
|
||||
{
|
||||
get { return base.FadeInDuration; }
|
||||
set { InitialCircle.FadeInDuration = base.FadeInDuration = value; }
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Tracking = ball.Tracking;
|
||||
Tracking = Ball.Tracking;
|
||||
|
||||
double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
|
||||
|
||||
@ -115,18 +124,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
progress = slider.ProgressAt(progress);
|
||||
|
||||
if (repeat > currentRepeat)
|
||||
{
|
||||
if (repeat < slider.RepeatCount && ball.Tracking)
|
||||
PlaySamples();
|
||||
currentRepeat = repeat;
|
||||
}
|
||||
|
||||
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
|
||||
if (!initialCircle.Judgements.Any(j => j.IsHit))
|
||||
initialCircle.Position = slider.Curve.PositionAt(progress);
|
||||
if (!InitialCircle.Judgements.Any(j => j.IsHit))
|
||||
InitialCircle.Position = slider.Curve.PositionAt(progress);
|
||||
|
||||
foreach (var c in components) c.UpdateProgress(progress, repeat);
|
||||
foreach (var t in ticks.Children) t.Tracking = ball.Tracking;
|
||||
foreach (var t in ticks.Children) t.Tracking = Ball.Tracking;
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
@ -135,13 +140,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
var judgementsCount = ticks.Children.Count + repeatPoints.Children.Count + 1;
|
||||
var judgementsHit = ticks.Children.Count(t => t.Judgements.Any(j => j.IsHit)) + repeatPoints.Children.Count(t => t.Judgements.Any(j => j.IsHit));
|
||||
if (initialCircle.Judgements.Any(j => j.IsHit))
|
||||
if (InitialCircle.Judgements.Any(j => j.IsHit))
|
||||
judgementsHit++;
|
||||
|
||||
var hitFraction = (double)judgementsHit / judgementsCount;
|
||||
if (hitFraction == 1 && initialCircle.Judgements.Any(j => j.Result == HitResult.Great))
|
||||
if (hitFraction == 1 && InitialCircle.Judgements.Any(j => j.Result == HitResult.Great))
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Great });
|
||||
else if (hitFraction >= 0.5 && initialCircle.Judgements.Any(j => j.Result >= HitResult.Good))
|
||||
else if (hitFraction >= 0.5 && InitialCircle.Judgements.Any(j => j.Result >= HitResult.Good))
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Good });
|
||||
else if (hitFraction > 0)
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
|
||||
@ -152,23 +157,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
protected override void UpdateCurrentState(ArmedState state)
|
||||
{
|
||||
ball.FadeIn();
|
||||
Ball.FadeIn();
|
||||
Ball.ScaleTo(HitObject.Scale);
|
||||
|
||||
using (BeginDelayedSequence(slider.Duration, true))
|
||||
{
|
||||
body.FadeOut(160);
|
||||
ball.FadeOut(160);
|
||||
const float fade_out_time = 450;
|
||||
|
||||
this.FadeOut(800)
|
||||
.Expire();
|
||||
// intentionally pile on an extra FadeOut to make it happen much faster.
|
||||
Ball.FadeOut(fade_out_time / 4, Easing.Out);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out);
|
||||
break;
|
||||
}
|
||||
|
||||
this.FadeOut(fade_out_time, Easing.OutQuint).Expire();
|
||||
}
|
||||
}
|
||||
|
||||
public Drawable ProxiedLayer => initialCircle.ApproachCircle;
|
||||
}
|
||||
public Drawable ProxiedLayer => InitialCircle.ApproachCircle;
|
||||
|
||||
internal interface ISliderProgress
|
||||
{
|
||||
void UpdateProgress(double progress, int repeat);
|
||||
public override Vector2 SelectionPoint => ToScreenSpace(Body.Position);
|
||||
public override Quad SelectionQuad => Body.PathDrawQuad;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
|
@ -13,20 +13,21 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableSpinner : DrawableOsuHitObject
|
||||
{
|
||||
private readonly Spinner spinner;
|
||||
protected readonly Spinner Spinner;
|
||||
|
||||
private readonly SpinnerDisc disc;
|
||||
private readonly SpinnerTicks ticks;
|
||||
public readonly SpinnerDisc Disc;
|
||||
public readonly SpinnerTicks Ticks;
|
||||
private readonly SpinnerSpmCounter spmCounter;
|
||||
|
||||
private readonly Container mainContainer;
|
||||
|
||||
private readonly SpinnerBackground background;
|
||||
public readonly SpinnerBackground Background;
|
||||
private readonly Container circleContainer;
|
||||
private readonly CirclePiece circle;
|
||||
private readonly GlowPiece glow;
|
||||
@ -49,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
// we are slightly bigger than our parent, to clip the top and bottom of the circle
|
||||
Height = 1.3f;
|
||||
|
||||
spinner = s;
|
||||
Spinner = s;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
@ -84,20 +85,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
background = new SpinnerBackground
|
||||
Background = new SpinnerBackground
|
||||
{
|
||||
Alpha = 0.6f,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
disc = new SpinnerDisc(spinner)
|
||||
Disc = new SpinnerDisc(Spinner)
|
||||
{
|
||||
Scale = Vector2.Zero,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
circleContainer.CreateProxy(),
|
||||
ticks = new SpinnerTicks
|
||||
Ticks = new SpinnerTicks
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -114,28 +115,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
};
|
||||
}
|
||||
|
||||
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1);
|
||||
public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1);
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (Time.Current < HitObject.StartTime) return;
|
||||
|
||||
if (Progress >= 1 && !disc.Complete)
|
||||
if (Progress >= 1 && !Disc.Complete)
|
||||
{
|
||||
disc.Complete = true;
|
||||
Disc.Complete = true;
|
||||
|
||||
const float duration = 200;
|
||||
|
||||
disc.FadeAccent(completeColour, duration);
|
||||
Disc.FadeAccent(completeColour, duration);
|
||||
|
||||
background.FadeAccent(completeColour, duration);
|
||||
background.FadeOut(duration);
|
||||
Background.FadeAccent(completeColour, duration);
|
||||
Background.FadeOut(duration);
|
||||
|
||||
circle.FadeColour(completeColour, duration);
|
||||
glow.FadeColour(completeColour, duration);
|
||||
}
|
||||
|
||||
if (!userTriggered && Time.Current >= spinner.EndTime)
|
||||
if (!userTriggered && Time.Current >= Spinner.EndTime)
|
||||
{
|
||||
if (Progress >= 1)
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Great });
|
||||
@ -143,7 +144,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Good });
|
||||
else if (Progress > .75)
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
|
||||
else if (Time.Current >= spinner.EndTime)
|
||||
else if (Time.Current >= Spinner.EndTime)
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Miss });
|
||||
}
|
||||
}
|
||||
@ -153,20 +154,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
normalColour = baseColour;
|
||||
|
||||
background.AccentColour = normalColour;
|
||||
Background.AccentColour = normalColour;
|
||||
|
||||
completeColour = colours.YellowLight.Opacity(0.75f);
|
||||
|
||||
disc.AccentColour = fillColour;
|
||||
Disc.AccentColour = fillColour;
|
||||
circle.Colour = colours.BlueDark;
|
||||
glow.Colour = colours.BlueDark;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
|
||||
if (!spmCounter.IsPresent && disc.Tracking)
|
||||
spmCounter.FadeIn(TIME_FADEIN);
|
||||
Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
|
||||
if (!spmCounter.IsPresent && Disc.Tracking)
|
||||
spmCounter.FadeIn(FadeInDuration);
|
||||
|
||||
base.Update();
|
||||
}
|
||||
@ -175,36 +176,36 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
|
||||
circle.Rotation = disc.Rotation;
|
||||
ticks.Rotation = disc.Rotation;
|
||||
spmCounter.SetRotation(disc.RotationAbsolute);
|
||||
circle.Rotation = Disc.Rotation;
|
||||
Ticks.Rotation = Disc.Rotation;
|
||||
spmCounter.SetRotation(Disc.RotationAbsolute);
|
||||
|
||||
float relativeCircleScale = spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
|
||||
disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint);
|
||||
float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
|
||||
Disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint);
|
||||
|
||||
symbol.RotateTo(disc.Rotation / 2, 500, Easing.OutQuint);
|
||||
symbol.RotateTo(Disc.Rotation / 2, 500, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void UpdatePreemptState()
|
||||
{
|
||||
base.UpdatePreemptState();
|
||||
|
||||
circleContainer.ScaleTo(spinner.Scale * 0.3f);
|
||||
circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, Easing.OutQuint);
|
||||
circleContainer.ScaleTo(Spinner.Scale * 0.3f);
|
||||
circleContainer.ScaleTo(Spinner.Scale, TIME_PREEMPT / 1.4f, Easing.OutQuint);
|
||||
|
||||
disc.RotateTo(-720);
|
||||
Disc.RotateTo(-720);
|
||||
symbol.RotateTo(-720);
|
||||
|
||||
mainContainer
|
||||
.ScaleTo(0)
|
||||
.ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, Easing.OutQuint)
|
||||
.ScaleTo(Spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, Easing.OutQuint)
|
||||
.Then()
|
||||
.ScaleTo(1, 500, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override void UpdateCurrentState(ArmedState state)
|
||||
{
|
||||
var sequence = this.Delay(spinner.Duration).FadeOut(160);
|
||||
var sequence = this.Delay(Spinner.Duration).FadeOut(160);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
}
|
||||
|
||||
private readonly Slider slider;
|
||||
private readonly Box follow;
|
||||
public readonly Box FollowCircle;
|
||||
private readonly Box ball;
|
||||
|
||||
public SliderBall(Slider slider)
|
||||
@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
follow = new Box
|
||||
FollowCircle = new Box
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
@ -101,11 +101,11 @@ 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)
|
||||
public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
|
||||
{
|
||||
// Consider the case of rewinding - children's transforms are handled internally, so propagating down
|
||||
// any further will cause weirdness with the Tracking bool below. Let's not propagate further at this point.
|
||||
base.ClearTransforms(false, targetMember);
|
||||
base.ClearTransformsAfter(time, false, targetMember);
|
||||
}
|
||||
|
||||
private bool tracking;
|
||||
@ -118,8 +118,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
return;
|
||||
tracking = value;
|
||||
|
||||
follow.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
|
||||
follow.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint);
|
||||
FollowCircle.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint);
|
||||
FollowCircle.FadeTo(tracking ? 0.2f : 0, 300, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,11 +129,14 @@ 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.
|
||||
Tracking = canCurrentlyTrack
|
||||
&& lastState != null
|
||||
&& base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
|
||||
&& ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
|
||||
if (Time.Current < slider.EndTime)
|
||||
{
|
||||
// Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
|
||||
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)
|
||||
@ -141,4 +144,4 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
Position = slider.Curve.PositionAt(progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
private const float idle_alpha = 0.2f;
|
||||
private const float tracking_alpha = 0.4f;
|
||||
|
||||
public override bool IsPresent => true; // handle input when hidden
|
||||
|
||||
public SpinnerDisc(Spinner s)
|
||||
{
|
||||
spinner = s;
|
||||
|
10
osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs
Normal file
10
osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public interface ISliderProgress
|
||||
{
|
||||
void UpdateProgress(double progress, int repeat);
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ using OpenTK;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
@ -68,9 +68,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
return HitResult.Miss;
|
||||
}
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
/// </summary>
|
||||
internal float LazyTravelDistance;
|
||||
|
||||
public List<SampleInfoList> RepeatSamples { get; set; } = new List<SampleInfoList>();
|
||||
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
|
||||
public int RepeatCount { get; set; } = 1;
|
||||
|
||||
private int stackHeight;
|
||||
@ -74,9 +74,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
public double Velocity;
|
||||
public double TickDistance;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
|
||||
@ -99,76 +99,74 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
|
||||
|
||||
public IEnumerable<SliderTick> Ticks
|
||||
protected override void CreateNestedHitObjects()
|
||||
{
|
||||
get
|
||||
base.CreateNestedHitObjects();
|
||||
|
||||
createTicks();
|
||||
createRepeatPoints();
|
||||
}
|
||||
|
||||
private void createTicks()
|
||||
{
|
||||
if (TickDistance == 0) return;
|
||||
|
||||
var length = Curve.Distance;
|
||||
var tickDistance = Math.Min(TickDistance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
for (var repeat = 0; repeat < RepeatCount; repeat++)
|
||||
{
|
||||
if (TickDistance == 0) yield break;
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var reversed = repeat % 2 == 1;
|
||||
|
||||
var length = Curve.Distance;
|
||||
var tickDistance = Math.Min(TickDistance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
for (var repeat = 0; repeat < RepeatCount; repeat++)
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var reversed = repeat % 2 == 1;
|
||||
if (d > length - minDistanceFromEnd)
|
||||
break;
|
||||
|
||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||
var distanceProgress = d / length;
|
||||
var timeProgress = reversed ? 1 - distanceProgress : distanceProgress;
|
||||
|
||||
AddNested(new SliderTick
|
||||
{
|
||||
if (d > length - minDistanceFromEnd)
|
||||
break;
|
||||
|
||||
var distanceProgress = d / length;
|
||||
var timeProgress = reversed ? 1 - distanceProgress : distanceProgress;
|
||||
|
||||
yield return new SliderTick
|
||||
RepeatIndex = repeat,
|
||||
StartTime = repeatStartTime + timeProgress * repeatDuration,
|
||||
Position = Curve.PositionAt(distanceProgress),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
RepeatIndex = repeat,
|
||||
StartTime = repeatStartTime + timeProgress * repeatDuration,
|
||||
Position = Curve.PositionAt(distanceProgress),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
};
|
||||
}
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
public IEnumerable<RepeatPoint> RepeatPoints
|
||||
|
||||
private void createRepeatPoints()
|
||||
{
|
||||
get
|
||||
var repeatDuration = Distance / Velocity;
|
||||
|
||||
for (var repeat = 1; repeat < RepeatCount; repeat++)
|
||||
{
|
||||
var length = Curve.Distance;
|
||||
var repeatPointDistance = Math.Min(Distance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
|
||||
for (var repeat = 1; repeat < RepeatCount; repeat++)
|
||||
AddNested(new RepeatPoint
|
||||
{
|
||||
for (var d = repeatPointDistance; d <= length; d += repeatPointDistance)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var distanceProgress = d / length;
|
||||
|
||||
yield return new RepeatPoint
|
||||
{
|
||||
RepeatIndex = repeat,
|
||||
StartTime = repeatStartTime,
|
||||
Position = Curve.PositionAt(distanceProgress),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
};
|
||||
}
|
||||
}
|
||||
RepeatIndex = repeat,
|
||||
StartTime = repeatStartTime,
|
||||
Position = Curve.PositionAt(repeat % 2),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
ComboColour = ComboColour,
|
||||
Samples = new List<SampleInfo>(RepeatSamples[repeat])
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,9 +19,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
|
||||
public override bool NewCombo => true;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5));
|
||||
|
||||
|
@ -93,6 +93,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
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;
|
||||
|
||||
@ -106,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing
|
||||
}
|
||||
});
|
||||
|
||||
var scoringTimes = slider.Ticks.Select(t => t.StartTime).Concat(slider.RepeatPoints.Select(r => r.StartTime)).OrderBy(t => t);
|
||||
var scoringTimes = slider.NestedHitObjects.Select(t => t.StartTime);
|
||||
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;
|
||||
@ -16,6 +15,10 @@ 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
|
||||
{
|
||||
@ -31,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)
|
||||
{
|
||||
new BeatmapStatistic
|
||||
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[]
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -118,8 +135,12 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
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;
|
||||
|
@ -9,9 +9,9 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Replays
|
||||
{
|
||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
|
||||
|
||||
beatmapMaxCombo = Beatmap.HitObjects.Count;
|
||||
beatmapMaxCombo += Beatmap.HitObjects.OfType<Slider>().Sum(s => s.RepeatCount + s.Ticks.Count());
|
||||
beatmapMaxCombo += Beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count) + 1;
|
||||
}
|
||||
|
||||
public override double Calculate(Dictionary<string, double> categoryRatings = null)
|
||||
@ -41,10 +41,10 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
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"]);
|
||||
count300 = Convert.ToInt32(Score.Statistics[HitResult.Great]);
|
||||
count100 = Convert.ToInt32(Score.Statistics[HitResult.Good]);
|
||||
count50 = Convert.ToInt32(Score.Statistics[HitResult.Meh]);
|
||||
countMiss = Convert.ToInt32(Score.Statistics[HitResult.Miss]);
|
||||
|
||||
// Don't count scores made with supposedly unranked mods
|
||||
if (mods.Any(m => !m.Ranked))
|
||||
|
@ -2,10 +2,10 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
@ -32,18 +32,17 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
|
||||
foreach (var obj in beatmap.HitObjects)
|
||||
{
|
||||
var slider = obj as Slider;
|
||||
if (slider != null)
|
||||
if (obj is Slider slider)
|
||||
{
|
||||
// Head
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Great });
|
||||
|
||||
// Ticks
|
||||
foreach (var unused in slider.Ticks)
|
||||
foreach (var unused in slider.NestedHitObjects.OfType<SliderTick>())
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Great });
|
||||
|
||||
//Repeats
|
||||
foreach (var unused in slider.RepeatPoints)
|
||||
foreach (var unused in slider.NestedHitObjects.OfType<RepeatPoint>())
|
||||
AddJudgement(new OsuJudgement { Result = HitResult.Great });
|
||||
}
|
||||
|
||||
@ -63,10 +62,10 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
{
|
||||
base.PopulateScore(score);
|
||||
|
||||
score.Statistics[@"300"] = scoreResultCounts.GetOrDefault(HitResult.Great);
|
||||
score.Statistics[@"100"] = scoreResultCounts.GetOrDefault(HitResult.Good);
|
||||
score.Statistics[@"50"] = scoreResultCounts.GetOrDefault(HitResult.Meh);
|
||||
score.Statistics[@"x"] = scoreResultCounts.GetOrDefault(HitResult.Miss);
|
||||
score.Statistics[HitResult.Great] = scoreResultCounts.GetOrDefault(HitResult.Great);
|
||||
score.Statistics[HitResult.Good] = scoreResultCounts.GetOrDefault(HitResult.Good);
|
||||
score.Statistics[HitResult.Meh] = scoreResultCounts.GetOrDefault(HitResult.Meh);
|
||||
score.Statistics[HitResult.Miss] = scoreResultCounts.GetOrDefault(HitResult.Miss);
|
||||
}
|
||||
|
||||
protected override void OnNewJudgement(Judgement judgement)
|
||||
|
117
osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs
Normal file
117
osu.Game.Rulesets.Osu/Tests/TestCaseHitCircle.cs
Normal file
@ -0,0 +1,117 @@
|
||||
// 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.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Tests.Visual;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseHitCircle : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(DrawableHitCircle)
|
||||
};
|
||||
|
||||
private readonly Container content;
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
private int depthIndex;
|
||||
protected readonly List<Mod> Mods = new List<Mod>();
|
||||
|
||||
public TestCaseHitCircle()
|
||||
{
|
||||
base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
|
||||
|
||||
AddStep("Miss Big Single", () => testSingle(2));
|
||||
AddStep("Miss Medium Single", () => testSingle(5));
|
||||
AddStep("Miss Small Single", () => testSingle(7));
|
||||
AddStep("Hit Big Single", () => testSingle(2, true));
|
||||
AddStep("Hit Medium Single", () => testSingle(5, true));
|
||||
AddStep("Hit Small Single", () => testSingle(7, true));
|
||||
AddStep("Miss Big Stream", () => testStream(2));
|
||||
AddStep("Miss Medium Stream", () => testStream(5));
|
||||
AddStep("Miss Small Stream", () => testStream(7));
|
||||
AddStep("Hit Big Stream", () => testStream(2, true));
|
||||
AddStep("Hit Medium Stream", () => testStream(5, true));
|
||||
AddStep("Hit Small Stream", () => testStream(7, true));
|
||||
}
|
||||
|
||||
private void testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
||||
{
|
||||
positionOffset = positionOffset ?? Vector2.Zero;
|
||||
|
||||
var circle = new HitCircle
|
||||
{
|
||||
StartTime = Time.Current + 1000 + timeOffset,
|
||||
Position = positionOffset.Value,
|
||||
ComboColour = Color4.LightSeaGreen
|
||||
};
|
||||
|
||||
circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize });
|
||||
|
||||
var drawable = new TestDrawableHitCircle(circle, auto)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Depth = depthIndex++
|
||||
};
|
||||
|
||||
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>())
|
||||
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
||||
|
||||
Add(drawable);
|
||||
}
|
||||
|
||||
private void testStream(float circleSize, bool auto = false)
|
||||
{
|
||||
Vector2 pos = new Vector2(-250, 0);
|
||||
|
||||
for (int i = 0; i <= 1000; i += 100)
|
||||
{
|
||||
testSingle(circleSize, auto, i, pos);
|
||||
pos.X += 50;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDrawableHitCircle : DrawableHitCircle
|
||||
{
|
||||
private readonly bool auto;
|
||||
|
||||
public TestDrawableHitCircle(HitCircle h, bool auto) : base(h)
|
||||
{
|
||||
this.auto = auto;
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (auto && !userTriggered && timeOffset > 0)
|
||||
{
|
||||
// force success
|
||||
AddJudgement(new OsuJudgement
|
||||
{
|
||||
Result = HitResult.Great
|
||||
});
|
||||
State.Value = ArmedState.Hit;
|
||||
}
|
||||
else
|
||||
base.CheckForJudgements(userTriggered, timeOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs
Normal file
22
osu.Game.Rulesets.Osu/Tests/TestCaseHitCircleHidden.cs
Normal file
@ -0,0 +1,22 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseHitCircleHidden : TestCaseHitCircle
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||
|
||||
public TestCaseHitCircleHidden()
|
||||
{
|
||||
Mods.Add(new OsuModHidden());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,129 +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.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Tests.Visual;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
internal class TestCaseHitObjects : OsuTestCase
|
||||
{
|
||||
private FramedClock framedClock;
|
||||
|
||||
private bool auto;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
var rateAdjustClock = new StopwatchClock(true);
|
||||
framedClock = new FramedClock(rateAdjustClock);
|
||||
|
||||
AddStep(@"circles", () => loadHitobjects(HitObjectType.Circle));
|
||||
AddStep(@"slider", () => loadHitobjects(HitObjectType.Slider));
|
||||
AddStep(@"spinner", () => loadHitobjects(HitObjectType.Spinner));
|
||||
|
||||
AddToggleStep("Auto", state => { auto = state; loadHitobjects(mode); });
|
||||
AddSliderStep("Playback speed", 0.0, 2.0, 0.5, v => rateAdjustClock.Rate = v);
|
||||
|
||||
framedClock.ProcessFrame();
|
||||
|
||||
var clockAdjustContainer = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = framedClock,
|
||||
Children = new[]
|
||||
{
|
||||
playfieldContainer = new OsuInputManager(rulesets.GetRuleset(0)) { RelativeSizeAxes = Axes.Both },
|
||||
approachContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||
}
|
||||
};
|
||||
|
||||
Add(clockAdjustContainer);
|
||||
}
|
||||
|
||||
private HitObjectType mode = HitObjectType.Slider;
|
||||
|
||||
private Container playfieldContainer;
|
||||
private Container approachContainer;
|
||||
|
||||
private void loadHitobjects(HitObjectType mode)
|
||||
{
|
||||
this.mode = mode;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case HitObjectType.Circle:
|
||||
const int count = 10;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var h = new HitCircle
|
||||
{
|
||||
StartTime = framedClock.CurrentTime + 600 + i * 80,
|
||||
Position = new Vector2((i - count / 2) * 14),
|
||||
};
|
||||
|
||||
add(new DrawableHitCircle(h));
|
||||
}
|
||||
break;
|
||||
case HitObjectType.Slider:
|
||||
add(new DrawableSlider(new Slider
|
||||
{
|
||||
StartTime = framedClock.CurrentTime + 600,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(400, 0),
|
||||
},
|
||||
Distance = 400,
|
||||
Position = new Vector2(-200, 0),
|
||||
Velocity = 1,
|
||||
TickDistance = 100,
|
||||
}));
|
||||
break;
|
||||
case HitObjectType.Spinner:
|
||||
add(new DrawableSpinner(new Spinner
|
||||
{
|
||||
StartTime = framedClock.CurrentTime + 600,
|
||||
EndTime = framedClock.CurrentTime + 1600,
|
||||
Position = new Vector2(0, 0),
|
||||
}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private int depth;
|
||||
|
||||
private void add(DrawableOsuHitObject h)
|
||||
{
|
||||
h.Anchor = Anchor.Centre;
|
||||
h.Depth = depth++;
|
||||
|
||||
if (auto)
|
||||
h.State.Value = ArmedState.Hit;
|
||||
|
||||
playfieldContainer.Add(h);
|
||||
var proxyable = h as IDrawableHitObjectWithProxiedApproach;
|
||||
if (proxyable != null)
|
||||
approachContainer.Add(proxyable.ProxiedLayer.CreateProxy());
|
||||
}
|
||||
|
||||
private enum HitObjectType
|
||||
{
|
||||
Circle,
|
||||
Slider,
|
||||
Spinner
|
||||
}
|
||||
}
|
||||
}
|
152
osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs
Normal file
152
osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs
Normal file
@ -0,0 +1,152 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Tests.Visual;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseSlider : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(SliderBall),
|
||||
typeof(SliderBody),
|
||||
typeof(DrawableSlider),
|
||||
typeof(DrawableRepeatPoint),
|
||||
typeof(DrawableOsuHitObject)
|
||||
};
|
||||
|
||||
private readonly Container content;
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
private int depthIndex;
|
||||
protected readonly List<Mod> Mods = new List<Mod>();
|
||||
|
||||
public TestCaseSlider()
|
||||
{
|
||||
base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
|
||||
|
||||
AddStep("Big Single", () => testSimpleBig());
|
||||
AddStep("Medium Single", () => testSimpleMedium());
|
||||
AddStep("Small Single", () => testSimpleSmall());
|
||||
AddStep("Big 1 Repeat", () => testSimpleBig(1));
|
||||
AddStep("Medium 1 Repeat", () => testSimpleMedium(1));
|
||||
AddStep("Small 1 Repeat", () => testSimpleSmall(1));
|
||||
AddStep("Big 2 Repeats", () => testSimpleBig(2));
|
||||
AddStep("Medium 2 Repeats", () => testSimpleMedium(2));
|
||||
AddStep("Small 2 Repeats", () => testSimpleSmall(2));
|
||||
|
||||
AddStep("Slow Slider", testSlowSpeed); // slow long sliders take ages already so no repeat steps
|
||||
AddStep("Slow Short Slider", () => testShortSlowSpeed());
|
||||
AddStep("Slow Short Slider 1 Repeats", () => testShortSlowSpeed(1));
|
||||
AddStep("Slow Short Slider 2 Repeats", () => testShortSlowSpeed(2));
|
||||
|
||||
AddStep("Fast Slider", () => testHighSpeed());
|
||||
AddStep("Fast Slider 1 Repeat", () => testHighSpeed(1));
|
||||
AddStep("Fast Slider 2 Repeats", () => testHighSpeed(2));
|
||||
AddStep("Fast Short Slider", () => testShortHighSpeed());
|
||||
AddStep("Fast Short Slider 1 Repeat", () => testShortHighSpeed(1));
|
||||
AddStep("Fast Short Slider 2 Repeats", () => testShortHighSpeed(2));
|
||||
|
||||
AddStep("Perfect Curve", testCurve);
|
||||
// TODO more curve types?
|
||||
}
|
||||
|
||||
private void testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats);
|
||||
|
||||
private void testSimpleMedium(int repeats = 0) => createSlider(5, repeats: repeats);
|
||||
|
||||
private void testSimpleSmall(int repeats = 0) => createSlider(7, repeats: repeats);
|
||||
|
||||
private void testSlowSpeed() => createSlider(speedMultiplier: 0.5);
|
||||
|
||||
private void testShortSlowSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 0.5);
|
||||
|
||||
private void testHighSpeed(int repeats = 0) => createSlider(repeats: repeats, speedMultiplier: 15);
|
||||
|
||||
private void testShortHighSpeed(int repeats = 0) => createSlider(distance: 100, repeats: repeats, speedMultiplier: 15);
|
||||
|
||||
private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2)
|
||||
{
|
||||
repeats++; // The first run through the slider is considered a repeat
|
||||
|
||||
var repeatSamples = new List<List<SampleInfo>>();
|
||||
if (repeats > 1)
|
||||
{
|
||||
for (int i = 0; i < repeats; i++)
|
||||
repeatSamples.Add(new List<SampleInfo>());
|
||||
}
|
||||
|
||||
var slider = new Slider
|
||||
{
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-(distance / 2), 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-(distance / 2), 0),
|
||||
new Vector2(distance / 2, 0),
|
||||
},
|
||||
Distance = distance,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = repeatSamples
|
||||
};
|
||||
|
||||
addSlider(slider, circleSize, speedMultiplier);
|
||||
}
|
||||
|
||||
private void testCurve()
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
StartTime = Time.Current + 1000,
|
||||
Position = new Vector2(-200, 0),
|
||||
ComboColour = Color4.LightSeaGreen,
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(-200, 0),
|
||||
new Vector2(0, 200),
|
||||
new Vector2(200, 0)
|
||||
},
|
||||
Distance = 600
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
}
|
||||
|
||||
private void addSlider(Slider slider, float circleSize, double speedMultiplier)
|
||||
{
|
||||
var cpi = new ControlPointInfo();
|
||||
cpi.DifficultyPoints.Add(new DifficultyControlPoint { SpeedMultiplier = speedMultiplier });
|
||||
|
||||
slider.ApplyDefaults(cpi, new BeatmapDifficulty { CircleSize = circleSize });
|
||||
|
||||
var drawable = new DrawableSlider(slider)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Depth = depthIndex++
|
||||
};
|
||||
|
||||
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>())
|
||||
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
||||
|
||||
Add(drawable);
|
||||
}
|
||||
}
|
||||
}
|
22
osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs
Normal file
22
osu.Game.Rulesets.Osu/Tests/TestCaseSliderHidden.cs
Normal file
@ -0,0 +1,22 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseSliderHidden : TestCaseSlider
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||
|
||||
public TestCaseSliderHidden()
|
||||
{
|
||||
Mods.Add(new OsuModHidden());
|
||||
}
|
||||
}
|
||||
}
|
88
osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs
Normal file
88
osu.Game.Rulesets.Osu/Tests/TestCaseSpinner.cs
Normal file
@ -0,0 +1,88 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseSpinner : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(SpinnerDisc),
|
||||
typeof(DrawableSpinner),
|
||||
typeof(DrawableOsuHitObject)
|
||||
};
|
||||
|
||||
private readonly Container content;
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
private int depthIndex;
|
||||
protected readonly List<Mod> Mods = new List<Mod>();
|
||||
|
||||
public TestCaseSpinner()
|
||||
{
|
||||
base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
|
||||
|
||||
AddStep("Miss Big", () => testSingle(2));
|
||||
AddStep("Miss Medium", () => testSingle(5));
|
||||
AddStep("Miss Small", () => testSingle(7));
|
||||
AddStep("Hit Big", () => testSingle(2, true));
|
||||
AddStep("Hit Medium", () => testSingle(5, true));
|
||||
AddStep("Hit Small", () => testSingle(7, true));
|
||||
}
|
||||
|
||||
private void testSingle(float circleSize, bool auto = false)
|
||||
{
|
||||
var spinner = new Spinner { StartTime = Time.Current + 1000, EndTime = Time.Current + 4000 };
|
||||
|
||||
spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize });
|
||||
|
||||
var drawable = new TestDrawableSpinner(spinner, auto)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Depth = depthIndex++
|
||||
};
|
||||
|
||||
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>())
|
||||
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
||||
|
||||
Add(drawable);
|
||||
}
|
||||
|
||||
private class TestDrawableSpinner : DrawableSpinner
|
||||
{
|
||||
private bool auto;
|
||||
|
||||
public TestDrawableSpinner(Spinner s, bool auto) : base(s)
|
||||
{
|
||||
this.auto = auto;
|
||||
}
|
||||
|
||||
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1)
|
||||
{
|
||||
// force completion only once to not break human interaction
|
||||
Disc.RotationAbsolute = Spinner.SpinsRequired * 360;
|
||||
auto = false;
|
||||
}
|
||||
|
||||
base.CheckForJudgements(userTriggered, timeOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
22
osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs
Normal file
22
osu.Game.Rulesets.Osu/Tests/TestCaseSpinnerHidden.cs
Normal file
@ -0,0 +1,22 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseSpinnerHidden : TestCaseSpinner
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList();
|
||||
|
||||
public TestCaseSpinnerHidden()
|
||||
{
|
||||
Mods.Add(new OsuModHidden());
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
@ -24,12 +25,19 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
|
||||
public override bool ProvidingUserCursor => true;
|
||||
|
||||
// Todo: This should not be a thing, but is currently required for the editor
|
||||
// https://github.com/ppy/osu-framework/issues/1283
|
||||
protected virtual bool ProxyApproachCircles => true;
|
||||
|
||||
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
|
||||
|
||||
public override Vector2 Size
|
||||
{
|
||||
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 +73,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)
|
||||
@ -73,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
h.Depth = (float)h.HitObject.StartTime;
|
||||
|
||||
var c = h as IDrawableHitObjectWithProxiedApproach;
|
||||
if (c != null)
|
||||
if (c != null && ProxyApproachCircles)
|
||||
approachCircles.Add(c.ProxiedLayer.CreateProxy());
|
||||
|
||||
base.Add(h);
|
||||
@ -102,5 +113,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
|
||||
judgementLayer.Add(explosion);
|
||||
}
|
||||
|
||||
protected virtual CursorContainer CreateCursor() => new GameplayCursor();
|
||||
}
|
||||
}
|
||||
|
@ -35,16 +35,13 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
|
||||
protected override DrawableHitObject<OsuHitObject> GetVisualRepresentation(OsuHitObject h)
|
||||
{
|
||||
var circle = h as HitCircle;
|
||||
if (circle != null)
|
||||
if (h is HitCircle circle)
|
||||
return new DrawableHitCircle(circle);
|
||||
|
||||
var slider = h as Slider;
|
||||
if (slider != null)
|
||||
if (h is Slider slider)
|
||||
return new DrawableSlider(slider);
|
||||
|
||||
var spinner = h as Spinner;
|
||||
if (spinner != null)
|
||||
if (h is Spinner spinner)
|
||||
return new DrawableSpinner(spinner);
|
||||
return null;
|
||||
}
|
||||
|
46
osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
Normal file
46
osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
Normal file
@ -0,0 +1,46 @@
|
||||
// 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.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Audio
|
||||
{
|
||||
public class DrumSampleMapping
|
||||
{
|
||||
private readonly ControlPointInfo controlPoints;
|
||||
private readonly Dictionary<double, DrumSample> mappings = new Dictionary<double, DrumSample>();
|
||||
|
||||
public DrumSampleMapping(ControlPointInfo controlPoints, AudioManager audio)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
|
||||
IEnumerable<SampleControlPoint> samplePoints;
|
||||
if (controlPoints.SamplePoints.Count == 0)
|
||||
// Get the default sample point
|
||||
samplePoints = new[] { controlPoints.SamplePointAt(double.MinValue) };
|
||||
else
|
||||
samplePoints = controlPoints.SamplePoints;
|
||||
|
||||
foreach (var s in samplePoints)
|
||||
{
|
||||
mappings[s.Time] = new DrumSample
|
||||
{
|
||||
Centre = s.GetSampleInfo().GetChannel(audio.Sample, "Taiko"),
|
||||
Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample, "Taiko")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(time).Time];
|
||||
|
||||
public class DrumSample
|
||||
{
|
||||
public SampleChannel Centre;
|
||||
public SampleChannel Rim;
|
||||
}
|
||||
}
|
||||
}
|
@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
var curveData = obj as IHasCurve;
|
||||
|
||||
// Old osu! used hit sounding to determine various hit type information
|
||||
SampleInfoList samples = obj.Samples;
|
||||
List<SampleInfo> samples = obj.Samples;
|
||||
|
||||
bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
|
||||
|
||||
@ -115,12 +115,12 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
|
||||
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
||||
{
|
||||
List<SampleInfoList> allSamples = curveData != null ? curveData.RepeatSamples : new List<SampleInfoList>(new[] { samples });
|
||||
List<List<SampleInfo>> allSamples = curveData != null ? curveData.RepeatSamples : new List<List<SampleInfo>>(new[] { samples });
|
||||
|
||||
int i = 0;
|
||||
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
|
||||
{
|
||||
SampleInfoList currentSamples = allSamples[i];
|
||||
List<SampleInfo> currentSamples = allSamples[i];
|
||||
bool isRim = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
|
||||
strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH);
|
||||
|
||||
|
@ -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 osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Judgements
|
||||
{
|
||||
@ -20,4 +20,4 @@ namespace osu.Game.Rulesets.Taiko.Judgements
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Judgements
|
||||
{
|
||||
|
@ -13,6 +13,7 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
@ -43,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
RelativeChildSize = new Vector2((float)HitObject.Duration, 1)
|
||||
});
|
||||
|
||||
foreach (var tick in drumRoll.Ticks)
|
||||
foreach (var tick in drumRoll.NestedHitObjects.OfType<DrumRollTick>())
|
||||
{
|
||||
var newTick = new DrawableDrumRollTick(tick);
|
||||
newTick.OnJudgement += onTickJudgement;
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
|
||||
@ -67,6 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
validKeyPressed = HitActions.Contains(action);
|
||||
|
||||
// Only count this as handled if the new judgement is a hit
|
||||
return UpdateJudgement(true);
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset > second_hit_window)
|
||||
AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Miss });
|
||||
AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.None });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
@ -34,10 +35,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
private readonly CircularContainer targetRing;
|
||||
private readonly CircularContainer expandingRing;
|
||||
|
||||
private readonly TaikoAction[] rimActions = { TaikoAction.LeftRim, TaikoAction.RightRim };
|
||||
private readonly TaikoAction[] centreActions = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
|
||||
private TaikoAction[] lastAction;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of times the user has hit this swell.
|
||||
/// </summary>
|
||||
@ -205,19 +202,20 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
}
|
||||
}
|
||||
|
||||
private bool? lastWasCentre;
|
||||
|
||||
public override bool OnPressed(TaikoAction action)
|
||||
{
|
||||
// Don't handle keys before the swell starts
|
||||
if (Time.Current < HitObject.StartTime)
|
||||
return false;
|
||||
|
||||
// Find the keyset which this key corresponds to
|
||||
var keySet = rimActions.Contains(action) ? rimActions : centreActions;
|
||||
var isCentre = action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre;
|
||||
|
||||
// Ensure alternating keysets
|
||||
if (keySet == lastAction)
|
||||
// Ensure alternating centre and rim hits
|
||||
if (lastWasCentre == isCentre)
|
||||
return false;
|
||||
lastAction = keySet;
|
||||
lastWasCentre = isCentre;
|
||||
|
||||
UpdateJudgement(true);
|
||||
|
||||
|
@ -6,6 +6,9 @@ using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
|
||||
using OpenTK;
|
||||
using System.Linq;
|
||||
using osu.Game.Audio;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
@ -35,6 +38,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
MainPiece.KiaiMode = HitObject.Kiai;
|
||||
}
|
||||
|
||||
// Normal and clap samples are handled by the drum
|
||||
protected override IEnumerable<SampleInfo> GetSamples() => HitObject.Samples.Where(s => s.Name != SampleInfo.HIT_NORMAL && s.Name != SampleInfo.HIT_CLAP);
|
||||
|
||||
protected override string SampleNamespace => "Taiko";
|
||||
|
||||
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece();
|
||||
|
||||
public abstract bool OnPressed(TaikoAction action);
|
||||
|
@ -3,9 +3,6 @@
|
||||
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
@ -37,64 +34,49 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
/// </summary>
|
||||
public double RequiredGreatHits { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total number of drum roll ticks.
|
||||
/// </summary>
|
||||
public int TotalTicks => Ticks.Count();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the drum roll ticks if not initialized and returns them.
|
||||
/// </summary>
|
||||
public IEnumerable<DrumRollTick> Ticks => ticks ?? (ticks = createTicks());
|
||||
|
||||
private List<DrumRollTick> ticks;
|
||||
|
||||
/// <summary>
|
||||
/// The length (in milliseconds) between ticks of this drumroll.
|
||||
/// <para>Half of this value is the hit window of the ticks.</para>
|
||||
/// </summary>
|
||||
private double tickSpacing = 100;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||
|
||||
tickSpacing = timingPoint.BeatLength / TickRate;
|
||||
|
||||
RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty);
|
||||
RequiredGreatHits = TotalTicks * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty);
|
||||
RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty);
|
||||
RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty);
|
||||
}
|
||||
|
||||
private List<DrumRollTick> createTicks()
|
||||
protected override void CreateNestedHitObjects()
|
||||
{
|
||||
var ret = new List<DrumRollTick>();
|
||||
base.CreateNestedHitObjects();
|
||||
|
||||
createTicks();
|
||||
}
|
||||
|
||||
private void createTicks()
|
||||
{
|
||||
if (tickSpacing == 0)
|
||||
return ret;
|
||||
return;
|
||||
|
||||
bool first = true;
|
||||
for (double t = StartTime; t < EndTime + tickSpacing / 2; t += tickSpacing)
|
||||
{
|
||||
ret.Add(new DrumRollTick
|
||||
AddNested(new DrumRollTick
|
||||
{
|
||||
FirstTick = first,
|
||||
TickSpacing = tickSpacing,
|
||||
StartTime = t,
|
||||
IsStrong = IsStrong,
|
||||
Samples = new SampleInfoList(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
IsStrong = IsStrong
|
||||
});
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,9 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
/// </summary>
|
||||
public double HitWindowMiss = 95;
|
||||
|
||||
public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
{
|
||||
base.ApplyDefaults(controlPointInfo, difficulty);
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20);
|
||||
HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50);
|
||||
|
@ -16,4 +16,4 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
/// </summary>
|
||||
public int RequiredHits = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@ -83,9 +84,9 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
||||
}
|
||||
else if (drumRoll != null)
|
||||
{
|
||||
foreach (var tick in drumRoll.Ticks)
|
||||
foreach (var tick in drumRoll.NestedHitObjects.OfType<DrumRollTick>())
|
||||
{
|
||||
Frames.Add(new ReplayFrame(tick.StartTime, null, null, hitButton ? ReplayButtonState.Left1 : ReplayButtonState.Left2));
|
||||
Frames.Add(new ReplayFrame(tick.StartTime, null, null, hitButton ? ReplayButtonState.Right1 : ReplayButtonState.Right2));
|
||||
hitButton = !hitButton;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
// 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.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Judgements;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
||||
}
|
||||
else if (obj is DrumRoll)
|
||||
{
|
||||
for (int i = 0; i < ((DrumRoll)obj).TotalTicks; i++)
|
||||
for (int i = 0; i < ((DrumRoll)obj).NestedHitObjects.OfType<DrumRollTick>().Count(); i++)
|
||||
{
|
||||
AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
|
||||
|
||||
|
@ -95,6 +95,8 @@ 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, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap);
|
||||
|
44
osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs
Normal file
44
osu.Game.Rulesets.Taiko/Tests/TestCaseInputDrum.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// 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 OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Taiko.Audio;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
[Ignore("getting CI working")]
|
||||
public class TestCaseInputDrum : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(InputDrum),
|
||||
typeof(DrumSampleMapping),
|
||||
typeof(SampleInfo),
|
||||
typeof(SampleControlPoint)
|
||||
};
|
||||
|
||||
public TestCaseInputDrum()
|
||||
{
|
||||
Add(new TaikoInputManager(new RulesetInfo { ID = 1 })
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200),
|
||||
Child = new InputDrum(new ControlPointInfo())
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -20,12 +20,13 @@ using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Visual;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
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;
|
||||
@ -165,11 +166,15 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
|
||||
private void addSwell(double duration = default_duration)
|
||||
{
|
||||
rulesetContainer.Playfield.Add(new DrawableSwell(new Swell
|
||||
var swell = new Swell
|
||||
{
|
||||
StartTime = rulesetContainer.Playfield.Time.Current + scroll_time,
|
||||
Duration = duration,
|
||||
}));
|
||||
};
|
||||
|
||||
swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
rulesetContainer.Playfield.Add(new DrawableSwell(swell));
|
||||
}
|
||||
|
||||
private void addDrumRoll(bool strong, double duration = default_duration)
|
||||
@ -184,6 +189,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
Duration = duration,
|
||||
};
|
||||
|
||||
d.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
rulesetContainer.Playfield.Add(new DrawableDrumRoll(d));
|
||||
}
|
||||
|
||||
@ -195,6 +202,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
IsStrong = strong
|
||||
};
|
||||
|
||||
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
if (strong)
|
||||
rulesetContainer.Playfield.Add(new DrawableCentreHitStrong(h));
|
||||
else
|
||||
@ -209,6 +218,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
IsStrong = strong
|
||||
};
|
||||
|
||||
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
if (strong)
|
||||
rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h));
|
||||
else
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
@ -49,4 +50,4 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
base.LoadComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,15 @@
|
||||
using System;
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
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.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Taiko.Audio;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
@ -18,16 +21,26 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
/// </summary>
|
||||
internal class InputDrum : Container
|
||||
{
|
||||
public InputDrum()
|
||||
private const float middle_split = 0.025f;
|
||||
|
||||
private readonly ControlPointInfo controlPoints;
|
||||
|
||||
public InputDrum(ControlPointInfo controlPoints)
|
||||
{
|
||||
this.controlPoints = controlPoints;
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
FillMode = FillMode.Fit;
|
||||
}
|
||||
|
||||
const float middle_split = 0.025f;
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
var sampleMappings = new DrumSampleMapping(controlPoints, audio);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new TaikoHalfDrum(false)
|
||||
new TaikoHalfDrum(false, sampleMappings)
|
||||
{
|
||||
Name = "Left Half",
|
||||
Anchor = Anchor.Centre,
|
||||
@ -38,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
RimAction = TaikoAction.LeftRim,
|
||||
CentreAction = TaikoAction.LeftCentre
|
||||
},
|
||||
new TaikoHalfDrum(true)
|
||||
new TaikoHalfDrum(true, sampleMappings)
|
||||
{
|
||||
Name = "Right Half",
|
||||
Anchor = Anchor.Centre,
|
||||
@ -72,8 +85,12 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
private readonly Sprite centre;
|
||||
private readonly Sprite centreHit;
|
||||
|
||||
public TaikoHalfDrum(bool flipped)
|
||||
private readonly DrumSampleMapping sampleMappings;
|
||||
|
||||
public TaikoHalfDrum(bool flipped, DrumSampleMapping sampleMappings)
|
||||
{
|
||||
this.sampleMappings = sampleMappings;
|
||||
|
||||
Masking = true;
|
||||
|
||||
Children = new Drawable[]
|
||||
@ -128,15 +145,21 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
Drawable target = null;
|
||||
Drawable back = null;
|
||||
|
||||
var drumSample = sampleMappings.SampleAt(Time.Current);
|
||||
|
||||
if (action == CentreAction)
|
||||
{
|
||||
target = centreHit;
|
||||
back = centre;
|
||||
|
||||
drumSample.Centre?.Play();
|
||||
}
|
||||
else if (action == RimAction)
|
||||
{
|
||||
target = rimHit;
|
||||
back = rim;
|
||||
|
||||
drumSample.Rim?.Play();
|
||||
}
|
||||
|
||||
if (target != null)
|
||||
|
@ -16,6 +16,7 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
@ -54,7 +55,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
private readonly Box overlayBackground;
|
||||
private readonly Box background;
|
||||
|
||||
public TaikoPlayfield()
|
||||
public TaikoPlayfield(ControlPointInfo controlPoints)
|
||||
: base(Axes.X)
|
||||
{
|
||||
AddRangeInternal(new Drawable[]
|
||||
@ -149,7 +150,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new InputDrum
|
||||
new InputDrum(controlPoints)
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
@ -249,7 +250,9 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
topLevelHitContainer.Add(judgedObject.CreateProxy());
|
||||
}
|
||||
catch { }
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
|
||||
|
@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
|
||||
|
||||
protected override Playfield CreatePlayfield() => new TaikoPlayfield
|
||||
protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo)
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft
|
||||
|
214
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
Normal file
214
osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
Normal file
@ -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.SamplePoints.Count);
|
||||
var soundPoint = controlPoints.SamplePoints[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(Path.Combine("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(Path.Combine("SB", "red jitter", "red_0000.jpg"), animation.Path);
|
||||
Assert.AreEqual(78993, animation.StartTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
176
osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
Normal file
176
osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
Normal file
@ -0,0 +1,176 @@
|
||||
// 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 DeepEqual.Syntax;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Tests.Resources;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
[TestFixture]
|
||||
public class OsuJsonDecoderTest
|
||||
{
|
||||
private const string normal = "Soleily - Renatus (Gamu) [Insane].osu";
|
||||
private const string marathon = "Within Temptation - The Unforgiving (Armin) [Marathon].osu";
|
||||
private const string with_sb = "Kozato snow - Rengetsu Ouka (_Kiva) [Yuki YukI].osu";
|
||||
|
||||
[Test]
|
||||
public void TestDecodeMetadata()
|
||||
{
|
||||
var beatmap = decodeAsJson(normal);
|
||||
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 beatmap = decodeAsJson(normal);
|
||||
var beatmapInfo = beatmap.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 beatmap = decodeAsJson(normal);
|
||||
var beatmapInfo = beatmap.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 TestDecodeDifficulty()
|
||||
{
|
||||
var beatmap = decodeAsJson(normal);
|
||||
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 beatmap = decodeAsJson(normal);
|
||||
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 beatmap = decodeAsJson(normal);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
[TestCase(normal)]
|
||||
[TestCase(marathon)]
|
||||
// Currently fails:
|
||||
// [TestCase(with_sb)]
|
||||
public void TestParity(string beatmap)
|
||||
{
|
||||
var beatmaps = decode(beatmap);
|
||||
beatmaps.jsonDecoded.ShouldDeepEqual(beatmaps.legacyDecoded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a .osu file first with a <see cref="LegacyBeatmapDecoder"/>, serializes the resulting <see cref="Beatmap"/> to JSON
|
||||
/// and then deserializes the result back into a <see cref="Beatmap"/> through an <see cref="JsonBeatmapDecoder"/>.
|
||||
/// </summary>
|
||||
/// <param name="filename">The .osu file to decode.</param>
|
||||
/// <returns>The <see cref="Beatmap"/> after being decoded by an <see cref="LegacyBeatmapDecoder"/>.</returns>
|
||||
private Beatmap decodeAsJson(string filename) => decode(filename).jsonDecoded;
|
||||
|
||||
/// <summary>
|
||||
/// Reads a .osu file first with a <see cref="LegacyBeatmapDecoder"/>, serializes the resulting <see cref="Beatmap"/> to JSON
|
||||
/// and then deserializes the result back into a <see cref="Beatmap"/> through an <see cref="JsonBeatmapDecoder"/>.
|
||||
/// </summary>
|
||||
/// <param name="filename">The .osu file to decode.</param>
|
||||
/// <returns>The <see cref="Beatmap"/> after being decoded by an <see cref="LegacyBeatmapDecoder"/>.</returns>
|
||||
private (Beatmap legacyDecoded, Beatmap jsonDecoded) decode(string filename)
|
||||
{
|
||||
using (var stream = Resource.OpenResource(filename))
|
||||
using (var sr = new StreamReader(stream))
|
||||
{
|
||||
|
||||
var legacyDecoded = new LegacyBeatmapDecoder().DecodeBeatmap(sr);
|
||||
using (var ms = new MemoryStream())
|
||||
using (var sw = new StreamWriter(ms))
|
||||
using (var sr2 = new StreamReader(ms))
|
||||
{
|
||||
sw.Write(legacyDecoded.Serialize());
|
||||
sw.Flush();
|
||||
|
||||
ms.Position = 0;
|
||||
return (legacyDecoded, new JsonBeatmapDecoder().DecodeBeatmap(sr2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 = (IHasPosition)beatmap.HitObjects[0];
|
||||
|
||||
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 = (IHasPosition)beatmap.HitObjects[1];
|
||||
|
||||
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);
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user