mirror of
https://github.com/ppy/osu.git
synced 2025-01-15 08:12:56 +08:00
Merge branch 'master' into Private_Messages
This commit is contained in:
commit
884ac71874
@ -29,7 +29,7 @@
|
|||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.1" />
|
||||||
<PackageReference Include="squirrel.windows" Version="1.8.0" Condition="'$(TargetFramework)' == 'net471'" />
|
<PackageReference Include="squirrel.windows" Version="1.8.0" Condition="'$(TargetFramework)' == 'net471'" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
@ -12,11 +13,14 @@ using osu.Game.Tests.Beatmaps;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
namespace osu.Game.Rulesets.Catch.Tests
|
||||||
{
|
{
|
||||||
internal class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
[TestFixture]
|
||||||
|
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||||
|
|
||||||
[TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")]
|
[TestCase("basic")]
|
||||||
|
[TestCase("spinner")]
|
||||||
|
[TestCase("spinner-and-circles")]
|
||||||
public new void Test(string name)
|
public new void Test(string name)
|
||||||
{
|
{
|
||||||
base.Test(name);
|
base.Test(name);
|
||||||
@ -27,36 +31,52 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
if (hitObject is JuiceStream stream)
|
if (hitObject is JuiceStream stream)
|
||||||
{
|
{
|
||||||
foreach (var nested in stream.NestedHitObjects)
|
foreach (var nested in stream.NestedHitObjects)
|
||||||
{
|
yield return new ConvertValue((CatchHitObject)nested);
|
||||||
yield return new ConvertValue
|
}
|
||||||
{
|
else if (hitObject is BananaShower shower)
|
||||||
StartTime = nested.StartTime,
|
{
|
||||||
Position = ((CatchHitObject)nested).X * CatchPlayfield.BASE_WIDTH
|
foreach (var nested in shower.NestedHitObjects)
|
||||||
};
|
yield return new ConvertValue((CatchHitObject)nested);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
yield return new ConvertValue((CatchHitObject)hitObject);
|
||||||
yield return new ConvertValue
|
|
||||||
{
|
|
||||||
StartTime = hitObject.StartTime,
|
|
||||||
Position = ((CatchHitObject)hitObject).X * CatchPlayfield.BASE_WIDTH
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Ruleset CreateRuleset() => new CatchRuleset();
|
protected override Ruleset CreateRuleset() => new CatchRuleset();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct ConvertValue : IEquatable<ConvertValue>
|
public struct ConvertValue : IEquatable<ConvertValue>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A sane value to account for osu!stable using ints everwhere.
|
/// A sane value to account for osu!stable using ints everwhere.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const float conversion_lenience = 2;
|
private const float conversion_lenience = 2;
|
||||||
|
|
||||||
public double StartTime;
|
[JsonIgnore]
|
||||||
public float Position;
|
public readonly CatchHitObject HitObject;
|
||||||
|
|
||||||
|
public ConvertValue(CatchHitObject hitObject)
|
||||||
|
{
|
||||||
|
HitObject = hitObject;
|
||||||
|
startTime = 0;
|
||||||
|
position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double startTime;
|
||||||
|
|
||||||
|
public double StartTime
|
||||||
|
{
|
||||||
|
get => HitObject?.StartTime ?? startTime;
|
||||||
|
set => startTime = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float position;
|
||||||
|
|
||||||
|
public float Position
|
||||||
|
{
|
||||||
|
get => HitObject?.X * CatchPlayfield.BASE_WIDTH ?? position;
|
||||||
|
set => position = value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool Equals(ConvertValue other)
|
public bool Equals(ConvertValue other)
|
||||||
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
|
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToggleHyperDash(bool status) => MovableCatcher.HyperDashModifier = status ? 2 : 1;
|
public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperdashState(status ? 2 : 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,22 +26,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
var positionData = obj as IHasXPosition;
|
var positionData = obj as IHasXPosition;
|
||||||
var comboData = obj as IHasCombo;
|
var comboData = obj as IHasCombo;
|
||||||
var endTime = obj as IHasEndTime;
|
var endTime = obj as IHasEndTime;
|
||||||
|
var legacyOffset = obj as IHasLegacyLastTickOffset;
|
||||||
if (positionData == null)
|
|
||||||
{
|
|
||||||
if (endTime != null)
|
|
||||||
{
|
|
||||||
yield return new BananaShower
|
|
||||||
{
|
|
||||||
StartTime = obj.StartTime,
|
|
||||||
Samples = obj.Samples,
|
|
||||||
Duration = endTime.Duration,
|
|
||||||
NewCombo = comboData?.NewCombo ?? false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curveData != null)
|
if (curveData != null)
|
||||||
{
|
{
|
||||||
@ -54,20 +39,31 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
Distance = curveData.Distance,
|
Distance = curveData.Distance,
|
||||||
RepeatSamples = curveData.RepeatSamples,
|
RepeatSamples = curveData.RepeatSamples,
|
||||||
RepeatCount = curveData.RepeatCount,
|
RepeatCount = curveData.RepeatCount,
|
||||||
X = positionData.X / CatchPlayfield.BASE_WIDTH,
|
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
|
||||||
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
|
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset ?? 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (endTime != null)
|
||||||
|
{
|
||||||
|
yield return new BananaShower
|
||||||
|
{
|
||||||
|
StartTime = obj.StartTime,
|
||||||
|
Samples = obj.Samples,
|
||||||
|
Duration = endTime.Duration,
|
||||||
NewCombo = comboData?.NewCombo ?? false
|
NewCombo = comboData?.NewCombo ?? false
|
||||||
};
|
};
|
||||||
|
|
||||||
yield break;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
yield return new Fruit
|
|
||||||
{
|
{
|
||||||
StartTime = obj.StartTime,
|
yield return new Fruit
|
||||||
Samples = obj.Samples,
|
{
|
||||||
NewCombo = comboData?.NewCombo ?? false,
|
StartTime = obj.StartTime,
|
||||||
X = positionData.X / CatchPlayfield.BASE_WIDTH
|
Samples = obj.Samples,
|
||||||
};
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
|
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Beatmap<CatchHitObject> CreateBeatmap() => new CatchBeatmap();
|
protected override Beatmap<CatchHitObject> CreateBeatmap() => new CatchBeatmap();
|
||||||
|
@ -9,6 +9,7 @@ using osu.Game.Rulesets.Catch.Objects;
|
|||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
using osu.Game.Rulesets.Catch.MathUtils;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||||
{
|
{
|
||||||
@ -21,13 +22,54 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
|
|
||||||
public override void PostProcess()
|
public override void PostProcess()
|
||||||
{
|
{
|
||||||
|
applyPositionOffsets();
|
||||||
|
|
||||||
initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects);
|
initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects);
|
||||||
|
|
||||||
base.PostProcess();
|
base.PostProcess();
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
foreach (var obj in Beatmap.HitObjects.OfType<CatchHitObject>())
|
foreach (var obj in Beatmap.HitObjects.OfType<CatchHitObject>())
|
||||||
|
{
|
||||||
obj.IndexInBeatmap = index++;
|
obj.IndexInBeatmap = index++;
|
||||||
|
if (obj.LastInCombo && obj.NestedHitObjects.LastOrDefault() is IHasComboInformation lastNested)
|
||||||
|
lastNested.LastInCombo = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public const int RNG_SEED = 1337;
|
||||||
|
|
||||||
|
private void applyPositionOffsets()
|
||||||
|
{
|
||||||
|
var rng = new FastRandom(RNG_SEED);
|
||||||
|
// todo: HardRock displacement should be applied here
|
||||||
|
|
||||||
|
foreach (var obj in Beatmap.HitObjects)
|
||||||
|
{
|
||||||
|
switch (obj)
|
||||||
|
{
|
||||||
|
case BananaShower bananaShower:
|
||||||
|
foreach (var nested in bananaShower.NestedHitObjects)
|
||||||
|
{
|
||||||
|
((BananaShower.Banana)nested).X = (float)rng.NextDouble();
|
||||||
|
rng.Next(); // osu!stable retrieved a random banana type
|
||||||
|
rng.Next(); // osu!stable retrieved a random banana rotation
|
||||||
|
rng.Next(); // osu!stable retrieved a random banana colour
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JuiceStream juiceStream:
|
||||||
|
foreach (var nested in juiceStream.NestedHitObjects)
|
||||||
|
{
|
||||||
|
var hitObject = (CatchHitObject)nested;
|
||||||
|
if (hitObject is TinyDroplet)
|
||||||
|
hitObject.X += rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH;
|
||||||
|
else if (hitObject is Droplet)
|
||||||
|
rng.Next(); // osu!stable retrieved a random droplet rotation
|
||||||
|
hitObject.X = MathHelper.Clamp(hitObject.X, 0, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialiseHyperDash(List<CatchHitObject> objects)
|
private void initialiseHyperDash(List<CatchHitObject> objects)
|
||||||
|
91
osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs
Normal file
91
osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.MathUtils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A PRNG specified in http://heliosphan.org/fastrandom.html.
|
||||||
|
/// </summary>
|
||||||
|
public class FastRandom
|
||||||
|
{
|
||||||
|
private const double int_to_real = 1.0 / (int.MaxValue + 1.0);
|
||||||
|
private const uint int_mask = 0x7FFFFFFF;
|
||||||
|
private const uint y = 842502087;
|
||||||
|
private const uint z = 3579807591;
|
||||||
|
private const uint w = 273326509;
|
||||||
|
private uint _x, _y = y, _z = z, _w = w;
|
||||||
|
|
||||||
|
public FastRandom(int seed)
|
||||||
|
{
|
||||||
|
_x = (uint)seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FastRandom()
|
||||||
|
: this(Environment.TickCount)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a random unsigned integer within the range [<see cref="uint.MinValue"/>, <see cref="uint.MaxValue"/>).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The random value.</returns>
|
||||||
|
public uint NextUInt()
|
||||||
|
{
|
||||||
|
uint t = _x ^ _x << 11;
|
||||||
|
_x = _y;
|
||||||
|
_y = _z;
|
||||||
|
_z = _w;
|
||||||
|
return _w = _w ^ _w >> 19 ^ t ^ t >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a random integer value within the range [0, <see cref="int.MaxValue"/>).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The random value.</returns>
|
||||||
|
public int Next() => (int)(int_mask & NextUInt());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a random integer value within the range [0, <paramref name="upperBound"/>).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="upperBound">The upper bound.</param>
|
||||||
|
/// <returns>The random value.</returns>
|
||||||
|
public int Next(int upperBound) => (int)(NextDouble() * upperBound);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a random integer value within the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lowerBound">The lower bound of the range.</param>
|
||||||
|
/// <param name="upperBound">The upper bound of the range.</param>
|
||||||
|
/// <returns>The random value.</returns>
|
||||||
|
public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a random double value within the range [0, 1).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The random value.</returns>
|
||||||
|
public double NextDouble() => int_to_real * Next();
|
||||||
|
|
||||||
|
private uint bitBuffer;
|
||||||
|
private int bitIndex = 32;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The random value.</returns>
|
||||||
|
public bool NextBool()
|
||||||
|
{
|
||||||
|
if (bitIndex == 32)
|
||||||
|
{
|
||||||
|
bitBuffer = NextUInt();
|
||||||
|
bitIndex = 1;
|
||||||
|
|
||||||
|
return (bitBuffer & 1) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitIndex++;
|
||||||
|
return ((bitBuffer >>= 1) & 1) == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.MathUtils;
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
@ -31,8 +30,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
AddNested(new Banana
|
AddNested(new Banana
|
||||||
{
|
{
|
||||||
Samples = Samples,
|
Samples = Samples,
|
||||||
StartTime = i,
|
StartTime = i
|
||||||
X = RNG.NextSingle()
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,6 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
protected override void CreateNestedHitObjects()
|
protected override void CreateNestedHitObjects()
|
||||||
{
|
{
|
||||||
base.CreateNestedHitObjects();
|
base.CreateNestedHitObjects();
|
||||||
|
|
||||||
createTicks();
|
createTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +77,13 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
double time = spanStartTime + timeProgress * spanDuration;
|
double time = spanStartTime + timeProgress * spanDuration;
|
||||||
|
|
||||||
|
if (LegacyLastTickOffset != null)
|
||||||
|
{
|
||||||
|
// If we're the last tick, apply the legacy offset
|
||||||
|
if (span == this.SpanCount() - 1 && d + tickDistance > length)
|
||||||
|
time = Math.Max(StartTime + Duration / 2, time - LegacyLastTickOffset.Value);
|
||||||
|
}
|
||||||
|
|
||||||
double tinyTickInterval = time - lastDropletTime;
|
double tinyTickInterval = time - lastDropletTime;
|
||||||
while (tinyTickInterval > 100)
|
while (tinyTickInterval > 100)
|
||||||
tinyTickInterval /= 2;
|
tinyTickInterval /= 2;
|
||||||
@ -124,9 +130,6 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NestedHitObjects.LastOrDefault() is IHasComboInformation lastNested)
|
|
||||||
lastNested.LastInCombo = LastInCombo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||||
@ -156,5 +159,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
get { return Curve.CurveType; }
|
get { return Curve.CurveType; }
|
||||||
set { Curve.CurveType = value; }
|
set { Curve.CurveType = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double? LegacyLastTickOffset { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"Mappings": [{
|
||||||
|
"StartTime": 2589,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 2589,
|
||||||
|
"Position": 256
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 2915,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 2915,
|
||||||
|
"Position": 65
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 2916,
|
||||||
|
"Position": 482
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 3078,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 3078,
|
||||||
|
"Position": 164
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 3079,
|
||||||
|
"Position": 315
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 3241,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 3241,
|
||||||
|
"Position": 145
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 3242,
|
||||||
|
"Position": 159
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 3404,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 3404,
|
||||||
|
"Position": 310
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 3405,
|
||||||
|
"Position": 441
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 5197,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 5197,
|
||||||
|
"Position": 256
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[General]
|
||||||
|
StackLeniency: 0.7
|
||||||
|
Mode: 2
|
||||||
|
|
||||||
|
[Difficulty]
|
||||||
|
HPDrainRate:5
|
||||||
|
CircleSize:2
|
||||||
|
OverallDifficulty:5
|
||||||
|
ApproachRate:8
|
||||||
|
SliderMultiplier:1.4
|
||||||
|
SliderTickRate:4
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
2589,326.086956521739,4,2,1,70,1,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
256,192,2589,5,0,0:0:0:0:
|
||||||
|
256,192,2915,12,0,2916,0:0:0:0:
|
||||||
|
256,192,3078,12,0,3079,0:0:0:0:
|
||||||
|
256,192,3241,12,0,3242,0:0:0:0:
|
||||||
|
256,192,3404,12,0,3405,0:0:0:0:
|
||||||
|
256,192,5197,5,0,0:0:0:0:
|
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"Mappings": [{
|
||||||
|
"StartTime": 18500,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 18500,
|
||||||
|
"Position": 65
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 18559,
|
||||||
|
"Position": 482
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 18618,
|
||||||
|
"Position": 164
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 18678,
|
||||||
|
"Position": 315
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 18737,
|
||||||
|
"Position": 145
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 18796,
|
||||||
|
"Position": 159
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 18856,
|
||||||
|
"Position": 310
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 18915,
|
||||||
|
"Position": 441
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 18975,
|
||||||
|
"Position": 428
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 19034,
|
||||||
|
"Position": 243
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 19093,
|
||||||
|
"Position": 422
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 19153,
|
||||||
|
"Position": 481
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 19212,
|
||||||
|
"Position": 104
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 19271,
|
||||||
|
"Position": 473
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 19331,
|
||||||
|
"Position": 135
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 19390,
|
||||||
|
"Position": 360
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"StartTime": 19450,
|
||||||
|
"Position": 123
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[General]
|
||||||
|
Mode: 2
|
||||||
|
|
||||||
|
[Difficulty]
|
||||||
|
HPDrainRate:6
|
||||||
|
CircleSize:4
|
||||||
|
OverallDifficulty:7
|
||||||
|
ApproachRate:8.3
|
||||||
|
SliderMultiplier:1.6
|
||||||
|
SliderTickRate:1
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
500,500,4,2,1,50,1,0
|
||||||
|
13426,-100,4,3,1,45,0,0
|
||||||
|
14884,-100,4,2,1,50,0,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
256,192,18500,12,0,19450,0:0:0:0:
|
@ -250,51 +250,62 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
if (validCatch && fruit.HyperDash)
|
if (validCatch && fruit.HyperDash)
|
||||||
{
|
{
|
||||||
HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED;
|
var target = fruit.HyperDashTarget;
|
||||||
HyperDashDirection = fruit.HyperDashTarget.X - fruit.X;
|
double timeDifference = target.StartTime - fruit.StartTime;
|
||||||
|
double positionDifference = target.X * CatchPlayfield.BASE_WIDTH - catcherPosition;
|
||||||
|
double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0);
|
||||||
|
|
||||||
|
SetHyperdashState(Math.Abs(velocity), target.X);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
HyperDashModifier = 1;
|
{
|
||||||
|
SetHyperdashState();
|
||||||
|
}
|
||||||
|
|
||||||
return validCatch;
|
return validCatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double hyperDashModifier = 1;
|
||||||
|
private int hyperDashDirection;
|
||||||
|
private float hyperDashTargetPosition;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether we are hypderdashing or not.
|
/// Whether we are hypderdashing or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HyperDashing => hyperDashModifier != 1;
|
public bool HyperDashing => hyperDashModifier != 1;
|
||||||
|
|
||||||
private double hyperDashModifier = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The direction in which hyperdash is allowed. 0 allows both directions.
|
/// Set hyperdash state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double HyperDashDirection;
|
/// <param name="modifier">The speed multiplier. If this is less or equals to 1, this catcher will be non-hyperdashing state.</param>
|
||||||
|
/// <param name="targetPosition">When this catcher crosses this position, this catcher ends hyperdashing.</param>
|
||||||
/// <summary>
|
public void SetHyperdashState(double modifier = 1, float targetPosition = -1)
|
||||||
/// The speed modifier resultant from hyperdash. Will trigger hyperdash when not equal to 1.
|
|
||||||
/// </summary>
|
|
||||||
public double HyperDashModifier
|
|
||||||
{
|
{
|
||||||
get { return hyperDashModifier; }
|
const float hyperdash_transition_length = 180;
|
||||||
set
|
|
||||||
|
bool previouslyHyperDashing = HyperDashing;
|
||||||
|
if (modifier <= 1 || X == targetPosition)
|
||||||
{
|
{
|
||||||
if (value == hyperDashModifier) return;
|
hyperDashModifier = 1;
|
||||||
hyperDashModifier = value;
|
hyperDashDirection = 0;
|
||||||
|
|
||||||
const float transition_length = 180;
|
if (previouslyHyperDashing)
|
||||||
|
|
||||||
if (HyperDashing)
|
|
||||||
{
|
{
|
||||||
this.FadeColour(Color4.OrangeRed, transition_length, Easing.OutQuint);
|
this.FadeColour(Color4.White, hyperdash_transition_length, Easing.OutQuint);
|
||||||
this.FadeTo(0.2f, transition_length, Easing.OutQuint);
|
this.FadeTo(1, hyperdash_transition_length, Easing.OutQuint);
|
||||||
Trail = true;
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hyperDashModifier = modifier;
|
||||||
|
hyperDashDirection = Math.Sign(targetPosition - X);
|
||||||
|
hyperDashTargetPosition = targetPosition;
|
||||||
|
|
||||||
|
if (!previouslyHyperDashing)
|
||||||
{
|
{
|
||||||
HyperDashDirection = 0;
|
this.FadeColour(Color4.OrangeRed, hyperdash_transition_length, Easing.OutQuint);
|
||||||
this.FadeColour(Color4.White, transition_length, Easing.OutQuint);
|
this.FadeTo(0.2f, hyperdash_transition_length, Easing.OutQuint);
|
||||||
this.FadeTo(1, transition_length, Easing.OutQuint);
|
Trail = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -349,12 +360,18 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
var direction = Math.Sign(currentDirection);
|
var direction = Math.Sign(currentDirection);
|
||||||
|
|
||||||
double dashModifier = Dashing ? 1 : 0.5;
|
double dashModifier = Dashing ? 1 : 0.5;
|
||||||
|
double speed = BASE_SPEED * dashModifier * hyperDashModifier;
|
||||||
if (hyperDashModifier != 1 && (HyperDashDirection == 0 || direction == Math.Sign(HyperDashDirection)))
|
|
||||||
dashModifier = hyperDashModifier;
|
|
||||||
|
|
||||||
Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y);
|
Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y);
|
||||||
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1);
|
X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * speed, 0, 1);
|
||||||
|
|
||||||
|
// Correct overshooting.
|
||||||
|
if (hyperDashDirection > 0 && hyperDashTargetPosition < X ||
|
||||||
|
hyperDashDirection < 0 && hyperDashTargetPosition > X)
|
||||||
|
{
|
||||||
|
X = hyperDashTargetPosition;
|
||||||
|
SetHyperdashState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -12,7 +12,8 @@ using osu.Game.Tests.Beatmaps;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
{
|
{
|
||||||
internal class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
[TestFixture]
|
||||||
|
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct ConvertValue : IEquatable<ConvertValue>
|
public struct ConvertValue : IEquatable<ConvertValue>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A sane value to account for osu!stable using ints everwhere.
|
/// A sane value to account for osu!stable using ints everwhere.
|
||||||
|
45
osu.Game.Rulesets.Mania.Tests/ManiaInputTestCase.cs
Normal file
45
osu.Game.Rulesets.Mania.Tests/ManiaInputTestCase.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
public abstract class ManiaInputTestCase : OsuTestCase
|
||||||
|
{
|
||||||
|
private readonly Container<Drawable> content;
|
||||||
|
protected override Container<Drawable> Content => content ?? base.Content;
|
||||||
|
|
||||||
|
protected ManiaInputTestCase(int keys)
|
||||||
|
{
|
||||||
|
base.Content.Add(content = new LocalInputManager(keys));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LocalInputManager : ManiaInputManager
|
||||||
|
{
|
||||||
|
public LocalInputManager(int variant)
|
||||||
|
: base(new ManiaRuleset().RulesetInfo, variant)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||||
|
=> new LocalKeyBindingContainer(ruleset, variant, unique);
|
||||||
|
|
||||||
|
private class LocalKeyBindingContainer : RulesetKeyBindingContainer
|
||||||
|
{
|
||||||
|
public LocalKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||||
|
: base(ruleset, variant, unique)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ReloadMappings()
|
||||||
|
{
|
||||||
|
KeyBindings = DefaultKeyBindings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs
Normal file
37
osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A container which provides a <see cref="IScrollingInfo"/> to children.
|
||||||
|
/// </summary>
|
||||||
|
public class ScrollingTestContainer : Container
|
||||||
|
{
|
||||||
|
private readonly ScrollingDirection direction;
|
||||||
|
|
||||||
|
public ScrollingTestContainer(ScrollingDirection direction)
|
||||||
|
{
|
||||||
|
this.direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||||
|
{
|
||||||
|
var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||||
|
dependencies.CacheAs<IScrollingInfo>(new ScrollingInfo { Direction = { Value = direction }});
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ScrollingInfo : IScrollingInfo
|
||||||
|
{
|
||||||
|
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||||
|
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
osu.Game.Rulesets.Mania.Tests/TestCaseColumn.cs
Normal file
111
osu.Game.Rulesets.Mania.Tests/TestCaseColumn.cs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Mania.UI.Components;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestCaseColumn : ManiaInputTestCase
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(Column),
|
||||||
|
typeof(ColumnBackground),
|
||||||
|
typeof(ColumnKeyArea),
|
||||||
|
typeof(ColumnHitObjectArea)
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly List<Column> columns = new List<Column>();
|
||||||
|
|
||||||
|
public TestCaseColumn()
|
||||||
|
: base(2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Child = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Spacing = new Vector2(20, 0),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
createColumn(ScrollingDirection.Up, ManiaAction.Key1),
|
||||||
|
createColumn(ScrollingDirection.Down, ManiaAction.Key2)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
AddStep("note", createNote);
|
||||||
|
AddStep("hold note", createHoldNote);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNote()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < columns.Count; i++)
|
||||||
|
{
|
||||||
|
var obj = new Note { Column = i, StartTime = Time.Current + 2000 };
|
||||||
|
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
columns[i].Add(new DrawableNote(obj, columns[i].Action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createHoldNote()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < columns.Count; i++)
|
||||||
|
{
|
||||||
|
var obj = new HoldNote { Column = i, StartTime = Time.Current + 2000, Duration = 500 };
|
||||||
|
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
columns[i].Add(new DrawableHoldNote(obj, columns[i].Action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable createColumn(ScrollingDirection direction, ManiaAction action)
|
||||||
|
{
|
||||||
|
var column = new Column(direction)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Height = 0.85f,
|
||||||
|
AccentColour = Color4.OrangeRed,
|
||||||
|
Action = action,
|
||||||
|
VisibleTimeRange = { Value = 2000 }
|
||||||
|
};
|
||||||
|
|
||||||
|
columns.Add(column);
|
||||||
|
|
||||||
|
return new ScrollingTestContainer(direction)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Child = column
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,106 +0,0 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
|
||||||
using osu.Game.Tests.Visual;
|
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class TestCaseManiaHitObjects : OsuTestCase
|
|
||||||
{
|
|
||||||
public TestCaseManiaHitObjects()
|
|
||||||
{
|
|
||||||
Note note1 = new Note();
|
|
||||||
Note note2 = new Note();
|
|
||||||
HoldNote holdNote = new HoldNote { StartTime = 1000 };
|
|
||||||
|
|
||||||
note1.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
||||||
note2.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
||||||
holdNote.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
||||||
|
|
||||||
Add(new FillFlowContainer
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Spacing = new Vector2(10, 0),
|
|
||||||
// Imagine that the containers containing the drawable notes are the "columns"
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Name = "Normal note column",
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Width = 50,
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Name = "Timing section",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
RelativeChildSize = new Vector2(1, 10000),
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new DrawableNote(note1, ManiaAction.Key1)
|
|
||||||
{
|
|
||||||
Y = 5000,
|
|
||||||
LifetimeStart = double.MinValue,
|
|
||||||
LifetimeEnd = double.MaxValue,
|
|
||||||
AccentColour = Color4.Red
|
|
||||||
},
|
|
||||||
new DrawableNote(note2, ManiaAction.Key1)
|
|
||||||
{
|
|
||||||
Y = 6000,
|
|
||||||
LifetimeStart = double.MinValue,
|
|
||||||
LifetimeEnd = double.MaxValue,
|
|
||||||
AccentColour = Color4.Red
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Name = "Hold note column",
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Width = 50,
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new Container
|
|
||||||
{
|
|
||||||
Name = "Timing section",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
RelativeChildSize = new Vector2(1, 10000),
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new DrawableHoldNote(holdNote, ManiaAction.Key1)
|
|
||||||
{
|
|
||||||
Y = 5000,
|
|
||||||
Height = 1000,
|
|
||||||
LifetimeStart = double.MinValue,
|
|
||||||
LifetimeEnd = double.MaxValue,
|
|
||||||
AccentColour = Color4.Red,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,185 +0,0 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
|
||||||
using osu.Game.Rulesets.Mania.Configuration;
|
|
||||||
using osu.Game.Rulesets.Mania.Judgements;
|
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Tests.Visual;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class TestCaseManiaPlayfield : OsuTestCase
|
|
||||||
{
|
|
||||||
private const double start_time = 500;
|
|
||||||
private const double duration = 500;
|
|
||||||
|
|
||||||
protected override double TimePerAction => 200;
|
|
||||||
|
|
||||||
private RulesetInfo maniaRuleset;
|
|
||||||
|
|
||||||
public TestCaseManiaPlayfield()
|
|
||||||
{
|
|
||||||
var rng = new Random(1337);
|
|
||||||
|
|
||||||
AddStep("1 column", () => createPlayfield(1));
|
|
||||||
AddStep("4 columns", () => createPlayfield(4));
|
|
||||||
AddStep("5 columns", () => createPlayfield(5));
|
|
||||||
AddStep("8 columns", () => createPlayfield(8));
|
|
||||||
AddStep("4 + 4 columns", () =>
|
|
||||||
{
|
|
||||||
var stages = new List<StageDefinition>
|
|
||||||
{
|
|
||||||
new StageDefinition { Columns = 4 },
|
|
||||||
new StageDefinition { Columns = 4 },
|
|
||||||
};
|
|
||||||
createPlayfield(stages);
|
|
||||||
});
|
|
||||||
|
|
||||||
AddStep("2 + 4 + 2 columns", () =>
|
|
||||||
{
|
|
||||||
var stages = new List<StageDefinition>
|
|
||||||
{
|
|
||||||
new StageDefinition { Columns = 2 },
|
|
||||||
new StageDefinition { Columns = 4 },
|
|
||||||
new StageDefinition { Columns = 2 },
|
|
||||||
};
|
|
||||||
createPlayfield(stages);
|
|
||||||
});
|
|
||||||
|
|
||||||
AddStep("1 + 8 + 1 columns", () =>
|
|
||||||
{
|
|
||||||
var stages = new List<StageDefinition>
|
|
||||||
{
|
|
||||||
new StageDefinition { Columns = 1 },
|
|
||||||
new StageDefinition { Columns = 8 },
|
|
||||||
new StageDefinition { Columns = 1 },
|
|
||||||
};
|
|
||||||
createPlayfield(stages);
|
|
||||||
});
|
|
||||||
|
|
||||||
AddStep("Reversed", () => createPlayfield(4, true));
|
|
||||||
|
|
||||||
AddStep("Notes with input", () => createPlayfieldWithNotes());
|
|
||||||
AddStep("Notes with input (reversed)", () => createPlayfieldWithNotes(true));
|
|
||||||
AddStep("Notes with gravity", () => createPlayfieldWithNotes());
|
|
||||||
AddStep("Notes with gravity (reversed)", () => createPlayfieldWithNotes(true));
|
|
||||||
|
|
||||||
AddStep("Hit explosion", () =>
|
|
||||||
{
|
|
||||||
var playfield = createPlayfield(4);
|
|
||||||
|
|
||||||
int col = rng.Next(0, 4);
|
|
||||||
|
|
||||||
var note = new Note { Column = col };
|
|
||||||
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
||||||
|
|
||||||
var drawableNote = new DrawableNote(note, ManiaAction.Key1)
|
|
||||||
{
|
|
||||||
AccentColour = playfield.Columns.ElementAt(col).AccentColour
|
|
||||||
};
|
|
||||||
|
|
||||||
playfield.OnJudgement(drawableNote, new ManiaJudgement { Result = HitResult.Perfect });
|
|
||||||
playfield.Columns[col].OnJudgement(drawableNote, new ManiaJudgement { Result = HitResult.Perfect });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(RulesetStore rulesets, SettingsStore settings)
|
|
||||||
{
|
|
||||||
maniaRuleset = rulesets.GetRuleset(3);
|
|
||||||
|
|
||||||
Dependencies.Cache(new ManiaConfigManager(settings, maniaRuleset, 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
private ManiaPlayfield createPlayfield(int cols, bool inverted = false)
|
|
||||||
{
|
|
||||||
var stages = new List<StageDefinition>
|
|
||||||
{
|
|
||||||
new StageDefinition { Columns = cols },
|
|
||||||
};
|
|
||||||
|
|
||||||
return createPlayfield(stages, inverted);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ManiaPlayfield createPlayfield(List<StageDefinition> stages, bool inverted = false)
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
|
|
||||||
var inputManager = new ManiaInputManager(maniaRuleset, stages.Sum(g => g.Columns)) { RelativeSizeAxes = Axes.Both };
|
|
||||||
Add(inputManager);
|
|
||||||
|
|
||||||
ManiaPlayfield playfield;
|
|
||||||
|
|
||||||
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
});
|
|
||||||
|
|
||||||
playfield.Inverted.Value = inverted;
|
|
||||||
|
|
||||||
return playfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createPlayfieldWithNotes(bool inverted = false)
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
|
|
||||||
var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
|
|
||||||
|
|
||||||
var inputManager = new ManiaInputManager(maniaRuleset, 4) { RelativeSizeAxes = Axes.Both };
|
|
||||||
Add(inputManager);
|
|
||||||
|
|
||||||
ManiaPlayfield playfield;
|
|
||||||
var stages = new List<StageDefinition>
|
|
||||||
{
|
|
||||||
new StageDefinition { Columns = 4 },
|
|
||||||
};
|
|
||||||
|
|
||||||
inputManager.Add(playfield = new ManiaPlayfield(stages)
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Clock = new FramedClock(rateAdjustClock)
|
|
||||||
});
|
|
||||||
|
|
||||||
playfield.Inverted.Value = inverted;
|
|
||||||
|
|
||||||
for (double t = start_time; t <= start_time + duration; t += 100)
|
|
||||||
{
|
|
||||||
var note1 = new Note { StartTime = t, Column = 0 };
|
|
||||||
var note2 = new Note { StartTime = t, Column = 3 };
|
|
||||||
|
|
||||||
note1.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
||||||
note2.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
||||||
|
|
||||||
playfield.Add(new DrawableNote(note1, ManiaAction.Key1));
|
|
||||||
playfield.Add(new DrawableNote(note2, ManiaAction.Key4));
|
|
||||||
}
|
|
||||||
|
|
||||||
var holdNote1 = new HoldNote { StartTime = start_time, Duration = duration, Column = 1 };
|
|
||||||
var holdNote2 = new HoldNote { StartTime = start_time, Duration = duration, Column = 2 };
|
|
||||||
|
|
||||||
holdNote1.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
||||||
holdNote2.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
||||||
|
|
||||||
playfield.Add(new DrawableHoldNote(holdNote1, ManiaAction.Key2));
|
|
||||||
playfield.Add(new DrawableHoldNote(holdNote2, ManiaAction.Key3));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
168
osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs
Normal file
168
osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestCaseNotes : OsuTestCase
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(DrawableNote),
|
||||||
|
typeof(DrawableHoldNote)
|
||||||
|
};
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Child = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(20),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
createNoteDisplay(ScrollingDirection.Down),
|
||||||
|
createNoteDisplay(ScrollingDirection.Up),
|
||||||
|
createHoldNoteDisplay(ScrollingDirection.Down),
|
||||||
|
createHoldNoteDisplay(ScrollingDirection.Up),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable createNoteDisplay(ScrollingDirection direction)
|
||||||
|
{
|
||||||
|
var note = new Note { StartTime = 999999999 };
|
||||||
|
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
return new ScrollingTestContainer(direction)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Child = new NoteContainer(direction, $"note, scrolling {direction.ToString().ToLower()}")
|
||||||
|
{
|
||||||
|
Child = new DrawableNote(note, ManiaAction.Key1) { AccentColour = Color4.OrangeRed }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable createHoldNoteDisplay(ScrollingDirection direction)
|
||||||
|
{
|
||||||
|
var note = new HoldNote { StartTime = 999999999, Duration = 1000 };
|
||||||
|
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
return new ScrollingTestContainer(direction)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Child = new NoteContainer(direction, $"hold note, scrolling {direction.ToString().ToLower()}")
|
||||||
|
{
|
||||||
|
Child = new DrawableHoldNote(note, ManiaAction.Key1)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
AccentColour = Color4.OrangeRed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NoteContainer : Container
|
||||||
|
{
|
||||||
|
private readonly Container content;
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
|
private readonly ScrollingDirection direction;
|
||||||
|
|
||||||
|
public NoteContainer(ScrollingDirection direction, string description)
|
||||||
|
{
|
||||||
|
this.direction = direction;
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Spacing = new Vector2(0, 10),
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Width = 45,
|
||||||
|
Height = 100,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Width = 1.25f,
|
||||||
|
Colour = Color4.Black.Opacity(0.5f)
|
||||||
|
},
|
||||||
|
content = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new SpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
TextSize = 14,
|
||||||
|
Text = description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
foreach (var obj in content.OfType<DrawableHitObject>())
|
||||||
|
{
|
||||||
|
if (!(obj.HitObject is IHasEndTime endTime))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!obj.HasNestedHitObjects)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var nested in obj.NestedHitObjects)
|
||||||
|
{
|
||||||
|
double finalPosition = (nested.HitObject.StartTime - obj.HitObject.StartTime) / endTime.Duration;
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case ScrollingDirection.Up:
|
||||||
|
nested.Y = (float)(finalPosition * content.DrawHeight);
|
||||||
|
break;
|
||||||
|
case ScrollingDirection.Down:
|
||||||
|
nested.Y = (float)(-finalPosition * content.DrawHeight);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
121
osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs
Normal file
121
osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestCaseStage : ManiaInputTestCase
|
||||||
|
{
|
||||||
|
private const int columns = 4;
|
||||||
|
|
||||||
|
private readonly List<ManiaStage> stages = new List<ManiaStage>();
|
||||||
|
|
||||||
|
public TestCaseStage()
|
||||||
|
: base(columns)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Child = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Spacing = new Vector2(20, 0),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
createStage(ScrollingDirection.Up, ManiaAction.Key1),
|
||||||
|
createStage(ScrollingDirection.Down, ManiaAction.Key3)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
AddStep("note", createNote);
|
||||||
|
AddStep("hold note", createHoldNote);
|
||||||
|
AddStep("minor bar line", () => createBarLine(false));
|
||||||
|
AddStep("major bar line", () => createBarLine(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createNote()
|
||||||
|
{
|
||||||
|
foreach (var stage in stages)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < stage.Columns.Count; i++)
|
||||||
|
{
|
||||||
|
var obj = new Note { Column = i, StartTime = Time.Current + 2000 };
|
||||||
|
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
stage.Add(new DrawableNote(obj, stage.Columns[i].Action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createHoldNote()
|
||||||
|
{
|
||||||
|
foreach (var stage in stages)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < stage.Columns.Count; i++)
|
||||||
|
{
|
||||||
|
var obj = new HoldNote { Column = i, StartTime = Time.Current + 2000, Duration = 500 };
|
||||||
|
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
stage.Add(new DrawableHoldNote(obj, stage.Columns[i].Action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createBarLine(bool major)
|
||||||
|
{
|
||||||
|
foreach (var stage in stages)
|
||||||
|
{
|
||||||
|
var obj = new BarLine
|
||||||
|
{
|
||||||
|
StartTime = Time.Current + 2000,
|
||||||
|
ControlPoint = new TimingControlPoint(),
|
||||||
|
BeatIndex = major ? 0 : 1
|
||||||
|
};
|
||||||
|
|
||||||
|
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
stage.Add(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable createStage(ScrollingDirection direction, ManiaAction action)
|
||||||
|
{
|
||||||
|
var specialAction = ManiaAction.Special1;
|
||||||
|
|
||||||
|
var stage = new ManiaStage(direction, 0, new StageDefinition { Columns = 2 }, ref action, ref specialAction) { VisibleTimeRange = { Value = 2000 } };
|
||||||
|
stages.Add(stage);
|
||||||
|
|
||||||
|
return new ScrollingTestContainer(direction)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Child = stage
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -329,7 +329,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP && sample.Name == SampleInfo.HIT_FINISH;
|
bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP || sample.Name == SampleInfo.HIT_FINISH;
|
||||||
|
|
||||||
bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0;
|
bool canGenerateTwoNotes = (convertType & PatternType.LowProbability) == 0;
|
||||||
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
|
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using osu.Framework.Configuration.Tracking;
|
using osu.Framework.Configuration.Tracking;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Configuration;
|
using osu.Game.Rulesets.Configuration;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Configuration
|
namespace osu.Game.Rulesets.Mania.Configuration
|
||||||
{
|
{
|
||||||
@ -19,6 +20,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
|||||||
base.InitialiseDefaults();
|
base.InitialiseDefaults();
|
||||||
|
|
||||||
Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0);
|
Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0);
|
||||||
|
Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
|
||||||
@ -29,6 +31,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
|||||||
|
|
||||||
public enum ManiaSetting
|
public enum ManiaSetting
|
||||||
{
|
{
|
||||||
ScrollTime
|
ScrollTime,
|
||||||
|
ScrollDirection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Difficulty
|
||||||
|
{
|
||||||
|
public class ManiaDifficultyAttributes : DifficultyAttributes
|
||||||
|
{
|
||||||
|
public double GreatHitWindow;
|
||||||
|
|
||||||
|
public ManiaDifficultyAttributes(Mod[] mods, double starRating)
|
||||||
|
: base(mods, starRating)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -39,6 +39,9 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
|
|
||||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||||
{
|
{
|
||||||
|
if (!beatmap.HitObjects.Any())
|
||||||
|
return new ManiaDifficultyAttributes(mods, 0);
|
||||||
|
|
||||||
var difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
|
var difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
|
||||||
|
|
||||||
int columnCount = ((ManiaBeatmap)beatmap).TotalColumns;
|
int columnCount = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||||
@ -50,9 +53,14 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
if (!calculateStrainValues(difficultyHitObjects, timeRate))
|
if (!calculateStrainValues(difficultyHitObjects, timeRate))
|
||||||
return new DifficultyAttributes(mods, 0);
|
return new DifficultyAttributes(mods, 0);
|
||||||
|
|
||||||
|
|
||||||
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
|
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
|
||||||
|
|
||||||
return new DifficultyAttributes(mods, starRating);
|
return new ManiaDifficultyAttributes(mods, starRating)
|
||||||
|
{
|
||||||
|
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
|
||||||
|
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool calculateStrainValues(List<ManiaHitObjectDifficulty> objects, double timeRate)
|
private bool calculateStrainValues(List<ManiaHitObjectDifficulty> objects, double timeRate)
|
||||||
|
@ -13,6 +13,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
{
|
{
|
||||||
public class ManiaPerformanceCalculator : PerformanceCalculator
|
public class ManiaPerformanceCalculator : PerformanceCalculator
|
||||||
{
|
{
|
||||||
|
protected new ManiaDifficultyAttributes Attributes => (ManiaDifficultyAttributes)base.Attributes;
|
||||||
|
|
||||||
private Mod[] mods;
|
private Mod[] mods;
|
||||||
|
|
||||||
// Score after being scaled by non-difficulty-increasing mods
|
// Score after being scaled by non-difficulty-increasing mods
|
||||||
@ -105,14 +107,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
|
|
||||||
private double computeAccuracyValue(double strainValue)
|
private double computeAccuracyValue(double strainValue)
|
||||||
{
|
{
|
||||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
|
if (Attributes.GreatHitWindow <= 0)
|
||||||
double hitWindowGreat = (int)(Beatmap.HitObjects.First().HitWindows.Great / 2) / TimeRate;
|
|
||||||
if (hitWindowGreat <= 0)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Lots of arbitrary values from testing.
|
// Lots of arbitrary values from testing.
|
||||||
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
||||||
double accuracyValue = Math.Max(0.0, 0.2 - (hitWindowGreat - 34) * 0.006667)
|
double accuracyValue = Math.Max(0.0, 0.2 - (Attributes.GreatHitWindow - 34) * 0.006667)
|
||||||
* strainValue
|
* strainValue
|
||||||
* Math.Pow(Math.Max(0.0, scaledScore - 960000) / 40000, 1.1);
|
* Math.Pow(Math.Max(0.0, scaledScore - 960000) / 40000, 1.1);
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ using osu.Game.Rulesets.Mania.Replays;
|
|||||||
using osu.Game.Rulesets.Replays.Types;
|
using osu.Game.Rulesets.Replays.Types;
|
||||||
using osu.Game.Beatmaps.Legacy;
|
using osu.Game.Beatmaps.Legacy;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
using osu.Game.Rulesets.Configuration;
|
using osu.Game.Rulesets.Configuration;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
@ -155,6 +156,8 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaConfigManager(settings, RulesetInfo);
|
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaConfigManager(settings, RulesetInfo);
|
||||||
|
|
||||||
|
public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this);
|
||||||
|
|
||||||
public ManiaRuleset(RulesetInfo rulesetInfo = null)
|
public ManiaRuleset(RulesetInfo rulesetInfo = null)
|
||||||
: base(rulesetInfo)
|
: base(rulesetInfo)
|
||||||
{
|
{
|
||||||
|
34
osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs
Normal file
34
osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Rulesets.Mania.Configuration;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania
|
||||||
|
{
|
||||||
|
public class ManiaSettingsSubsection : RulesetSettingsSubsection
|
||||||
|
{
|
||||||
|
protected override string Header => "osu!mania";
|
||||||
|
|
||||||
|
public ManiaSettingsSubsection(ManiaRuleset ruleset)
|
||||||
|
: base(ruleset)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ManiaConfigManager config)
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new SettingsEnumDropdown<ManiaScrollingDirection>
|
||||||
|
{
|
||||||
|
LabelText = "Scrolling direction",
|
||||||
|
Bindable = config.GetBindable<ManiaScrollingDirection>(ManiaSetting.ScrollDirection)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Game.Rulesets.Mania.Judgements;
|
using osu.Game.Rulesets.Mania.Judgements;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -75,6 +76,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
AddNested(tail);
|
AddNested(tail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnDirectionChanged(ScrollingDirection direction)
|
||||||
|
{
|
||||||
|
base.OnDirectionChanged(direction);
|
||||||
|
|
||||||
|
bodyPiece.Anchor = bodyPiece.Origin = direction == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||||
|
}
|
||||||
|
|
||||||
public override Color4 AccentColour
|
public override Color4 AccentColour
|
||||||
{
|
{
|
||||||
get { return base.AccentColour; }
|
get { return base.AccentColour; }
|
||||||
@ -100,7 +108,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
// Make the body piece not lie under the head note
|
// Make the body piece not lie under the head note
|
||||||
bodyPiece.Y = head.Height / 2;
|
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * head.Height / 2;
|
||||||
bodyPiece.Height = DrawHeight - head.Height / 2 + tail.Height / 2;
|
bodyPiece.Height = DrawHeight - head.Height / 2 + tail.Height / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -16,18 +20,29 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
|
|
||||||
public new TObject HitObject;
|
public new TObject HitObject;
|
||||||
|
|
||||||
|
protected readonly IBindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null)
|
protected DrawableManiaHitObject(TObject hitObject, ManiaAction? action = null)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre;
|
|
||||||
Origin = Anchor.TopCentre;
|
|
||||||
|
|
||||||
HitObject = hitObject;
|
HitObject = hitObject;
|
||||||
|
|
||||||
if (action != null)
|
if (action != null)
|
||||||
Action = action.Value;
|
Action = action.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
|
{
|
||||||
|
Direction.BindTo(scrollingInfo.Direction);
|
||||||
|
Direction.BindValueChanged(OnDirectionChanged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnDirectionChanged(ScrollingDirection direction)
|
||||||
|
{
|
||||||
|
Anchor = Origin = direction == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateState(ArmedState state)
|
protected override void UpdateState(ArmedState state)
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Input.Bindings;
|
|||||||
using osu.Game.Rulesets.Mania.Judgements;
|
using osu.Game.Rulesets.Mania.Judgements;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -28,14 +29,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
CornerRadius = 5;
|
CornerRadius = 5;
|
||||||
Masking = true;
|
Masking = true;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChild = headPiece = new NotePiece();
|
||||||
{
|
}
|
||||||
headPiece = new NotePiece
|
|
||||||
{
|
protected override void OnDirectionChanged(ScrollingDirection direction)
|
||||||
Anchor = Anchor.TopCentre,
|
{
|
||||||
Origin = Anchor.TopCentre
|
base.OnDirectionChanged(direction);
|
||||||
}
|
|
||||||
};
|
headPiece.Anchor = headPiece.Origin = direction == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Color4 AccentColour
|
public override Color4 AccentColour
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||||
{
|
{
|
||||||
@ -18,6 +22,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
|||||||
public const float NOTE_HEIGHT = 10;
|
public const float NOTE_HEIGHT = 10;
|
||||||
private const float head_colour_height = 6;
|
private const float head_colour_height = 6;
|
||||||
|
|
||||||
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
private readonly Box colouredBox;
|
private readonly Box colouredBox;
|
||||||
|
|
||||||
public NotePiece()
|
public NotePiece()
|
||||||
@ -33,8 +39,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
|||||||
},
|
},
|
||||||
colouredBox = new Box
|
colouredBox = new Box
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = head_colour_height,
|
Height = head_colour_height,
|
||||||
Alpha = 0.2f
|
Alpha = 0.2f
|
||||||
@ -42,6 +46,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
|
{
|
||||||
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
|
direction.BindValueChanged(direction =>
|
||||||
|
{
|
||||||
|
colouredBox.Anchor = colouredBox.Origin = direction == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
private Color4 accentColour;
|
private Color4 accentColour;
|
||||||
public Color4 AccentColour
|
public Color4 AccentColour
|
||||||
{
|
{
|
||||||
|
@ -1,50 +1,51 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using OpenTK;
|
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Graphics.Colour;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mania.UI.Components;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
public class Column : ScrollingPlayfield, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
public class Column : ManiaScrollingPlayfield, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
||||||
{
|
{
|
||||||
private const float key_icon_size = 10;
|
|
||||||
private const float key_icon_corner_radius = 3;
|
|
||||||
private const float key_icon_border_radius = 2;
|
|
||||||
|
|
||||||
private const float hit_target_height = 10;
|
|
||||||
private const float hit_target_bar_height = 2;
|
|
||||||
|
|
||||||
private const float column_width = 45;
|
private const float column_width = 45;
|
||||||
private const float special_column_width = 70;
|
private const float special_column_width = 70;
|
||||||
|
|
||||||
public ManiaAction Action;
|
private ManiaAction action;
|
||||||
|
|
||||||
private readonly Box background;
|
public ManiaAction Action
|
||||||
private readonly Box backgroundOverlay;
|
{
|
||||||
private readonly Container hitTargetBar;
|
get => action;
|
||||||
private readonly Container keyIcon;
|
set
|
||||||
|
{
|
||||||
|
if (action == value)
|
||||||
|
return;
|
||||||
|
action = value;
|
||||||
|
|
||||||
|
background.Action = value;
|
||||||
|
keyArea.Action = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ColumnBackground background;
|
||||||
|
private readonly ColumnKeyArea keyArea;
|
||||||
|
private readonly ColumnHitObjectArea hitObjectArea;
|
||||||
|
|
||||||
internal readonly Container TopLevelContainer;
|
internal readonly Container TopLevelContainer;
|
||||||
private readonly Container explosionContainer;
|
private readonly Container explosionContainer;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => hitObjectArea;
|
||||||
private readonly Container<Drawable> content;
|
|
||||||
|
|
||||||
public Column()
|
public Column(ScrollingDirection direction)
|
||||||
: base(ScrollingDirection.Up)
|
: base(direction)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
Width = column_width;
|
Width = column_width;
|
||||||
@ -52,71 +53,21 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Masking = true;
|
Masking = true;
|
||||||
CornerRadius = 5;
|
CornerRadius = 5;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
background = new ColumnBackground { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
|
Container hitTargetContainer;
|
||||||
|
|
||||||
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
background = new Box
|
// For input purposes, the background is added at the highest depth, but is then proxied back below all other elements
|
||||||
{
|
background.CreateProxy(),
|
||||||
Name = "Background",
|
hitTargetContainer = new Container
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0.3f
|
|
||||||
},
|
|
||||||
backgroundOverlay = new Box
|
|
||||||
{
|
|
||||||
Name = "Background Gradient Overlay",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Height = 0.5f,
|
|
||||||
Anchor = Anchor.TopLeft,
|
|
||||||
Origin = Anchor.TopLeft,
|
|
||||||
Blending = BlendingMode.Additive,
|
|
||||||
Alpha = 0
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
{
|
||||||
Name = "Hit target + hit objects",
|
Name = "Hit target + hit objects",
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Top = ManiaStage.HIT_TARGET_POSITION },
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
hitObjectArea = new ColumnHitObjectArea { RelativeSizeAxes = Axes.Both },
|
||||||
{
|
|
||||||
Name = "Hit target",
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = hit_target_height,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Name = "Background",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4.Black
|
|
||||||
},
|
|
||||||
hitTargetBar = new Container
|
|
||||||
{
|
|
||||||
Name = "Bar",
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = hit_target_bar_height,
|
|
||||||
Masking = true,
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
content = new Container
|
|
||||||
{
|
|
||||||
Name = "Hit objects",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
// For column lighting, we need to capture input events before the notes
|
|
||||||
new InputTarget
|
|
||||||
{
|
|
||||||
Pressed = onPressed,
|
|
||||||
Released = onReleased
|
|
||||||
},
|
|
||||||
explosionContainer = new Container
|
explosionContainer = new Container
|
||||||
{
|
{
|
||||||
Name = "Hit explosions",
|
Name = "Hit explosions",
|
||||||
@ -124,46 +75,27 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new Container
|
keyArea = new ColumnKeyArea
|
||||||
{
|
{
|
||||||
Name = "Key",
|
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = ManiaStage.HIT_TARGET_POSITION,
|
Height = ManiaStage.HIT_TARGET_POSITION,
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Name = "Key gradient",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = ColourInfo.GradientVertical(Color4.Black, Color4.Black.Opacity(0)),
|
|
||||||
Alpha = 0.5f
|
|
||||||
},
|
|
||||||
keyIcon = new Container
|
|
||||||
{
|
|
||||||
Name = "Key icon",
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Size = new Vector2(key_icon_size),
|
|
||||||
Masking = true,
|
|
||||||
CornerRadius = key_icon_corner_radius,
|
|
||||||
BorderThickness = 2,
|
|
||||||
BorderColour = Color4.White, // Not true
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Alpha = 0,
|
|
||||||
AlwaysPresent = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
background,
|
||||||
TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
TopLevelContainer = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
};
|
};
|
||||||
|
|
||||||
TopLevelContainer.Add(explosionContainer.CreateProxy());
|
TopLevelContainer.Add(explosionContainer.CreateProxy());
|
||||||
|
|
||||||
|
Direction.BindValueChanged(d =>
|
||||||
|
{
|
||||||
|
hitTargetContainer.Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Top = d == ScrollingDirection.Up ? ManiaStage.HIT_TARGET_POSITION : 0,
|
||||||
|
Bottom = d == ScrollingDirection.Down ? ManiaStage.HIT_TARGET_POSITION : 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
keyArea.Anchor = keyArea.Origin= d == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Axes RelativeSizeAxes => Axes.Y;
|
public override Axes RelativeSizeAxes => Axes.Y;
|
||||||
@ -192,22 +124,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
return;
|
return;
|
||||||
accentColour = value;
|
accentColour = value;
|
||||||
|
|
||||||
background.Colour = accentColour;
|
background.AccentColour = value;
|
||||||
backgroundOverlay.Colour = ColourInfo.GradientVertical(accentColour.Opacity(0.6f), accentColour.Opacity(0));
|
keyArea.AccentColour = value;
|
||||||
|
hitObjectArea.AccentColour = value;
|
||||||
hitTargetBar.EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Glow,
|
|
||||||
Radius = 5,
|
|
||||||
Colour = accentColour.Opacity(0.5f),
|
|
||||||
};
|
|
||||||
|
|
||||||
keyIcon.EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
|
||||||
Type = EdgeEffectType.Glow,
|
|
||||||
Radius = 5,
|
|
||||||
Colour = accentColour.Opacity(0.5f),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,48 +147,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
if (!judgement.IsHit || !judgedObject.DisplayJudgement)
|
if (!judgement.IsHit || !judgedObject.DisplayJudgement)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
explosionContainer.Add(new HitExplosion(judgedObject));
|
explosionContainer.Add(new HitExplosion(judgedObject)
|
||||||
}
|
|
||||||
|
|
||||||
private bool onPressed(ManiaAction action)
|
|
||||||
{
|
|
||||||
if (action == Action)
|
|
||||||
{
|
{
|
||||||
backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
|
Anchor = Direction == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre
|
||||||
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint).Then().ScaleTo(1.3f, 250, Easing.OutQuint);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool onReleased(ManiaAction action)
|
|
||||||
{
|
|
||||||
if (action == Action)
|
|
||||||
{
|
|
||||||
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
|
|
||||||
keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This is a simple container which delegates various input events that have to be captured before the notes.
|
|
||||||
/// </summary>
|
|
||||||
private class InputTarget : Container, IKeyBindingHandler<ManiaAction>
|
|
||||||
{
|
|
||||||
public Func<ManiaAction, bool> Pressed;
|
|
||||||
public Func<ManiaAction, bool> Released;
|
|
||||||
|
|
||||||
public InputTarget()
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
AlwaysPresent = true;
|
|
||||||
Alpha = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OnPressed(ManiaAction action) => Pressed?.Invoke(action) ?? false;
|
|
||||||
public bool OnReleased(ManiaAction action) => Released?.Invoke(action) ?? false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool OnPressed(ManiaAction action)
|
public bool OnPressed(ManiaAction action)
|
||||||
|
106
osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs
Normal file
106
osu.Game.Rulesets.Mania/UI/Components/ColumnBackground.cs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.UI.Components
|
||||||
|
{
|
||||||
|
public class ColumnBackground : CompositeDrawable, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
||||||
|
{
|
||||||
|
public ManiaAction Action;
|
||||||
|
|
||||||
|
private Box background;
|
||||||
|
private Box backgroundOverlay;
|
||||||
|
|
||||||
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
|
{
|
||||||
|
InternalChildren = new[]
|
||||||
|
{
|
||||||
|
background = new Box
|
||||||
|
{
|
||||||
|
Name = "Background",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0.3f
|
||||||
|
},
|
||||||
|
backgroundOverlay = new Box
|
||||||
|
{
|
||||||
|
Name = "Background Gradient Overlay",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Height = 0.5f,
|
||||||
|
Blending = BlendingMode.Additive,
|
||||||
|
Alpha = 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
|
direction.BindValueChanged(direction =>
|
||||||
|
{
|
||||||
|
backgroundOverlay.Anchor = backgroundOverlay.Origin = direction == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||||
|
updateColours();
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
updateColours();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 accentColour;
|
||||||
|
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get => accentColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (accentColour == value)
|
||||||
|
return;
|
||||||
|
accentColour = value;
|
||||||
|
|
||||||
|
updateColours();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateColours()
|
||||||
|
{
|
||||||
|
if (!IsLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
background.Colour = AccentColour;
|
||||||
|
|
||||||
|
var brightPoint = AccentColour.Opacity(0.6f);
|
||||||
|
var dimPoint = AccentColour.Opacity(0);
|
||||||
|
|
||||||
|
backgroundOverlay.Colour = ColourInfo.GradientVertical(
|
||||||
|
direction.Value == ScrollingDirection.Up ? brightPoint : dimPoint,
|
||||||
|
direction.Value == ScrollingDirection.Up ? dimPoint : brightPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(ManiaAction action)
|
||||||
|
{
|
||||||
|
if (action == Action)
|
||||||
|
backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(ManiaAction action)
|
||||||
|
{
|
||||||
|
if (action == Action)
|
||||||
|
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
Normal file
99
osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.UI.Components
|
||||||
|
{
|
||||||
|
public class ColumnHitObjectArea : Container, IHasAccentColour
|
||||||
|
{
|
||||||
|
private const float hit_target_height = 10;
|
||||||
|
private const float hit_target_bar_height = 2;
|
||||||
|
|
||||||
|
private Container<Drawable> content;
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
private Container hitTargetLine;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
|
{
|
||||||
|
Drawable hitTargetBar;
|
||||||
|
|
||||||
|
InternalChildren = new[]
|
||||||
|
{
|
||||||
|
hitTargetBar = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = hit_target_height,
|
||||||
|
Colour = Color4.Black
|
||||||
|
},
|
||||||
|
hitTargetLine = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = hit_target_bar_height,
|
||||||
|
Masking = true,
|
||||||
|
Child = new Box { RelativeSizeAxes = Axes.Both }
|
||||||
|
},
|
||||||
|
content = new Container
|
||||||
|
{
|
||||||
|
Name = "Hit objects",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
|
direction.BindValueChanged(direction =>
|
||||||
|
{
|
||||||
|
Anchor anchor = direction == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||||
|
|
||||||
|
hitTargetBar.Anchor = hitTargetBar.Origin = anchor;
|
||||||
|
hitTargetLine.Anchor = hitTargetLine.Origin = anchor;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
updateColours();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 accentColour;
|
||||||
|
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get => accentColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (accentColour == value)
|
||||||
|
return;
|
||||||
|
accentColour = value;
|
||||||
|
|
||||||
|
updateColours();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateColours()
|
||||||
|
{
|
||||||
|
if (!IsLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hitTargetLine.EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Radius = 5,
|
||||||
|
Colour = accentColour.Opacity(0.5f),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
122
osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
Normal file
122
osu.Game.Rulesets.Mania/UI/Components/ColumnKeyArea.cs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.UI.Components
|
||||||
|
{
|
||||||
|
public class ColumnKeyArea : CompositeDrawable, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
||||||
|
{
|
||||||
|
private const float key_icon_size = 10;
|
||||||
|
private const float key_icon_corner_radius = 3;
|
||||||
|
|
||||||
|
public ManiaAction Action;
|
||||||
|
|
||||||
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
private Container keyIcon;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
|
{
|
||||||
|
Drawable gradient;
|
||||||
|
|
||||||
|
InternalChildren = new[]
|
||||||
|
{
|
||||||
|
gradient = new Box
|
||||||
|
{
|
||||||
|
Name = "Key gradient",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0.5f
|
||||||
|
},
|
||||||
|
keyIcon = new Container
|
||||||
|
{
|
||||||
|
Name = "Key icon",
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(key_icon_size),
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = key_icon_corner_radius,
|
||||||
|
BorderThickness = 2,
|
||||||
|
BorderColour = Color4.White, // Not true
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Alpha = 0,
|
||||||
|
AlwaysPresent = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
|
direction.BindValueChanged(direction =>
|
||||||
|
{
|
||||||
|
gradient.Colour = ColourInfo.GradientVertical(
|
||||||
|
direction == ScrollingDirection.Up ? Color4.Black : Color4.Black.Opacity(0),
|
||||||
|
direction == ScrollingDirection.Up ? Color4.Black.Opacity(0) : Color4.Black);
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
updateColours();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 accentColour;
|
||||||
|
|
||||||
|
public Color4 AccentColour
|
||||||
|
{
|
||||||
|
get => accentColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (accentColour == value)
|
||||||
|
return;
|
||||||
|
accentColour = value;
|
||||||
|
|
||||||
|
updateColours();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateColours()
|
||||||
|
{
|
||||||
|
if (!IsLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
keyIcon.EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Glow,
|
||||||
|
Radius = 5,
|
||||||
|
Colour = accentColour.Opacity(0.5f),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(ManiaAction action)
|
||||||
|
{
|
||||||
|
if (action == Action)
|
||||||
|
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint).Then().ScaleTo(1.3f, 250, Easing.OutQuint);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnReleased(ManiaAction action)
|
||||||
|
{
|
||||||
|
if (action == Action)
|
||||||
|
keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
bool isTick = judgedObject is DrawableHoldNoteTick;
|
bool isTick = judgedObject is DrawableHoldNoteTick;
|
||||||
|
|
||||||
Anchor = Anchor.TopCentre;
|
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
17
osu.Game.Rulesets.Mania/UI/IScrollingInfo.cs
Normal file
17
osu.Game.Rulesets.Mania/UI/IScrollingInfo.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
|
{
|
||||||
|
public interface IScrollingInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The direction <see cref="HitObject"/>s should scroll in.
|
||||||
|
/// </summary>
|
||||||
|
IBindable<ScrollingDirection> Direction { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Configuration;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -17,18 +16,13 @@ using osu.Game.Rulesets.UI.Scrolling;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
public class ManiaPlayfield : ScrollingPlayfield
|
public class ManiaPlayfield : ManiaScrollingPlayfield
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Whether this playfield should be inverted. This flips everything inside the playfield.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
|
|
||||||
|
|
||||||
public List<Column> Columns => stages.SelectMany(x => x.Columns).ToList();
|
public List<Column> Columns => stages.SelectMany(x => x.Columns).ToList();
|
||||||
private readonly List<ManiaStage> stages = new List<ManiaStage>();
|
private readonly List<ManiaStage> stages = new List<ManiaStage>();
|
||||||
|
|
||||||
public ManiaPlayfield(List<StageDefinition> stageDefinitions)
|
public ManiaPlayfield(ScrollingDirection direction, List<StageDefinition> stageDefinitions)
|
||||||
: base(ScrollingDirection.Up)
|
: base(direction)
|
||||||
{
|
{
|
||||||
if (stageDefinitions == null)
|
if (stageDefinitions == null)
|
||||||
throw new ArgumentNullException(nameof(stageDefinitions));
|
throw new ArgumentNullException(nameof(stageDefinitions));
|
||||||
@ -36,8 +30,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
if (stageDefinitions.Count <= 0)
|
if (stageDefinitions.Count <= 0)
|
||||||
throw new ArgumentException("Can't have zero or fewer stages.");
|
throw new ArgumentException("Can't have zero or fewer stages.");
|
||||||
|
|
||||||
Inverted.Value = true;
|
|
||||||
|
|
||||||
GridContainer playfieldGrid;
|
GridContainer playfieldGrid;
|
||||||
InternalChild = playfieldGrid = new GridContainer
|
InternalChild = playfieldGrid = new GridContainer
|
||||||
{
|
{
|
||||||
@ -50,9 +42,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
int firstColumnIndex = 0;
|
int firstColumnIndex = 0;
|
||||||
for (int i = 0; i < stageDefinitions.Count; i++)
|
for (int i = 0; i < stageDefinitions.Count; i++)
|
||||||
{
|
{
|
||||||
var newStage = new ManiaStage(firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
|
var newStage = new ManiaStage(direction, firstColumnIndex, stageDefinitions[i], ref normalColumnAction, ref specialColumnAction);
|
||||||
newStage.VisibleTimeRange.BindTo(VisibleTimeRange);
|
newStage.VisibleTimeRange.BindTo(VisibleTimeRange);
|
||||||
newStage.Inverted.BindTo(Inverted);
|
|
||||||
|
|
||||||
playfieldGrid.Content[0][i] = newStage;
|
playfieldGrid.Content[0][i] = newStage;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
@ -12,6 +13,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Configuration;
|
||||||
using osu.Game.Rulesets.Mania.Mods;
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
@ -33,6 +35,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
public IEnumerable<BarLine> BarLines;
|
public IEnumerable<BarLine> BarLines;
|
||||||
|
|
||||||
|
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
|
||||||
|
private ScrollingInfo scrollingInfo;
|
||||||
|
|
||||||
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
|
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||||
: base(ruleset, beatmap)
|
: base(ruleset, beatmap)
|
||||||
{
|
{
|
||||||
@ -65,12 +70,24 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(ManiaConfigManager config)
|
||||||
{
|
{
|
||||||
BarLines.ForEach(Playfield.Add);
|
BarLines.ForEach(Playfield.Add);
|
||||||
|
|
||||||
|
config.BindWith(ManiaSetting.ScrollDirection, configDirection);
|
||||||
|
configDirection.BindValueChanged(d => scrollingInfo.Direction.Value = (ScrollingDirection)d, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
|
private DependencyContainer dependencies;
|
||||||
|
|
||||||
|
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent)
|
||||||
|
{
|
||||||
|
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||||
|
dependencies.CacheAs<IScrollingInfo>(scrollingInfo = new ScrollingInfo());
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(scrollingInfo.Direction, Beatmap.Stages)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -100,5 +117,11 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
|
protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
|
||||||
|
|
||||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
|
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
|
||||||
|
|
||||||
|
private class ScrollingInfo : IScrollingInfo
|
||||||
|
{
|
||||||
|
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||||
|
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
osu.Game.Rulesets.Mania/UI/ManiaScrollingDirection.cs
Normal file
13
osu.Game.Rulesets.Mania/UI/ManiaScrollingDirection.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
|
{
|
||||||
|
public enum ManiaScrollingDirection
|
||||||
|
{
|
||||||
|
Up = ScrollingDirection.Up,
|
||||||
|
Down = ScrollingDirection.Down
|
||||||
|
}
|
||||||
|
}
|
26
osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs
Normal file
26
osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Configuration;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
|
{
|
||||||
|
public class ManiaScrollingPlayfield : ScrollingPlayfield
|
||||||
|
{
|
||||||
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
public ManiaScrollingPlayfield(ScrollingDirection direction)
|
||||||
|
: base(direction)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
|
{
|
||||||
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
|
direction.BindValueChanged(direction => Direction.Value = direction, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Configuration;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@ -24,20 +23,15 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A collection of <see cref="Column"/>s.
|
/// A collection of <see cref="Column"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class ManiaStage : ScrollingPlayfield
|
internal class ManiaStage : ManiaScrollingPlayfield
|
||||||
{
|
{
|
||||||
public const float HIT_TARGET_POSITION = 50;
|
public const float HIT_TARGET_POSITION = 50;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether this playfield should be inverted. This flips everything inside the playfield.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Bindable<bool> Inverted = new Bindable<bool>(true);
|
|
||||||
|
|
||||||
public IReadOnlyList<Column> Columns => columnFlow.Children;
|
public IReadOnlyList<Column> Columns => columnFlow.Children;
|
||||||
private readonly FillFlowContainer<Column> columnFlow;
|
private readonly FillFlowContainer<Column> columnFlow;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => barLineContainer;
|
||||||
private readonly Container<Drawable> content;
|
private readonly Container<Drawable> barLineContainer;
|
||||||
|
|
||||||
public Container<DrawableManiaJudgement> Judgements => judgements;
|
public Container<DrawableManiaJudgement> Judgements => judgements;
|
||||||
private readonly JudgementContainer<DrawableManiaJudgement> judgements;
|
private readonly JudgementContainer<DrawableManiaJudgement> judgements;
|
||||||
@ -49,8 +43,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
private readonly int firstColumnIndex;
|
private readonly int firstColumnIndex;
|
||||||
|
|
||||||
public ManiaStage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
|
public ManiaStage(ScrollingDirection direction, int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction)
|
||||||
: base(ScrollingDirection.Up)
|
: base(direction)
|
||||||
{
|
{
|
||||||
this.firstColumnIndex = firstColumnIndex;
|
this.firstColumnIndex = firstColumnIndex;
|
||||||
|
|
||||||
@ -106,13 +100,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Width = 1366, // Bar lines should only be masked on the vertical axis
|
Width = 1366, // Bar lines should only be masked on the vertical axis
|
||||||
BypassAutoSizeAxes = Axes.Both,
|
BypassAutoSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
Child = content = new Container
|
Child = barLineContainer = new Container
|
||||||
{
|
{
|
||||||
Name = "Bar lines",
|
Name = "Bar lines",
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Top = HIT_TARGET_POSITION }
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
judgements = new JudgementContainer<DrawableManiaJudgement>
|
judgements = new JudgementContainer<DrawableManiaJudgement>
|
||||||
@ -131,7 +124,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
for (int i = 0; i < definition.Columns; i++)
|
for (int i = 0; i < definition.Columns; i++)
|
||||||
{
|
{
|
||||||
var isSpecial = definition.IsSpecialColumn(i);
|
var isSpecial = definition.IsSpecialColumn(i);
|
||||||
var column = new Column
|
var column = new Column(direction)
|
||||||
{
|
{
|
||||||
IsSpecial = isSpecial,
|
IsSpecial = isSpecial,
|
||||||
Action = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++
|
Action = isSpecial ? specialColumnStartAction++ : normalColumnStartAction++
|
||||||
@ -140,14 +133,14 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
AddColumn(column);
|
AddColumn(column);
|
||||||
}
|
}
|
||||||
|
|
||||||
Inverted.ValueChanged += invertedChanged;
|
Direction.BindValueChanged(d =>
|
||||||
Inverted.TriggerChange();
|
{
|
||||||
}
|
barLineContainer.Padding = new MarginPadding
|
||||||
|
{
|
||||||
private void invertedChanged(bool newValue)
|
Top = d == ScrollingDirection.Up ? HIT_TARGET_POSITION : 0,
|
||||||
{
|
Bottom = d == ScrollingDirection.Down ? HIT_TARGET_POSITION : 0,
|
||||||
Scale = new Vector2(1, newValue ? -1 : 1);
|
};
|
||||||
Judgements.Scale = Scale;
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddColumn(Column c)
|
public void AddColumn(Column c)
|
||||||
@ -218,7 +211,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
{
|
{
|
||||||
// Due to masking differences, it is not possible to get the width of the columns container automatically
|
// Due to masking differences, it is not possible to get the width of the columns container automatically
|
||||||
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
|
// While masking on effectively only the Y-axis, so we need to set the width of the bar line container manually
|
||||||
content.Width = columnFlow.Width;
|
barLineContainer.Width = columnFlow.Width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,17 +8,19 @@ using osu.Framework.MathUtils;
|
|||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
using OpenTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
{
|
{
|
||||||
internal class OsuBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
[TestFixture]
|
||||||
|
public class OsuBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||||
|
|
||||||
[TestCase("basic")]
|
[TestCase("basic")]
|
||||||
[TestCase("colinear-perfect-curve")]
|
[TestCase("colinear-perfect-curve")]
|
||||||
|
[TestCase("slider-ticks")]
|
||||||
public new void Test(string name)
|
public new void Test(string name)
|
||||||
{
|
{
|
||||||
base.Test(name);
|
base.Test(name);
|
||||||
@ -26,24 +28,30 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||||
{
|
{
|
||||||
var startPosition = (hitObject as IHasPosition)?.Position ?? new Vector2(256, 192);
|
switch (hitObject)
|
||||||
var endPosition = (hitObject as Slider)?.EndPosition ?? startPosition;
|
|
||||||
|
|
||||||
yield return new ConvertValue
|
|
||||||
{
|
{
|
||||||
StartTime = hitObject.StartTime,
|
case Slider slider:
|
||||||
EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime,
|
foreach (var nested in slider.NestedHitObjects)
|
||||||
StartX = startPosition.X,
|
yield return createConvertValue(nested);
|
||||||
StartY = startPosition.Y,
|
break;
|
||||||
EndX = endPosition.X,
|
default:
|
||||||
EndY = endPosition.Y
|
yield return createConvertValue(hitObject);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvertValue createConvertValue(HitObject obj) => new ConvertValue
|
||||||
|
{
|
||||||
|
StartTime = obj.StartTime,
|
||||||
|
EndTime = (obj as IHasEndTime)?.EndTime ?? obj.StartTime,
|
||||||
|
X = (obj as IHasPosition)?.X ?? OsuPlayfield.BASE_SIZE.X / 2,
|
||||||
|
Y = (obj as IHasPosition)?.Y ?? OsuPlayfield.BASE_SIZE.Y / 2,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Ruleset CreateRuleset() => new OsuRuleset();
|
protected override Ruleset CreateRuleset() => new OsuRuleset();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct ConvertValue : IEquatable<ConvertValue>
|
public struct ConvertValue : IEquatable<ConvertValue>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A sane value to account for osu!stable using ints everwhere.
|
/// A sane value to account for osu!stable using ints everwhere.
|
||||||
@ -52,17 +60,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
public double StartTime;
|
public double StartTime;
|
||||||
public double EndTime;
|
public double EndTime;
|
||||||
public float StartX;
|
public float X;
|
||||||
public float StartY;
|
public float Y;
|
||||||
public float EndX;
|
|
||||||
public float EndY;
|
|
||||||
|
|
||||||
public bool Equals(ConvertValue other)
|
public bool Equals(ConvertValue other)
|
||||||
=> Precision.AlmostEquals(StartTime, other.StartTime)
|
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
|
||||||
&& Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience)
|
&& Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience)
|
||||||
&& Precision.AlmostEquals(StartX, other.StartX)
|
&& Precision.AlmostEquals(X, other.X, conversion_lenience)
|
||||||
&& Precision.AlmostEquals(StartY, other.StartY, conversion_lenience)
|
&& Precision.AlmostEquals(Y, other.Y, conversion_lenience);
|
||||||
&& Precision.AlmostEquals(EndX, other.EndX, conversion_lenience)
|
|
||||||
&& Precision.AlmostEquals(EndY, other.EndY, conversion_lenience);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
var endTimeData = original as IHasEndTime;
|
var endTimeData = original as IHasEndTime;
|
||||||
var positionData = original as IHasPosition;
|
var positionData = original as IHasPosition;
|
||||||
var comboData = original as IHasCombo;
|
var comboData = original as IHasCombo;
|
||||||
|
var legacyOffset = original as IHasLegacyLastTickOffset;
|
||||||
|
|
||||||
if (curveData != null)
|
if (curveData != null)
|
||||||
{
|
{
|
||||||
@ -40,7 +41,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
RepeatSamples = curveData.RepeatSamples,
|
RepeatSamples = curveData.RepeatSamples,
|
||||||
RepeatCount = curveData.RepeatCount,
|
RepeatCount = curveData.RepeatCount,
|
||||||
Position = positionData?.Position ?? Vector2.Zero,
|
Position = positionData?.Position ?? Vector2.Zero,
|
||||||
NewCombo = comboData?.NewCombo ?? false
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
|
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (endTimeData != null)
|
else if (endTimeData != null)
|
||||||
|
@ -10,6 +10,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
{
|
{
|
||||||
public double AimStrain;
|
public double AimStrain;
|
||||||
public double SpeedStrain;
|
public double SpeedStrain;
|
||||||
|
public double ApproachRate;
|
||||||
|
public double OverallDifficulty;
|
||||||
|
public int MaxCombo;
|
||||||
|
|
||||||
public OsuDifficultyAttributes(Mod[] mods, double starRating)
|
public OsuDifficultyAttributes(Mod[] mods, double starRating)
|
||||||
: base(mods, starRating)
|
: base(mods, starRating)
|
||||||
|
@ -25,6 +25,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||||
{
|
{
|
||||||
|
if (!beatmap.HitObjects.Any())
|
||||||
|
return new OsuDifficultyAttributes(mods, 0);
|
||||||
|
|
||||||
OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast<OsuHitObject>().ToList(), timeRate);
|
OsuDifficultyBeatmap difficultyBeatmap = new OsuDifficultyBeatmap(beatmap.HitObjects.Cast<OsuHitObject>().ToList(), timeRate);
|
||||||
Skill[] skills =
|
Skill[] skills =
|
||||||
{
|
{
|
||||||
@ -58,10 +61,21 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
||||||
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
|
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
|
||||||
|
|
||||||
|
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
|
||||||
|
double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate;
|
||||||
|
double preEmpt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
|
||||||
|
|
||||||
|
int maxCombo = beatmap.HitObjects.Count();
|
||||||
|
// Add the ticks + tail of the slider. 1 is subtracted because the "headcircle" would be counted twice (once for the slider itself in the line above)
|
||||||
|
maxCombo += beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
|
||||||
|
|
||||||
return new OsuDifficultyAttributes(mods, starRating)
|
return new OsuDifficultyAttributes(mods, starRating)
|
||||||
{
|
{
|
||||||
AimStrain = aimRating,
|
AimStrain = aimRating,
|
||||||
SpeedStrain = speedRating
|
SpeedStrain = speedRating,
|
||||||
|
ApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5,
|
||||||
|
OverallDifficulty = (80 - hitWindowGreat) / 6,
|
||||||
|
MaxCombo = maxCombo
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,16 +22,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
private Mod[] mods;
|
private Mod[] mods;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Approach rate adjusted by mods.
|
|
||||||
/// </summary>
|
|
||||||
private double realApproachRate;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Overall difficulty adjusted by mods.
|
|
||||||
/// </summary>
|
|
||||||
private double realOverallDifficulty;
|
|
||||||
|
|
||||||
private double accuracy;
|
private double accuracy;
|
||||||
private int scoreMaxCombo;
|
private int scoreMaxCombo;
|
||||||
private int countGreat;
|
private int countGreat;
|
||||||
@ -63,13 +53,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
if (mods.Any(m => !m.Ranked))
|
if (mods.Any(m => !m.Ranked))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
|
|
||||||
double hitWindowGreat = (int)(Beatmap.HitObjects.First().HitWindows.Great / 2) / TimeRate;
|
|
||||||
double preEmpt = (int)BeatmapDifficulty.DifficultyRange(Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / TimeRate;
|
|
||||||
|
|
||||||
realApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5;
|
|
||||||
realOverallDifficulty = (80 - hitWindowGreat) / 6;
|
|
||||||
|
|
||||||
// Custom multipliers for NoFail and SpunOut.
|
// Custom multipliers for NoFail and SpunOut.
|
||||||
double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
|
double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
|
||||||
|
|
||||||
@ -94,8 +77,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
categoryRatings.Add("Aim", aimValue);
|
categoryRatings.Add("Aim", aimValue);
|
||||||
categoryRatings.Add("Speed", speedValue);
|
categoryRatings.Add("Speed", speedValue);
|
||||||
categoryRatings.Add("Accuracy", accuracyValue);
|
categoryRatings.Add("Accuracy", accuracyValue);
|
||||||
categoryRatings.Add("OD", realOverallDifficulty);
|
categoryRatings.Add("OD", Attributes.OverallDifficulty);
|
||||||
categoryRatings.Add("AR", realApproachRate);
|
categoryRatings.Add("AR", Attributes.ApproachRate);
|
||||||
categoryRatings.Add("Max Combo", beatmapMaxCombo);
|
categoryRatings.Add("Max Combo", beatmapMaxCombo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,22 +103,22 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
|
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
|
||||||
|
|
||||||
double approachRateFactor = 1.0f;
|
double approachRateFactor = 1.0f;
|
||||||
if (realApproachRate > 10.33f)
|
if (Attributes.ApproachRate > 10.33f)
|
||||||
approachRateFactor += 0.45f * (realApproachRate - 10.33f);
|
approachRateFactor += 0.45f * (Attributes.ApproachRate - 10.33f);
|
||||||
else if (realApproachRate < 8.0f)
|
else if (Attributes.ApproachRate < 8.0f)
|
||||||
{
|
{
|
||||||
// HD is worth more with lower ar!
|
// HD is worth more with lower ar!
|
||||||
if (mods.Any(h => h is OsuModHidden))
|
if (mods.Any(h => h is OsuModHidden))
|
||||||
approachRateFactor += 0.02f * (8.0f - realApproachRate);
|
approachRateFactor += 0.02f * (8.0f - Attributes.ApproachRate);
|
||||||
else
|
else
|
||||||
approachRateFactor += 0.01f * (8.0f - realApproachRate);
|
approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
aimValue *= approachRateFactor;
|
aimValue *= approachRateFactor;
|
||||||
|
|
||||||
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
|
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
|
||||||
if (mods.Any(h => h is OsuModHidden))
|
if (mods.Any(h => h is OsuModHidden))
|
||||||
aimValue *= 1.02 + (11.0f - realApproachRate) / 50.0; // Gives a 1.04 bonus for AR10, a 1.06 bonus for AR9, a 1.02 bonus for AR11.
|
aimValue *= 1.02 + (11.0f - Attributes.ApproachRate) / 50.0; // Gives a 1.04 bonus for AR10, a 1.06 bonus for AR9, a 1.02 bonus for AR11.
|
||||||
|
|
||||||
if (mods.Any(h => h is OsuModFlashlight))
|
if (mods.Any(h => h is OsuModFlashlight))
|
||||||
{
|
{
|
||||||
@ -146,7 +129,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
// Scale the aim value with accuracy _slightly_
|
// Scale the aim value with accuracy _slightly_
|
||||||
aimValue *= 0.5f + accuracy / 2.0f;
|
aimValue *= 0.5f + accuracy / 2.0f;
|
||||||
// It is important to also consider accuracy difficulty when doing that
|
// It is important to also consider accuracy difficulty when doing that
|
||||||
aimValue *= 0.98f + Math.Pow(realOverallDifficulty, 2) / 2500;
|
aimValue *= 0.98f + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
|
||||||
|
|
||||||
return aimValue;
|
return aimValue;
|
||||||
}
|
}
|
||||||
@ -172,7 +155,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
// Scale the speed value with accuracy _slightly_
|
// Scale the speed value with accuracy _slightly_
|
||||||
speedValue *= 0.5f + accuracy / 2.0f;
|
speedValue *= 0.5f + accuracy / 2.0f;
|
||||||
// It is important to also consider accuracy difficulty when doing that
|
// It is important to also consider accuracy difficulty when doing that
|
||||||
speedValue *= 0.98f + Math.Pow(realOverallDifficulty, 2) / 2500;
|
speedValue *= 0.98f + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
|
||||||
|
|
||||||
return speedValue;
|
return speedValue;
|
||||||
}
|
}
|
||||||
@ -194,7 +177,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
// Lots of arbitrary values from testing.
|
// Lots of arbitrary values from testing.
|
||||||
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
||||||
double accuracyValue = Math.Pow(1.52163f, realOverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f;
|
double accuracyValue = Math.Pow(1.52163f, Attributes.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f;
|
||||||
|
|
||||||
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
||||||
accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
|
accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
|
||||||
|
@ -93,6 +93,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
base.AccentColour = value;
|
base.AccentColour = value;
|
||||||
Body.AccentColour = AccentColour;
|
Body.AccentColour = AccentColour;
|
||||||
Ball.AccentColour = AccentColour;
|
Ball.AccentColour = AccentColour;
|
||||||
|
if (HasNestedHitObjects)
|
||||||
|
foreach (var drawableHitObject in NestedHitObjects)
|
||||||
|
drawableHitObject.AccentColour = AccentColour;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -45,6 +46,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
set { Curve.Distance = value; }
|
set { Curve.Distance = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double? LegacyLastTickOffset { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The position of the cursor at the point of completion of this <see cref="Slider"/> if it was hit
|
/// The position of the cursor at the point of completion of this <see cref="Slider"/> if it was hit
|
||||||
/// with as few movements as possible. This is set and used by difficulty calculation.
|
/// with as few movements as possible. This is set and used by difficulty calculation.
|
||||||
@ -91,6 +94,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
createSliderEnds();
|
createSliderEnds();
|
||||||
createTicks();
|
createTicks();
|
||||||
createRepeatPoints();
|
createRepeatPoints();
|
||||||
|
|
||||||
|
if (LegacyLastTickOffset != null)
|
||||||
|
TailCircle.StartTime = Math.Max(StartTime + Duration / 2, TailCircle.StartTime - LegacyLastTickOffset.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createSliderEnds()
|
private void createSliderEnds()
|
||||||
|
@ -6,6 +6,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constants (for spinners).
|
/// Constants (for spinners).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected static readonly Vector2 SPINNER_CENTRE = new Vector2(256, 192);
|
protected static readonly Vector2 SPINNER_CENTRE = OsuPlayfield.BASE_SIZE / 2;
|
||||||
protected const float SPIN_RADIUS = 50;
|
protected const float SPIN_RADIUS = 50;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,124 +1,256 @@
|
|||||||
{
|
{
|
||||||
"Mappings": [{
|
"Mappings": [{
|
||||||
"StartTime": 500,
|
"StartTime": 500.0,
|
||||||
"Objects": [{
|
"Objects": [{
|
||||||
"StartTime": 500,
|
"StartTime": 500.0,
|
||||||
"EndTime": 2500,
|
"EndTime": 500.0,
|
||||||
"StartX": 96,
|
"X": 96.0,
|
||||||
"StartY": 192,
|
"Y": 192.0
|
||||||
"EndX": 96,
|
}, {
|
||||||
"EndY": 192
|
"StartTime": 1000.0,
|
||||||
}]
|
"EndTime": 1000.0,
|
||||||
},
|
"X": 256.0,
|
||||||
{
|
"Y": 192.0
|
||||||
"StartTime": 3000,
|
}, {
|
||||||
"Objects": [{
|
"StartTime": 1500.0,
|
||||||
"StartTime": 3000,
|
"EndTime": 1500.0,
|
||||||
"EndTime": 4000,
|
"X": 416.0,
|
||||||
"StartX": 256,
|
"Y": 192.0
|
||||||
"StartY": 192,
|
}, {
|
||||||
"EndX": 256,
|
"StartTime": 2000.0,
|
||||||
"EndY": 192
|
"EndTime": 2000.0,
|
||||||
}]
|
"X": 256.0,
|
||||||
},
|
"Y": 192.0
|
||||||
{
|
}, {
|
||||||
"StartTime": 4500,
|
"StartTime": 2464.0,
|
||||||
"Objects": [{
|
"EndTime": 2464.0,
|
||||||
"StartTime": 4500,
|
"X": 96.0,
|
||||||
"EndTime": 5500,
|
"Y": 192.0
|
||||||
"StartX": 256,
|
}]
|
||||||
"StartY": 192,
|
}, {
|
||||||
"EndX": 256,
|
"StartTime": 3000.0,
|
||||||
"EndY": 192
|
"Objects": [{
|
||||||
}]
|
"StartTime": 3000.0,
|
||||||
},
|
"EndTime": 4000.0,
|
||||||
{
|
"X": 256.0,
|
||||||
"StartTime": 6000,
|
"Y": 192.0
|
||||||
"Objects": [{
|
}]
|
||||||
"StartTime": 6000,
|
}, {
|
||||||
"EndTime": 6500,
|
"StartTime": 4500.0,
|
||||||
"StartX": 256,
|
"Objects": [{
|
||||||
"StartY": 192,
|
"StartTime": 4500.0,
|
||||||
"EndX": 256,
|
"EndTime": 5500.0,
|
||||||
"EndY": 192
|
"X": 256.0,
|
||||||
}]
|
"Y": 192.0
|
||||||
},
|
}]
|
||||||
{
|
}, {
|
||||||
"StartTime": 7000,
|
"StartTime": 6000.0,
|
||||||
"Objects": [{
|
"Objects": [{
|
||||||
"StartTime": 7000,
|
"StartTime": 6000.0,
|
||||||
"EndTime": 8000,
|
"EndTime": 6500.0,
|
||||||
"StartX": 256,
|
"X": 256.0,
|
||||||
"StartY": 128,
|
"Y": 192.0
|
||||||
"EndX": 256,
|
}]
|
||||||
"EndY": 128
|
}, {
|
||||||
}]
|
"StartTime": 7000.0,
|
||||||
},
|
"Objects": [{
|
||||||
{
|
"StartTime": 7000.0,
|
||||||
"StartTime": 8500,
|
"EndTime": 7000.0,
|
||||||
"Objects": [{
|
"X": 256.0,
|
||||||
"StartTime": 8500,
|
"Y": 128.0
|
||||||
"EndTime": 10999,
|
}, {
|
||||||
"StartX": 32,
|
"StartTime": 7250.0,
|
||||||
"StartY": 192,
|
"EndTime": 7250.0,
|
||||||
"EndX": 508.166229,
|
"X": 336.0,
|
||||||
"EndY": 153.299271
|
"Y": 128.0
|
||||||
}]
|
}, {
|
||||||
},
|
"StartTime": 7500.0,
|
||||||
{
|
"EndTime": 7500.0,
|
||||||
"StartTime": 11500,
|
"X": 256.0,
|
||||||
"Objects": [{
|
"Y": 128.0
|
||||||
"StartTime": 11500,
|
}, {
|
||||||
"EndTime": 12000,
|
"StartTime": 7750.0,
|
||||||
"StartX": 256,
|
"EndTime": 7750.0,
|
||||||
"StartY": 192,
|
"X": 336.0,
|
||||||
"EndX": 256,
|
"Y": 128.0
|
||||||
"EndY": 192
|
}, {
|
||||||
}]
|
"StartTime": 7964.0,
|
||||||
},
|
"EndTime": 7964.0,
|
||||||
{
|
"X": 256.0,
|
||||||
"StartTime": 12500,
|
"Y": 128.0
|
||||||
"Objects": [{
|
}]
|
||||||
"StartTime": 12500,
|
}, {
|
||||||
"EndTime": 16500,
|
"StartTime": 8500.0,
|
||||||
"StartX": 512,
|
"Objects": [{
|
||||||
"StartY": 320,
|
"StartTime": 8500.0,
|
||||||
"EndX": 291.1977,
|
"EndTime": 8500.0,
|
||||||
"EndY": 40.799427
|
"X": 32.0,
|
||||||
}]
|
"Y": 192.0
|
||||||
},
|
}, {
|
||||||
{
|
"StartTime": 9000.0,
|
||||||
"StartTime": 17000,
|
"EndTime": 9000.0,
|
||||||
"Objects": [{
|
"X": 101.81015,
|
||||||
"StartTime": 17000,
|
"Y": 326.4915
|
||||||
"EndTime": 18000,
|
}, {
|
||||||
"StartX": 256,
|
"StartTime": 9500.0,
|
||||||
"StartY": 256,
|
"EndTime": 9500.0,
|
||||||
"EndX": 256,
|
"X": 237.2304,
|
||||||
"EndY": 256
|
"Y": 276.282928
|
||||||
}]
|
}, {
|
||||||
},
|
"StartTime": 10000.0,
|
||||||
{
|
"EndTime": 10000.0,
|
||||||
"StartTime": 18500,
|
"X": 270.339874,
|
||||||
"Objects": [{
|
"Y": 121.1423
|
||||||
"StartTime": 18500,
|
}, {
|
||||||
"EndTime": 19450,
|
"StartTime": 10500.0,
|
||||||
"StartX": 256,
|
"EndTime": 10500.0,
|
||||||
"StartY": 192,
|
"X": 401.0588,
|
||||||
"EndX": 256,
|
"Y": 49.1515045
|
||||||
"EndY": 192
|
}, {
|
||||||
}]
|
"StartTime": 10964.0,
|
||||||
},
|
"EndTime": 10964.0,
|
||||||
{
|
"X": 508.166229,
|
||||||
"StartTime": 19875,
|
"Y": 153.299271
|
||||||
"Objects": [{
|
}]
|
||||||
"StartTime": 19875,
|
}, {
|
||||||
"EndTime": 23874,
|
"StartTime": 11500.0,
|
||||||
"StartX": 216,
|
"Objects": [{
|
||||||
"StartY": 231,
|
"StartTime": 11500.0,
|
||||||
"EndX": 408.720825,
|
"EndTime": 12000.0,
|
||||||
"EndY": 339.810455
|
"X": 256.0,
|
||||||
}]
|
"Y": 192.0
|
||||||
}
|
}]
|
||||||
]
|
}, {
|
||||||
|
"StartTime": 12500.0,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 12500.0,
|
||||||
|
"EndTime": 12500.0,
|
||||||
|
"X": 512.0,
|
||||||
|
"Y": 320.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 13000.0,
|
||||||
|
"EndTime": 13000.0,
|
||||||
|
"X": 353.235535,
|
||||||
|
"Y": 300.154449
|
||||||
|
}, {
|
||||||
|
"StartTime": 13500.0,
|
||||||
|
"EndTime": 13500.0,
|
||||||
|
"X": 194.471069,
|
||||||
|
"Y": 280.3089
|
||||||
|
}, {
|
||||||
|
"StartTime": 14000.0,
|
||||||
|
"EndTime": 14000.0,
|
||||||
|
"X": 35.7066345,
|
||||||
|
"Y": 260.463318
|
||||||
|
}, {
|
||||||
|
"StartTime": 14500.0,
|
||||||
|
"EndTime": 14500.0,
|
||||||
|
"X": 118.370323,
|
||||||
|
"Y": 219.009277
|
||||||
|
}, {
|
||||||
|
"StartTime": 15000.0,
|
||||||
|
"EndTime": 15000.0,
|
||||||
|
"X": 271.087128,
|
||||||
|
"Y": 171.285278
|
||||||
|
}, {
|
||||||
|
"StartTime": 15500.0,
|
||||||
|
"EndTime": 15500.0,
|
||||||
|
"X": 423.803925,
|
||||||
|
"Y": 123.561279
|
||||||
|
}, {
|
||||||
|
"StartTime": 16000.0,
|
||||||
|
"EndTime": 16000.0,
|
||||||
|
"X": 446.420532,
|
||||||
|
"Y": 79.60513
|
||||||
|
}, {
|
||||||
|
"StartTime": 16464.0,
|
||||||
|
"EndTime": 16464.0,
|
||||||
|
"X": 291.1977,
|
||||||
|
"Y": 40.799427
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"StartTime": 17000.0,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 17000.0,
|
||||||
|
"EndTime": 17000.0,
|
||||||
|
"X": 256.0,
|
||||||
|
"Y": 256.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 17250.0,
|
||||||
|
"EndTime": 17250.0,
|
||||||
|
"X": 176.0,
|
||||||
|
"Y": 256.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 17500.0,
|
||||||
|
"EndTime": 17500.0,
|
||||||
|
"X": 256.0,
|
||||||
|
"Y": 256.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 17750.0,
|
||||||
|
"EndTime": 17750.0,
|
||||||
|
"X": 176.0,
|
||||||
|
"Y": 256.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 17964.0,
|
||||||
|
"EndTime": 17964.0,
|
||||||
|
"X": 256.0,
|
||||||
|
"Y": 256.0
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"StartTime": 18500.0,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 18500.0,
|
||||||
|
"EndTime": 19450.0,
|
||||||
|
"X": 256.0,
|
||||||
|
"Y": 192.0
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
"StartTime": 19875.0,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 19875.0,
|
||||||
|
"EndTime": 19875.0,
|
||||||
|
"X": 216.0,
|
||||||
|
"Y": 231.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 20375.0,
|
||||||
|
"EndTime": 20375.0,
|
||||||
|
"X": 317.446747,
|
||||||
|
"Y": 171.345245
|
||||||
|
}, {
|
||||||
|
"StartTime": 20875.0,
|
||||||
|
"EndTime": 20875.0,
|
||||||
|
"X": 270.3294,
|
||||||
|
"Y": 310.4395
|
||||||
|
}, {
|
||||||
|
"StartTime": 21375.0,
|
||||||
|
"EndTime": 21375.0,
|
||||||
|
"X": 119.121056,
|
||||||
|
"Y": 322.8657
|
||||||
|
}, {
|
||||||
|
"StartTime": 21875.0,
|
||||||
|
"EndTime": 21875.0,
|
||||||
|
"X": 124.28746,
|
||||||
|
"Y": 165.224731
|
||||||
|
}, {
|
||||||
|
"StartTime": 22375.0,
|
||||||
|
"EndTime": 22375.0,
|
||||||
|
"X": 240.4715,
|
||||||
|
"Y": 62.65587
|
||||||
|
}, {
|
||||||
|
"StartTime": 22875.0,
|
||||||
|
"EndTime": 22875.0,
|
||||||
|
"X": 398.054047,
|
||||||
|
"Y": 39.064167
|
||||||
|
}, {
|
||||||
|
"StartTime": 23375.0,
|
||||||
|
"EndTime": 23375.0,
|
||||||
|
"X": 439.749878,
|
||||||
|
"Y": 183.668091
|
||||||
|
}, {
|
||||||
|
"StartTime": 23839.0,
|
||||||
|
"EndTime": 23839.0,
|
||||||
|
"X": 408.720825,
|
||||||
|
"Y": 339.810455
|
||||||
|
}]
|
||||||
|
}]
|
||||||
}
|
}
|
@ -1,13 +1,16 @@
|
|||||||
{
|
{
|
||||||
"Mappings": [{
|
"Mappings": [{
|
||||||
"StartTime": 118858,
|
"StartTime": 118858.0,
|
||||||
"Objects": [{
|
"Objects": [{
|
||||||
"StartTime": 118858,
|
"StartTime": 118858.0,
|
||||||
"EndTime": 119088,
|
"EndTime": 118858.0,
|
||||||
"StartX": 219,
|
"X": 219.0,
|
||||||
"StartY": 215,
|
"Y": 215.0
|
||||||
"EndX": 239.6507,
|
}, {
|
||||||
"EndY": 29.1437378
|
"StartTime": 119052.0,
|
||||||
|
"EndTime": 119052.0,
|
||||||
|
"X": 239.6507,
|
||||||
|
"Y": 29.1437378
|
||||||
|
}]
|
||||||
}]
|
}]
|
||||||
}]
|
|
||||||
}
|
}
|
@ -0,0 +1,331 @@
|
|||||||
|
{
|
||||||
|
"Mappings": [{
|
||||||
|
"StartTime": 500.0,
|
||||||
|
"Objects": [{
|
||||||
|
"StartTime": 500.0,
|
||||||
|
"EndTime": 500.0,
|
||||||
|
"X": 96.0,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 624.0,
|
||||||
|
"EndTime": 624.0,
|
||||||
|
"X": 105.921242,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 749.0,
|
||||||
|
"EndTime": 749.0,
|
||||||
|
"X": 115.922493,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 874.0,
|
||||||
|
"EndTime": 874.0,
|
||||||
|
"X": 125.923737,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 999.0,
|
||||||
|
"EndTime": 999.0,
|
||||||
|
"X": 135.924988,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 1124.0,
|
||||||
|
"EndTime": 1124.0,
|
||||||
|
"X": 145.926239,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 1249.0,
|
||||||
|
"EndTime": 1249.0,
|
||||||
|
"X": 155.92749,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 1374.0,
|
||||||
|
"EndTime": 1374.0,
|
||||||
|
"X": 165.928741,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 1499.0,
|
||||||
|
"EndTime": 1499.0,
|
||||||
|
"X": 175.93,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 1624.0,
|
||||||
|
"EndTime": 1624.0,
|
||||||
|
"X": 185.931244,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 1749.0,
|
||||||
|
"EndTime": 1749.0,
|
||||||
|
"X": 195.9325,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 1874.0,
|
||||||
|
"EndTime": 1874.0,
|
||||||
|
"X": 205.933746,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 1999.0,
|
||||||
|
"EndTime": 1999.0,
|
||||||
|
"X": 215.935,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 2124.0,
|
||||||
|
"EndTime": 2124.0,
|
||||||
|
"X": 225.936234,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 2249.0,
|
||||||
|
"EndTime": 2249.0,
|
||||||
|
"X": 235.9375,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 2374.0,
|
||||||
|
"EndTime": 2374.0,
|
||||||
|
"X": 245.938751,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 2499.0,
|
||||||
|
"EndTime": 2499.0,
|
||||||
|
"X": 255.94,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 2624.0,
|
||||||
|
"EndTime": 2624.0,
|
||||||
|
"X": 265.941223,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 2749.0,
|
||||||
|
"EndTime": 2749.0,
|
||||||
|
"X": 275.9425,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 2874.0,
|
||||||
|
"EndTime": 2874.0,
|
||||||
|
"X": 285.943756,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 2999.0,
|
||||||
|
"EndTime": 2999.0,
|
||||||
|
"X": 295.945,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 3124.0,
|
||||||
|
"EndTime": 3124.0,
|
||||||
|
"X": 305.946259,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 3249.0,
|
||||||
|
"EndTime": 3249.0,
|
||||||
|
"X": 315.9475,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 3374.0,
|
||||||
|
"EndTime": 3374.0,
|
||||||
|
"X": 325.94873,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 3499.0,
|
||||||
|
"EndTime": 3499.0,
|
||||||
|
"X": 335.949982,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 3624.0,
|
||||||
|
"EndTime": 3624.0,
|
||||||
|
"X": 345.951233,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 3749.0,
|
||||||
|
"EndTime": 3749.0,
|
||||||
|
"X": 355.952484,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 3874.0,
|
||||||
|
"EndTime": 3874.0,
|
||||||
|
"X": 365.953766,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 3999.0,
|
||||||
|
"EndTime": 3999.0,
|
||||||
|
"X": 375.955,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 4124.0,
|
||||||
|
"EndTime": 4124.0,
|
||||||
|
"X": 385.956238,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 4249.0,
|
||||||
|
"EndTime": 4249.0,
|
||||||
|
"X": 395.9575,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 4374.0,
|
||||||
|
"EndTime": 4374.0,
|
||||||
|
"X": 405.95874,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 4499.0,
|
||||||
|
"EndTime": 4499.0,
|
||||||
|
"X": 415.960022,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 4624.0,
|
||||||
|
"EndTime": 4624.0,
|
||||||
|
"X": 406.038757,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 4749.0,
|
||||||
|
"EndTime": 4749.0,
|
||||||
|
"X": 396.0375,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 4874.0,
|
||||||
|
"EndTime": 4874.0,
|
||||||
|
"X": 386.036255,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 4999.0,
|
||||||
|
"EndTime": 4999.0,
|
||||||
|
"X": 376.035034,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 5124.0,
|
||||||
|
"EndTime": 5124.0,
|
||||||
|
"X": 366.033752,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 5249.0,
|
||||||
|
"EndTime": 5249.0,
|
||||||
|
"X": 356.0325,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 5374.0,
|
||||||
|
"EndTime": 5374.0,
|
||||||
|
"X": 346.03125,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 5499.0,
|
||||||
|
"EndTime": 5499.0,
|
||||||
|
"X": 336.030029,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 5624.0,
|
||||||
|
"EndTime": 5624.0,
|
||||||
|
"X": 326.028748,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 5749.0,
|
||||||
|
"EndTime": 5749.0,
|
||||||
|
"X": 316.0275,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 5874.0,
|
||||||
|
"EndTime": 5874.0,
|
||||||
|
"X": 306.026245,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 5999.0,
|
||||||
|
"EndTime": 5999.0,
|
||||||
|
"X": 296.025,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 6124.0,
|
||||||
|
"EndTime": 6124.0,
|
||||||
|
"X": 286.023773,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 6249.0,
|
||||||
|
"EndTime": 6249.0,
|
||||||
|
"X": 276.022522,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 6374.0,
|
||||||
|
"EndTime": 6374.0,
|
||||||
|
"X": 266.02124,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 6499.0,
|
||||||
|
"EndTime": 6499.0,
|
||||||
|
"X": 256.02,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 6624.0,
|
||||||
|
"EndTime": 6624.0,
|
||||||
|
"X": 246.018768,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 6749.0,
|
||||||
|
"EndTime": 6749.0,
|
||||||
|
"X": 236.017517,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 6874.0,
|
||||||
|
"EndTime": 6874.0,
|
||||||
|
"X": 226.016251,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 6999.0,
|
||||||
|
"EndTime": 6999.0,
|
||||||
|
"X": 216.014984,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 7124.0,
|
||||||
|
"EndTime": 7124.0,
|
||||||
|
"X": 206.013733,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 7249.0,
|
||||||
|
"EndTime": 7249.0,
|
||||||
|
"X": 196.012512,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 7374.0,
|
||||||
|
"EndTime": 7374.0,
|
||||||
|
"X": 186.011261,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 7499.0,
|
||||||
|
"EndTime": 7499.0,
|
||||||
|
"X": 176.01,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 7624.0,
|
||||||
|
"EndTime": 7624.0,
|
||||||
|
"X": 166.008728,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 7749.0,
|
||||||
|
"EndTime": 7749.0,
|
||||||
|
"X": 156.0075,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 7874.0,
|
||||||
|
"EndTime": 7874.0,
|
||||||
|
"X": 146.006256,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 7999.0,
|
||||||
|
"EndTime": 7999.0,
|
||||||
|
"X": 136.005,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 8124.0,
|
||||||
|
"EndTime": 8124.0,
|
||||||
|
"X": 126.003738,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 8249.0,
|
||||||
|
"EndTime": 8249.0,
|
||||||
|
"X": 116.002518,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 8374.0,
|
||||||
|
"EndTime": 8374.0,
|
||||||
|
"X": 106.001259,
|
||||||
|
"Y": 192.0
|
||||||
|
}, {
|
||||||
|
"StartTime": 8463.0,
|
||||||
|
"EndTime": 8463.0,
|
||||||
|
"X": 96.0,
|
||||||
|
"Y": 192.0
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[General]
|
||||||
|
StackLeniency: 0.7
|
||||||
|
|
||||||
|
[Difficulty]
|
||||||
|
HPDrainRate:6
|
||||||
|
CircleSize:4
|
||||||
|
OverallDifficulty:7
|
||||||
|
ApproachRate:8.3
|
||||||
|
SliderMultiplier:0.400000005960464
|
||||||
|
SliderTickRate:4
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
500,500,4,2,1,50,1,0
|
||||||
|
13426,-100,4,3,1,45,0,0
|
||||||
|
14884,-100,4,2,1,50,0,0
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
96,192,500,6,0,L|416:192,2,320.000004768372
|
@ -12,7 +12,8 @@ using osu.Game.Tests.Beatmaps;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Tests
|
namespace osu.Game.Rulesets.Taiko.Tests
|
||||||
{
|
{
|
||||||
internal class TaikoBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
[TestFixture]
|
||||||
|
public class TaikoBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
protected override Ruleset CreateRuleset() => new TaikoRuleset();
|
protected override Ruleset CreateRuleset() => new TaikoRuleset();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal struct ConvertValue : IEquatable<ConvertValue>
|
public struct ConvertValue : IEquatable<ConvertValue>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A sane value to account for osu!stable using ints everwhere.
|
/// A sane value to account for osu!stable using ints everwhere.
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||||
|
{
|
||||||
|
public class TaikoDifficultyAttributes : DifficultyAttributes
|
||||||
|
{
|
||||||
|
public double GreatHitWindow;
|
||||||
|
public int MaxCombo;
|
||||||
|
|
||||||
|
public TaikoDifficultyAttributes(Mod[] mods, double starRating)
|
||||||
|
: base(mods, starRating)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -34,6 +35,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
|
|
||||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||||
{
|
{
|
||||||
|
if (!beatmap.HitObjects.Any())
|
||||||
|
return new TaikoDifficultyAttributes(mods, 0);
|
||||||
|
|
||||||
var difficultyHitObjects = new List<TaikoHitObjectDifficulty>();
|
var difficultyHitObjects = new List<TaikoHitObjectDifficulty>();
|
||||||
|
|
||||||
foreach (var hitObject in beatmap.HitObjects)
|
foreach (var hitObject in beatmap.HitObjects)
|
||||||
@ -47,7 +51,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
|
|
||||||
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
|
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
|
||||||
|
|
||||||
return new DifficultyAttributes(mods, starRating);
|
return new TaikoDifficultyAttributes(mods, starRating)
|
||||||
|
{
|
||||||
|
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
|
||||||
|
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate,
|
||||||
|
MaxCombo = beatmap.HitObjects.Count(h => h is Hit)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool calculateStrainValues(List<TaikoHitObjectDifficulty> objects, double timeRate)
|
private bool calculateStrainValues(List<TaikoHitObjectDifficulty> objects, double timeRate)
|
||||||
|
@ -8,13 +8,12 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Difficulty
|
namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||||
{
|
{
|
||||||
public class TaikoPerformanceCalculator : PerformanceCalculator
|
public class TaikoPerformanceCalculator : PerformanceCalculator
|
||||||
{
|
{
|
||||||
private readonly int beatmapMaxCombo;
|
protected new TaikoDifficultyAttributes Attributes => (TaikoDifficultyAttributes)base.Attributes;
|
||||||
|
|
||||||
private Mod[] mods;
|
private Mod[] mods;
|
||||||
private int countGreat;
|
private int countGreat;
|
||||||
@ -25,7 +24,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
public TaikoPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
|
public TaikoPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, Score score)
|
||||||
: base(ruleset, beatmap, score)
|
: base(ruleset, beatmap, score)
|
||||||
{
|
{
|
||||||
beatmapMaxCombo = Beatmap.HitObjects.Count(h => h is Hit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||||
@ -78,8 +76,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
strainValue *= Math.Pow(0.985, countMiss);
|
strainValue *= Math.Pow(0.985, countMiss);
|
||||||
|
|
||||||
// Combo scaling
|
// Combo scaling
|
||||||
if (beatmapMaxCombo > 0)
|
if (Attributes.MaxCombo > 0)
|
||||||
strainValue *= Math.Min(Math.Pow(Score.MaxCombo, 0.5) / Math.Pow(beatmapMaxCombo, 0.5), 1.0);
|
strainValue *= Math.Min(Math.Pow(Score.MaxCombo, 0.5) / Math.Pow(Attributes.MaxCombo, 0.5), 1.0);
|
||||||
|
|
||||||
if (mods.Any(m => m is ModHidden))
|
if (mods.Any(m => m is ModHidden))
|
||||||
strainValue *= 1.025;
|
strainValue *= 1.025;
|
||||||
@ -94,14 +92,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
|
|
||||||
private double computeAccuracyValue()
|
private double computeAccuracyValue()
|
||||||
{
|
{
|
||||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
|
if (Attributes.GreatHitWindow <= 0)
|
||||||
double hitWindowGreat = (int)(Beatmap.HitObjects.First().HitWindows.Great / 2) / TimeRate;
|
|
||||||
if (hitWindowGreat <= 0)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Lots of arbitrary values from testing.
|
// Lots of arbitrary values from testing.
|
||||||
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
||||||
double accValue = Math.Pow(150.0 / hitWindowGreat, 1.1) * Math.Pow(Score.Accuracy, 15) * 22.0;
|
double accValue = Math.Pow(150.0 / Attributes.GreatHitWindow, 1.1) * Math.Pow(Score.Accuracy, 15) * 22.0;
|
||||||
|
|
||||||
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
||||||
return accValue * Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
return accValue * Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Judgements
|
||||||
|
{
|
||||||
|
public class TaikoIntermediateSwellJudgement : TaikoJudgement
|
||||||
|
{
|
||||||
|
public override HitResult MaxResult => HitResult.Perfect;
|
||||||
|
|
||||||
|
public override bool AffectsCombo => false;
|
||||||
|
|
||||||
|
public TaikoIntermediateSwellJudgement()
|
||||||
|
{
|
||||||
|
Final = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes the numeric result value for the combo portion of the score.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="result">The result to compute the value for.</param>
|
||||||
|
/// <returns>The numeric result value.</returns>
|
||||||
|
protected override int NumericResultFor(HitResult result) => 0;
|
||||||
|
}
|
||||||
|
}
|
@ -86,6 +86,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
switch (State.Value)
|
switch (State.Value)
|
||||||
{
|
{
|
||||||
case ArmedState.Idle:
|
case ArmedState.Idle:
|
||||||
|
SecondHitAllowed = false;
|
||||||
|
validKeyPressed = false;
|
||||||
|
|
||||||
UnproxyContent();
|
UnproxyContent();
|
||||||
this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire();
|
this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire();
|
||||||
break;
|
break;
|
||||||
@ -95,8 +98,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
break;
|
break;
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
// If we're far enough away from the left stage, we should bring outselves in front of it
|
// If we're far enough away from the left stage, we should bring outselves in front of it
|
||||||
if (X >= -0.05f)
|
ProxyContent();
|
||||||
ProxyContent();
|
|
||||||
|
|
||||||
var flash = circlePiece?.FlashBox;
|
var flash = circlePiece?.FlashBox;
|
||||||
if (flash != null)
|
if (flash != null)
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Judgements;
|
using osu.Game.Rulesets.Taiko.Judgements;
|
||||||
|
|
||||||
@ -46,6 +47,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Great });
|
AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Great });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(ArmedState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case ArmedState.Idle:
|
||||||
|
firstHitTime = 0;
|
||||||
|
firstKeyHeld = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override bool OnReleased(TaikoAction action)
|
public override bool OnReleased(TaikoAction action)
|
||||||
{
|
{
|
||||||
if (action == firstHitAction)
|
if (action == firstHitAction)
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -20,6 +19,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
public class DrawableSwell : DrawableTaikoHitObject<Swell>
|
public class DrawableSwell : DrawableTaikoHitObject<Swell>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A judgement is only displayed when the user has complete the swell (either a hit or miss).
|
||||||
|
/// </summary>
|
||||||
|
public override bool DisplayJudgement => AllJudged;
|
||||||
|
|
||||||
private const float target_ring_thick_border = 1.4f;
|
private const float target_ring_thick_border = 1.4f;
|
||||||
private const float target_ring_thin_border = 1f;
|
private const float target_ring_thin_border = 1f;
|
||||||
private const float target_ring_scale = 5f;
|
private const float target_ring_scale = 5f;
|
||||||
@ -29,11 +33,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
private readonly CircularContainer targetRing;
|
private readonly CircularContainer targetRing;
|
||||||
private readonly CircularContainer expandingRing;
|
private readonly CircularContainer expandingRing;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of times the user has hit this swell.
|
|
||||||
/// </summary>
|
|
||||||
private int userHits;
|
|
||||||
|
|
||||||
private readonly SwellSymbolPiece symbol;
|
private readonly SwellSymbolPiece symbol;
|
||||||
|
|
||||||
public DrawableSwell(Swell swell)
|
public DrawableSwell(Swell swell)
|
||||||
@ -129,9 +128,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
{
|
{
|
||||||
if (userTriggered)
|
if (userTriggered)
|
||||||
{
|
{
|
||||||
userHits++;
|
AddJudgement(new TaikoIntermediateSwellJudgement());
|
||||||
|
|
||||||
var completion = (float)userHits / HitObject.RequiredHits;
|
var completion = (float)Judgements.Count / HitObject.RequiredHits;
|
||||||
|
|
||||||
expandingRing
|
expandingRing
|
||||||
.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50)
|
.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50)
|
||||||
@ -142,7 +141,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
|
|
||||||
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
|
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
|
||||||
|
|
||||||
if (userHits == HitObject.RequiredHits)
|
if (Judgements.Count == HitObject.RequiredHits)
|
||||||
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
|
AddJudgement(new TaikoJudgement { Result = HitResult.Great });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -151,7 +150,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
//TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
|
//TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
|
||||||
AddJudgement(userHits > HitObject.RequiredHits / 2
|
AddJudgement(Judgements.Count > HitObject.RequiredHits / 2
|
||||||
? new TaikoJudgement { Result = HitResult.Good }
|
? new TaikoJudgement { Result = HitResult.Good }
|
||||||
: new TaikoJudgement { Result = HitResult.Miss });
|
: new TaikoJudgement { Result = HitResult.Miss });
|
||||||
}
|
}
|
||||||
@ -162,23 +161,22 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
const float preempt = 100;
|
const float preempt = 100;
|
||||||
const float out_transition_time = 300;
|
const float out_transition_time = 300;
|
||||||
|
|
||||||
double untilStartTime = HitObject.StartTime - Time.Current;
|
|
||||||
double untilJudgement = untilStartTime + (Judgements.FirstOrDefault()?.TimeOffset ?? 0) + HitObject.Duration;
|
|
||||||
|
|
||||||
targetRing.Delay(untilStartTime - preempt).ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint);
|
|
||||||
this.Delay(untilJudgement).FadeOut(out_transition_time, Easing.Out);
|
|
||||||
|
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Idle:
|
case ArmedState.Idle:
|
||||||
UnproxyContent();
|
UnproxyContent();
|
||||||
|
expandingRing.FadeTo(0);
|
||||||
|
using (BeginAbsoluteSequence(HitObject.StartTime - preempt, true))
|
||||||
|
targetRing.ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint);
|
||||||
break;
|
break;
|
||||||
|
case ArmedState.Miss:
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
bodyContainer.Delay(untilJudgement).ScaleTo(1.4f, out_transition_time);
|
this.FadeOut(out_transition_time, Easing.Out);
|
||||||
|
bodyContainer.ScaleTo(1.4f, out_transition_time);
|
||||||
|
|
||||||
|
Expire();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Expire();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
@ -69,11 +69,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0;
|
bool isMajor = currentBeat % (int)currentPoint.TimeSignature == 0;
|
||||||
Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
|
Playfield.Add(isMajor ? new DrawableBarLineMajor(barLine) : new DrawableBarLine(barLine));
|
||||||
|
|
||||||
double bl = currentPoint.BeatLength;
|
time += currentPoint.BeatLength * (int)currentPoint.TimeSignature;
|
||||||
if (bl < 800)
|
|
||||||
bl *= (int)currentPoint.TimeSignature;
|
|
||||||
|
|
||||||
time += bl;
|
|
||||||
currentBeat++;
|
currentBeat++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using NUnit.Framework;
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -10,9 +9,6 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Rulesets.Edit;
|
|
||||||
using osu.Game.Rulesets.Edit.Tools;
|
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Tests.Beatmaps;
|
using osu.Game.Tests.Beatmaps;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
@ -20,10 +16,9 @@ using OpenTK.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Tests.Visual
|
namespace osu.Game.Tests.Visual
|
||||||
{
|
{
|
||||||
|
[TestFixture]
|
||||||
public class TestCaseEditorSeekSnapping : EditorClockTestCase
|
public class TestCaseEditorSeekSnapping : EditorClockTestCase
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(HitObjectComposer) };
|
|
||||||
|
|
||||||
public TestCaseEditorSeekSnapping()
|
public TestCaseEditorSeekSnapping()
|
||||||
{
|
{
|
||||||
BeatDivisor.Value = 4;
|
BeatDivisor.Value = 4;
|
||||||
@ -56,22 +51,13 @@ namespace osu.Game.Tests.Visual
|
|||||||
Beatmap.Value = new TestWorkingBeatmap(testBeatmap);
|
Beatmap.Value = new TestWorkingBeatmap(testBeatmap);
|
||||||
|
|
||||||
Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock };
|
Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock };
|
||||||
|
|
||||||
testSeekNoSnapping();
|
|
||||||
testSeekSnappingOnBeat();
|
|
||||||
testSeekSnappingInBetweenBeat();
|
|
||||||
testSeekForwardNoSnapping();
|
|
||||||
testSeekForwardSnappingOnBeat();
|
|
||||||
testSeekForwardSnappingFromInBetweenBeat();
|
|
||||||
testSeekBackwardSnappingOnBeat();
|
|
||||||
testSeekBackwardSnappingFromInBetweenBeat();
|
|
||||||
testSeekingWithFloatingPointBeatLength();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests whether time is correctly seeked without snapping.
|
/// Tests whether time is correctly seeked without snapping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void testSeekNoSnapping()
|
[Test]
|
||||||
|
public void TestSeekNoSnapping()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
@ -94,7 +80,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
/// Tests whether seeking to exact beat times puts us on the beat time.
|
/// Tests whether seeking to exact beat times puts us on the beat time.
|
||||||
/// These are the white/yellow ticks on the graph.
|
/// These are the white/yellow ticks on the graph.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void testSeekSnappingOnBeat()
|
[Test]
|
||||||
|
public void TestSeekSnappingOnBeat()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
@ -117,9 +104,9 @@ namespace osu.Game.Tests.Visual
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests whether seeking to somewhere in the middle between beats puts us on the expected beats.
|
/// Tests whether seeking to somewhere in the middle between beats puts us on the expected beats.
|
||||||
/// For example, snapping between a white/yellow beat should put us on either the yellow or white, depending on which one we're closer too.
|
/// For example, snapping between a white/yellow beat should put us on either the yellow or white, depending on which one we're closer too.
|
||||||
/// If
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void testSeekSnappingInBetweenBeat()
|
[Test]
|
||||||
|
public void TestSeekSnappingInBetweenBeat()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
@ -140,7 +127,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests that when seeking forward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it).
|
/// Tests that when seeking forward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void testSeekForwardNoSnapping()
|
[Test]
|
||||||
|
public void TestSeekForwardNoSnapping()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
@ -159,7 +147,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests that when seeking forward with beat snapping, all beats are snapped to and timing points are never skipped.
|
/// Tests that when seeking forward with beat snapping, all beats are snapped to and timing points are never skipped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void testSeekForwardSnappingOnBeat()
|
[Test]
|
||||||
|
public void TestSeekForwardSnappingOnBeat()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
@ -181,7 +170,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
/// Tests that when seeking forward from in-between two beats, the next beat or timing point is snapped to, and no beats are skipped.
|
/// Tests that when seeking forward from in-between two beats, the next beat or timing point is snapped to, and no beats are skipped.
|
||||||
/// This will also test being extremely close to the next beat/timing point, to ensure rounding is not an issue.
|
/// This will also test being extremely close to the next beat/timing point, to ensure rounding is not an issue.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void testSeekForwardSnappingFromInBetweenBeat()
|
[Test]
|
||||||
|
public void TestSeekForwardSnappingFromInBetweenBeat()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
@ -214,21 +204,20 @@ namespace osu.Game.Tests.Visual
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests that when seeking backward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it).
|
/// Tests that when seeking backward with no beat snapping, beats are never explicitly snapped to, nor the next timing point (if we've skipped it).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void testSeekBackwardNoSnapping()
|
[Test]
|
||||||
|
public void TestSeekBackwardNoSnapping()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
AddStep("Seek(450)", () => Clock.Seek(450));
|
AddStep("Seek(450)", () => Clock.Seek(450));
|
||||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||||
AddAssert("Time = 425", () => Clock.CurrentTime == 425);
|
AddAssert("Time = 400", () => Clock.CurrentTime == 400);
|
||||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||||
AddAssert("Time = 375", () => Clock.CurrentTime == 375);
|
AddAssert("Time = 350", () => Clock.CurrentTime == 350);
|
||||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||||
AddAssert("Time = 325", () => Clock.CurrentTime == 325);
|
AddAssert("Time = 150", () => Clock.CurrentTime == 150);
|
||||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||||
AddAssert("Time = 125", () => Clock.CurrentTime == 125);
|
AddAssert("Time = 50", () => Clock.CurrentTime == 50);
|
||||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
|
||||||
AddAssert("Time = 25", () => Clock.CurrentTime == 25);
|
|
||||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||||
AddAssert("Time = 0", () => Clock.CurrentTime == 0);
|
AddAssert("Time = 0", () => Clock.CurrentTime == 0);
|
||||||
}
|
}
|
||||||
@ -236,7 +225,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests that when seeking backward with beat snapping, all beats are snapped to and timing points are never skipped.
|
/// Tests that when seeking backward with beat snapping, all beats are snapped to and timing points are never skipped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void testSeekBackwardSnappingOnBeat()
|
[Test]
|
||||||
|
public void TestSeekBackwardSnappingOnBeat()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
@ -259,7 +249,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
/// Tests that when seeking backward from in-between two beats, the previous beat or timing point is snapped to, and no beats are skipped.
|
/// Tests that when seeking backward from in-between two beats, the previous beat or timing point is snapped to, and no beats are skipped.
|
||||||
/// This will also test being extremely close to the previous beat/timing point, to ensure rounding is not an issue.
|
/// This will also test being extremely close to the previous beat/timing point, to ensure rounding is not an issue.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void testSeekBackwardSnappingFromInBetweenBeat()
|
[Test]
|
||||||
|
public void TestSeekBackwardSnappingFromInBetweenBeat()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
@ -280,7 +271,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests that there are no rounding issues when snapping to beats within a timing point with a floating-point beatlength.
|
/// Tests that there are no rounding issues when snapping to beats within a timing point with a floating-point beatlength.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void testSeekingWithFloatingPointBeatLength()
|
[Test]
|
||||||
|
public void TestSeekingWithFloatingPointBeatLength()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
@ -288,7 +280,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
AddStep("Seek(0)", () => Clock.Seek(0));
|
AddStep("Seek(0)", () => Clock.Seek(0));
|
||||||
|
|
||||||
for (int i = 0; i < 20; i++)
|
for (int i = 0; i < 9; i++)
|
||||||
{
|
{
|
||||||
AddStep("SeekForward, Snap", () =>
|
AddStep("SeekForward, Snap", () =>
|
||||||
{
|
{
|
||||||
@ -298,7 +290,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
AddAssert("Time > lastTime", () => Clock.CurrentTime > lastTime);
|
AddAssert("Time > lastTime", () => Clock.CurrentTime > lastTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 20; i++)
|
for (int i = 0; i < 9; i++)
|
||||||
{
|
{
|
||||||
AddStep("SeekBackward, Snap", () =>
|
AddStep("SeekBackward, Snap", () =>
|
||||||
{
|
{
|
||||||
@ -316,16 +308,6 @@ namespace osu.Game.Tests.Visual
|
|||||||
AddStep("Reset", () => Clock.Seek(0));
|
AddStep("Reset", () => Clock.Seek(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestHitObjectComposer : HitObjectComposer
|
|
||||||
{
|
|
||||||
public TestHitObjectComposer(Ruleset ruleset)
|
|
||||||
: base(ruleset)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override IReadOnlyList<ICompositionTool> CompositionTools => new ICompositionTool[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TimingPointVisualiser : CompositeDrawable
|
private class TimingPointVisualiser : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly double length;
|
private readonly double length;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
@ -17,6 +19,12 @@ namespace osu.Game.Tests.Visual
|
|||||||
[Description("PlaySongSelect leaderboard")]
|
[Description("PlaySongSelect leaderboard")]
|
||||||
public class TestCaseLeaderboard : OsuTestCase
|
public class TestCaseLeaderboard : OsuTestCase
|
||||||
{
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[] {
|
||||||
|
typeof(Placeholder),
|
||||||
|
typeof(MessagePlaceholder),
|
||||||
|
typeof(RetrievalFailurePlaceholder),
|
||||||
|
};
|
||||||
|
|
||||||
private RulesetStore rulesets;
|
private RulesetStore rulesets;
|
||||||
|
|
||||||
private readonly FailableLeaderboard leaderboard;
|
private readonly FailableLeaderboard leaderboard;
|
||||||
|
63
osu.Game.Tests/Visual/TestCaseLoadingAnimation.cs
Normal file
63
osu.Game.Tests/Visual/TestCaseLoadingAnimation.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual
|
||||||
|
{
|
||||||
|
public class TestCaseLoadingAnimation : GridTestCase
|
||||||
|
{
|
||||||
|
public TestCaseLoadingAnimation()
|
||||||
|
: base(2, 2)
|
||||||
|
{
|
||||||
|
LoadingAnimation loading;
|
||||||
|
|
||||||
|
Cell(0).AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = Color4.Black,
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
loading = new LoadingAnimation()
|
||||||
|
});
|
||||||
|
|
||||||
|
loading.Show();
|
||||||
|
|
||||||
|
Cell(1).AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = Color4.White,
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
loading = new LoadingAnimation()
|
||||||
|
});
|
||||||
|
|
||||||
|
loading.Show();
|
||||||
|
|
||||||
|
Cell(2).AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = Color4.Gray,
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
loading = new LoadingAnimation()
|
||||||
|
});
|
||||||
|
|
||||||
|
loading.Show();
|
||||||
|
|
||||||
|
Cell(3).AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
loading = new LoadingAnimation()
|
||||||
|
});
|
||||||
|
|
||||||
|
Scheduler.AddDelayed(() => loading.ToggleVisibility(), 200, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
AddStep(@"set rooms", () => lounge.Rooms = rooms);
|
AddStep(@"set rooms", () => lounge.Rooms = rooms);
|
||||||
selectAssert(1);
|
selectAssert(1);
|
||||||
AddStep(@"open room 1", () => clickRoom(1));
|
AddStep(@"open room 1", () => clickRoom(1));
|
||||||
AddUntilStep(() => !lounge.IsCurrentScreen, "wait until room current");
|
AddUntilStep(() => lounge.ChildScreen?.IsCurrentScreen == true, "wait until room current");
|
||||||
AddStep(@"make lounge current", lounge.MakeCurrent);
|
AddStep(@"make lounge current", lounge.MakeCurrent);
|
||||||
filterAssert(@"THE FINAL", LoungeTab.Public, 1);
|
filterAssert(@"THE FINAL", LoungeTab.Public, 1);
|
||||||
filterAssert(string.Empty, LoungeTab.Public, 2);
|
filterAssert(string.Empty, LoungeTab.Public, 2);
|
||||||
|
@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
private class TestPlayfield : ScrollingPlayfield
|
private class TestPlayfield : ScrollingPlayfield
|
||||||
{
|
{
|
||||||
public readonly ScrollingDirection Direction;
|
public new readonly ScrollingDirection Direction;
|
||||||
|
|
||||||
public TestPlayfield(ScrollingDirection direction)
|
public TestPlayfield(ScrollingDirection direction)
|
||||||
: base(direction)
|
: base(direction)
|
||||||
|
@ -138,7 +138,7 @@ namespace osu.Game.Beatmaps
|
|||||||
PostNotification?.Invoke(new SimpleNotification
|
PostNotification?.Invoke(new SimpleNotification
|
||||||
{
|
{
|
||||||
Icon = FontAwesome.fa_superpowers,
|
Icon = FontAwesome.fa_superpowers,
|
||||||
Text = "You gotta be a supporter to download for now 'yo"
|
Text = "You gotta be an osu!supporter to download for now 'yo"
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -69,11 +69,22 @@ namespace osu.Game.Beatmaps
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return new TrackVirtual();
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile)));
|
protected override Waveform GetWaveform()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var trackData = store.GetStream(getPathForFile(Metadata.AudioFile));
|
||||||
|
return trackData == null ? null : new Waveform(trackData);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override Storyboard GetStoryboard()
|
protected override Storyboard GetStoryboard()
|
||||||
{
|
{
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
protected override Texture GetBackground() => game?.Textures.Get(@"Backgrounds/bg4");
|
protected override Texture GetBackground() => game?.Textures.Get(@"Backgrounds/bg4");
|
||||||
|
|
||||||
protected override Track GetTrack() => new TrackVirtual();
|
protected override Track GetTrack() => new TrackVirtual { Length = 1000 };
|
||||||
|
|
||||||
private class DummyRulesetInfo : RulesetInfo
|
private class DummyRulesetInfo : RulesetInfo
|
||||||
{
|
{
|
||||||
|
@ -19,7 +19,7 @@ using osu.Game.Skinning;
|
|||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
{
|
{
|
||||||
public abstract class WorkingBeatmap : IDisposable
|
public abstract partial class WorkingBeatmap : IDisposable
|
||||||
{
|
{
|
||||||
public readonly BeatmapInfo BeatmapInfo;
|
public readonly BeatmapInfo BeatmapInfo;
|
||||||
|
|
||||||
@ -116,9 +116,6 @@ namespace osu.Game.Beatmaps
|
|||||||
mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
|
mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post-process
|
|
||||||
rulesetInstance.CreateBeatmapProcessor(converted)?.PostProcess();
|
|
||||||
|
|
||||||
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
|
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
|
||||||
foreach (var obj in converted.HitObjects)
|
foreach (var obj in converted.HitObjects)
|
||||||
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
|
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
|
||||||
@ -127,6 +124,9 @@ namespace osu.Game.Beatmaps
|
|||||||
foreach (var obj in converted.HitObjects)
|
foreach (var obj in converted.HitObjects)
|
||||||
mod.ApplyToHitObject(obj);
|
mod.ApplyToHitObject(obj);
|
||||||
|
|
||||||
|
// Post-process
|
||||||
|
rulesetInstance.CreateBeatmapProcessor(converted)?.PostProcess();
|
||||||
|
|
||||||
return converted;
|
return converted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ namespace osu.Game.Beatmaps
|
|||||||
private Track populateTrack()
|
private Track populateTrack()
|
||||||
{
|
{
|
||||||
// we want to ensure that we always have a track, even if it's a fake one.
|
// we want to ensure that we always have a track, even if it's a fake one.
|
||||||
var t = GetTrack() ?? new TrackVirtual();
|
var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap);
|
||||||
applyRateAdjustments(t);
|
applyRateAdjustments(t);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
38
osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs
Normal file
38
osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps
|
||||||
|
{
|
||||||
|
public partial class WorkingBeatmap
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A type of <see cref="TrackVirtual"/> which provides a valid length based on the <see cref="HitObject"/>s of an <see cref="IBeatmap"/>.
|
||||||
|
/// </summary>
|
||||||
|
protected class VirtualBeatmapTrack : TrackVirtual
|
||||||
|
{
|
||||||
|
private const double excess_length = 1000;
|
||||||
|
|
||||||
|
public VirtualBeatmapTrack(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
var lastObject = beatmap.HitObjects.LastOrDefault();
|
||||||
|
|
||||||
|
switch (lastObject)
|
||||||
|
{
|
||||||
|
case null:
|
||||||
|
Length = excess_length;
|
||||||
|
break;
|
||||||
|
case IHasEndTime endTime:
|
||||||
|
Length = endTime.EndTime + excess_length;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Length = lastObject.StartTime + excess_length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
@ -16,6 +17,8 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
protected override SpriteText CreateSpriteText() => new OsuSpriteText();
|
protected override SpriteText CreateSpriteText() => new OsuSpriteText();
|
||||||
|
|
||||||
|
public void AddArbitraryDrawable(Drawable drawable) => AddInternal(drawable);
|
||||||
|
|
||||||
public void AddIcon(FontAwesome icon, Action<SpriteText> creationParameters = null) => AddText(((char)icon).ToString(), creationParameters);
|
public void AddIcon(FontAwesome icon, Action<SpriteText> creationParameters = null) => AddText(((char)icon).ToString(), creationParameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,14 +12,14 @@ namespace osu.Game.Graphics
|
|||||||
{
|
{
|
||||||
public class DrawableDate : OsuSpriteText, IHasTooltip
|
public class DrawableDate : OsuSpriteText, IHasTooltip
|
||||||
{
|
{
|
||||||
private readonly DateTimeOffset date;
|
protected readonly DateTimeOffset Date;
|
||||||
|
|
||||||
public DrawableDate(DateTimeOffset date)
|
public DrawableDate(DateTimeOffset date)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Font = "Exo2.0-RegularItalic";
|
Font = "Exo2.0-RegularItalic";
|
||||||
|
|
||||||
this.date = date.ToLocalTime();
|
Date = date.ToLocalTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -38,7 +38,7 @@ namespace osu.Game.Graphics
|
|||||||
{
|
{
|
||||||
updateTime();
|
updateTime();
|
||||||
|
|
||||||
var diffToNow = DateTimeOffset.Now.Subtract(date);
|
var diffToNow = DateTimeOffset.Now.Subtract(Date);
|
||||||
|
|
||||||
double timeUntilNextUpdate = 1000;
|
double timeUntilNextUpdate = 1000;
|
||||||
if (diffToNow.TotalSeconds > 60)
|
if (diffToNow.TotalSeconds > 60)
|
||||||
@ -58,8 +58,10 @@ namespace osu.Game.Graphics
|
|||||||
|
|
||||||
public override bool HandleMouseInput => true;
|
public override bool HandleMouseInput => true;
|
||||||
|
|
||||||
private void updateTime() => Text = date.Humanize();
|
protected virtual string Format() => Date.Humanize();
|
||||||
|
|
||||||
public string TooltipText => date.ToString();
|
private void updateTime() => Text = Format();
|
||||||
|
|
||||||
|
public virtual string TooltipText => string.Format($"{Date:MMMM d, yyyy h:mm tt \"UTC\"z}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,17 @@
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class LoadingAnimation : VisibilityContainer
|
public class LoadingAnimation : VisibilityContainer
|
||||||
{
|
{
|
||||||
private readonly SpriteIcon spinner;
|
private readonly SpriteIcon spinner;
|
||||||
|
private readonly SpriteIcon spinnerShadow;
|
||||||
|
|
||||||
|
private const float spin_duration = 600;
|
||||||
|
private const float transition_duration = 200;
|
||||||
|
|
||||||
public LoadingAnimation()
|
public LoadingAnimation()
|
||||||
{
|
{
|
||||||
@ -20,12 +25,22 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
spinner = new SpriteIcon
|
spinnerShadow = new SpriteIcon
|
||||||
{
|
{
|
||||||
Size = new Vector2(20),
|
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Icon = FontAwesome.fa_spinner
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Position = new Vector2(1, 1),
|
||||||
|
Colour = Color4.Black,
|
||||||
|
Alpha = 0.4f,
|
||||||
|
Icon = FontAwesome.fa_circle_o_notch
|
||||||
|
},
|
||||||
|
spinner = new SpriteIcon
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Icon = FontAwesome.fa_circle_o_notch
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -34,12 +49,11 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
spinner.Spin(2000, RotationDirection.Clockwise);
|
spinner.Spin(spin_duration, RotationDirection.Clockwise);
|
||||||
|
spinnerShadow.Spin(spin_duration, RotationDirection.Clockwise);
|
||||||
}
|
}
|
||||||
|
|
||||||
private const float transition_duration = 500;
|
protected override void PopIn() => this.FadeIn(transition_duration * 2, Easing.OutQuint);
|
||||||
|
|
||||||
protected override void PopIn() => this.FadeIn(transition_duration * 5, Easing.OutQuint);
|
|
||||||
|
|
||||||
protected override void PopOut() => this.FadeOut(transition_duration, Easing.OutQuint);
|
protected override void PopOut() => this.FadeOut(transition_duration, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,7 @@ namespace osu.Game.Online.API
|
|||||||
// The Success callback event is fired on the main thread, so we should wait for that to run before proceeding.
|
// The Success callback event is fired on the main thread, so we should wait for that to run before proceeding.
|
||||||
// Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests
|
// Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests
|
||||||
// before actually going online.
|
// before actually going online.
|
||||||
while (State != APIState.Online)
|
while (State > APIState.Offline && State < APIState.Online)
|
||||||
Thread.Sleep(500);
|
Thread.Sleep(500);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -158,7 +158,6 @@ namespace osu.Game.Online.API
|
|||||||
if (authentication.RequestAccessToken() == null)
|
if (authentication.RequestAccessToken() == null)
|
||||||
{
|
{
|
||||||
Logout(false);
|
Logout(false);
|
||||||
State = APIState.Offline;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,6 +207,14 @@ namespace osu.Game.Online.API
|
|||||||
{
|
{
|
||||||
HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout);
|
HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout);
|
||||||
|
|
||||||
|
// special cases for un-typed but useful message responses.
|
||||||
|
switch (we.Message)
|
||||||
|
{
|
||||||
|
case "Unauthorized":
|
||||||
|
statusCode = HttpStatusCode.Unauthorized;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (statusCode)
|
switch (statusCode)
|
||||||
{
|
{
|
||||||
case HttpStatusCode.Unauthorized:
|
case HttpStatusCode.Unauthorized:
|
||||||
@ -292,6 +299,7 @@ namespace osu.Game.Online.API
|
|||||||
password = null;
|
password = null;
|
||||||
authentication.Clear();
|
authentication.Clear();
|
||||||
LocalUser.Value = createGuestUser();
|
LocalUser.Value = createGuestUser();
|
||||||
|
State = APIState.Offline;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static User createGuestUser() => new User
|
private static User createGuestUser() => new User
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Audio;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using OpenTK;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Direct
|
namespace osu.Game.Overlays.Direct
|
||||||
@ -48,9 +49,15 @@ namespace osu.Game.Overlays.Direct
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value)
|
if (value)
|
||||||
|
{
|
||||||
|
icon.FadeTo(0.5f, transition_duration, Easing.OutQuint);
|
||||||
loadingAnimation.Show();
|
loadingAnimation.Show();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
icon.FadeTo(1, transition_duration, Easing.OutQuint);
|
||||||
loadingAnimation.Hide();
|
loadingAnimation.Hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +74,10 @@ namespace osu.Game.Overlays.Direct
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Icon = FontAwesome.fa_play,
|
Icon = FontAwesome.fa_play,
|
||||||
},
|
},
|
||||||
loadingAnimation = new LoadingAnimation(),
|
loadingAnimation = new LoadingAnimation
|
||||||
|
{
|
||||||
|
Size = new Vector2(15),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Playing.ValueChanged += playingStateChanged;
|
Playing.ValueChanged += playingStateChanged;
|
||||||
|
@ -186,7 +186,7 @@ namespace osu.Game.Overlays.KeyBinding
|
|||||||
{
|
{
|
||||||
if (bindTarget.IsHovered)
|
if (bindTarget.IsHovered)
|
||||||
{
|
{
|
||||||
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
|
bindTarget.UpdateKeyCombination(new KeyCombination(KeyCombination.FromInputState(state).Keys.Append(state.Mouse.ScrollDelta.Y > 0 ? InputKey.MouseWheelUp : InputKey.MouseWheelDown)));
|
||||||
finalise();
|
finalise();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
20
osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs
Normal file
20
osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Profile.Components
|
||||||
|
{
|
||||||
|
public class DrawableJoinDate : DrawableDate
|
||||||
|
{
|
||||||
|
public DrawableJoinDate(DateTimeOffset date)
|
||||||
|
: base(date)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string Format() => Text = Date.ToUniversalTime().Year < 2008 ? "Here since the beginning" : $"{Date:MMMM yyyy}";
|
||||||
|
|
||||||
|
public override string TooltipText => $"{Date:MMMM d, yyyy}";
|
||||||
|
}
|
||||||
|
}
|
50
osu.Game/Overlays/Profile/Components/GradeBadge.cs
Normal file
50
osu.Game/Overlays/Profile/Components/GradeBadge.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Profile.Components
|
||||||
|
{
|
||||||
|
public class GradeBadge : Container
|
||||||
|
{
|
||||||
|
private const float width = 50;
|
||||||
|
private readonly string grade;
|
||||||
|
private readonly Sprite badge;
|
||||||
|
private readonly SpriteText numberText;
|
||||||
|
|
||||||
|
public int DisplayCount
|
||||||
|
{
|
||||||
|
set => numberText.Text = value.ToString(@"#,0");
|
||||||
|
}
|
||||||
|
|
||||||
|
public GradeBadge(string grade)
|
||||||
|
{
|
||||||
|
this.grade = grade;
|
||||||
|
Width = width;
|
||||||
|
Height = 41;
|
||||||
|
Add(badge = new Sprite
|
||||||
|
{
|
||||||
|
Width = width,
|
||||||
|
Height = 26
|
||||||
|
});
|
||||||
|
Add(numberText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
TextSize = 14,
|
||||||
|
Font = @"Exo2.0-Bold"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(TextureStore textures)
|
||||||
|
{
|
||||||
|
badge.Texture = textures.Get($"Grades/{grade}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays.Profile.Components;
|
||||||
using osu.Game.Overlays.Profile.Header;
|
using osu.Game.Overlays.Profile.Header;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
@ -375,12 +376,12 @@ namespace osu.Game.Overlays.Profile
|
|||||||
|
|
||||||
if (user.JoinDate.ToUniversalTime().Year < 2008)
|
if (user.JoinDate.ToUniversalTime().Year < 2008)
|
||||||
{
|
{
|
||||||
infoTextLeft.AddText("Here since the beginning", boldItalic);
|
infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), lightText);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
infoTextLeft.AddText("Joined ", lightText);
|
infoTextLeft.AddText("Joined ", lightText);
|
||||||
infoTextLeft.AddText(new DrawableDate(user.JoinDate), boldItalic);
|
infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), boldItalic);
|
||||||
}
|
}
|
||||||
|
|
||||||
infoTextLeft.NewLine();
|
infoTextLeft.NewLine();
|
||||||
@ -470,43 +471,5 @@ namespace osu.Game.Overlays.Profile
|
|||||||
|
|
||||||
infoTextRight.NewLine();
|
infoTextRight.NewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class GradeBadge : Container
|
|
||||||
{
|
|
||||||
private const float width = 50;
|
|
||||||
private readonly string grade;
|
|
||||||
private readonly Sprite badge;
|
|
||||||
private readonly SpriteText numberText;
|
|
||||||
|
|
||||||
public int DisplayCount
|
|
||||||
{
|
|
||||||
set { numberText.Text = value.ToString(@"#,0"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public GradeBadge(string grade)
|
|
||||||
{
|
|
||||||
this.grade = grade;
|
|
||||||
Width = width;
|
|
||||||
Height = 41;
|
|
||||||
Add(badge = new Sprite
|
|
||||||
{
|
|
||||||
Width = width,
|
|
||||||
Height = 26
|
|
||||||
});
|
|
||||||
Add(numberText = new OsuSpriteText
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
Origin = Anchor.BottomCentre,
|
|
||||||
TextSize = 14,
|
|
||||||
Font = @"Exo2.0-Bold"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(TextureStore textures)
|
|
||||||
{
|
|
||||||
badge.Texture = textures.Get($"Grades/{grade}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,11 +141,11 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RecentActivityType.UserSupportFirst:
|
case RecentActivityType.UserSupportFirst:
|
||||||
message = $"{userLinkTemplate()} has become an osu! supporter - thanks for your generosity!";
|
message = $"{userLinkTemplate()} has become an osu!supporter - thanks for your generosity!";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RecentActivityType.UserSupportGift:
|
case RecentActivityType.UserSupportGift:
|
||||||
message = $"{userLinkTemplate()} has received the gift of osu! supporter!";
|
message = $"{userLinkTemplate()} has received the gift of osu!supporter!";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RecentActivityType.UsernameChange:
|
case RecentActivityType.UsernameChange:
|
||||||
|
@ -10,7 +10,7 @@ using osu.Game.Beatmaps.ControlPoints;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Legacy
|
namespace osu.Game.Rulesets.Objects.Legacy
|
||||||
{
|
{
|
||||||
internal abstract class ConvertSlider : HitObject, IHasCurve
|
internal abstract class ConvertSlider : HitObject, IHasCurve, IHasLegacyLastTickOffset
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Scoring distance with a speed-adjusted beat length of 1 second.
|
/// Scoring distance with a speed-adjusted beat length of 1 second.
|
||||||
@ -45,5 +45,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double LegacyLastTickOffset => 36;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
osu.Game/Rulesets/Objects/Types/IHasLegacyLastTickOffset.cs
Normal file
14
osu.Game/Rulesets/Objects/Types/IHasLegacyLastTickOffset.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Objects.Types
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A type of <see cref="HitObject"/> which may require the last tick to be offset.
|
||||||
|
/// This is specific to osu!stable conversion, and should not be used elsewhere.
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasLegacyLastTickOffset
|
||||||
|
{
|
||||||
|
double LegacyLastTickOffset { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -84,10 +84,11 @@ namespace osu.Game.Rulesets
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var instance = r.CreateInstance();
|
var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo), (RulesetInfo)null)).RulesetInfo;
|
||||||
|
|
||||||
r.Name = instance.Description;
|
r.Name = instanceInfo.Name;
|
||||||
r.ShortName = instance.ShortName;
|
r.ShortName = instanceInfo.ShortName;
|
||||||
|
r.InstantiationInfo = instanceInfo.InstantiationInfo;
|
||||||
|
|
||||||
r.Available = true;
|
r.Available = true;
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||||
{
|
{
|
||||||
InternalChild = KeyBindingContainer = new RulesetKeyBindingContainer(ruleset, variant, unique);
|
InternalChild = KeyBindingContainer = CreateKeyBindingContainer(ruleset, variant, unique);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Action mapping (for replays)
|
#region Action mapping (for replays)
|
||||||
@ -267,6 +267,9 @@ namespace osu.Game.Rulesets.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
protected virtual RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||||
|
=> new RulesetKeyBindingContainer(ruleset, variant, unique);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -29,17 +29,16 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly SortedList<MultiplierControlPoint> ControlPoints = new SortedList<MultiplierControlPoint>();
|
protected readonly SortedList<MultiplierControlPoint> ControlPoints = new SortedList<MultiplierControlPoint>();
|
||||||
|
|
||||||
private readonly ScrollingDirection direction;
|
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
private Cached initialStateCache = new Cached();
|
private Cached initialStateCache = new Cached();
|
||||||
|
|
||||||
public ScrollingHitObjectContainer(ScrollingDirection direction)
|
public ScrollingHitObjectContainer()
|
||||||
{
|
{
|
||||||
this.direction = direction;
|
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
TimeRange.ValueChanged += v => initialStateCache.Invalidate();
|
TimeRange.ValueChanged += _ => initialStateCache.Invalidate();
|
||||||
|
Direction.ValueChanged += _ => initialStateCache.Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ISpeedChangeVisualiser speedChangeVisualiser;
|
private ISpeedChangeVisualiser speedChangeVisualiser;
|
||||||
@ -100,7 +99,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
|
|
||||||
if (!initialStateCache.IsValid)
|
if (!initialStateCache.IsValid)
|
||||||
{
|
{
|
||||||
speedChangeVisualiser.ComputeInitialStates(Objects, direction, TimeRange, DrawSize);
|
speedChangeVisualiser.ComputeInitialStates(Objects, Direction, TimeRange, DrawSize);
|
||||||
initialStateCache.Validate();
|
initialStateCache.Validate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,7 +109,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
base.UpdateAfterChildrenLife();
|
base.UpdateAfterChildrenLife();
|
||||||
|
|
||||||
// We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions
|
// We need to calculate this as soon as possible after lifetimes so that hitobjects get the final say in their positions
|
||||||
speedChangeVisualiser.UpdatePositions(AliveObjects, direction, Time.Current, TimeRange, DrawSize);
|
speedChangeVisualiser.UpdatePositions(AliveObjects, Direction, Time.Current, TimeRange, DrawSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects;
|
public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects;
|
||||||
|
|
||||||
private readonly ScrollingDirection direction;
|
protected readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="ScrollingPlayfield"/>.
|
/// Creates a new <see cref="ScrollingPlayfield"/>.
|
||||||
@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null, float? customHeight = null)
|
protected ScrollingPlayfield(ScrollingDirection direction, float? customWidth = null, float? customHeight = null)
|
||||||
: base(customWidth, customHeight)
|
: base(customWidth, customHeight)
|
||||||
{
|
{
|
||||||
this.direction = direction;
|
Direction.Value = direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -99,6 +99,11 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction);
|
protected sealed override HitObjectContainer CreateHitObjectContainer()
|
||||||
|
{
|
||||||
|
var container = new ScrollingHitObjectContainer();
|
||||||
|
container.Direction.BindTo(Direction);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,9 @@ namespace osu.Game.Rulesets.UI.Scrolling.Visualisers
|
|||||||
/// <returns>A positive value indicating the position at <paramref name="time"/>.</returns>
|
/// <returns>A positive value indicating the position at <paramref name="time"/>.</returns>
|
||||||
private double positionAt(double time, double timeRange)
|
private double positionAt(double time, double timeRange)
|
||||||
{
|
{
|
||||||
|
if (controlPoints.Count == 0)
|
||||||
|
return time / timeRange;
|
||||||
|
|
||||||
double length = 0;
|
double length = 0;
|
||||||
|
|
||||||
// We need to consider all timing points until the specified time and not just the currently-active one,
|
// We need to consider all timing points until the specified time and not just the currently-active one,
|
||||||
|
@ -52,8 +52,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
if (Beatmap.Value == null)
|
if (Beatmap.Value == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Beatmap.Value.Track.Length == double.PositiveInfinity) return;
|
|
||||||
|
|
||||||
float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth);
|
float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth);
|
||||||
adjustableClock.Seek(markerPos / DrawWidth * Beatmap.Value.Track.Length);
|
adjustableClock.Seek(markerPos / DrawWidth * Beatmap.Value.Track.Length);
|
||||||
}
|
}
|
||||||
|
@ -47,8 +47,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: This should be handled more gracefully
|
timeline.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1);
|
||||||
timeline.RelativeChildSize = Beatmap.Value.Track.Length == double.PositiveInfinity ? Vector2.One : new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void Add(Drawable visualisation) => timeline.Add(visualisation);
|
protected void Add(Drawable visualisation) => timeline.Add(visualisation);
|
||||||
|
@ -52,17 +52,20 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
|
|||||||
WaveformVisible.ValueChanged += visible => waveform.FadeTo(visible ? 1 : 0, 200, Easing.OutQuint);
|
WaveformVisible.ValueChanged += visible => waveform.FadeTo(visible ? 1 : 0, 200, Easing.OutQuint);
|
||||||
|
|
||||||
Beatmap.BindTo(beatmap);
|
Beatmap.BindTo(beatmap);
|
||||||
}
|
Beatmap.BindValueChanged(b =>
|
||||||
|
{
|
||||||
protected override void LoadComplete()
|
waveform.Waveform = b.Waveform;
|
||||||
{
|
track = b.Track;
|
||||||
base.LoadComplete();
|
}, true);
|
||||||
Beatmap.BindValueChanged(b => waveform.Waveform = b.Waveform);
|
|
||||||
waveform.Waveform = Beatmap.Value.Waveform;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The track's time in the previous frame.
|
/// The timeline's scroll position in the last frame.
|
||||||
|
/// </summary>
|
||||||
|
private float lastScrollPosition;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The track time in the last frame.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private double lastTrackTime;
|
private double lastTrackTime;
|
||||||
|
|
||||||
@ -76,6 +79,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private bool trackWasPlaying;
|
private bool trackWasPlaying;
|
||||||
|
|
||||||
|
private Track track;
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
@ -83,49 +88,48 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
|
|||||||
// The extrema of track time should be positioned at the centre of the container when scrolled to the start or end
|
// The extrema of track time should be positioned at the centre of the container when scrolled to the start or end
|
||||||
Content.Margin = new MarginPadding { Horizontal = DrawWidth / 2 };
|
Content.Margin = new MarginPadding { Horizontal = DrawWidth / 2 };
|
||||||
|
|
||||||
if (handlingDragInput)
|
// This needs to happen after transforms are updated, but before the scroll position is updated in base.UpdateAfterChildren
|
||||||
{
|
if (adjustableClock.IsRunning)
|
||||||
// The user is dragging - the track should always follow the timeline
|
|
||||||
seekTrackToCurrent();
|
|
||||||
}
|
|
||||||
else if (adjustableClock.IsRunning)
|
|
||||||
{
|
|
||||||
// If the user hasn't provided mouse input but the track is running, always follow the track
|
|
||||||
scrollToTrackTime();
|
scrollToTrackTime();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
protected override void UpdateAfterChildren()
|
||||||
|
{
|
||||||
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
|
if (handlingDragInput)
|
||||||
|
seekTrackToCurrent();
|
||||||
|
else if (!adjustableClock.IsRunning)
|
||||||
{
|
{
|
||||||
// The track isn't playing, so we want to smooth-scroll once more, and re-enable wheel scrolling
|
// The track isn't running. There are two cases we have to be wary of:
|
||||||
// There are two cases we have to be wary of:
|
// 1) The user flick-drags on this timeline: We want the track to follow us
|
||||||
// 1) The user scrolls on this timeline: We want the track to follow us
|
|
||||||
// 2) The user changes the track time through some other means (scrolling in the editor or overview timeline): We want to follow the track time
|
// 2) The user changes the track time through some other means (scrolling in the editor or overview timeline): We want to follow the track time
|
||||||
|
|
||||||
// The simplest way to cover both cases is by checking that inter-frame track times are identical
|
// The simplest way to cover both cases is by checking whether the scroll position has changed and the audio hasn't been changed externally
|
||||||
if (adjustableClock.CurrentTime == lastTrackTime)
|
if (Current != lastScrollPosition && adjustableClock.CurrentTime == lastTrackTime)
|
||||||
{
|
|
||||||
// The track hasn't been seeked externally
|
|
||||||
seekTrackToCurrent();
|
seekTrackToCurrent();
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
// The track has been seeked externally
|
|
||||||
scrollToTrackTime();
|
scrollToTrackTime();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastScrollPosition = Current;
|
||||||
lastTrackTime = adjustableClock.CurrentTime;
|
lastTrackTime = adjustableClock.CurrentTime;
|
||||||
|
}
|
||||||
|
|
||||||
void seekTrackToCurrent()
|
private void seekTrackToCurrent()
|
||||||
{
|
{
|
||||||
if (!(Beatmap.Value.Track is TrackVirtual))
|
if (!track.IsLoaded)
|
||||||
adjustableClock.Seek(Current / Content.DrawWidth * Beatmap.Value.Track.Length);
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
void scrollToTrackTime()
|
adjustableClock.Seek(Current / Content.DrawWidth * track.Length);
|
||||||
{
|
}
|
||||||
if (!(Beatmap.Value.Track is TrackVirtual))
|
|
||||||
ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false);
|
private void scrollToTrackTime()
|
||||||
}
|
{
|
||||||
|
if (!track.IsLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ScrollTo((float)(adjustableClock.CurrentTime / track.Length) * Content.DrawWidth, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||||
|
@ -146,7 +146,7 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
replacePlaceholder(new MessagePlaceholder(@"Please sign in to view online leaderboards!"));
|
replacePlaceholder(new MessagePlaceholder(@"Please sign in to view online leaderboards!"));
|
||||||
break;
|
break;
|
||||||
case PlaceholderState.NotSupporter:
|
case PlaceholderState.NotSupporter:
|
||||||
replacePlaceholder(new MessagePlaceholder(@"Please invest in a supporter tag to view this leaderboard!"));
|
replacePlaceholder(new MessagePlaceholder(@"Please invest in an osu!supporter tag to view this leaderboard!"));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
replacePlaceholder(null);
|
replacePlaceholder(null);
|
||||||
@ -331,23 +331,4 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum LeaderboardScope
|
|
||||||
{
|
|
||||||
Local,
|
|
||||||
Country,
|
|
||||||
Global,
|
|
||||||
Friend,
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PlaceholderState
|
|
||||||
{
|
|
||||||
Successful,
|
|
||||||
Retrieving,
|
|
||||||
NetworkFailure,
|
|
||||||
Unavailable,
|
|
||||||
NoScores,
|
|
||||||
NotLoggedIn,
|
|
||||||
NotSupporter,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
13
osu.Game/Screens/Select/Leaderboards/LeaderboardScope.cs
Normal file
13
osu.Game/Screens/Select/Leaderboards/LeaderboardScope.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Select.Leaderboards
|
||||||
|
{
|
||||||
|
public enum LeaderboardScope
|
||||||
|
{
|
||||||
|
Local,
|
||||||
|
Country,
|
||||||
|
Global,
|
||||||
|
Friend,
|
||||||
|
}
|
||||||
|
}
|
@ -2,10 +2,7 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using OpenTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select.Leaderboards
|
namespace osu.Game.Screens.Select.Leaderboards
|
||||||
{
|
{
|
||||||
@ -15,22 +12,13 @@ namespace osu.Game.Screens.Select.Leaderboards
|
|||||||
|
|
||||||
public MessagePlaceholder(string message)
|
public MessagePlaceholder(string message)
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Horizontal;
|
AddIcon(FontAwesome.fa_exclamation_circle, cp =>
|
||||||
AutoSizeAxes = Axes.Both;
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new SpriteIcon
|
cp.TextSize = TEXT_SIZE;
|
||||||
{
|
cp.Padding = new MarginPadding { Right = 10 };
|
||||||
Icon = FontAwesome.fa_exclamation_circle,
|
});
|
||||||
Size = new Vector2(26),
|
|
||||||
Margin = new MarginPadding { Right = 10 },
|
AddText(this.message = message);
|
||||||
},
|
|
||||||
new OsuSpriteText
|
|
||||||
{
|
|
||||||
Text = this.message = message,
|
|
||||||
TextSize = 22,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(Placeholder other) => (other as MessagePlaceholder)?.message == message;
|
public override bool Equals(Placeholder other) => (other as MessagePlaceholder)?.message == message;
|
||||||
|
@ -3,16 +3,27 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Select.Leaderboards
|
namespace osu.Game.Screens.Select.Leaderboards
|
||||||
{
|
{
|
||||||
public abstract class Placeholder : FillFlowContainer, IEquatable<Placeholder>
|
public abstract class Placeholder : OsuTextFlowContainer, IEquatable<Placeholder>
|
||||||
{
|
{
|
||||||
|
protected const float TEXT_SIZE = 22;
|
||||||
|
|
||||||
|
public override bool HandleMouseInput => true;
|
||||||
|
|
||||||
protected Placeholder()
|
protected Placeholder()
|
||||||
|
: base(cp => cp.TextSize = TEXT_SIZE)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
TextAnchor = Anchor.TopCentre;
|
||||||
|
|
||||||
|
Padding = new MarginPadding(20);
|
||||||
|
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool Equals(Placeholder other) => GetType() == other?.GetType();
|
public virtual bool Equals(Placeholder other) => GetType() == other?.GetType();
|
||||||
|
16
osu.Game/Screens/Select/Leaderboards/PlaceholderState.cs
Normal file
16
osu.Game/Screens/Select/Leaderboards/PlaceholderState.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Select.Leaderboards
|
||||||
|
{
|
||||||
|
public enum PlaceholderState
|
||||||
|
{
|
||||||
|
Successful,
|
||||||
|
Retrieving,
|
||||||
|
NetworkFailure,
|
||||||
|
Unavailable,
|
||||||
|
NoScores,
|
||||||
|
NotLoggedIn,
|
||||||
|
NotSupporter,
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user