mirror of
https://github.com/ppy/osu.git
synced 2025-01-14 23:22:55 +08:00
Merge master into netstandard
This commit is contained in:
commit
7a793eaded
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"
|
||||
}
|
||||
]
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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,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.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
@ -28,7 +29,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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -77,7 +77,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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -116,7 +116,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()
|
||||
{
|
||||
|
@ -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>()
|
||||
};
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
@ -56,6 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Scale = s.Scale,
|
||||
ComboColour = s.ComboColour,
|
||||
Samples = s.Samples,
|
||||
SampleControlPoint = s.SampleControlPoint
|
||||
})
|
||||
};
|
||||
|
||||
@ -65,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
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);
|
||||
@ -82,7 +84,7 @@ 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);
|
||||
@ -165,6 +167,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
}
|
||||
|
||||
public Drawable ProxiedLayer => initialCircle.ApproachCircle;
|
||||
|
||||
public override Vector2 SelectionPoint => ToScreenSpace(body.Position);
|
||||
public override Quad SelectionQuad => body.PathDrawQuad;
|
||||
}
|
||||
|
||||
internal interface ISliderProgress
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,75 +99,79 @@ 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 length = Curve.Distance;
|
||||
var repeatPointDistance = Math.Min(Distance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
|
||||
for (var repeat = 1; repeat < RepeatCount; repeat++)
|
||||
{
|
||||
var length = Curve.Distance;
|
||||
var repeatPointDistance = Math.Min(Distance, length);
|
||||
var repeatDuration = length / Velocity;
|
||||
|
||||
for (var repeat = 1; repeat < RepeatCount; repeat++)
|
||||
for (var d = repeatPointDistance; d <= length; d += repeatPointDistance)
|
||||
{
|
||||
for (var d = repeatPointDistance; d <= length; d += repeatPointDistance)
|
||||
{
|
||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||
var distanceProgress = d / length;
|
||||
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,
|
||||
};
|
||||
}
|
||||
AddNested(new RepeatPoint
|
||||
{
|
||||
RepeatIndex = repeat,
|
||||
StartTime = repeatStartTime,
|
||||
Position = Curve.PositionAt(distanceProgress),
|
||||
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;
|
||||
|
@ -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)
|
||||
|
@ -2,6 +2,7 @@
|
||||
// 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;
|
||||
@ -39,11 +40,11 @@ namespace osu.Game.Rulesets.Osu.Scoring
|
||||
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 });
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Ignore("getting CI working")]
|
||||
internal class TestCaseHitObjects : OsuTestCase
|
||||
public class TestCaseHitObjects : OsuTestCase
|
||||
{
|
||||
private FramedClock framedClock;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
47
osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
Normal file
47
osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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<SampleControlPoint, DrumSample> mappings = new Dictionary<SampleControlPoint, 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.Distinct())
|
||||
{
|
||||
mappings[s] = new DrumSample
|
||||
{
|
||||
Centre = s.GetSampleInfo().GetChannel(audio.Sample),
|
||||
Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public DrumSample SampleAt(double time) => mappings[controlPoints.SamplePointAt(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);
|
||||
|
||||
|
@ -43,7 +43,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;
|
||||
|
@ -67,6 +67,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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -34,10 +34,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 +201,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,9 @@ 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 virtual TaikoPiece CreateMainPiece() => new CirclePiece();
|
||||
|
||||
public abstract bool OnPressed(TaikoAction action);
|
||||
|
@ -37,53 +37,46 @@ 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
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
@ -93,8 +86,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
|
||||
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,7 +84,7 @@ 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));
|
||||
hitButton = !hitButton;
|
||||
|
@ -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.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@ -88,7 +89,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);
|
||||
|
@ -25,7 +25,7 @@ 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 +165,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 +188,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
Duration = duration,
|
||||
};
|
||||
|
||||
d.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
rulesetContainer.Playfield.Add(new DrawableDrumRoll(d));
|
||||
}
|
||||
|
||||
@ -195,6 +201,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 +217,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
IsStrong = strong
|
||||
};
|
||||
|
||||
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
if (strong)
|
||||
rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h));
|
||||
else
|
||||
|
@ -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("SB/lyric/ja-21.png", sprite.Path);
|
||||
|
||||
var animation = background.Elements.ElementAt(12) as StoryboardAnimation;
|
||||
Assert.NotNull(animation);
|
||||
Assert.AreEqual(141175, animation.EndTime);
|
||||
Assert.AreEqual(10, animation.FrameCount);
|
||||
Assert.AreEqual(30, animation.FrameDelay);
|
||||
Assert.IsTrue(animation.HasCommands);
|
||||
Assert.AreEqual(new Vector2(320, 240), animation.InitialPosition);
|
||||
Assert.IsTrue(animation.IsDrawable);
|
||||
Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType);
|
||||
Assert.AreEqual(Anchor.Centre, animation.Origin);
|
||||
Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path);
|
||||
Assert.AreEqual(78993, animation.StartTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -7,17 +7,17 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Overlays;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Lists;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseBeatSyncedContainer : OsuTestCase
|
||||
public class TestCaseBeatSyncedContainer : OsuTestCase
|
||||
{
|
||||
private readonly MusicController mc;
|
||||
|
||||
|
394
osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
Normal file
394
osu.Game.Tests/Visual/TestCaseBeatmapCarousel.cs
Normal file
@ -0,0 +1,394 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Carousel;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseBeatmapCarousel : OsuTestCase
|
||||
{
|
||||
private TestBeatmapCarousel carousel;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(CarouselItem),
|
||||
typeof(CarouselGroup),
|
||||
typeof(CarouselGroupEagerSelect),
|
||||
typeof(CarouselBeatmap),
|
||||
typeof(CarouselBeatmapSet),
|
||||
|
||||
typeof(DrawableCarouselItem),
|
||||
typeof(CarouselItemState),
|
||||
|
||||
typeof(DrawableCarouselBeatmap),
|
||||
typeof(DrawableCarouselBeatmapSet),
|
||||
};
|
||||
|
||||
|
||||
private readonly Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>();
|
||||
|
||||
private BeatmapInfo currentSelection;
|
||||
|
||||
private const int set_count = 5;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Add(carousel = new TestBeatmapCarousel
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
});
|
||||
|
||||
List<BeatmapSetInfo> beatmapSets = new List<BeatmapSetInfo>();
|
||||
|
||||
for (int i = 1; i <= set_count; i++)
|
||||
beatmapSets.Add(createTestBeatmapSet(i));
|
||||
|
||||
carousel.SelectionChanged = s => currentSelection = s;
|
||||
|
||||
AddStep("Load Beatmaps", () => { carousel.BeatmapSets = beatmapSets; });
|
||||
|
||||
AddUntilStep(() => carousel.BeatmapSets.Any(), "Wait for load");
|
||||
|
||||
testTraversal();
|
||||
testFiltering();
|
||||
testRandom();
|
||||
testAddRemove();
|
||||
testSorting();
|
||||
|
||||
testRemoveAll();
|
||||
testEmptyTraversal();
|
||||
testHiding();
|
||||
}
|
||||
|
||||
private void ensureRandomFetchSuccess() =>
|
||||
AddAssert("ensure prev random fetch worked", () => selectedSets.Peek() == carousel.SelectedBeatmapSet);
|
||||
|
||||
private void checkSelected(int set, int? diff = null) =>
|
||||
AddAssert($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () =>
|
||||
{
|
||||
if (diff != null)
|
||||
return carousel.SelectedBeatmap == carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff.Value - 1).First();
|
||||
|
||||
return carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Contains(carousel.SelectedBeatmap);
|
||||
});
|
||||
|
||||
private void setSelected(int set, int diff) =>
|
||||
AddStep($"select set{set} diff{diff}", () =>
|
||||
carousel.SelectBeatmap(carousel.BeatmapSets.Skip(set - 1).First().Beatmaps.Skip(diff - 1).First()));
|
||||
|
||||
private void advanceSelection(bool diff, int direction = 1, int count = 1)
|
||||
{
|
||||
if (count == 1)
|
||||
AddStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () =>
|
||||
carousel.SelectNext(direction, !diff));
|
||||
else
|
||||
{
|
||||
AddRepeatStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () =>
|
||||
carousel.SelectNext(direction, !diff), count);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkVisibleItemCount(bool diff, int count) =>
|
||||
AddAssert($"{count} {(diff ? "diffs" : "sets")} visible", () =>
|
||||
carousel.Items.Count(s => (diff ? s.Item is CarouselBeatmap : s.Item is CarouselBeatmapSet) && s.Item.Visible) == count);
|
||||
|
||||
private void checkNoSelection() => AddAssert("Selection is null", () => currentSelection == null);
|
||||
|
||||
private void nextRandom() =>
|
||||
AddStep("select random next", () =>
|
||||
{
|
||||
carousel.RandomAlgorithm.Value = RandomSelectAlgorithm.RandomPermutation;
|
||||
|
||||
if (!selectedSets.Any() && carousel.SelectedBeatmap != null)
|
||||
selectedSets.Push(carousel.SelectedBeatmapSet);
|
||||
|
||||
carousel.SelectNextRandom();
|
||||
selectedSets.Push(carousel.SelectedBeatmapSet);
|
||||
});
|
||||
|
||||
private void ensureRandomDidntRepeat() =>
|
||||
AddAssert("ensure no repeats", () => selectedSets.Distinct().Count() == selectedSets.Count);
|
||||
|
||||
private void prevRandom() => AddStep("select random last", () =>
|
||||
{
|
||||
carousel.SelectPreviousRandom();
|
||||
selectedSets.Pop();
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Test keyboard traversal
|
||||
/// </summary>
|
||||
private void testTraversal()
|
||||
{
|
||||
advanceSelection(direction: 1, diff: false);
|
||||
checkSelected(1, 1);
|
||||
|
||||
advanceSelection(direction: 1, diff: true);
|
||||
checkSelected(1, 2);
|
||||
|
||||
advanceSelection(direction: -1, diff: false);
|
||||
checkSelected(set_count, 1);
|
||||
|
||||
advanceSelection(direction: -1, diff: true);
|
||||
checkSelected(set_count - 1, 3);
|
||||
|
||||
advanceSelection(diff: false);
|
||||
advanceSelection(diff: false);
|
||||
checkSelected(1, 2);
|
||||
|
||||
advanceSelection(direction: -1, diff: true);
|
||||
advanceSelection(direction: -1, diff: true);
|
||||
checkSelected(set_count, 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test filtering
|
||||
/// </summary>
|
||||
private void testFiltering()
|
||||
{
|
||||
// basic filtering
|
||||
|
||||
setSelected(1, 1);
|
||||
|
||||
AddStep("Filter", () => carousel.Filter(new FilterCriteria { SearchText = "set #3!" }, false));
|
||||
checkVisibleItemCount(diff: false, count: 1);
|
||||
checkVisibleItemCount(diff: true, count: 3);
|
||||
checkSelected(3, 1);
|
||||
|
||||
advanceSelection(diff: true, count: 4);
|
||||
checkSelected(3, 2);
|
||||
|
||||
AddStep("Un-filter (debounce)", () => carousel.Filter(new FilterCriteria()));
|
||||
AddUntilStep(() => !carousel.PendingFilterTask, "Wait for debounce");
|
||||
checkVisibleItemCount(diff: false, count: set_count);
|
||||
checkVisibleItemCount(diff: true, count: 3);
|
||||
|
||||
// test filtering some difficulties (and keeping current beatmap set selected).
|
||||
|
||||
setSelected(1, 2);
|
||||
AddStep("Filter some difficulties", () => carousel.Filter(new FilterCriteria { SearchText = "Normal" }, false));
|
||||
checkSelected(1, 1);
|
||||
|
||||
AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false));
|
||||
checkSelected(1, 1);
|
||||
|
||||
AddStep("Filter all", () => carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false));
|
||||
|
||||
checkVisibleItemCount(false, 0);
|
||||
checkVisibleItemCount(true, 0);
|
||||
AddAssert("Selection is null", () => currentSelection == null);
|
||||
|
||||
AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false));
|
||||
|
||||
AddAssert("Selection is non-null", () => currentSelection != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test random non-repeating algorithm
|
||||
/// </summary>
|
||||
private void testRandom()
|
||||
{
|
||||
setSelected(1, 1);
|
||||
|
||||
nextRandom();
|
||||
ensureRandomDidntRepeat();
|
||||
nextRandom();
|
||||
ensureRandomDidntRepeat();
|
||||
nextRandom();
|
||||
ensureRandomDidntRepeat();
|
||||
|
||||
prevRandom();
|
||||
ensureRandomFetchSuccess();
|
||||
prevRandom();
|
||||
ensureRandomFetchSuccess();
|
||||
|
||||
nextRandom();
|
||||
ensureRandomDidntRepeat();
|
||||
nextRandom();
|
||||
ensureRandomDidntRepeat();
|
||||
|
||||
nextRandom();
|
||||
AddAssert("ensure repeat", () => selectedSets.Contains(carousel.SelectedBeatmapSet));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test adding and removing beatmap sets
|
||||
/// </summary>
|
||||
private void testAddRemove()
|
||||
{
|
||||
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 1)));
|
||||
AddStep("Add new set", () => carousel.UpdateBeatmapSet(createTestBeatmapSet(set_count + 2)));
|
||||
|
||||
checkVisibleItemCount(false, set_count + 2);
|
||||
|
||||
AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 2)));
|
||||
|
||||
checkVisibleItemCount(false, set_count + 1);
|
||||
|
||||
setSelected(set_count + 1, 1);
|
||||
|
||||
AddStep("Remove set", () => carousel.RemoveBeatmapSet(createTestBeatmapSet(set_count + 1)));
|
||||
|
||||
checkVisibleItemCount(false, set_count);
|
||||
|
||||
checkSelected(set_count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test sorting
|
||||
/// </summary>
|
||||
private void testSorting()
|
||||
{
|
||||
AddStep("Sort by author", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Author }, false));
|
||||
AddAssert("Check zzzzz is at bottom", () => carousel.BeatmapSets.Last().Metadata.AuthorString == "zzzzz");
|
||||
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
|
||||
AddAssert($"Check #{set_count} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Title.EndsWith($"#{set_count}!"));
|
||||
}
|
||||
|
||||
private void testRemoveAll()
|
||||
{
|
||||
setSelected(2, 1);
|
||||
AddAssert("Selection is non-null", () => currentSelection != null);
|
||||
|
||||
AddStep("Remove selected", () => carousel.RemoveBeatmapSet(carousel.SelectedBeatmapSet));
|
||||
checkSelected(2);
|
||||
|
||||
AddStep("Remove first", () => carousel.RemoveBeatmapSet(carousel.BeatmapSets.First()));
|
||||
AddStep("Remove first", () => carousel.RemoveBeatmapSet(carousel.BeatmapSets.First()));
|
||||
checkSelected(1);
|
||||
|
||||
AddUntilStep(() =>
|
||||
{
|
||||
if (!carousel.BeatmapSets.Any()) return true;
|
||||
|
||||
carousel.RemoveBeatmapSet(carousel.BeatmapSets.Last());
|
||||
return false;
|
||||
}, "Remove all");
|
||||
|
||||
checkNoSelection();
|
||||
}
|
||||
|
||||
private void testEmptyTraversal()
|
||||
{
|
||||
advanceSelection(direction: 1, diff: false);
|
||||
checkNoSelection();
|
||||
|
||||
advanceSelection(direction: 1, diff: true);
|
||||
checkNoSelection();
|
||||
|
||||
advanceSelection(direction: -1, diff: false);
|
||||
checkNoSelection();
|
||||
|
||||
advanceSelection(direction: -1, diff: true);
|
||||
checkNoSelection();
|
||||
}
|
||||
|
||||
private void testHiding()
|
||||
{
|
||||
var hidingSet = createTestBeatmapSet(1);
|
||||
hidingSet.Beatmaps[1].Hidden = true;
|
||||
AddStep("Add set with diff 2 hidden", () => carousel.UpdateBeatmapSet(hidingSet));
|
||||
setSelected(1, 1);
|
||||
|
||||
checkVisibleItemCount(true, 2);
|
||||
advanceSelection(true);
|
||||
checkSelected(1, 3);
|
||||
|
||||
setHidden(3);
|
||||
checkSelected(1, 1);
|
||||
|
||||
setHidden(2, false);
|
||||
advanceSelection(true);
|
||||
checkSelected(1, 2);
|
||||
|
||||
setHidden(1);
|
||||
checkSelected(1, 2);
|
||||
|
||||
setHidden(2);
|
||||
checkNoSelection();
|
||||
|
||||
void setHidden(int diff, bool hidden = true)
|
||||
{
|
||||
AddStep((hidden ? "" : "un") + $"hide diff {diff}", () =>
|
||||
{
|
||||
hidingSet.Beatmaps[diff - 1].Hidden = hidden;
|
||||
carousel.UpdateBeatmapSet(hidingSet);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private BeatmapSetInfo createTestBeatmapSet(int i)
|
||||
{
|
||||
return new BeatmapSetInfo
|
||||
{
|
||||
ID = i,
|
||||
OnlineBeatmapSetID = i,
|
||||
Hash = new MemoryStream(Encoding.UTF8.GetBytes(Guid.NewGuid().ToString())).ComputeMD5Hash(),
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
OnlineBeatmapSetID = i,
|
||||
// Create random metadata, then we can check if sorting works based on these
|
||||
Artist = $"peppy{i.ToString().PadLeft(6, '0')}",
|
||||
Title = $"test set #{i}!",
|
||||
AuthorString = string.Concat(Enumerable.Repeat((char)('z' - Math.Min(25, i - 1)), 5))
|
||||
},
|
||||
Beatmaps = new List<BeatmapInfo>(new[]
|
||||
{
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineBeatmapID = i * 10,
|
||||
Path = "normal.osu",
|
||||
Version = "Normal",
|
||||
StarDifficulty = 2,
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
OverallDifficulty = 3.5f,
|
||||
}
|
||||
},
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineBeatmapID = i * 10 + 1,
|
||||
Path = "hard.osu",
|
||||
Version = "Hard",
|
||||
StarDifficulty = 5,
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
OverallDifficulty = 5,
|
||||
}
|
||||
},
|
||||
new BeatmapInfo
|
||||
{
|
||||
OnlineBeatmapID = i * 10 + 2,
|
||||
Path = "insane.osu",
|
||||
Version = "Insane",
|
||||
StarDifficulty = 6,
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
OverallDifficulty = 7,
|
||||
}
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
private class TestBeatmapCarousel : BeatmapCarousel
|
||||
{
|
||||
public new List<DrawableCarouselItem> Items => base.Items;
|
||||
|
||||
public bool PendingFilterTask => FilterTask != null;
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[TestFixture]
|
||||
[System.ComponentModel.Description("PlaySongSelect leaderboard/details area")]
|
||||
internal class TestCaseBeatmapDetailArea : OsuTestCase
|
||||
public class TestCaseBeatmapDetailArea : OsuTestCase
|
||||
{
|
||||
public TestCaseBeatmapDetailArea()
|
||||
{
|
||||
|
@ -10,7 +10,7 @@ using osu.Game.Screens.Select;
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[Description("PlaySongSelect beatmap details")]
|
||||
internal class TestCaseBeatmapDetails : OsuTestCase
|
||||
public class TestCaseBeatmapDetails : OsuTestCase
|
||||
{
|
||||
public TestCaseBeatmapDetails()
|
||||
{
|
||||
|
161
osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
Normal file
161
osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
Normal file
@ -0,0 +1,161 @@
|
||||
// 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.Linq;
|
||||
using OpenTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseBeatmapInfoWedge : OsuTestCase
|
||||
{
|
||||
private RulesetStore rulesets;
|
||||
private TestBeatmapInfoWedge infoWedge;
|
||||
private readonly List<Beatmap> beatmaps = new List<Beatmap>();
|
||||
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game, RulesetStore rulesets)
|
||||
{
|
||||
this.rulesets = rulesets;
|
||||
|
||||
beatmap.BindTo(game.Beatmap);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Add(infoWedge = new TestBeatmapInfoWedge
|
||||
{
|
||||
Size = new Vector2(0.5f, 245),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Margin = new MarginPadding { Top = 20 }
|
||||
});
|
||||
|
||||
AddStep("show", () =>
|
||||
{
|
||||
infoWedge.State = Visibility.Visible;
|
||||
infoWedge.UpdateBeatmap(beatmap);
|
||||
});
|
||||
|
||||
AddWaitStep(3);
|
||||
|
||||
AddStep("hide", () => { infoWedge.State = Visibility.Hidden; });
|
||||
|
||||
AddWaitStep(3);
|
||||
|
||||
AddStep("show", () => { infoWedge.State = Visibility.Visible; });
|
||||
|
||||
foreach (var rulesetInfo in rulesets.AvailableRulesets)
|
||||
{
|
||||
var ruleset = rulesetInfo.CreateInstance();
|
||||
beatmaps.Add(createTestBeatmap(rulesetInfo));
|
||||
|
||||
var name = rulesetInfo.ShortName;
|
||||
selectBeatmap(name);
|
||||
|
||||
// TODO: adjust cases once more info is shown for other gamemodes
|
||||
switch (ruleset)
|
||||
{
|
||||
case OsuRuleset osu:
|
||||
testOsuBeatmap(osu);
|
||||
testInfoLabels(5);
|
||||
break;
|
||||
default:
|
||||
testInfoLabels(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
testNullBeatmap();
|
||||
}
|
||||
|
||||
private void testOsuBeatmap(OsuRuleset ruleset)
|
||||
{
|
||||
AddAssert("check version", () => infoWedge.Info.VersionLabel.Text == $"{ruleset.ShortName}Version");
|
||||
AddAssert("check title", () => infoWedge.Info.TitleLabel.Text == $"{ruleset.ShortName}Source — {ruleset.ShortName}Title");
|
||||
AddAssert("check artist", () => infoWedge.Info.ArtistLabel.Text == $"{ruleset.ShortName}Artist");
|
||||
AddAssert("check author", () => infoWedge.Info.MapperContainer.Children.OfType<OsuSpriteText>().Any(s => s.Text == $"{ruleset.ShortName}Author"));
|
||||
}
|
||||
|
||||
private void testInfoLabels(int expectedCount)
|
||||
{
|
||||
AddAssert("check infolabels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any());
|
||||
AddAssert("check infolabels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount);
|
||||
}
|
||||
|
||||
private void testNullBeatmap()
|
||||
{
|
||||
selectNullBeatmap();
|
||||
AddAssert("check empty version", () => string.IsNullOrEmpty(infoWedge.Info.VersionLabel.Text));
|
||||
AddAssert("check default title", () => infoWedge.Info.TitleLabel.Text == beatmap.Default.BeatmapInfo.Metadata.Title);
|
||||
AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Text == beatmap.Default.BeatmapInfo.Metadata.Artist);
|
||||
AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any());
|
||||
AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
|
||||
}
|
||||
|
||||
private void selectBeatmap(string name)
|
||||
{
|
||||
var infoBefore = infoWedge.Info;
|
||||
|
||||
AddStep($"select {name} beatmap", () =>
|
||||
{
|
||||
beatmap.Value = new TestWorkingBeatmap(beatmaps.First(b => b.BeatmapInfo.Ruleset.ShortName == name));
|
||||
infoWedge.UpdateBeatmap(beatmap);
|
||||
});
|
||||
|
||||
AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load");
|
||||
}
|
||||
|
||||
private void selectNullBeatmap()
|
||||
{
|
||||
AddStep("select null beatmap", () =>
|
||||
{
|
||||
beatmap.Value = beatmap.Default;
|
||||
infoWedge.UpdateBeatmap(beatmap);
|
||||
});
|
||||
}
|
||||
|
||||
private Beatmap createTestBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
List<HitObject> objects = new List<HitObject>();
|
||||
for (double i = 0; i < 50000; i += 1000)
|
||||
objects.Add(new HitObject { StartTime = i });
|
||||
|
||||
return new Beatmap
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
AuthorString = $"{ruleset.ShortName}Author",
|
||||
Artist = $"{ruleset.ShortName}Artist",
|
||||
Source = $"{ruleset.ShortName}Source",
|
||||
Title = $"{ruleset.ShortName}Title"
|
||||
},
|
||||
Ruleset = ruleset,
|
||||
StarDifficulty = 6,
|
||||
Version = $"{ruleset.ShortName}Version"
|
||||
},
|
||||
HitObjects = objects
|
||||
};
|
||||
}
|
||||
|
||||
private class TestBeatmapInfoWedge : BeatmapInfoWedge
|
||||
{
|
||||
public new BufferedWedgeInfo Info => base.Info;
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ using OpenTK.Input;
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[Description("bottom beatmap details")]
|
||||
internal class TestCaseBeatmapOptionsOverlay : OsuTestCase
|
||||
public class TestCaseBeatmapOptionsOverlay : OsuTestCase
|
||||
{
|
||||
public TestCaseBeatmapOptionsOverlay()
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseBeatmapSetOverlay : OsuTestCase
|
||||
public class TestCaseBeatmapSetOverlay : OsuTestCase
|
||||
{
|
||||
private readonly BeatmapSetOverlay overlay;
|
||||
|
||||
|
@ -6,7 +6,7 @@ using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseBreadcrumbs : OsuTestCase
|
||||
public class TestCaseBreadcrumbs : OsuTestCase
|
||||
{
|
||||
public TestCaseBreadcrumbs()
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseBreakOverlay : OsuTestCase
|
||||
public class TestCaseBreakOverlay : OsuTestCase
|
||||
{
|
||||
private readonly BreakOverlay breakOverlay;
|
||||
|
||||
|
@ -9,7 +9,7 @@ using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseButtonSystem : OsuTestCase
|
||||
public class TestCaseButtonSystem : OsuTestCase
|
||||
{
|
||||
public TestCaseButtonSystem()
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ using osu.Game.Overlays;
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[Description("Testing chat api and overlay")]
|
||||
internal class TestCaseChatDisplay : OsuTestCase
|
||||
public class TestCaseChatDisplay : OsuTestCase
|
||||
{
|
||||
public TestCaseChatDisplay()
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ using osu.Game.Graphics.Cursor;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseContextMenu : OsuTestCase
|
||||
public class TestCaseContextMenu : OsuTestCase
|
||||
{
|
||||
private const int start_time = 0;
|
||||
private const int duration = 1000;
|
||||
|
@ -7,7 +7,7 @@ using osu.Game.Overlays.Dialog;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseDialogOverlay : OsuTestCase
|
||||
public class TestCaseDialogOverlay : OsuTestCase
|
||||
{
|
||||
public TestCaseDialogOverlay()
|
||||
{
|
||||
|
@ -12,7 +12,7 @@ using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseDrawableRoom : OsuTestCase
|
||||
public class TestCaseDrawableRoom : OsuTestCase
|
||||
{
|
||||
private RulesetStore rulesets;
|
||||
|
||||
|
@ -9,7 +9,7 @@ using osu.Game.Screens.Tournament.Teams;
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[Description("for tournament use")]
|
||||
internal class TestCaseDrawings : OsuTestCase
|
||||
public class TestCaseDrawings : OsuTestCase
|
||||
{
|
||||
public TestCaseDrawings()
|
||||
{
|
||||
|
46
osu.Game.Tests/Visual/TestCaseEditorCompose.cs
Normal file
46
osu.Game.Tests/Visual/TestCaseEditorCompose.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;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Edit.Screens.Compose;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseEditorCompose : OsuTestCase
|
||||
{
|
||||
private readonly Random random;
|
||||
private readonly Compose compose;
|
||||
|
||||
public TestCaseEditorCompose()
|
||||
{
|
||||
random = new Random(1337);
|
||||
|
||||
Add(compose = new Compose());
|
||||
AddStep("Next beatmap", nextBeatmap);
|
||||
}
|
||||
|
||||
private OsuGameBase osuGame;
|
||||
private BeatmapManager beatmaps;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase osuGame, BeatmapManager beatmaps)
|
||||
{
|
||||
this.osuGame = osuGame;
|
||||
this.beatmaps = beatmaps;
|
||||
|
||||
compose.Beatmap.BindTo(osuGame.Beatmap);
|
||||
}
|
||||
|
||||
private void nextBeatmap()
|
||||
{
|
||||
var sets = beatmaps.GetAllUsableBeatmapSets();
|
||||
if (sets.Count == 0)
|
||||
return;
|
||||
|
||||
var b = sets[random.Next(0, sets.Count)].Beatmaps[0];
|
||||
osuGame.Beatmap.Value = beatmaps.GetWorkingBeatmap(b);
|
||||
}
|
||||
}
|
||||
}
|
41
osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs
Normal file
41
osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs
Normal file
@ -0,0 +1,41 @@
|
||||
// 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 osu.Framework.Graphics;
|
||||
using osu.Game.Screens.Edit.Screens.Compose.RadioButtons;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseEditorComposeRadioButtons : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(DrawableRadioButton) };
|
||||
|
||||
public TestCaseEditorComposeRadioButtons()
|
||||
{
|
||||
RadioButtonCollection collection;
|
||||
Add(collection = new RadioButtonCollection
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 150,
|
||||
Items = new[]
|
||||
{
|
||||
new RadioButton("Item 1", () => { }),
|
||||
new RadioButton("Item 2", () => { }),
|
||||
new RadioButton("Item 3", () => { }),
|
||||
new RadioButton("Item 4", () => { }),
|
||||
new RadioButton("Item 5", () => { })
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i < collection.Items.Count; i++)
|
||||
{
|
||||
int l = i;
|
||||
AddStep($"Select item {l + 1}", () => collection.Items[l].Select());
|
||||
AddStep($"Deselect item {l + 1}", () => collection.Items[l].Deselect());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
54
osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs
Normal file
54
osu.Game.Tests/Visual/TestCaseEditorSelectionLayer.cs
Normal file
@ -0,0 +1,54 @@
|
||||
// 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 OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Edit.Layers.Selection;
|
||||
using osu.Game.Rulesets.Osu.Edit;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseEditorSelectionLayer : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(SelectionLayer) };
|
||||
|
||||
public TestCaseEditorSelectionLayer()
|
||||
{
|
||||
var playfield = new OsuEditPlayfield
|
||||
{
|
||||
new DrawableHitCircle(new HitCircle { Position = new Vector2(256, 192), Scale = 0.5f }),
|
||||
new DrawableHitCircle(new HitCircle { Position = new Vector2(344, 148), Scale = 0.5f }),
|
||||
new DrawableSlider(new Slider
|
||||
{
|
||||
ControlPoints = new List<Vector2>
|
||||
{
|
||||
new Vector2(128, 256),
|
||||
new Vector2(344, 256),
|
||||
},
|
||||
Distance = 400,
|
||||
Position = new Vector2(128, 256),
|
||||
Velocity = 1,
|
||||
TickDistance = 100,
|
||||
Scale = 0.5f
|
||||
})
|
||||
};
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = new FramedClock(new StopwatchClock()),
|
||||
Child = playfield
|
||||
},
|
||||
new SelectionLayer(playfield)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ using osu.Framework.Configuration;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseEditorSummaryTimeline : OsuTestCase
|
||||
public class TestCaseEditorSummaryTimeline : OsuTestCase
|
||||
{
|
||||
private const int length = 60000;
|
||||
private readonly Random random;
|
||||
@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual
|
||||
b.ControlPointInfo.EffectPoints.Add(new EffectControlPoint { Time = random.Next(0, length) });
|
||||
|
||||
for (int i = 0; i < random.Next(1, 5); i++)
|
||||
b.ControlPointInfo.SoundPoints.Add(new SoundControlPoint { Time = random.Next(0, length) });
|
||||
b.ControlPointInfo.SamplePoints.Add(new SampleControlPoint { Time = random.Next(0, length) });
|
||||
|
||||
b.BeatmapInfo.Bookmarks = new int[random.Next(10, 30)];
|
||||
for (int i = 0; i < b.BeatmapInfo.Bookmarks.Length; i++)
|
||||
|
@ -5,7 +5,7 @@ using osu.Game.Beatmaps.ControlPoints;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseGamefield : OsuTestCase
|
||||
public class TestCaseGamefield : OsuTestCase
|
||||
{
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
|
258
osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
Normal file
258
osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
Normal file
@ -0,0 +1,258 @@
|
||||
// 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.ComponentModel;
|
||||
using System.Linq;
|
||||
using OpenTK.Input;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[Description("player pause/fail screens")]
|
||||
public class TestCaseGameplayMenuOverlay : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) };
|
||||
|
||||
private FailOverlay failOverlay;
|
||||
private PauseContainer.PauseOverlay pauseOverlay;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Add(pauseOverlay = new PauseContainer.PauseOverlay
|
||||
{
|
||||
OnResume = () => Logger.Log(@"Resume"),
|
||||
OnRetry = () => Logger.Log(@"Retry"),
|
||||
OnQuit = () => Logger.Log(@"Quit"),
|
||||
});
|
||||
|
||||
Add(failOverlay = new FailOverlay
|
||||
{
|
||||
OnRetry = () => Logger.Log(@"Retry"),
|
||||
OnQuit = () => Logger.Log(@"Quit"),
|
||||
});
|
||||
|
||||
var retryCount = 0;
|
||||
|
||||
AddStep("Add retry", () =>
|
||||
{
|
||||
retryCount++;
|
||||
pauseOverlay.Retries = failOverlay.Retries = retryCount;
|
||||
});
|
||||
|
||||
AddToggleStep("Toggle pause overlay", t => pauseOverlay.ToggleVisibility());
|
||||
AddToggleStep("Toggle fail overlay", t => failOverlay.ToggleVisibility());
|
||||
|
||||
testHideResets();
|
||||
|
||||
testEnterWithoutSelection();
|
||||
testKeyUpFromInitial();
|
||||
testKeyDownFromInitial();
|
||||
testKeyUpWrapping();
|
||||
testKeyDownWrapping();
|
||||
|
||||
testMouseSelectionAfterKeySelection();
|
||||
testKeySelectionAfterMouseSelection();
|
||||
|
||||
testMouseDeselectionResets();
|
||||
|
||||
testClickSelection();
|
||||
testEnterKeySelection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that hiding the overlay after hovering a button will reset the overlay to the initial state with no buttons selected.
|
||||
/// </summary>
|
||||
private void testHideResets()
|
||||
{
|
||||
AddStep("Show overlay", () => failOverlay.Show());
|
||||
|
||||
AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnHover(null));
|
||||
AddStep("Hide overlay", () => failOverlay.Hide());
|
||||
|
||||
AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred.
|
||||
/// </summary>
|
||||
private void testEnterWithoutSelection()
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
|
||||
AddStep("Press enter", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter }));
|
||||
AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible);
|
||||
|
||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that pressing the up arrow from the initial state selects the last button.
|
||||
/// </summary>
|
||||
private void testKeyUpFromInitial()
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
|
||||
AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
|
||||
AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected);
|
||||
|
||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that pressing the down arrow from the initial state selects the first button.
|
||||
/// </summary>
|
||||
private void testKeyDownFromInitial()
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
|
||||
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
|
||||
|
||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that pressing the up arrow repeatedly causes the selected button to wrap correctly.
|
||||
/// </summary>
|
||||
private void testKeyUpWrapping()
|
||||
{
|
||||
AddStep("Show overlay", () => failOverlay.Show());
|
||||
|
||||
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
|
||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
|
||||
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
|
||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
|
||||
AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
|
||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
|
||||
|
||||
AddStep("Hide overlay", () => failOverlay.Hide());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that pressing the down arrow repeatedly causes the selected button to wrap correctly.
|
||||
/// </summary>
|
||||
private void testKeyDownWrapping()
|
||||
{
|
||||
AddStep("Show overlay", () => failOverlay.Show());
|
||||
|
||||
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
|
||||
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
|
||||
AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
|
||||
|
||||
AddStep("Hide overlay", () => failOverlay.Hide());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that hovering a button that was previously selected with the keyboard correctly selects the new button and deselects the previous button.
|
||||
/// </summary>
|
||||
private void testMouseSelectionAfterKeySelection()
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
|
||||
var secondButton = pauseOverlay.Buttons.Skip(1).First();
|
||||
|
||||
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddStep("Hover second button", () => secondButton.TriggerOnHover(null));
|
||||
AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected);
|
||||
AddAssert("Second button selected", () => secondButton.Selected);
|
||||
|
||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that pressing a key after selecting a button with a hover event correctly selects a new button and deselects the previous button.
|
||||
/// </summary>
|
||||
private void testKeySelectionAfterMouseSelection()
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
|
||||
var secondButton = pauseOverlay.Buttons.Skip(1).First();
|
||||
|
||||
AddStep("Hover second button", () => secondButton.TriggerOnHover(null));
|
||||
AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
|
||||
AddAssert("Second button not selected", () => !secondButton.Selected);
|
||||
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
|
||||
|
||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that deselecting with the mouse by losing hover will reset the overlay to the initial state.
|
||||
/// </summary>
|
||||
private void testMouseDeselectionResets()
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
|
||||
var secondButton = pauseOverlay.Buttons.Skip(1).First();
|
||||
|
||||
AddStep("Hover second button", () => secondButton.TriggerOnHover(null));
|
||||
AddStep("Unhover second button", () => secondButton.TriggerOnHoverLost(null));
|
||||
AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
|
||||
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition
|
||||
|
||||
AddStep("Hide overlay", () => pauseOverlay.Hide());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that clicking on a button correctly causes a click event for that button.
|
||||
/// </summary>
|
||||
private void testClickSelection()
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
|
||||
var retryButton = pauseOverlay.Buttons.Skip(1).First();
|
||||
|
||||
bool triggered = false;
|
||||
AddStep("Click retry button", () =>
|
||||
{
|
||||
var lastAction = pauseOverlay.OnRetry;
|
||||
pauseOverlay.OnRetry = () => triggered = true;
|
||||
|
||||
retryButton.TriggerOnClick();
|
||||
pauseOverlay.OnRetry = lastAction;
|
||||
});
|
||||
|
||||
AddAssert("Action was triggered", () => triggered);
|
||||
AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that pressing the enter key with a button selected correctly causes a click event for that button.
|
||||
/// </summary>
|
||||
private void testEnterKeySelection()
|
||||
{
|
||||
AddStep("Show overlay", () => pauseOverlay.Show());
|
||||
|
||||
AddStep("Select second button", () =>
|
||||
{
|
||||
pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down });
|
||||
pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down });
|
||||
});
|
||||
|
||||
var retryButton = pauseOverlay.Buttons.Skip(1).First();
|
||||
|
||||
bool triggered = false;
|
||||
AddStep("Press enter", () =>
|
||||
{
|
||||
var lastAction = pauseOverlay.OnRetry;
|
||||
pauseOverlay.OnRetry = () => triggered = true;
|
||||
|
||||
retryButton.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter });
|
||||
pauseOverlay.OnRetry = lastAction;
|
||||
});
|
||||
|
||||
AddAssert("Action was triggered", () => triggered);
|
||||
AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ using OpenTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseGraph : OsuTestCase
|
||||
public class TestCaseGraph : OsuTestCase
|
||||
{
|
||||
public TestCaseGraph()
|
||||
{
|
||||
|
47
osu.Game.Tests/Visual/TestCaseHistoricalSection.cs
Normal file
47
osu.Game.Tests/Visual/TestCaseHistoricalSection.cs
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays.Profile.Sections;
|
||||
using osu.Game.Overlays.Profile.Sections.Historical;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseHistoricalSection : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes =>
|
||||
new[]
|
||||
{
|
||||
typeof(HistoricalSection),
|
||||
typeof(PaginatedMostPlayedBeatmapContainer),
|
||||
typeof(DrawableMostPlayedRow),
|
||||
typeof(DrawableProfileRow)
|
||||
};
|
||||
|
||||
public TestCaseHistoricalSection()
|
||||
{
|
||||
HistoricalSection section;
|
||||
|
||||
Add(new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(0.2f)
|
||||
});
|
||||
|
||||
Add(new ScrollContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = section = new HistoricalSection(),
|
||||
});
|
||||
|
||||
AddStep("Show peppy", () => section.User.Value = new User { Id = 2 });
|
||||
AddStep("Show WubWoofWolf", () => section.User.Value = new User { Id = 39828 });
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ using OpenTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseKeyCounter : OsuTestCase
|
||||
public class TestCaseKeyCounter : OsuTestCase
|
||||
{
|
||||
public TestCaseKeyCounter()
|
||||
{
|
||||
|
@ -6,14 +6,45 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Select.Leaderboards;
|
||||
using osu.Game.Users;
|
||||
using osu.Framework.Allocation;
|
||||
using OpenTK;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[Description("PlaySongSelect leaderboard")]
|
||||
internal class TestCaseLeaderboard : OsuTestCase
|
||||
public class TestCaseLeaderboard : OsuTestCase
|
||||
{
|
||||
private readonly Leaderboard leaderboard;
|
||||
private RulesetStore rulesets;
|
||||
|
||||
private readonly FailableLeaderboard leaderboard;
|
||||
|
||||
public TestCaseLeaderboard()
|
||||
{
|
||||
Add(leaderboard = new FailableLeaderboard
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Size = new Vector2(550f, 450f),
|
||||
Scope = LeaderboardScope.Global,
|
||||
});
|
||||
|
||||
AddStep(@"New Scores", newScores);
|
||||
AddStep(@"Empty Scores", () => leaderboard.SetRetrievalState(PlaceholderState.NoScores));
|
||||
AddStep(@"Network failure", () => leaderboard.SetRetrievalState(PlaceholderState.NetworkFailure));
|
||||
AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter));
|
||||
AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn));
|
||||
AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable));
|
||||
AddStep(@"Real beatmap", realBeatmap);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
this.rulesets = rulesets;
|
||||
}
|
||||
|
||||
private void newScores()
|
||||
{
|
||||
@ -204,17 +235,44 @@ namespace osu.Game.Tests.Visual
|
||||
leaderboard.Scores = scores;
|
||||
}
|
||||
|
||||
public TestCaseLeaderboard()
|
||||
private void realBeatmap()
|
||||
{
|
||||
Add(leaderboard = new Leaderboard
|
||||
leaderboard.Beatmap = new BeatmapInfo
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Size = new Vector2(550f, 450f),
|
||||
});
|
||||
StarDifficulty = 1.36,
|
||||
Version = @"BASIC",
|
||||
OnlineBeatmapID = 1113057,
|
||||
Ruleset = rulesets.GetRuleset(0),
|
||||
BaseDifficulty = new BeatmapDifficulty
|
||||
{
|
||||
CircleSize = 4,
|
||||
DrainRate = 6.5f,
|
||||
OverallDifficulty = 6.5f,
|
||||
ApproachRate = 5,
|
||||
},
|
||||
OnlineInfo = new BeatmapOnlineInfo
|
||||
{
|
||||
Length = 115000,
|
||||
CircleCount = 265,
|
||||
SliderCount = 71,
|
||||
PlayCount = 47906,
|
||||
PassCount = 19899,
|
||||
},
|
||||
Metrics = new BeatmapMetrics
|
||||
{
|
||||
Ratings = Enumerable.Range(0, 11),
|
||||
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6),
|
||||
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
AddStep(@"New Scores", newScores);
|
||||
newScores();
|
||||
private class FailableLeaderboard : Leaderboard
|
||||
{
|
||||
public void SetRetrievalState(PlaceholderState state)
|
||||
{
|
||||
PlaceholderState = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseMedalOverlay : OsuTestCase
|
||||
public class TestCaseMedalOverlay : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
|
@ -1,57 +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.ComponentModel;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[Description("player pause/fail screens")]
|
||||
internal class TestCaseMenuOverlays : OsuTestCase
|
||||
{
|
||||
public TestCaseMenuOverlays()
|
||||
{
|
||||
FailOverlay failOverlay;
|
||||
PauseContainer.PauseOverlay pauseOverlay;
|
||||
|
||||
var retryCount = 0;
|
||||
|
||||
Add(pauseOverlay = new PauseContainer.PauseOverlay
|
||||
{
|
||||
OnResume = () => Logger.Log(@"Resume"),
|
||||
OnRetry = () => Logger.Log(@"Retry"),
|
||||
OnQuit = () => Logger.Log(@"Quit"),
|
||||
});
|
||||
Add(failOverlay = new FailOverlay
|
||||
{
|
||||
OnRetry = () => Logger.Log(@"Retry"),
|
||||
OnQuit = () => Logger.Log(@"Quit"),
|
||||
});
|
||||
|
||||
AddStep(@"Pause", delegate
|
||||
{
|
||||
if (failOverlay.State == Visibility.Visible)
|
||||
{
|
||||
failOverlay.Hide();
|
||||
}
|
||||
pauseOverlay.Show();
|
||||
});
|
||||
AddStep("Fail", delegate
|
||||
{
|
||||
if (pauseOverlay.State == Visibility.Visible)
|
||||
{
|
||||
pauseOverlay.Hide();
|
||||
}
|
||||
failOverlay.Show();
|
||||
});
|
||||
AddStep("Add Retry", delegate
|
||||
{
|
||||
retryCount++;
|
||||
pauseOverlay.Retries = retryCount;
|
||||
failOverlay.Retries = retryCount;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -8,17 +8,25 @@ using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using OpenTK;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[Description("mod select and icon display")]
|
||||
internal class TestCaseMods : OsuTestCase
|
||||
public class TestCaseMods : OsuTestCase
|
||||
{
|
||||
private ModSelectOverlay modSelect;
|
||||
private ModDisplay modDisplay;
|
||||
private const string unranked_suffix = " (Unranked)";
|
||||
|
||||
private RulesetStore rulesets;
|
||||
|
||||
private ModDisplay modDisplay;
|
||||
private TestModSelectOverlay modSelect;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
@ -30,7 +38,7 @@ namespace osu.Game.Tests.Visual
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Add(modSelect = new ModSelectOverlay
|
||||
Add(modSelect = new TestModSelectOverlay
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Origin = Anchor.BottomCentre,
|
||||
@ -48,9 +56,156 @@ namespace osu.Game.Tests.Visual
|
||||
modDisplay.Current.BindTo(modSelect.SelectedMods);
|
||||
|
||||
AddStep("Toggle", modSelect.ToggleVisibility);
|
||||
AddStep("Hide", modSelect.Hide);
|
||||
AddStep("Show", modSelect.Show);
|
||||
|
||||
foreach (var ruleset in rulesets.AvailableRulesets)
|
||||
AddStep(ruleset.CreateInstance().Description, () => modSelect.Ruleset.Value = ruleset);
|
||||
foreach (var rulesetInfo in rulesets.AvailableRulesets)
|
||||
{
|
||||
Ruleset ruleset = rulesetInfo.CreateInstance();
|
||||
AddStep($"switch to {ruleset.Description}", () => modSelect.Ruleset.Value = rulesetInfo);
|
||||
|
||||
switch (ruleset) {
|
||||
case OsuRuleset or:
|
||||
testOsuMods(or);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void testOsuMods(OsuRuleset ruleset)
|
||||
{
|
||||
var easierMods = ruleset.GetModsFor(ModType.DifficultyReduction);
|
||||
var harderMods = ruleset.GetModsFor(ModType.DifficultyIncrease);
|
||||
var assistMods = ruleset.GetModsFor(ModType.Special);
|
||||
|
||||
var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail);
|
||||
var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden);
|
||||
var doubleTimeMod = harderMods.OfType<MultiMod>().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime));
|
||||
var autoPilotMod = assistMods.FirstOrDefault(m => m is OsuModAutopilot);
|
||||
|
||||
testSingleMod(noFailMod);
|
||||
testMultiMod(doubleTimeMod);
|
||||
testIncompatibleMods(noFailMod, autoPilotMod);
|
||||
testDeselectAll(easierMods.Where(m => !(m is MultiMod)));
|
||||
testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour);
|
||||
testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour);
|
||||
testMultiplierTextUnranked(autoPilotMod);
|
||||
}
|
||||
|
||||
private void testSingleMod(Mod mod)
|
||||
{
|
||||
selectNext(mod);
|
||||
checkSelected(mod);
|
||||
|
||||
selectPrevious(mod);
|
||||
checkNotSelected(mod);
|
||||
|
||||
selectNext(mod);
|
||||
selectNext(mod);
|
||||
checkNotSelected(mod);
|
||||
|
||||
selectPrevious(mod);
|
||||
selectPrevious(mod);
|
||||
checkNotSelected(mod);
|
||||
}
|
||||
|
||||
private void testMultiMod(MultiMod multiMod)
|
||||
{
|
||||
foreach (var mod in multiMod.Mods)
|
||||
{
|
||||
selectNext(mod);
|
||||
checkSelected(mod);
|
||||
}
|
||||
|
||||
for (int index = multiMod.Mods.Length - 1; index >= 0; index--)
|
||||
selectPrevious(multiMod.Mods[index]);
|
||||
|
||||
foreach (var mod in multiMod.Mods)
|
||||
checkNotSelected(mod);
|
||||
}
|
||||
|
||||
private void testIncompatibleMods(Mod modA, Mod modB)
|
||||
{
|
||||
selectNext(modA);
|
||||
checkSelected(modA);
|
||||
checkNotSelected(modB);
|
||||
|
||||
selectNext(modB);
|
||||
checkSelected(modB);
|
||||
checkNotSelected(modA);
|
||||
|
||||
selectPrevious(modB);
|
||||
checkNotSelected(modA);
|
||||
checkNotSelected(modB);
|
||||
}
|
||||
|
||||
private void testDeselectAll(IEnumerable<Mod> mods)
|
||||
{
|
||||
foreach (var mod in mods)
|
||||
selectNext(mod);
|
||||
|
||||
AddAssert("check for any selection", () => modSelect.SelectedMods.Value.Any());
|
||||
AddStep("deselect all", modSelect.DeselectAllButton.Action.Invoke);
|
||||
AddAssert("check for no selection", () => !modSelect.SelectedMods.Value.Any());
|
||||
}
|
||||
|
||||
private void testMultiplierTextColour(Mod mod, Color4 colour)
|
||||
{
|
||||
checkLabelColor(Color4.White);
|
||||
selectNext(mod);
|
||||
AddWaitStep(1, "wait for changing colour");
|
||||
checkLabelColor(colour);
|
||||
selectPrevious(mod);
|
||||
AddWaitStep(1, "wait for changing colour");
|
||||
checkLabelColor(Color4.White);
|
||||
}
|
||||
|
||||
private void testMultiplierTextUnranked(Mod mod)
|
||||
{
|
||||
AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix));
|
||||
selectNext(mod);
|
||||
AddAssert("check for unranked", () => modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix));
|
||||
selectPrevious(mod);
|
||||
AddAssert("check for ranked", () => !modSelect.MultiplierLabel.Text.EndsWith(unranked_suffix));
|
||||
}
|
||||
|
||||
private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext());
|
||||
|
||||
private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectPrevious());
|
||||
|
||||
private void checkSelected(Mod mod)
|
||||
{
|
||||
AddAssert($"check {mod.Name} is selected", () =>
|
||||
{
|
||||
var button = modSelect.GetModButton(mod);
|
||||
return modSelect.SelectedMods.Value.Single(m => m.Name == mod.Name) != null && button.SelectedMod.GetType() == mod.GetType() && button.Selected;
|
||||
});
|
||||
}
|
||||
|
||||
private void checkNotSelected(Mod mod)
|
||||
{
|
||||
AddAssert($"check {mod.Name} is not selected", () =>
|
||||
{
|
||||
var button = modSelect.GetModButton(mod);
|
||||
return modSelect.SelectedMods.Value.All(m => m.GetType() != mod.GetType()) && button.SelectedMod?.GetType() != mod.GetType();
|
||||
});
|
||||
}
|
||||
|
||||
private void checkLabelColor(Color4 color) => AddAssert("check label has expected colour", () => modSelect.MultiplierLabel.Colour.AverageColour == color);
|
||||
|
||||
private class TestModSelectOverlay : ModSelectOverlay
|
||||
{
|
||||
public ModButton GetModButton(Mod mod)
|
||||
{
|
||||
var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type);
|
||||
return section.ButtonsContainer.OfType<ModButton>().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType()));
|
||||
}
|
||||
|
||||
public new OsuSpriteText MultiplierLabel => base.MultiplierLabel;
|
||||
public new TriangleButton DeselectAllButton => base.DeselectAllButton;
|
||||
|
||||
public new Color4 LowMultiplierColour => base.LowMultiplierColour;
|
||||
public new Color4 HighMultiplierColour => base.HighMultiplierColour;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseMusicController : OsuTestCase
|
||||
public class TestCaseMusicController : OsuTestCase
|
||||
{
|
||||
private readonly Bindable<WorkingBeatmap> beatmapBacking = new Bindable<WorkingBeatmap>();
|
||||
|
||||
|
@ -1,21 +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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
[TestFixture]
|
||||
internal class TestCaseNotificationOverlay : OsuTestCase
|
||||
public class TestCaseNotificationOverlay : OsuTestCase
|
||||
{
|
||||
private readonly NotificationOverlay manager;
|
||||
private readonly List<ProgressNotification> progressingNotifications = new List<ProgressNotification>();
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(Notification),
|
||||
typeof(ProgressNotification),
|
||||
typeof(ProgressCompletionNotification),
|
||||
typeof(SimpleNotification),
|
||||
typeof(IHasCompletionTarget),
|
||||
};
|
||||
|
||||
public TestCaseNotificationOverlay()
|
||||
{
|
||||
@ -24,15 +33,20 @@ namespace osu.Game.Tests.Visual
|
||||
Content.Add(manager = new NotificationOverlay
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight
|
||||
});
|
||||
|
||||
AddToggleStep(@"show", state => manager.State = state ? Visibility.Visible : Visibility.Hidden);
|
||||
SpriteText displayedCount = new SpriteText();
|
||||
|
||||
AddStep(@"simple #1", sendNotification1);
|
||||
AddStep(@"simple #2", sendNotification2);
|
||||
AddStep(@"progress #1", sendProgress1);
|
||||
AddStep(@"progress #2", sendProgress2);
|
||||
Content.Add(displayedCount);
|
||||
|
||||
manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count}"; };
|
||||
|
||||
AddStep(@"toggle", manager.ToggleVisibility);
|
||||
AddStep(@"simple #1", sendHelloNotification);
|
||||
AddStep(@"simple #2", sendAmazingNotification);
|
||||
AddStep(@"progress #1", sendUploadProgress);
|
||||
AddStep(@"progress #2", sendDownloadProgress);
|
||||
AddStep(@"barrage", () => sendBarrage());
|
||||
}
|
||||
|
||||
@ -41,16 +55,16 @@ namespace osu.Game.Tests.Visual
|
||||
switch (RNG.Next(0, 4))
|
||||
{
|
||||
case 0:
|
||||
sendNotification1();
|
||||
sendHelloNotification();
|
||||
break;
|
||||
case 1:
|
||||
sendNotification2();
|
||||
sendAmazingNotification();
|
||||
break;
|
||||
case 2:
|
||||
sendProgress1();
|
||||
sendUploadProgress();
|
||||
break;
|
||||
case 3:
|
||||
sendProgress2();
|
||||
sendDownloadProgress();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -80,28 +94,34 @@ namespace osu.Game.Tests.Visual
|
||||
}
|
||||
}
|
||||
|
||||
private void sendProgress2()
|
||||
private void sendDownloadProgress()
|
||||
{
|
||||
var n = new ProgressNotification { Text = @"Downloading Haitai..." };
|
||||
var n = new ProgressNotification
|
||||
{
|
||||
Text = @"Downloading Haitai...",
|
||||
CompletionText = "Downloaded Haitai!",
|
||||
};
|
||||
manager.Post(n);
|
||||
progressingNotifications.Add(n);
|
||||
}
|
||||
|
||||
private readonly List<ProgressNotification> progressingNotifications = new List<ProgressNotification>();
|
||||
|
||||
private void sendProgress1()
|
||||
private void sendUploadProgress()
|
||||
{
|
||||
var n = new ProgressNotification { Text = @"Uploading to BSS..." };
|
||||
var n = new ProgressNotification
|
||||
{
|
||||
Text = @"Uploading to BSS...",
|
||||
CompletionText = "Uploaded to BSS!",
|
||||
};
|
||||
manager.Post(n);
|
||||
progressingNotifications.Add(n);
|
||||
}
|
||||
|
||||
private void sendNotification2()
|
||||
private void sendAmazingNotification()
|
||||
{
|
||||
manager.Post(new SimpleNotification { Text = @"You are amazing" });
|
||||
}
|
||||
|
||||
private void sendNotification1()
|
||||
private void sendHelloNotification()
|
||||
{
|
||||
manager.Post(new SimpleNotification { Text = @"Welcome to osu!. Enjoy your stay!" });
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseOnScreenDisplay : OsuTestCase
|
||||
public class TestCaseOnScreenDisplay : OsuTestCase
|
||||
{
|
||||
private FrameworkConfigManager config;
|
||||
private Bindable<FrameSync> frameSyncMode;
|
||||
|
@ -13,43 +13,96 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Carousel;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
using osu.Game.Tests.Platform;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCasePlaySongSelect : OsuTestCase
|
||||
public class TestCasePlaySongSelect : OsuTestCase
|
||||
{
|
||||
private BeatmapManager manager;
|
||||
|
||||
private RulesetStore rulesets;
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
private WorkingBeatmap defaultBeatmap;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(SongSelect),
|
||||
typeof(BeatmapCarousel),
|
||||
|
||||
typeof(CarouselItem),
|
||||
typeof(CarouselGroup),
|
||||
typeof(CarouselGroupEagerSelect),
|
||||
typeof(CarouselBeatmap),
|
||||
typeof(CarouselBeatmapSet),
|
||||
|
||||
typeof(DrawableCarouselItem),
|
||||
typeof(CarouselItemState),
|
||||
|
||||
typeof(DrawableCarouselBeatmap),
|
||||
typeof(DrawableCarouselBeatmapSet),
|
||||
};
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private class TestSongSelect : PlaySongSelect
|
||||
{
|
||||
PlaySongSelect songSelect;
|
||||
public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
|
||||
public new BeatmapCarousel Carousel => base.Carousel;
|
||||
}
|
||||
|
||||
if (manager == null)
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager baseManager)
|
||||
{
|
||||
TestSongSelect songSelect = null;
|
||||
|
||||
var storage = new TestStorage(@"TestCasePlaySongSelect");
|
||||
|
||||
// this is by no means clean. should be replacing inside of OsuGameBase somehow.
|
||||
var context = new OsuDbContext();
|
||||
|
||||
Func<OsuDbContext> contextFactory = () => context;
|
||||
|
||||
dependencies.Cache(rulesets = new RulesetStore(contextFactory));
|
||||
dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null)
|
||||
{
|
||||
var storage = new TestStorage(@"TestCasePlaySongSelect");
|
||||
DefaultBeatmap = defaultBeatmap = baseManager.GetWorkingBeatmap(null)
|
||||
});
|
||||
|
||||
// this is by no means clean. should be replacing inside of OsuGameBase somehow.
|
||||
var context = new OsuDbContext();
|
||||
void loadNewSongSelect(bool deleteMaps = false) => AddStep("reload song select", () =>
|
||||
{
|
||||
if (deleteMaps) manager.DeleteAll();
|
||||
|
||||
Func<OsuDbContext> contextFactory = () => context;
|
||||
if (songSelect != null)
|
||||
{
|
||||
Remove(songSelect);
|
||||
songSelect.Dispose();
|
||||
}
|
||||
|
||||
dependencies.Cache(rulesets = new RulesetStore(contextFactory));
|
||||
dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null));
|
||||
Add(songSelect = new TestSongSelect());
|
||||
});
|
||||
|
||||
loadNewSongSelect(true);
|
||||
|
||||
AddWaitStep(3);
|
||||
|
||||
AddAssert("dummy selected", () => songSelect.CurrentBeatmap == defaultBeatmap);
|
||||
|
||||
AddStep("import test maps", () =>
|
||||
{
|
||||
for (int i = 0; i < 100; i += 10)
|
||||
manager.Import(createTestBeatmapSet(i));
|
||||
}
|
||||
});
|
||||
|
||||
Add(songSelect = new PlaySongSelect());
|
||||
AddWaitStep(3);
|
||||
AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap);
|
||||
|
||||
loadNewSongSelect();
|
||||
AddWaitStep(3);
|
||||
AddAssert("random map selected", () => songSelect.CurrentBeatmap != defaultBeatmap);
|
||||
|
||||
AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; });
|
||||
AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; });
|
||||
|
37
osu.Game.Tests/Visual/TestCasePopupDialog.cs
Normal file
37
osu.Game.Tests/Visual/TestCasePopupDialog.cs
Normal file
@ -0,0 +1,37 @@
|
||||
// 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;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCasePopupDialog : OsuTestCase
|
||||
{
|
||||
public TestCasePopupDialog()
|
||||
{
|
||||
var popup = new PopupDialog
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
State = Framework.Graphics.Containers.Visibility.Visible,
|
||||
Icon = FontAwesome.fa_assistive_listening_systems,
|
||||
HeaderText = @"This is a test popup",
|
||||
BodyText = "I can say lots of stuff and even wrap my words!",
|
||||
Buttons = new PopupDialogButton[]
|
||||
{
|
||||
new PopupDialogCancelButton
|
||||
{
|
||||
Text = @"Yes. That you can.",
|
||||
},
|
||||
new PopupDialogOkButton
|
||||
{
|
||||
Text = @"You're a fake!",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
Add(popup);
|
||||
}
|
||||
}
|
||||
}
|
123
osu.Game.Tests/Visual/TestCaseRankGraph.cs
Normal file
123
osu.Game.Tests/Visual/TestCaseRankGraph.cs
Normal file
@ -0,0 +1,123 @@
|
||||
// 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.Overlays.Profile;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
public class TestCaseRankGraph : OsuTestCase
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(RankGraph),
|
||||
typeof(LineGraph)
|
||||
};
|
||||
|
||||
public TestCaseRankGraph()
|
||||
{
|
||||
RankGraph graph;
|
||||
|
||||
var data = new int[89];
|
||||
var dataWithZeros = new int[89];
|
||||
var smallData = new int[89];
|
||||
|
||||
for (int i = 0; i < 89; i++)
|
||||
data[i] = dataWithZeros[i] = (i + 1) * 1000;
|
||||
|
||||
for (int i = 20; i < 60; i++)
|
||||
dataWithZeros[i] = 0;
|
||||
|
||||
for (int i = 79; i < 89; i++)
|
||||
smallData[i] = 100000 - i * 1000;
|
||||
|
||||
Add(new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(300, 150),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(0.2f)
|
||||
},
|
||||
graph = new RankGraph
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AddStep("null user", () => graph.User.Value = null);
|
||||
AddStep("rank only", () =>
|
||||
{
|
||||
graph.User.Value = new User
|
||||
{
|
||||
Statistics = new UserStatistics
|
||||
{
|
||||
Rank = 123456,
|
||||
PP = 12345,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("with rank history", () =>
|
||||
{
|
||||
graph.User.Value = new User
|
||||
{
|
||||
Statistics = new UserStatistics
|
||||
{
|
||||
Rank = 89000,
|
||||
PP = 12345,
|
||||
},
|
||||
RankHistory = new User.RankHistoryData
|
||||
{
|
||||
Data = data,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("with zero values", () =>
|
||||
{
|
||||
graph.User.Value = new User
|
||||
{
|
||||
Statistics = new UserStatistics
|
||||
{
|
||||
Rank = 89000,
|
||||
PP = 12345,
|
||||
},
|
||||
RankHistory = new User.RankHistoryData
|
||||
{
|
||||
Data = dataWithZeros,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("small amount of data", () =>
|
||||
{
|
||||
graph.User.Value = new User
|
||||
{
|
||||
Statistics = new UserStatistics
|
||||
{
|
||||
Rank = 12000,
|
||||
PP = 12345,
|
||||
},
|
||||
RankHistory = new User.RankHistoryData
|
||||
{
|
||||
Data = smallData,
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseReplay : TestCasePlayer
|
||||
public class TestCaseReplay : TestCasePlayer
|
||||
{
|
||||
protected override Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset)
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ using osu.Game.Screens.Play.ReplaySettings;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
{
|
||||
internal class TestCaseReplaySettingsOverlay : OsuTestCase
|
||||
public class TestCaseReplaySettingsOverlay : OsuTestCase
|
||||
{
|
||||
public TestCaseReplaySettingsOverlay()
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user