Merge remote-tracking branch 'upstream/master' into re-order-events
@ -5,7 +5,7 @@
|
||||
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Cake" Version="0.30.0" />
|
||||
<PackageReference Include="Cake.CoreCLR" Version="0.30.0" />
|
||||
<PackageReference Include="Cake" Version="0.34.1" />
|
||||
<PackageReference Include="Cake.CoreCLR" Version="0.34.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -63,6 +63,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.723.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.730.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
@ -38,9 +38,9 @@ namespace osu.Desktop
|
||||
if (Host is DesktopGameHost desktopHost)
|
||||
return new StableStorage(desktopHost);
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
Logger.Error(e, "Error while searching for stable install");
|
||||
Logger.Log("Could not find a stable install", LoggingTarget.Runtime, LogLevel.Important);
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -52,11 +52,7 @@ namespace osu.Desktop
|
||||
|
||||
if (!noVersionOverlay)
|
||||
{
|
||||
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, v =>
|
||||
{
|
||||
Add(v);
|
||||
v.Show();
|
||||
});
|
||||
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, Add);
|
||||
|
||||
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
|
||||
Add(new SquirrelUpdateManager());
|
||||
@ -71,7 +67,7 @@ namespace osu.Desktop
|
||||
|
||||
switch (newScreen)
|
||||
{
|
||||
case Intro _:
|
||||
case IntroScreen _:
|
||||
case MainMenu _:
|
||||
versionManager?.Show();
|
||||
break;
|
||||
|
@ -27,6 +27,8 @@ namespace osu.Desktop.Updater
|
||||
|
||||
public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited();
|
||||
|
||||
private static readonly Logger logger = Logger.GetLogger("updater");
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(NotificationOverlay notification, OsuGameBase game)
|
||||
{
|
||||
@ -77,7 +79,7 @@ namespace osu.Desktop.Updater
|
||||
{
|
||||
if (useDeltaPatching)
|
||||
{
|
||||
Logger.Error(e, @"delta patching failed!");
|
||||
logger.Add(@"delta patching failed; will attempt full download!");
|
||||
|
||||
//could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959)
|
||||
//try again without deltas.
|
||||
@ -163,16 +165,11 @@ namespace osu.Desktop.Updater
|
||||
{
|
||||
public LogLevel Level { get; set; } = LogLevel.Info;
|
||||
|
||||
private Logger logger;
|
||||
|
||||
public void Write(string message, LogLevel logLevel)
|
||||
{
|
||||
if (logLevel < Level)
|
||||
return;
|
||||
|
||||
if (logger == null)
|
||||
logger = Logger.GetLogger("updater");
|
||||
|
||||
logger.Add(message);
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,8 @@
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
|
||||
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Resources">
|
||||
<EmbeddedResource Include="lazer.ico" />
|
||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Catch.Mods;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -22,10 +23,10 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[TestCase("spinner")]
|
||||
[TestCase("spinner-and-circles")]
|
||||
[TestCase("slider")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
}
|
||||
[TestCase("hardrock-stream", new[] { typeof(CatchModHardRock) })]
|
||||
[TestCase("hardrock-repeat-slider", new[] { typeof(CatchModHardRock) })]
|
||||
[TestCase("hardrock-spinner", new[] { typeof(CatchModHardRock) })]
|
||||
public new void Test(string name, params Type[] mods) => base.Test(name, mods);
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
|
@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osuTK;
|
||||
using osu.Game.Rulesets.Catch.MathUtils;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
{
|
||||
@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
{
|
||||
base.PostProcess();
|
||||
|
||||
applyPositionOffsets();
|
||||
ApplyPositionOffsets(Beatmap);
|
||||
|
||||
initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects);
|
||||
|
||||
@ -40,19 +41,29 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
private void applyPositionOffsets()
|
||||
public static void ApplyPositionOffsets(IBeatmap beatmap, params Mod[] mods)
|
||||
{
|
||||
var rng = new FastRandom(RNG_SEED);
|
||||
// todo: HardRock displacement should be applied here
|
||||
|
||||
foreach (var obj in Beatmap.HitObjects)
|
||||
bool shouldApplyHardRockOffset = mods.Any(m => m is ModHardRock);
|
||||
float? lastPosition = null;
|
||||
double lastStartTime = 0;
|
||||
|
||||
foreach (var obj in beatmap.HitObjects.OfType<CatchHitObject>())
|
||||
{
|
||||
obj.XOffset = 0;
|
||||
|
||||
switch (obj)
|
||||
{
|
||||
case Fruit fruit:
|
||||
if (shouldApplyHardRockOffset)
|
||||
applyHardRockOffset(fruit, ref lastPosition, ref lastStartTime, rng);
|
||||
break;
|
||||
|
||||
case BananaShower bananaShower:
|
||||
foreach (var banana in bananaShower.NestedHitObjects.OfType<Banana>())
|
||||
{
|
||||
banana.X = (float)rng.NextDouble();
|
||||
banana.XOffset = (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
|
||||
@ -63,12 +74,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
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)
|
||||
var catchObject = (CatchHitObject)nested;
|
||||
catchObject.XOffset = 0;
|
||||
|
||||
if (catchObject is TinyDroplet)
|
||||
catchObject.XOffset = MathHelper.Clamp(rng.Next(-20, 20) / CatchPlayfield.BASE_WIDTH, -catchObject.X, 1 - catchObject.X);
|
||||
else if (catchObject is Droplet)
|
||||
rng.Next(); // osu!stable retrieved a random droplet rotation
|
||||
hitObject.X = MathHelper.Clamp(hitObject.X, 0, 1);
|
||||
}
|
||||
|
||||
break;
|
||||
@ -76,6 +88,105 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, FastRandom rng)
|
||||
{
|
||||
if (hitObject is JuiceStream stream)
|
||||
{
|
||||
lastPosition = stream.EndX;
|
||||
lastStartTime = stream.EndTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(hitObject is Fruit))
|
||||
return;
|
||||
|
||||
float offsetPosition = hitObject.X;
|
||||
double startTime = hitObject.StartTime;
|
||||
|
||||
if (lastPosition == null)
|
||||
{
|
||||
lastPosition = offsetPosition;
|
||||
lastStartTime = startTime;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float positionDiff = offsetPosition - lastPosition.Value;
|
||||
double timeDiff = startTime - lastStartTime;
|
||||
|
||||
if (timeDiff > 1000)
|
||||
{
|
||||
lastPosition = offsetPosition;
|
||||
lastStartTime = startTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (positionDiff == 0)
|
||||
{
|
||||
applyRandomOffset(ref offsetPosition, timeDiff / 4d, rng);
|
||||
hitObject.XOffset = offsetPosition - hitObject.X;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d)
|
||||
applyOffset(ref offsetPosition, positionDiff);
|
||||
|
||||
hitObject.XOffset = offsetPosition - hitObject.X;
|
||||
|
||||
lastPosition = offsetPosition;
|
||||
lastStartTime = startTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a random offset in a random direction to a position, ensuring that the final position remains within the boundary of the playfield.
|
||||
/// </summary>
|
||||
/// <param name="position">The position which the offset should be applied to.</param>
|
||||
/// <param name="maxOffset">The maximum offset, cannot exceed 20px.</param>
|
||||
/// <param name="rng">The random number generator.</param>
|
||||
private static void applyRandomOffset(ref float position, double maxOffset, FastRandom rng)
|
||||
{
|
||||
bool right = rng.NextBool();
|
||||
float rand = Math.Min(20, (float)rng.Next(0, Math.Max(0, maxOffset))) / CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
if (right)
|
||||
{
|
||||
// Clamp to the right bound
|
||||
if (position + rand <= 1)
|
||||
position += rand;
|
||||
else
|
||||
position -= rand;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clamp to the left bound
|
||||
if (position - rand >= 0)
|
||||
position -= rand;
|
||||
else
|
||||
position += rand;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an offset to a position, ensuring that the final position remains within the boundary of the playfield.
|
||||
/// </summary>
|
||||
/// <param name="position">The position which the offset should be applied to.</param>
|
||||
/// <param name="amount">The amount to offset by.</param>
|
||||
private static void applyOffset(ref float position, float amount)
|
||||
{
|
||||
if (amount > 0)
|
||||
{
|
||||
// Clamp to the right bound
|
||||
if (position + amount < 1)
|
||||
position += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clamp to the left bound
|
||||
if (position + amount > 0)
|
||||
position += amount;
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseHyperDash(List<CatchHitObject> objects)
|
||||
{
|
||||
List<CatchHitObject> objectWithDroplets = new List<CatchHitObject>();
|
||||
|
@ -11,7 +11,6 @@ using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
@ -108,7 +107,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
case ModType.Fun:
|
||||
return new Mod[]
|
||||
{
|
||||
new MultiMod(new ModWindUp<CatchHitObject>(), new ModWindDown<CatchHitObject>())
|
||||
new MultiMod(new ModWindUp(), new ModWindDown())
|
||||
};
|
||||
|
||||
default:
|
||||
|
@ -61,6 +61,14 @@ namespace osu.Game.Rulesets.Catch.MathUtils
|
||||
/// <returns>The random value.</returns>
|
||||
public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound));
|
||||
|
||||
/// <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(double lowerBound, double upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound));
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random double value within the range [0, 1).
|
||||
/// </summary>
|
||||
|
@ -1,121 +1,17 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using System;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModHardRock : ModHardRock, IApplicableToHitObject
|
||||
public class CatchModHardRock : ModHardRock, IApplicableToBeatmap
|
||||
{
|
||||
public override double ScoreMultiplier => 1.12;
|
||||
public override bool Ranked => true;
|
||||
|
||||
private float? lastPosition;
|
||||
private double lastStartTime;
|
||||
|
||||
public void ApplyToHitObject(HitObject hitObject)
|
||||
{
|
||||
if (hitObject is JuiceStream stream)
|
||||
{
|
||||
lastPosition = stream.EndX;
|
||||
lastStartTime = stream.EndTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(hitObject is Fruit))
|
||||
return;
|
||||
|
||||
var catchObject = (CatchHitObject)hitObject;
|
||||
|
||||
float position = catchObject.X;
|
||||
double startTime = hitObject.StartTime;
|
||||
|
||||
if (lastPosition == null)
|
||||
{
|
||||
lastPosition = position;
|
||||
lastStartTime = startTime;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float positionDiff = position - lastPosition.Value;
|
||||
double timeDiff = startTime - lastStartTime;
|
||||
|
||||
if (timeDiff > 1000)
|
||||
{
|
||||
lastPosition = position;
|
||||
lastStartTime = startTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if (positionDiff == 0)
|
||||
{
|
||||
applyRandomOffset(ref position, timeDiff / 4d);
|
||||
catchObject.X = position;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(positionDiff * CatchPlayfield.BASE_WIDTH) < timeDiff / 3d)
|
||||
applyOffset(ref position, positionDiff);
|
||||
|
||||
catchObject.X = position;
|
||||
|
||||
lastPosition = position;
|
||||
lastStartTime = startTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a random offset in a random direction to a position, ensuring that the final position remains within the boundary of the playfield.
|
||||
/// </summary>
|
||||
/// <param name="position">The position which the offset should be applied to.</param>
|
||||
/// <param name="maxOffset">The maximum offset, cannot exceed 20px.</param>
|
||||
private void applyRandomOffset(ref float position, double maxOffset)
|
||||
{
|
||||
bool right = RNG.NextBool();
|
||||
float rand = Math.Min(20, (float)RNG.NextDouble(0, Math.Max(0, maxOffset))) / CatchPlayfield.BASE_WIDTH;
|
||||
|
||||
if (right)
|
||||
{
|
||||
// Clamp to the right bound
|
||||
if (position + rand <= 1)
|
||||
position += rand;
|
||||
else
|
||||
position -= rand;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clamp to the left bound
|
||||
if (position - rand >= 0)
|
||||
position -= rand;
|
||||
else
|
||||
position += rand;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an offset to a position, ensuring that the final position remains within the boundary of the playfield.
|
||||
/// </summary>
|
||||
/// <param name="position">The position which the offset should be applied to.</param>
|
||||
/// <param name="amount">The amount to offset by.</param>
|
||||
private void applyOffset(ref float position, float amount)
|
||||
{
|
||||
if (amount > 0)
|
||||
{
|
||||
// Clamp to the right bound
|
||||
if (position + amount < 1)
|
||||
position += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clamp to the left bound
|
||||
if (position + amount > 0)
|
||||
position += amount;
|
||||
}
|
||||
}
|
||||
public void ApplyToBeatmap(IBeatmap beatmap) => CatchBeatmapProcessor.ApplyPositionOffsets(beatmap, this);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
|
||||
@ -12,7 +13,18 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
{
|
||||
public const double OBJECT_RADIUS = 44;
|
||||
|
||||
public float X { get; set; }
|
||||
private float x;
|
||||
|
||||
public float X
|
||||
{
|
||||
get => x + XOffset;
|
||||
set => x = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A random offset applied to <see cref="X"/>, set by the <see cref="CatchBeatmapProcessor"/>.
|
||||
/// </summary>
|
||||
internal float XOffset { get; set; }
|
||||
|
||||
public double TimePreempt = 1000;
|
||||
|
||||
|
@ -0,0 +1,150 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 369,
|
||||
"Objects": [{
|
||||
"StartTime": 369,
|
||||
"Position": 177
|
||||
},
|
||||
{
|
||||
"StartTime": 450,
|
||||
"Position": 216.539276
|
||||
},
|
||||
{
|
||||
"StartTime": 532,
|
||||
"Position": 256.5667
|
||||
},
|
||||
{
|
||||
"StartTime": 614,
|
||||
"Position": 296.594116
|
||||
},
|
||||
{
|
||||
"StartTime": 696,
|
||||
"Position": 336.621521
|
||||
},
|
||||
{
|
||||
"StartTime": 778,
|
||||
"Position": 376.99762
|
||||
},
|
||||
{
|
||||
"StartTime": 860,
|
||||
"Position": 337.318878
|
||||
},
|
||||
{
|
||||
"StartTime": 942,
|
||||
"Position": 297.291443
|
||||
},
|
||||
{
|
||||
"StartTime": 1024,
|
||||
"Position": 257.264038
|
||||
},
|
||||
{
|
||||
"StartTime": 1106,
|
||||
"Position": 217.2366
|
||||
},
|
||||
{
|
||||
"StartTime": 1188,
|
||||
"Position": 177
|
||||
},
|
||||
{
|
||||
"StartTime": 1270,
|
||||
"Position": 216.818192
|
||||
},
|
||||
{
|
||||
"StartTime": 1352,
|
||||
"Position": 256.8456
|
||||
},
|
||||
{
|
||||
"StartTime": 1434,
|
||||
"Position": 296.873047
|
||||
},
|
||||
{
|
||||
"StartTime": 1516,
|
||||
"Position": 336.900452
|
||||
},
|
||||
{
|
||||
"StartTime": 1598,
|
||||
"Position": 376.99762
|
||||
},
|
||||
{
|
||||
"StartTime": 1680,
|
||||
"Position": 337.039948
|
||||
},
|
||||
{
|
||||
"StartTime": 1762,
|
||||
"Position": 297.0125
|
||||
},
|
||||
{
|
||||
"StartTime": 1844,
|
||||
"Position": 256.9851
|
||||
},
|
||||
{
|
||||
"StartTime": 1926,
|
||||
"Position": 216.957672
|
||||
},
|
||||
{
|
||||
"StartTime": 2008,
|
||||
"Position": 177
|
||||
},
|
||||
{
|
||||
"StartTime": 2090,
|
||||
"Position": 217.097137
|
||||
},
|
||||
{
|
||||
"StartTime": 2172,
|
||||
"Position": 257.124573
|
||||
},
|
||||
{
|
||||
"StartTime": 2254,
|
||||
"Position": 297.152
|
||||
},
|
||||
{
|
||||
"StartTime": 2336,
|
||||
"Position": 337.179443
|
||||
},
|
||||
{
|
||||
"StartTime": 2418,
|
||||
"Position": 376.99762
|
||||
},
|
||||
{
|
||||
"StartTime": 2500,
|
||||
"Position": 336.760956
|
||||
},
|
||||
{
|
||||
"StartTime": 2582,
|
||||
"Position": 296.733643
|
||||
},
|
||||
{
|
||||
"StartTime": 2664,
|
||||
"Position": 256.7062
|
||||
},
|
||||
{
|
||||
"StartTime": 2746,
|
||||
"Position": 216.678772
|
||||
},
|
||||
{
|
||||
"StartTime": 2828,
|
||||
"Position": 177
|
||||
},
|
||||
{
|
||||
"StartTime": 2909,
|
||||
"Position": 216.887909
|
||||
},
|
||||
{
|
||||
"StartTime": 2991,
|
||||
"Position": 256.915344
|
||||
},
|
||||
{
|
||||
"StartTime": 3073,
|
||||
"Position": 296.942749
|
||||
},
|
||||
{
|
||||
"StartTime": 3155,
|
||||
"Position": 336.970184
|
||||
},
|
||||
{
|
||||
"StartTime": 3237,
|
||||
"Position": 376.99762
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.4
|
||||
Mode: 0
|
||||
|
||||
[Difficulty]
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:4
|
||||
|
||||
[TimingPoints]
|
||||
369,327.868852459016,4,2,2,32,1,0
|
||||
|
||||
[HitObjects]
|
||||
177,191,369,6,0,L|382:192,7,200
|
@ -0,0 +1,74 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 369,
|
||||
"Objects": [{
|
||||
"StartTime": 369,
|
||||
"Position": 65
|
||||
},
|
||||
{
|
||||
"StartTime": 450,
|
||||
"Position": 482
|
||||
},
|
||||
{
|
||||
"StartTime": 532,
|
||||
"Position": 164
|
||||
},
|
||||
{
|
||||
"StartTime": 614,
|
||||
"Position": 315
|
||||
},
|
||||
{
|
||||
"StartTime": 696,
|
||||
"Position": 145
|
||||
},
|
||||
{
|
||||
"StartTime": 778,
|
||||
"Position": 159
|
||||
},
|
||||
{
|
||||
"StartTime": 860,
|
||||
"Position": 310
|
||||
},
|
||||
{
|
||||
"StartTime": 942,
|
||||
"Position": 441
|
||||
},
|
||||
{
|
||||
"StartTime": 1024,
|
||||
"Position": 428
|
||||
},
|
||||
{
|
||||
"StartTime": 1106,
|
||||
"Position": 243
|
||||
},
|
||||
{
|
||||
"StartTime": 1188,
|
||||
"Position": 422
|
||||
},
|
||||
{
|
||||
"StartTime": 1270,
|
||||
"Position": 481
|
||||
},
|
||||
{
|
||||
"StartTime": 1352,
|
||||
"Position": 104
|
||||
},
|
||||
{
|
||||
"StartTime": 1434,
|
||||
"Position": 473
|
||||
},
|
||||
{
|
||||
"StartTime": 1516,
|
||||
"Position": 135
|
||||
},
|
||||
{
|
||||
"StartTime": 1598,
|
||||
"Position": 360
|
||||
},
|
||||
{
|
||||
"StartTime": 1680,
|
||||
"Position": 123
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.4
|
||||
Mode: 0
|
||||
|
||||
[Difficulty]
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:4
|
||||
|
||||
[TimingPoints]
|
||||
369,327.868852459016,4,2,2,32,1,0
|
||||
|
||||
[HitObjects]
|
||||
256,192,369,12,0,1680,0:0:0:0:
|
@ -0,0 +1,234 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 369,
|
||||
"Objects": [{
|
||||
"StartTime": 369,
|
||||
"Position": 258
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 450,
|
||||
"Objects": [{
|
||||
"StartTime": 450,
|
||||
"Position": 254
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 532,
|
||||
"Objects": [{
|
||||
"StartTime": 532,
|
||||
"Position": 241
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 614,
|
||||
"Objects": [{
|
||||
"StartTime": 614,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 696,
|
||||
"Objects": [{
|
||||
"StartTime": 696,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 778,
|
||||
"Objects": [{
|
||||
"StartTime": 778,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 860,
|
||||
"Objects": [{
|
||||
"StartTime": 860,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 942,
|
||||
"Objects": [{
|
||||
"StartTime": 942,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1024,
|
||||
"Objects": [{
|
||||
"StartTime": 1024,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1106,
|
||||
"Objects": [{
|
||||
"StartTime": 1106,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1188,
|
||||
"Objects": [{
|
||||
"StartTime": 1188,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1270,
|
||||
"Objects": [{
|
||||
"StartTime": 1270,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1352,
|
||||
"Objects": [{
|
||||
"StartTime": 1352,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1434,
|
||||
"Objects": [{
|
||||
"StartTime": 1434,
|
||||
"Position": 258
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1516,
|
||||
"Objects": [{
|
||||
"StartTime": 1516,
|
||||
"Position": 253
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1598,
|
||||
"Objects": [{
|
||||
"StartTime": 1598,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1680,
|
||||
"Objects": [{
|
||||
"StartTime": 1680,
|
||||
"Position": 260
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1762,
|
||||
"Objects": [{
|
||||
"StartTime": 1762,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1844,
|
||||
"Objects": [{
|
||||
"StartTime": 1844,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 1926,
|
||||
"Objects": [{
|
||||
"StartTime": 1926,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2008,
|
||||
"Objects": [{
|
||||
"StartTime": 2008,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2090,
|
||||
"Objects": [{
|
||||
"StartTime": 2090,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2172,
|
||||
"Objects": [{
|
||||
"StartTime": 2172,
|
||||
"Position": 243
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2254,
|
||||
"Objects": [{
|
||||
"StartTime": 2254,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2336,
|
||||
"Objects": [{
|
||||
"StartTime": 2336,
|
||||
"Position": 278
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2418,
|
||||
"Objects": [{
|
||||
"StartTime": 2418,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2500,
|
||||
"Objects": [{
|
||||
"StartTime": 2500,
|
||||
"Position": 258
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2582,
|
||||
"Objects": [{
|
||||
"StartTime": 2582,
|
||||
"Position": 256
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2664,
|
||||
"Objects": [{
|
||||
"StartTime": 2664,
|
||||
"Position": 242
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2746,
|
||||
"Objects": [{
|
||||
"StartTime": 2746,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2828,
|
||||
"Objects": [{
|
||||
"StartTime": 2828,
|
||||
"Position": 238
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2909,
|
||||
"Objects": [{
|
||||
"StartTime": 2909,
|
||||
"Position": 271
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 2991,
|
||||
"Objects": [{
|
||||
"StartTime": 2991,
|
||||
"Position": 254
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.4
|
||||
Mode: 0
|
||||
|
||||
[Difficulty]
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
369,327.868852459016,4,2,2,32,1,0
|
||||
|
||||
[HitObjects]
|
||||
258,189,369,1,0,0:0:0:0:
|
||||
258,189,450,1,0,0:0:0:0:
|
||||
258,189,532,1,0,0:0:0:0:
|
||||
258,189,614,1,0,0:0:0:0:
|
||||
258,189,696,1,0,0:0:0:0:
|
||||
258,189,778,1,0,0:0:0:0:
|
||||
258,189,860,1,0,0:0:0:0:
|
||||
258,189,942,1,0,0:0:0:0:
|
||||
258,189,1024,1,0,0:0:0:0:
|
||||
258,189,1106,1,0,0:0:0:0:
|
||||
258,189,1188,1,0,0:0:0:0:
|
||||
258,189,1270,1,0,0:0:0:0:
|
||||
258,189,1352,1,0,0:0:0:0:
|
||||
258,189,1434,1,0,0:0:0:0:
|
||||
258,189,1516,1,0,0:0:0:0:
|
||||
258,189,1598,1,0,0:0:0:0:
|
||||
258,189,1680,1,0,0:0:0:0:
|
||||
258,189,1762,1,0,0:0:0:0:
|
||||
258,189,1844,1,0,0:0:0:0:
|
||||
258,189,1926,1,0,0:0:0:0:
|
||||
258,189,2008,1,0,0:0:0:0:
|
||||
258,189,2090,1,0,0:0:0:0:
|
||||
258,189,2172,1,0,0:0:0:0:
|
||||
258,189,2254,1,0,0:0:0:0:
|
||||
258,189,2336,1,0,0:0:0:0:
|
||||
258,189,2418,1,0,0:0:0:0:
|
||||
258,189,2500,1,0,0:0:0:0:
|
||||
258,189,2582,1,0,0:0:0:0:
|
||||
258,189,2664,1,0,0:0:0:0:
|
||||
258,189,2746,1,0,0:0:0:0:
|
||||
258,189,2828,1,0,0:0:0:0:
|
||||
258,189,2909,1,0,0:0:0:0:
|
||||
258,189,2991,1,0,0:0:0:0:
|
@ -20,10 +20,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||
|
||||
[TestCase("basic")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
}
|
||||
public void Test(string name) => base.Test(name);
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
@ -35,11 +32,37 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
};
|
||||
}
|
||||
|
||||
protected override ManiaConvertMapping CreateConvertMapping() => new ManiaConvertMapping(Converter);
|
||||
private readonly Dictionary<HitObject, RngSnapshot> rngSnapshots = new Dictionary<HitObject, RngSnapshot>();
|
||||
|
||||
protected override void OnConversionGenerated(HitObject original, IEnumerable<HitObject> result, IBeatmapConverter beatmapConverter)
|
||||
{
|
||||
base.OnConversionGenerated(original, result, beatmapConverter);
|
||||
|
||||
rngSnapshots[original] = new RngSnapshot(beatmapConverter);
|
||||
}
|
||||
|
||||
protected override ManiaConvertMapping CreateConvertMapping(HitObject source) => new ManiaConvertMapping(rngSnapshots[source]);
|
||||
|
||||
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
||||
}
|
||||
|
||||
public class RngSnapshot
|
||||
{
|
||||
public readonly uint RandomW;
|
||||
public readonly uint RandomX;
|
||||
public readonly uint RandomY;
|
||||
public readonly uint RandomZ;
|
||||
|
||||
public RngSnapshot(IBeatmapConverter converter)
|
||||
{
|
||||
var maniaConverter = (ManiaBeatmapConverter)converter;
|
||||
RandomW = maniaConverter.Random.W;
|
||||
RandomX = maniaConverter.Random.X;
|
||||
RandomY = maniaConverter.Random.Y;
|
||||
RandomZ = maniaConverter.Random.Z;
|
||||
}
|
||||
}
|
||||
|
||||
public class ManiaConvertMapping : ConvertMapping<ConvertValue>, IEquatable<ManiaConvertMapping>
|
||||
{
|
||||
public uint RandomW;
|
||||
@ -51,13 +74,12 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
}
|
||||
|
||||
public ManiaConvertMapping(IBeatmapConverter converter)
|
||||
public ManiaConvertMapping(RngSnapshot snapshot)
|
||||
{
|
||||
var maniaConverter = (ManiaBeatmapConverter)converter;
|
||||
RandomW = maniaConverter.Random.W;
|
||||
RandomX = maniaConverter.Random.X;
|
||||
RandomY = maniaConverter.Random.Y;
|
||||
RandomZ = maniaConverter.Random.Z;
|
||||
RandomW = snapshot.RandomW;
|
||||
RandomX = snapshot.RandomX;
|
||||
RandomY = snapshot.RandomY;
|
||||
RandomZ = snapshot.RandomZ;
|
||||
}
|
||||
|
||||
public bool Equals(ManiaConvertMapping other) => other != null && RandomW == other.RandomW && RandomX == other.RandomX && RandomY == other.RandomY && RandomZ == other.RandomZ;
|
||||
|
@ -13,7 +13,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
@ -154,7 +153,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
case ModType.Fun:
|
||||
return new Mod[]
|
||||
{
|
||||
new MultiMod(new ModWindUp<ManiaHitObject>(), new ModWindDown<ManiaHitObject>())
|
||||
new MultiMod(new ModWindUp(), new ModWindDown())
|
||||
};
|
||||
|
||||
default:
|
||||
|
@ -10,7 +10,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModMirror : Mod, IApplicableToBeatmap<ManiaHitObject>
|
||||
public class ManiaModMirror : Mod, IApplicableToBeatmap
|
||||
{
|
||||
public override string Name => "Mirror";
|
||||
public override string Acronym => "MR";
|
||||
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override bool Ranked => true;
|
||||
|
||||
public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||
|
||||
|
@ -13,7 +13,7 @@ using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModRandom : Mod, IApplicableToBeatmap<ManiaHitObject>
|
||||
public class ManiaModRandom : Mod, IApplicableToBeatmap
|
||||
{
|
||||
public override string Name => "Random";
|
||||
public override string Acronym => "RD";
|
||||
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
public override string Description => @"Shuffle around the keys!";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
|
||||
|
@ -21,10 +21,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[TestCase("basic")]
|
||||
[TestCase("colinear-perfect-curve")]
|
||||
[TestCase("slider-ticks")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
}
|
||||
[TestCase("repeat-slider")]
|
||||
[TestCase("uneven-repeat-slider")]
|
||||
public void Test(string name) => base.Test(name);
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
|
BIN
osu.Game.Rulesets.Osu.Tests/Resources/default-skin/approachcircle@2x.png
Executable file
After Width: | Height: | Size: 18 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hit300k@2x.png
Executable file
After Width: | Height: | Size: 28 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircle@2x.png
Executable file
After Width: | Height: | Size: 7.6 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/default-skin/hitcircleoverlay@2x.png
Executable file
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 37 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/special-skin/approachcircle@2x.png
Executable file
After Width: | Height: | Size: 26 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircle@2x.png
Executable file
After Width: | Height: | Size: 240 KiB |
BIN
osu.Game.Rulesets.Osu.Tests/Resources/special-skin/hitcircleoverlay@2x.png
Executable file
After Width: | Height: | Size: 241 KiB |
66
osu.Game.Rulesets.Osu.Tests/SkinnableTestScene.cs
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public abstract class SkinnableTestScene : OsuGridTestScene
|
||||
{
|
||||
private Skin metricsSkin;
|
||||
private Skin defaultSkin;
|
||||
private Skin specialSkin;
|
||||
|
||||
protected SkinnableTestScene()
|
||||
: base(2, 2)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
var skins = new SkinManager(LocalStorage, ContextFactory, null, audio);
|
||||
|
||||
metricsSkin = getSkinFromResources(skins, "metrics_skin");
|
||||
defaultSkin = getSkinFromResources(skins, "default_skin");
|
||||
specialSkin = getSkinFromResources(skins, "special_skin");
|
||||
}
|
||||
|
||||
public void SetContents(Func<Drawable> creationFunction)
|
||||
{
|
||||
Cell(0).Child = new LocalSkinOverrideContainer(null) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction());
|
||||
Cell(1).Child = new LocalSkinOverrideContainer(metricsSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction());
|
||||
Cell(2).Child = new LocalSkinOverrideContainer(defaultSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction());
|
||||
Cell(3).Child = new LocalSkinOverrideContainer(specialSkin) { RelativeSizeAxes = Axes.Both }.WithChild(creationFunction());
|
||||
}
|
||||
|
||||
private static Skin getSkinFromResources(SkinManager skins, string name)
|
||||
{
|
||||
using (var storage = new DllResourceStore("osu.Game.Rulesets.Osu.Tests.dll"))
|
||||
{
|
||||
var tempName = Path.GetTempFileName();
|
||||
|
||||
File.Delete(tempName);
|
||||
Directory.CreateDirectory(tempName);
|
||||
|
||||
var files = storage.GetAvailableResources().Where(f => f.StartsWith($"Resources/{name}"));
|
||||
|
||||
foreach (var file in files)
|
||||
using (var stream = storage.GetStream(file))
|
||||
using (var newFile = File.Create(Path.Combine(tempName, Path.GetFileName(file))))
|
||||
stream.CopyTo(newFile);
|
||||
|
||||
return skins.GetSkin(skins.Import(tempName).Result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
@ -19,37 +18,32 @@ using osu.Game.Rulesets.Scoring;
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneHitCircle : OsuTestScene
|
||||
public class TestSceneHitCircle : SkinnableTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(DrawableHitCircle)
|
||||
};
|
||||
|
||||
private readonly Container content;
|
||||
protected override Container<Drawable> Content => content;
|
||||
|
||||
private int depthIndex;
|
||||
|
||||
public TestSceneHitCircle()
|
||||
{
|
||||
base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 }));
|
||||
|
||||
AddStep("Miss Big Single", () => testSingle(2));
|
||||
AddStep("Miss Medium Single", () => testSingle(5));
|
||||
AddStep("Miss Small Single", () => testSingle(7));
|
||||
AddStep("Hit Big Single", () => testSingle(2, true));
|
||||
AddStep("Hit Medium Single", () => testSingle(5, true));
|
||||
AddStep("Hit Small Single", () => testSingle(7, true));
|
||||
AddStep("Miss Big Stream", () => testStream(2));
|
||||
AddStep("Miss Medium Stream", () => testStream(5));
|
||||
AddStep("Miss Small Stream", () => testStream(7));
|
||||
AddStep("Hit Big Stream", () => testStream(2, true));
|
||||
AddStep("Hit Medium Stream", () => testStream(5, true));
|
||||
AddStep("Hit Small Stream", () => testStream(7, true));
|
||||
AddStep("Miss Big Single", () => SetContents(() => testSingle(2)));
|
||||
AddStep("Miss Medium Single", () => SetContents(() => testSingle(5)));
|
||||
AddStep("Miss Small Single", () => SetContents(() => testSingle(7)));
|
||||
AddStep("Hit Big Single", () => SetContents(() => testSingle(2, true)));
|
||||
AddStep("Hit Medium Single", () => SetContents(() => testSingle(5, true)));
|
||||
AddStep("Hit Small Single", () => SetContents(() => testSingle(7, true)));
|
||||
AddStep("Miss Big Stream", () => SetContents(() => testStream(2)));
|
||||
AddStep("Miss Medium Stream", () => SetContents(() => testStream(5)));
|
||||
AddStep("Miss Small Stream", () => SetContents(() => testStream(7)));
|
||||
AddStep("Hit Big Stream", () => SetContents(() => testStream(2, true)));
|
||||
AddStep("Hit Medium Stream", () => SetContents(() => testStream(5, true)));
|
||||
AddStep("Hit Small Stream", () => SetContents(() => testStream(7, true)));
|
||||
}
|
||||
|
||||
private void testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
||||
private Drawable testSingle(float circleSize, bool auto = false, double timeOffset = 0, Vector2? positionOffset = null)
|
||||
{
|
||||
positionOffset = positionOffset ?? Vector2.Zero;
|
||||
|
||||
@ -61,27 +55,33 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize });
|
||||
|
||||
var drawable = new TestDrawableHitCircle(circle, auto)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Depth = depthIndex++
|
||||
};
|
||||
var drawable = CreateDrawableHitCircle(circle, auto);
|
||||
|
||||
foreach (var mod in Mods.Value.OfType<IApplicableToDrawableHitObjects>())
|
||||
mod.ApplyToDrawableHitObjects(new[] { drawable });
|
||||
|
||||
Add(drawable);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private void testStream(float circleSize, bool auto = false)
|
||||
protected virtual TestDrawableHitCircle CreateDrawableHitCircle(HitCircle circle, bool auto) => new TestDrawableHitCircle(circle, auto)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Depth = depthIndex++
|
||||
};
|
||||
|
||||
private Drawable testStream(float circleSize, bool auto = false)
|
||||
{
|
||||
var container = new Container { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
Vector2 pos = new Vector2(-250, 0);
|
||||
|
||||
for (int i = 0; i <= 1000; i += 100)
|
||||
{
|
||||
testSingle(circleSize, auto, i, pos);
|
||||
container.Add(testSingle(circleSize, auto, i, pos));
|
||||
pos.X += 50;
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
protected class TestDrawableHitCircle : DrawableHitCircle
|
||||
|
@ -1,23 +1,22 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class TestSceneShaking : TestSceneHitCircle
|
||||
{
|
||||
public override void Add(Drawable drawable)
|
||||
protected override TestDrawableHitCircle CreateDrawableHitCircle(HitCircle circle, bool auto)
|
||||
{
|
||||
base.Add(drawable);
|
||||
var drawableHitObject = base.CreateDrawableHitCircle(circle, auto);
|
||||
|
||||
if (drawable is TestDrawableHitCircle hitObject)
|
||||
{
|
||||
Scheduler.AddDelayed(() => hitObject.TriggerJudgement(),
|
||||
hitObject.HitObject.StartTime - (hitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current);
|
||||
}
|
||||
Scheduler.AddDelayed(() => drawableHitObject.TriggerJudgement(),
|
||||
drawableHitObject.HitObject.StartTime - (drawableHitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current);
|
||||
|
||||
return drawableHitObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public override string Description => @"Play with no approach circles and fading circles/sliders.";
|
||||
public override double ScoreMultiplier => 1.06;
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn) };
|
||||
private const double fade_in_duration_multiplier = 0.4;
|
||||
private const double fade_out_duration_multiplier = 0.3;
|
||||
|
||||
|
92
osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModSpinIn : Mod, IApplicableToDrawableHitObjects, IReadFromConfig
|
||||
{
|
||||
public override string Name => "Spin In";
|
||||
public override string Acronym => "SI";
|
||||
public override IconUsage Icon => FontAwesome.Solid.Undo;
|
||||
public override ModType Type => ModType.Fun;
|
||||
public override string Description => "Circles spin in. No approach circles.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
// todo: this mod should be able to be compatible with hidden with a bit of further implementation.
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModeObjectScaleTween), typeof(OsuModHidden) };
|
||||
|
||||
private const int rotate_offset = 360;
|
||||
private const float rotate_starting_width = 2;
|
||||
|
||||
private Bindable<bool> increaseFirstObjectVisibility = new Bindable<bool>();
|
||||
|
||||
public void ReadFromConfig(OsuConfigManager config)
|
||||
{
|
||||
increaseFirstObjectVisibility = config.GetBindable<bool>(OsuSetting.IncreaseFirstObjectVisibility);
|
||||
}
|
||||
|
||||
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||
{
|
||||
foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0))
|
||||
{
|
||||
switch (drawable)
|
||||
{
|
||||
case DrawableSpinner _:
|
||||
continue;
|
||||
|
||||
default:
|
||||
drawable.ApplyCustomUpdateState += applyZoomState;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void applyZoomState(DrawableHitObject drawable, ArmedState state)
|
||||
{
|
||||
var h = (OsuHitObject)drawable.HitObject;
|
||||
|
||||
switch (drawable)
|
||||
{
|
||||
case DrawableHitCircle circle:
|
||||
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
|
||||
{
|
||||
circle.ApproachCircle.Hide();
|
||||
|
||||
circle.RotateTo(rotate_offset).Then().RotateTo(0, h.TimePreempt, Easing.InOutSine);
|
||||
circle.ScaleTo(new Vector2(rotate_starting_width, 0)).Then().ScaleTo(1, h.TimePreempt, Easing.InOutSine);
|
||||
|
||||
// bypass fade in.
|
||||
if (state == ArmedState.Idle)
|
||||
circle.FadeIn();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DrawableSlider slider:
|
||||
using (slider.BeginAbsoluteSequence(h.StartTime - h.TimePreempt))
|
||||
{
|
||||
slider.ScaleTo(0).Then().ScaleTo(1, h.TimePreempt, Easing.InOutSine);
|
||||
|
||||
// bypass fade in.
|
||||
if (state == ArmedState.Idle)
|
||||
slider.FadeIn();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
@ -28,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
private Bindable<bool> increaseFirstObjectVisibility = new Bindable<bool>();
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn) };
|
||||
|
||||
public void ReadFromConfig(OsuConfigManager config)
|
||||
{
|
||||
increaseFirstObjectVisibility = config.GetBindable<bool>(OsuSetting.IncreaseFirstObjectVisibility);
|
||||
@ -64,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
case DrawableSlider _:
|
||||
case DrawableHitCircle _:
|
||||
{
|
||||
using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
|
||||
using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt))
|
||||
drawable.ScaleTo(StartScale).Then().ScaleTo(EndScale, h.TimePreempt, Easing.OutSine);
|
||||
break;
|
||||
}
|
||||
@ -75,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
case DrawableHitCircle circle:
|
||||
// we don't want to see the approach circle
|
||||
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
|
||||
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt))
|
||||
circle.ApproachCircle.Hide();
|
||||
break;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
Anchor = Anchor.Centre,
|
||||
Alpha = 0.5f,
|
||||
}
|
||||
}, restrictSize: false);
|
||||
}, confineMode: ConfineMode.NoScaling);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,13 +97,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
Position = pointStartPosition,
|
||||
Rotation = rotation,
|
||||
Alpha = 0,
|
||||
Scale = new Vector2(1.5f),
|
||||
Scale = new Vector2(1.5f * currHitObject.Scale),
|
||||
});
|
||||
|
||||
using (fp.BeginAbsoluteSequence(fadeInTime))
|
||||
{
|
||||
fp.FadeIn(currHitObject.TimeFadeIn);
|
||||
fp.ScaleTo(1, currHitObject.TimeFadeIn, Easing.Out);
|
||||
fp.ScaleTo(currHitObject.Scale, currHitObject.TimeFadeIn, Easing.Out);
|
||||
|
||||
fp.MoveTo(pointEndPosition, currHitObject.TimeFadeIn, Easing.Out);
|
||||
|
||||
|
@ -6,33 +6,29 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using osuTK;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
|
||||
{
|
||||
public ApproachCircle ApproachCircle;
|
||||
private readonly CirclePiece circle;
|
||||
private readonly RingPiece ring;
|
||||
private readonly FlashPiece flash;
|
||||
private readonly ExplodePiece explode;
|
||||
private readonly NumberPiece number;
|
||||
private readonly GlowPiece glow;
|
||||
|
||||
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
||||
private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
|
||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||
|
||||
public OsuAction? HitAction => circle.HitAction;
|
||||
|
||||
private readonly Container explodeContainer;
|
||||
public OsuAction? HitAction => hitArea.HitAction;
|
||||
|
||||
private readonly Container scaleContainer;
|
||||
|
||||
private readonly HitArea hitArea;
|
||||
|
||||
public DrawableHitCircle(HitCircle h)
|
||||
: base(h)
|
||||
{
|
||||
@ -47,44 +43,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Child = explodeContainer = new Container
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
hitArea = new HitArea
|
||||
{
|
||||
glow = new GlowPiece(),
|
||||
circle = new CirclePiece
|
||||
Hit = () =>
|
||||
{
|
||||
Hit = () =>
|
||||
{
|
||||
if (AllJudged)
|
||||
return false;
|
||||
if (AllJudged)
|
||||
return false;
|
||||
|
||||
UpdateResult(true);
|
||||
return true;
|
||||
},
|
||||
UpdateResult(true);
|
||||
return true;
|
||||
},
|
||||
number = new NumberPiece
|
||||
{
|
||||
Text = (HitObject.IndexInCurrentCombo + 1).ToString(),
|
||||
},
|
||||
ring = new RingPiece(),
|
||||
flash = new FlashPiece(),
|
||||
explode = new ExplodePiece(),
|
||||
ApproachCircle = new ApproachCircle
|
||||
{
|
||||
Alpha = 0,
|
||||
Scale = new Vector2(4),
|
||||
}
|
||||
},
|
||||
new SkinnableDrawable("Play/osu/hitcircle", _ => new MainCirclePiece(HitObject.IndexInCurrentCombo)),
|
||||
ApproachCircle = new ApproachCircle
|
||||
{
|
||||
Alpha = 0,
|
||||
Scale = new Vector2(4),
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
//may not be so correct
|
||||
Size = circle.DrawSize;
|
||||
Size = hitArea.DrawSize;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -98,13 +80,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
stackHeightBindable.BindTo(HitObject.StackHeightBindable);
|
||||
scaleBindable.BindTo(HitObject.ScaleBindable);
|
||||
|
||||
AccentColour.BindValueChanged(colour =>
|
||||
{
|
||||
explode.Colour = colour.NewValue;
|
||||
glow.Colour = colour.NewValue;
|
||||
circle.Colour = colour.NewValue;
|
||||
ApproachCircle.Colour = colour.NewValue;
|
||||
}, true);
|
||||
AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue, true);
|
||||
}
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
@ -133,14 +109,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
base.UpdateInitialTransforms();
|
||||
|
||||
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt));
|
||||
ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt);
|
||||
ApproachCircle.ScaleTo(1f, HitObject.TimePreempt);
|
||||
ApproachCircle.Expire(true);
|
||||
}
|
||||
|
||||
protected override void UpdateStateTransforms(ArmedState state)
|
||||
{
|
||||
glow.FadeOut(400);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Idle:
|
||||
@ -148,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
Expire(true);
|
||||
|
||||
circle.HitAction = null;
|
||||
hitArea.HitAction = null;
|
||||
|
||||
// override lifetime end as FadeIn may have been changed externally, causing out expiration to be too early.
|
||||
LifetimeEnd = HitObject.StartTime + HitObject.HitWindows.HalfWindowFor(HitResult.Miss);
|
||||
@ -163,29 +137,50 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
case ArmedState.Hit:
|
||||
ApproachCircle.FadeOut(50);
|
||||
|
||||
const double flash_in = 40;
|
||||
flash.FadeTo(0.8f, flash_in)
|
||||
.Then()
|
||||
.FadeOut(100);
|
||||
|
||||
explode.FadeIn(flash_in);
|
||||
|
||||
using (BeginDelayedSequence(flash_in, true))
|
||||
{
|
||||
//after the flash, we can hide some elements that were behind it
|
||||
ring.FadeOut();
|
||||
circle.FadeOut();
|
||||
number.FadeOut();
|
||||
|
||||
this.FadeOut(800);
|
||||
explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad);
|
||||
}
|
||||
|
||||
Expire();
|
||||
// todo: temporary / arbitrary
|
||||
this.Delay(800).Expire();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public Drawable ProxiedLayer => ApproachCircle;
|
||||
|
||||
private class HitArea : Drawable, IKeyBindingHandler<OsuAction>
|
||||
{
|
||||
// IsHovered is used
|
||||
public override bool HandlePositionalInput => true;
|
||||
|
||||
public Func<bool> Hit;
|
||||
|
||||
public OsuAction? HitAction;
|
||||
|
||||
public HitArea()
|
||||
{
|
||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
public bool OnPressed(OsuAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
if (IsHovered && (Hit?.Invoke() ?? false))
|
||||
{
|
||||
HitAction = action;
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(OsuAction action) => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.MathUtils;
|
||||
@ -20,27 +22,40 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
private double animDuration;
|
||||
|
||||
private readonly SkinnableDrawable scaleContainer;
|
||||
|
||||
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
|
||||
: base(repeatPoint)
|
||||
{
|
||||
this.repeatPoint = repeatPoint;
|
||||
this.drawableSlider = drawableSlider;
|
||||
|
||||
Size = new Vector2(45 * repeatPoint.Scale);
|
||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||
|
||||
Blending = BlendingMode.Additive;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
InternalChild = scaleContainer = new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon
|
||||
{
|
||||
new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Icon = FontAwesome.Solid.ChevronRight
|
||||
}, restrictSize: false)
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Icon = FontAwesome.Solid.ChevronRight,
|
||||
Size = new Vector2(0.35f)
|
||||
}, confineMode: ConfineMode.NoScaling)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
}
|
||||
|
||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
|
||||
scaleBindable.BindTo(HitObject.ScaleBindable);
|
||||
}
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (repeatPoint.StartTime <= Time.Current)
|
||||
|
@ -48,10 +48,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
Body = new SnakingSliderBody(s)
|
||||
{
|
||||
PathRadius = s.Scale * OsuHitObject.OBJECT_RADIUS,
|
||||
},
|
||||
Body = new SnakingSliderBody(s),
|
||||
ticks = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
||||
repeatPoints = new Container<DrawableRepeatPoint> { RelativeSizeAxes = Axes.Both },
|
||||
Ball = new SliderBall(s, this)
|
||||
@ -105,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
|
||||
scaleBindable.BindValueChanged(scale =>
|
||||
{
|
||||
Body.PathRadius = scale.NewValue * 64;
|
||||
updatePathRadius();
|
||||
Ball.Scale = new Vector2(scale.NewValue);
|
||||
});
|
||||
|
||||
@ -118,7 +115,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
AccentColour.BindValueChanged(colour =>
|
||||
{
|
||||
Body.AccentColour = colour.NewValue;
|
||||
Ball.AccentColour = colour.NewValue;
|
||||
|
||||
foreach (var drawableHitObject in NestedHitObjects)
|
||||
drawableHitObject.AccentColour.Value = colour.NewValue;
|
||||
@ -157,16 +153,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Body.RecyclePath();
|
||||
}
|
||||
|
||||
private float sliderPathRadius;
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
|
||||
Body.BorderSize = skin.GetValue<SkinConfiguration, float?>(s => s.SliderBorderSize) ?? SliderBody.DEFAULT_BORDER_SIZE;
|
||||
sliderPathRadius = skin.GetValue<SkinConfiguration, float?>(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS;
|
||||
updatePathRadius();
|
||||
|
||||
Body.AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour.Value;
|
||||
Body.BorderColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White;
|
||||
Ball.AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? AccentColour.Value;
|
||||
}
|
||||
|
||||
private void updatePathRadius() => Body.PathRadius = slider.Scale * sliderPathRadius;
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (userTriggered || Time.Current < slider.EndTime)
|
||||
|
@ -1,6 +1,8 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osuTK;
|
||||
@ -16,36 +18,49 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
{
|
||||
public const double ANIM_DURATION = 150;
|
||||
|
||||
private const float default_tick_size = 16;
|
||||
|
||||
public bool Tracking { get; set; }
|
||||
|
||||
public override bool DisplayResult => false;
|
||||
|
||||
private readonly SkinnableDrawable scaleContainer;
|
||||
|
||||
public DrawableSliderTick(SliderTick sliderTick)
|
||||
: base(sliderTick)
|
||||
{
|
||||
Size = new Vector2(16) * sliderTick.Scale;
|
||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
InternalChild = scaleContainer = new SkinnableDrawable("Play/osu/sliderscorepoint", _ => new CircularContainer
|
||||
{
|
||||
new SkinnableDrawable("Play/osu/sliderscorepoint", _ => new Container
|
||||
Masking = true,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(default_tick_size),
|
||||
BorderThickness = default_tick_size / 4,
|
||||
BorderColour = Color4.White,
|
||||
Child = new Box
|
||||
{
|
||||
Masking = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
CornerRadius = Size.X / 2,
|
||||
BorderThickness = 2,
|
||||
BorderColour = Color4.White,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = AccentColour.Value,
|
||||
Alpha = 0.3f,
|
||||
}
|
||||
}, restrictSize: false)
|
||||
Colour = AccentColour.Value,
|
||||
Alpha = 0.3f,
|
||||
}
|
||||
})
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
}
|
||||
|
||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
|
||||
scaleBindable.BindTo(HitObject.ScaleBindable);
|
||||
}
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
if (timeOffset >= 0)
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
@ -24,7 +25,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
Child = new SkinnableSprite("Play/osu/approachcircle");
|
||||
Child = new SkinnableApproachCircle();
|
||||
}
|
||||
|
||||
private class SkinnableApproachCircle : SkinnableSprite
|
||||
{
|
||||
public SkinnableApproachCircle()
|
||||
: base("Play/osu/approachcircle")
|
||||
{
|
||||
}
|
||||
|
||||
protected override Drawable CreateDefault(string name)
|
||||
{
|
||||
var drawable = base.CreateDefault(name);
|
||||
|
||||
// account for the sprite being used for the default approach circle being taken from stable,
|
||||
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.
|
||||
drawable.Scale = new Vector2(128 / 118f);
|
||||
|
||||
return drawable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,17 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class CirclePiece : Container, IKeyBindingHandler<OsuAction>
|
||||
public class CirclePiece : CompositeDrawable
|
||||
{
|
||||
// IsHovered is used
|
||||
public override bool HandlePositionalInput => true;
|
||||
|
||||
public Func<bool> Hit;
|
||||
|
||||
public OsuAction? HitAction;
|
||||
|
||||
public CirclePiece()
|
||||
{
|
||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||
@ -27,28 +20,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
InternalChild = new SkinnableDrawable("Play/osu/hitcircle", _ => new DefaultCirclePiece());
|
||||
}
|
||||
|
||||
public bool OnPressed(OsuAction action)
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
switch (action)
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
case OsuAction.RightButton:
|
||||
if (IsHovered && (Hit?.Invoke() ?? false))
|
||||
{
|
||||
HitAction = action;
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = textures.Get(@"Play/osu/disc"),
|
||||
},
|
||||
new TrianglesPiece
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingMode.Additive,
|
||||
Alpha = 0.5f,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public bool OnReleased(OsuAction action) => false;
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class DefaultCirclePiece : Container
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Sprite
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Texture = textures.Get(@"Play/osu/disc"),
|
||||
},
|
||||
new TrianglesPiece
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Blending = BlendingMode.Additive,
|
||||
Alpha = 0.5f,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class MainCirclePiece : CompositeDrawable
|
||||
{
|
||||
private readonly CirclePiece circle;
|
||||
private readonly RingPiece ring;
|
||||
private readonly FlashPiece flash;
|
||||
private readonly ExplodePiece explode;
|
||||
private readonly NumberPiece number;
|
||||
private readonly GlowPiece glow;
|
||||
|
||||
public MainCirclePiece(int index)
|
||||
{
|
||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
glow = new GlowPiece(),
|
||||
circle = new CirclePiece(),
|
||||
number = new NumberPiece
|
||||
{
|
||||
Text = (index + 1).ToString(),
|
||||
},
|
||||
ring = new RingPiece(),
|
||||
flash = new FlashPiece(),
|
||||
explode = new ExplodePiece(),
|
||||
};
|
||||
}
|
||||
|
||||
private readonly IBindable<ArmedState> state = new Bindable<ArmedState>();
|
||||
|
||||
private readonly Bindable<Color4> accentColour = new Bindable<Color4>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(DrawableHitObject drawableObject)
|
||||
{
|
||||
state.BindTo(drawableObject.State);
|
||||
state.BindValueChanged(updateState, true);
|
||||
|
||||
accentColour.BindTo(drawableObject.AccentColour);
|
||||
accentColour.BindValueChanged(colour =>
|
||||
{
|
||||
explode.Colour = colour.NewValue;
|
||||
glow.Colour = colour.NewValue;
|
||||
circle.Colour = colour.NewValue;
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void updateState(ValueChangedEvent<ArmedState> state)
|
||||
{
|
||||
glow.FadeOut(400);
|
||||
|
||||
switch (state.NewValue)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
const double flash_in = 40;
|
||||
const double flash_out = 100;
|
||||
|
||||
flash.FadeTo(0.8f, flash_in)
|
||||
.Then()
|
||||
.FadeOut(flash_out);
|
||||
|
||||
explode.FadeIn(flash_in);
|
||||
this.ScaleTo(1.5f, 400, Easing.OutQuad);
|
||||
|
||||
using (BeginDelayedSequence(flash_in, true))
|
||||
{
|
||||
//after the flash, we can hide some elements that were behind it
|
||||
ring.FadeOut();
|
||||
circle.FadeOut();
|
||||
number.FadeOut();
|
||||
|
||||
this.FadeOut(800);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
Font = OsuFont.Numeric.With(size: 40),
|
||||
UseFullGlyphHeight = false,
|
||||
}, restrictSize: false)
|
||||
}, confineMode: ConfineMode.NoScaling)
|
||||
{
|
||||
Text = @"1"
|
||||
}
|
||||
|
@ -3,11 +3,13 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
@ -17,88 +19,44 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition
|
||||
{
|
||||
private Color4 accentColour = Color4.Black;
|
||||
|
||||
public Func<OsuAction?> GetInitialHitAction;
|
||||
|
||||
/// <summary>
|
||||
/// The colour that is used for the slider ball.
|
||||
/// </summary>
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
set
|
||||
{
|
||||
accentColour = value;
|
||||
if (drawableBall != null)
|
||||
drawableBall.Colour = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Slider slider;
|
||||
public readonly Drawable FollowCircle;
|
||||
private Drawable drawableBall;
|
||||
private readonly DrawableSlider drawableSlider;
|
||||
|
||||
public SliderBall(Slider slider, DrawableSlider drawableSlider = null)
|
||||
{
|
||||
this.drawableSlider = drawableSlider;
|
||||
this.slider = slider;
|
||||
Masking = true;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Blending = BlendingMode.Additive;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||
|
||||
Children = new[]
|
||||
{
|
||||
FollowCircle = new Container
|
||||
FollowCircle = new FollowCircleContainer
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Width = OsuHitObject.OBJECT_RADIUS * 2,
|
||||
Height = OsuHitObject.OBJECT_RADIUS * 2,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new CircularContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderThickness = 5,
|
||||
BorderColour = Color4.Orange,
|
||||
Blending = BlendingMode.Additive,
|
||||
Child = new Box
|
||||
{
|
||||
Colour = Color4.Orange,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.2f,
|
||||
}
|
||||
}),
|
||||
Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new DefaultFollowCircle()),
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Masking = true,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Alpha = 1,
|
||||
Child = new Container
|
||||
{
|
||||
Width = OsuHitObject.OBJECT_RADIUS * 2,
|
||||
Height = OsuHitObject.OBJECT_RADIUS * 2,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
// TODO: support skin filename animation (sliderb0, sliderb1...)
|
||||
Child = new SkinnableDrawable("Play/osu/sliderb", _ => new CircularContainer
|
||||
{
|
||||
Masking = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
BorderThickness = 10,
|
||||
BorderColour = Color4.White,
|
||||
Alpha = 1,
|
||||
Child = drawableBall = new Box
|
||||
{
|
||||
Colour = AccentColour,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.4f,
|
||||
}
|
||||
}),
|
||||
Child = new SkinnableDrawable("Play/osu/sliderball", _ => new DefaultSliderBall()),
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -191,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
// in valid time range
|
||||
Time.Current >= slider.StartTime && Time.Current < slider.EndTime &&
|
||||
// in valid position range
|
||||
lastScreenSpaceMousePosition.HasValue && base.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
|
||||
lastScreenSpaceMousePosition.HasValue && FollowCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
|
||||
// valid action
|
||||
(actions?.Any(isValidTrackingAction) ?? false);
|
||||
}
|
||||
@ -214,5 +172,62 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
Position = slider.CurvePositionAt(completionProgress);
|
||||
}
|
||||
|
||||
private class FollowCircleContainer : Container
|
||||
{
|
||||
public override bool HandlePositionalInput => true;
|
||||
}
|
||||
|
||||
public class DefaultFollowCircle : CompositeDrawable
|
||||
{
|
||||
public DefaultFollowCircle()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
InternalChild = new CircularContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderThickness = 5,
|
||||
BorderColour = Color4.Orange,
|
||||
Blending = BlendingMode.Additive,
|
||||
Child = new Box
|
||||
{
|
||||
Colour = Color4.Orange,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0.2f,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class DefaultSliderBall : CompositeDrawable
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(DrawableHitObject drawableObject, ISkinSource skin)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
float radius = skin.GetValue<SkinConfiguration, float?>(s => s.SliderPathRadius) ?? OsuHitObject.OBJECT_RADIUS;
|
||||
|
||||
InternalChild = new CircularContainer
|
||||
{
|
||||
Masking = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(radius / OsuHitObject.OBJECT_RADIUS),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
BorderThickness = 10,
|
||||
BorderColour = Color4.White,
|
||||
Alpha = 1,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
Alpha = 0.4f,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Lines;
|
||||
@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
protected Path Path => path;
|
||||
|
||||
public float PathRadius
|
||||
public virtual float PathRadius
|
||||
{
|
||||
get => path.PathRadius;
|
||||
set => path.PathRadius = value;
|
||||
@ -75,22 +76,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
protected SliderBody()
|
||||
{
|
||||
InternalChild = path = new SliderPath();
|
||||
RecyclePath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialises a new <see cref="SliderPath"/>, releasing all resources retained by the old one.
|
||||
/// </summary>
|
||||
public void RecyclePath()
|
||||
public virtual void RecyclePath()
|
||||
{
|
||||
InternalChild = path = new SliderPath
|
||||
{
|
||||
Position = path.Position,
|
||||
PathRadius = path.PathRadius,
|
||||
AccentColour = path.AccentColour,
|
||||
BorderColour = path.BorderColour,
|
||||
BorderSize = path.BorderSize,
|
||||
Vertices = path.Vertices
|
||||
Position = path?.Position ?? Vector2.Zero,
|
||||
PathRadius = path?.PathRadius ?? 10,
|
||||
AccentColour = path?.AccentColour ?? Color4.White,
|
||||
BorderColour = path?.BorderColour ?? Color4.White,
|
||||
BorderSize = path?.BorderSize ?? DEFAULT_BORDER_SIZE,
|
||||
Vertices = path?.Vertices ?? Array.Empty<Vector2>()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osuTK;
|
||||
|
||||
@ -23,6 +24,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
public double? SnakedStart { get; private set; }
|
||||
public double? SnakedEnd { get; private set; }
|
||||
|
||||
public override float PathRadius
|
||||
{
|
||||
get => base.PathRadius;
|
||||
set
|
||||
{
|
||||
if (base.PathRadius == value)
|
||||
return;
|
||||
|
||||
base.PathRadius = value;
|
||||
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public override Vector2 PathOffset => snakedPathOffset;
|
||||
|
||||
/// <summary>
|
||||
@ -78,9 +93,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
slider.Path.GetPathToProgress(CurrentCurve, 0, 1);
|
||||
SetVertices(CurrentCurve);
|
||||
|
||||
// The body is sized to the full path size to avoid excessive autosize computations
|
||||
// Force the body to be the final path size to avoid excessive autosize computations
|
||||
Path.AutoSizeAxes = Axes.Both;
|
||||
Size = Path.Size;
|
||||
|
||||
updatePathSize();
|
||||
|
||||
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
|
||||
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
|
||||
|
||||
@ -93,6 +111,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
setRange(lastSnakedStart, lastSnakedEnd);
|
||||
}
|
||||
|
||||
public override void RecyclePath()
|
||||
{
|
||||
base.RecyclePath();
|
||||
updatePathSize();
|
||||
}
|
||||
|
||||
private void updatePathSize()
|
||||
{
|
||||
// Force the path to its final size to avoid excessive framebuffer resizes
|
||||
Path.AutoSizeAxes = Axes.None;
|
||||
Path.Size = Size;
|
||||
}
|
||||
|
||||
private void setRange(double p0, double p1)
|
||||
{
|
||||
if (p0 > p1)
|
||||
|
@ -14,7 +14,6 @@ using osu.Game.Overlays.Settings;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Osu.Edit;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
@ -134,8 +133,9 @@ namespace osu.Game.Rulesets.Osu
|
||||
{
|
||||
new OsuModTransform(),
|
||||
new OsuModWiggle(),
|
||||
new OsuModSpinIn(),
|
||||
new MultiMod(new OsuModGrow(), new OsuModDeflate()),
|
||||
new MultiMod(new ModWindUp<OsuHitObject>(), new ModWindDown<OsuHitObject>()),
|
||||
new MultiMod(new ModWindUp(), new ModWindDown()),
|
||||
};
|
||||
|
||||
case ModType.System:
|
||||
|
@ -0,0 +1,222 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 369,
|
||||
"Objects": [{
|
||||
"StartTime": 369,
|
||||
"EndTime": 369,
|
||||
"X": 177,
|
||||
"Y": 191
|
||||
},
|
||||
{
|
||||
"StartTime": 450,
|
||||
"EndTime": 450,
|
||||
"X": 216.539276,
|
||||
"Y": 191.192871
|
||||
},
|
||||
{
|
||||
"StartTime": 532,
|
||||
"EndTime": 532,
|
||||
"X": 256.5667,
|
||||
"Y": 191.388138
|
||||
},
|
||||
{
|
||||
"StartTime": 614,
|
||||
"EndTime": 614,
|
||||
"X": 296.594116,
|
||||
"Y": 191.583389
|
||||
},
|
||||
{
|
||||
"StartTime": 696,
|
||||
"EndTime": 696,
|
||||
"X": 336.621521,
|
||||
"Y": 191.778641
|
||||
},
|
||||
{
|
||||
"StartTime": 778,
|
||||
"EndTime": 778,
|
||||
"X": 376.648926,
|
||||
"Y": 191.9739
|
||||
},
|
||||
{
|
||||
"StartTime": 860,
|
||||
"EndTime": 860,
|
||||
"X": 337.318878,
|
||||
"Y": 191.782043
|
||||
},
|
||||
{
|
||||
"StartTime": 942,
|
||||
"EndTime": 942,
|
||||
"X": 297.291443,
|
||||
"Y": 191.586792
|
||||
},
|
||||
{
|
||||
"StartTime": 1024,
|
||||
"EndTime": 1024,
|
||||
"X": 257.264038,
|
||||
"Y": 191.391541
|
||||
},
|
||||
{
|
||||
"StartTime": 1106,
|
||||
"EndTime": 1106,
|
||||
"X": 217.2366,
|
||||
"Y": 191.196274
|
||||
},
|
||||
{
|
||||
"StartTime": 1188,
|
||||
"EndTime": 1188,
|
||||
"X": 177.209213,
|
||||
"Y": 191.001022
|
||||
},
|
||||
{
|
||||
"StartTime": 1270,
|
||||
"EndTime": 1270,
|
||||
"X": 216.818192,
|
||||
"Y": 191.194229
|
||||
},
|
||||
{
|
||||
"StartTime": 1352,
|
||||
"EndTime": 1352,
|
||||
"X": 256.8456,
|
||||
"Y": 191.3895
|
||||
},
|
||||
{
|
||||
"StartTime": 1434,
|
||||
"EndTime": 1434,
|
||||
"X": 296.873047,
|
||||
"Y": 191.584747
|
||||
},
|
||||
{
|
||||
"StartTime": 1516,
|
||||
"EndTime": 1516,
|
||||
"X": 336.900452,
|
||||
"Y": 191.78
|
||||
},
|
||||
{
|
||||
"StartTime": 1598,
|
||||
"EndTime": 1598,
|
||||
"X": 376.927917,
|
||||
"Y": 191.975266
|
||||
},
|
||||
{
|
||||
"StartTime": 1680,
|
||||
"EndTime": 1680,
|
||||
"X": 337.039948,
|
||||
"Y": 191.780685
|
||||
},
|
||||
{
|
||||
"StartTime": 1762,
|
||||
"EndTime": 1762,
|
||||
"X": 297.0125,
|
||||
"Y": 191.585434
|
||||
},
|
||||
{
|
||||
"StartTime": 1844,
|
||||
"EndTime": 1844,
|
||||
"X": 256.9851,
|
||||
"Y": 191.390167
|
||||
},
|
||||
{
|
||||
"StartTime": 1926,
|
||||
"EndTime": 1926,
|
||||
"X": 216.957672,
|
||||
"Y": 191.194916
|
||||
},
|
||||
{
|
||||
"StartTime": 2008,
|
||||
"EndTime": 2008,
|
||||
"X": 177.069717,
|
||||
"Y": 191.000336
|
||||
},
|
||||
{
|
||||
"StartTime": 2090,
|
||||
"EndTime": 2090,
|
||||
"X": 217.097137,
|
||||
"Y": 191.1956
|
||||
},
|
||||
{
|
||||
"StartTime": 2172,
|
||||
"EndTime": 2172,
|
||||
"X": 257.124573,
|
||||
"Y": 191.390854
|
||||
},
|
||||
{
|
||||
"StartTime": 2254,
|
||||
"EndTime": 2254,
|
||||
"X": 297.152,
|
||||
"Y": 191.5861
|
||||
},
|
||||
{
|
||||
"StartTime": 2336,
|
||||
"EndTime": 2336,
|
||||
"X": 337.179443,
|
||||
"Y": 191.781372
|
||||
},
|
||||
{
|
||||
"StartTime": 2418,
|
||||
"EndTime": 2418,
|
||||
"X": 376.7884,
|
||||
"Y": 191.974579
|
||||
},
|
||||
{
|
||||
"StartTime": 2500,
|
||||
"EndTime": 2500,
|
||||
"X": 336.760956,
|
||||
"Y": 191.779327
|
||||
},
|
||||
{
|
||||
"StartTime": 2582,
|
||||
"EndTime": 2582,
|
||||
"X": 296.733643,
|
||||
"Y": 191.584076
|
||||
},
|
||||
{
|
||||
"StartTime": 2664,
|
||||
"EndTime": 2664,
|
||||
"X": 256.7062,
|
||||
"Y": 191.388809
|
||||
},
|
||||
{
|
||||
"StartTime": 2746,
|
||||
"EndTime": 2746,
|
||||
"X": 216.678772,
|
||||
"Y": 191.193558
|
||||
},
|
||||
{
|
||||
"StartTime": 2828,
|
||||
"EndTime": 2828,
|
||||
"X": 177.348663,
|
||||
"Y": 191.0017
|
||||
},
|
||||
{
|
||||
"StartTime": 2909,
|
||||
"EndTime": 2909,
|
||||
"X": 216.887909,
|
||||
"Y": 191.19458
|
||||
},
|
||||
{
|
||||
"StartTime": 2991,
|
||||
"EndTime": 2991,
|
||||
"X": 256.915344,
|
||||
"Y": 191.389832
|
||||
},
|
||||
{
|
||||
"StartTime": 3073,
|
||||
"EndTime": 3073,
|
||||
"X": 296.942749,
|
||||
"Y": 191.585083
|
||||
},
|
||||
{
|
||||
"StartTime": 3155,
|
||||
"EndTime": 3155,
|
||||
"X": 336.970184,
|
||||
"Y": 191.78035
|
||||
},
|
||||
{
|
||||
"StartTime": 3201,
|
||||
"EndTime": 3201,
|
||||
"X": 376.99762,
|
||||
"Y": 191.9756
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.4
|
||||
Mode: 0
|
||||
|
||||
[Difficulty]
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:4
|
||||
|
||||
[TimingPoints]
|
||||
369,327.868852459016,4,2,2,32,1,0
|
||||
|
||||
[HitObjects]
|
||||
177,191,369,6,0,L|382:192,7,200
|
@ -0,0 +1,348 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 369,
|
||||
"Objects": [{
|
||||
"StartTime": 369,
|
||||
"EndTime": 369,
|
||||
"X": 127,
|
||||
"Y": 194
|
||||
},
|
||||
{
|
||||
"StartTime": 450,
|
||||
"EndTime": 450,
|
||||
"X": 166.53389,
|
||||
"Y": 193.8691
|
||||
},
|
||||
{
|
||||
"StartTime": 532,
|
||||
"EndTime": 532,
|
||||
"X": 206.555847,
|
||||
"Y": 193.736572
|
||||
},
|
||||
{
|
||||
"StartTime": 614,
|
||||
"EndTime": 614,
|
||||
"X": 246.57782,
|
||||
"Y": 193.60405
|
||||
},
|
||||
{
|
||||
"StartTime": 696,
|
||||
"EndTime": 696,
|
||||
"X": 286.5998,
|
||||
"Y": 193.471527
|
||||
},
|
||||
{
|
||||
"StartTime": 778,
|
||||
"EndTime": 778,
|
||||
"X": 326.621765,
|
||||
"Y": 193.339
|
||||
},
|
||||
{
|
||||
"StartTime": 860,
|
||||
"EndTime": 860,
|
||||
"X": 366.6437,
|
||||
"Y": 193.206482
|
||||
},
|
||||
{
|
||||
"StartTime": 942,
|
||||
"EndTime": 942,
|
||||
"X": 406.66568,
|
||||
"Y": 193.073959
|
||||
},
|
||||
{
|
||||
"StartTime": 970,
|
||||
"EndTime": 970,
|
||||
"X": 420.331726,
|
||||
"Y": 193.0287
|
||||
},
|
||||
{
|
||||
"StartTime": 997,
|
||||
"EndTime": 997,
|
||||
"X": 407.153748,
|
||||
"Y": 193.072342
|
||||
},
|
||||
{
|
||||
"StartTime": 1079,
|
||||
"EndTime": 1079,
|
||||
"X": 367.131775,
|
||||
"Y": 193.204865
|
||||
},
|
||||
{
|
||||
"StartTime": 1161,
|
||||
"EndTime": 1161,
|
||||
"X": 327.1098,
|
||||
"Y": 193.337387
|
||||
},
|
||||
{
|
||||
"StartTime": 1243,
|
||||
"EndTime": 1243,
|
||||
"X": 287.08783,
|
||||
"Y": 193.46991
|
||||
},
|
||||
{
|
||||
"StartTime": 1325,
|
||||
"EndTime": 1325,
|
||||
"X": 247.0659,
|
||||
"Y": 193.602432
|
||||
},
|
||||
{
|
||||
"StartTime": 1407,
|
||||
"EndTime": 1407,
|
||||
"X": 207.043915,
|
||||
"Y": 193.734955
|
||||
},
|
||||
{
|
||||
"StartTime": 1489,
|
||||
"EndTime": 1489,
|
||||
"X": 167.021988,
|
||||
"Y": 193.867477
|
||||
},
|
||||
{
|
||||
"StartTime": 1571,
|
||||
"EndTime": 1571,
|
||||
"X": 127,
|
||||
"Y": 194
|
||||
},
|
||||
{
|
||||
"StartTime": 1653,
|
||||
"EndTime": 1653,
|
||||
"X": 167.021988,
|
||||
"Y": 193.867477
|
||||
},
|
||||
{
|
||||
"StartTime": 1735,
|
||||
"EndTime": 1735,
|
||||
"X": 207.043976,
|
||||
"Y": 193.734955
|
||||
},
|
||||
{
|
||||
"StartTime": 1817,
|
||||
"EndTime": 1817,
|
||||
"X": 247.065887,
|
||||
"Y": 193.602432
|
||||
},
|
||||
{
|
||||
"StartTime": 1899,
|
||||
"EndTime": 1899,
|
||||
"X": 287.08783,
|
||||
"Y": 193.46991
|
||||
},
|
||||
{
|
||||
"StartTime": 1981,
|
||||
"EndTime": 1981,
|
||||
"X": 327.1098,
|
||||
"Y": 193.337387
|
||||
},
|
||||
{
|
||||
"StartTime": 2062,
|
||||
"EndTime": 2062,
|
||||
"X": 366.643738,
|
||||
"Y": 193.206482
|
||||
},
|
||||
{
|
||||
"StartTime": 2144,
|
||||
"EndTime": 2144,
|
||||
"X": 406.665649,
|
||||
"Y": 193.073959
|
||||
},
|
||||
{
|
||||
"StartTime": 2172,
|
||||
"EndTime": 2172,
|
||||
"X": 420.331726,
|
||||
"Y": 193.0287
|
||||
},
|
||||
{
|
||||
"StartTime": 2199,
|
||||
"EndTime": 2199,
|
||||
"X": 407.153748,
|
||||
"Y": 193.072342
|
||||
},
|
||||
{
|
||||
"StartTime": 2281,
|
||||
"EndTime": 2281,
|
||||
"X": 367.1318,
|
||||
"Y": 193.204865
|
||||
},
|
||||
{
|
||||
"StartTime": 2363,
|
||||
"EndTime": 2363,
|
||||
"X": 327.1098,
|
||||
"Y": 193.337387
|
||||
},
|
||||
{
|
||||
"StartTime": 2445,
|
||||
"EndTime": 2445,
|
||||
"X": 287.08783,
|
||||
"Y": 193.46991
|
||||
},
|
||||
{
|
||||
"StartTime": 2527,
|
||||
"EndTime": 2527,
|
||||
"X": 247.065887,
|
||||
"Y": 193.602432
|
||||
},
|
||||
{
|
||||
"StartTime": 2609,
|
||||
"EndTime": 2609,
|
||||
"X": 207.043976,
|
||||
"Y": 193.734955
|
||||
},
|
||||
{
|
||||
"StartTime": 2691,
|
||||
"EndTime": 2691,
|
||||
"X": 167.021988,
|
||||
"Y": 193.867477
|
||||
},
|
||||
{
|
||||
"StartTime": 2773,
|
||||
"EndTime": 2773,
|
||||
"X": 127,
|
||||
"Y": 194
|
||||
},
|
||||
{
|
||||
"StartTime": 2855,
|
||||
"EndTime": 2855,
|
||||
"X": 167.021988,
|
||||
"Y": 193.867477
|
||||
},
|
||||
{
|
||||
"StartTime": 2937,
|
||||
"EndTime": 2937,
|
||||
"X": 207.043976,
|
||||
"Y": 193.734955
|
||||
},
|
||||
{
|
||||
"StartTime": 3019,
|
||||
"EndTime": 3019,
|
||||
"X": 247.065948,
|
||||
"Y": 193.602432
|
||||
},
|
||||
{
|
||||
"StartTime": 3101,
|
||||
"EndTime": 3101,
|
||||
"X": 287.087952,
|
||||
"Y": 193.46991
|
||||
},
|
||||
{
|
||||
"StartTime": 3183,
|
||||
"EndTime": 3183,
|
||||
"X": 327.109772,
|
||||
"Y": 193.337387
|
||||
},
|
||||
{
|
||||
"StartTime": 3265,
|
||||
"EndTime": 3265,
|
||||
"X": 367.131775,
|
||||
"Y": 193.204865
|
||||
},
|
||||
{
|
||||
"StartTime": 3347,
|
||||
"EndTime": 3347,
|
||||
"X": 407.153748,
|
||||
"Y": 193.072342
|
||||
},
|
||||
{
|
||||
"StartTime": 3374,
|
||||
"EndTime": 3374,
|
||||
"X": 420.331726,
|
||||
"Y": 193.0287
|
||||
},
|
||||
{
|
||||
"StartTime": 3401,
|
||||
"EndTime": 3401,
|
||||
"X": 407.153748,
|
||||
"Y": 193.072342
|
||||
},
|
||||
{
|
||||
"StartTime": 3483,
|
||||
"EndTime": 3483,
|
||||
"X": 367.131775,
|
||||
"Y": 193.204865
|
||||
},
|
||||
{
|
||||
"StartTime": 3565,
|
||||
"EndTime": 3565,
|
||||
"X": 327.109772,
|
||||
"Y": 193.337387
|
||||
},
|
||||
{
|
||||
"StartTime": 3647,
|
||||
"EndTime": 3647,
|
||||
"X": 287.087952,
|
||||
"Y": 193.46991
|
||||
},
|
||||
{
|
||||
"StartTime": 3729,
|
||||
"EndTime": 3729,
|
||||
"X": 247.065948,
|
||||
"Y": 193.602432
|
||||
},
|
||||
{
|
||||
"StartTime": 3811,
|
||||
"EndTime": 3811,
|
||||
"X": 207.043976,
|
||||
"Y": 193.734955
|
||||
},
|
||||
{
|
||||
"StartTime": 3893,
|
||||
"EndTime": 3893,
|
||||
"X": 167.021988,
|
||||
"Y": 193.867477
|
||||
},
|
||||
{
|
||||
"StartTime": 3975,
|
||||
"EndTime": 3975,
|
||||
"X": 127,
|
||||
"Y": 194
|
||||
},
|
||||
{
|
||||
"StartTime": 4057,
|
||||
"EndTime": 4057,
|
||||
"X": 167.021988,
|
||||
"Y": 193.867477
|
||||
},
|
||||
{
|
||||
"StartTime": 4139,
|
||||
"EndTime": 4139,
|
||||
"X": 207.043976,
|
||||
"Y": 193.734955
|
||||
},
|
||||
{
|
||||
"StartTime": 4221,
|
||||
"EndTime": 4221,
|
||||
"X": 247.065948,
|
||||
"Y": 193.602432
|
||||
},
|
||||
{
|
||||
"StartTime": 4303,
|
||||
"EndTime": 4303,
|
||||
"X": 287.087952,
|
||||
"Y": 193.46991
|
||||
},
|
||||
{
|
||||
"StartTime": 4385,
|
||||
"EndTime": 4385,
|
||||
"X": 327.109772,
|
||||
"Y": 193.337387
|
||||
},
|
||||
{
|
||||
"StartTime": 4467,
|
||||
"EndTime": 4467,
|
||||
"X": 367.131775,
|
||||
"Y": 193.204865
|
||||
},
|
||||
{
|
||||
"StartTime": 4540,
|
||||
"EndTime": 4540,
|
||||
"X": 420.331726,
|
||||
"Y": 193.0287
|
||||
},
|
||||
{
|
||||
"StartTime": 4549,
|
||||
"EndTime": 4549,
|
||||
"X": 407.153748,
|
||||
"Y": 193.072342
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.4
|
||||
Mode: 0
|
||||
|
||||
[Difficulty]
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:4
|
||||
|
||||
[TimingPoints]
|
||||
369,327.868852459016,4,2,2,32,1,0
|
||||
|
||||
[HitObjects]
|
||||
// A slider with an un-even amount of ticks
|
||||
127,194,369,6,0,L|429:193,7,293.333333333333
|
@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
{
|
||||
public class OsuCursor : SkinReloadableDrawable
|
||||
{
|
||||
private const float size = 28;
|
||||
|
||||
private bool cursorExpand;
|
||||
|
||||
private Bindable<double> cursorScale;
|
||||
@ -30,7 +32,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
public OsuCursor()
|
||||
{
|
||||
Origin = Anchor.Centre;
|
||||
Size = new Vector2(28);
|
||||
|
||||
Size = new Vector2(size);
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
@ -46,66 +49,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Child = scaleTarget = new SkinnableDrawable("cursor", _ => new CircularContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderThickness = Size.X / 6,
|
||||
BorderColour = Color4.White,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Pink.Opacity(0.5f),
|
||||
Radius = 5,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderThickness = Size.X / 3,
|
||||
BorderColour = Color4.White.Opacity(0.5f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(0.1f),
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}, restrictSize: false)
|
||||
Child = scaleTarget = new SkinnableDrawable("Play/osu/cursor", _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling)
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
|
||||
@ -145,5 +92,76 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
}
|
||||
|
||||
public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad);
|
||||
|
||||
private class DefaultCursor : CompositeDrawable
|
||||
{
|
||||
public DefaultCursor()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new CircularContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderThickness = size / 6,
|
||||
BorderColour = Color4.White,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
Colour = Color4.Pink.Opacity(0.5f),
|
||||
Radius = 5,
|
||||
},
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
BorderThickness = size / 3,
|
||||
BorderColour = Color4.White.Opacity(0.5f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
AlwaysPresent = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
new CircularContainer
|
||||
{
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Scale = new Vector2(0.1f),
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.White,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
|
||||
Size = new Vector2(0.75f);
|
||||
// Calculated from osu!stable as 512 (default gamefield size) / 640 (default window size)
|
||||
Size = new Vector2(0.8f);
|
||||
|
||||
InternalChild = new Container
|
||||
{
|
||||
|
@ -20,10 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
[NonParallelizable]
|
||||
[TestCase("basic")]
|
||||
[TestCase("slider-generating-drumroll")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
}
|
||||
public void Test(string name) => base.Test(name);
|
||||
|
||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
|
@ -12,7 +12,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Replays.Types;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Replays;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
@ -107,7 +106,7 @@ namespace osu.Game.Rulesets.Taiko
|
||||
case ModType.Fun:
|
||||
return new Mod[]
|
||||
{
|
||||
new MultiMod(new ModWindUp<TaikoHitObject>(), new ModWindDown<TaikoHitObject>())
|
||||
new MultiMod(new ModWindUp(), new ModWindDown())
|
||||
};
|
||||
|
||||
default:
|
||||
|
116
osu.Game.Tests/Beatmaps/SliderEventGenerationTest.cs
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
[TestFixture]
|
||||
public class SliderEventGenerationTest
|
||||
{
|
||||
private const double start_time = 0;
|
||||
private const double span_duration = 1000;
|
||||
|
||||
[Test]
|
||||
public void TestSingleSpan()
|
||||
{
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, null).ToArray();
|
||||
|
||||
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
|
||||
Assert.That(events[0].Time, Is.EqualTo(start_time));
|
||||
|
||||
Assert.That(events[1].Type, Is.EqualTo(SliderEventType.Tick));
|
||||
Assert.That(events[1].Time, Is.EqualTo(span_duration / 2));
|
||||
|
||||
Assert.That(events[3].Type, Is.EqualTo(SliderEventType.Tail));
|
||||
Assert.That(events[3].Time, Is.EqualTo(span_duration));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRepeat()
|
||||
{
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 2, null).ToArray();
|
||||
|
||||
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
|
||||
Assert.That(events[0].Time, Is.EqualTo(start_time));
|
||||
|
||||
Assert.That(events[1].Type, Is.EqualTo(SliderEventType.Tick));
|
||||
Assert.That(events[1].Time, Is.EqualTo(span_duration / 2));
|
||||
|
||||
Assert.That(events[2].Type, Is.EqualTo(SliderEventType.Repeat));
|
||||
Assert.That(events[2].Time, Is.EqualTo(span_duration));
|
||||
|
||||
Assert.That(events[3].Type, Is.EqualTo(SliderEventType.Tick));
|
||||
Assert.That(events[3].Time, Is.EqualTo(span_duration + span_duration / 2));
|
||||
|
||||
Assert.That(events[5].Type, Is.EqualTo(SliderEventType.Tail));
|
||||
Assert.That(events[5].Time, Is.EqualTo(2 * span_duration));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNonEvenTicks()
|
||||
{
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, 300, span_duration, 2, null).ToArray();
|
||||
|
||||
Assert.That(events[0].Type, Is.EqualTo(SliderEventType.Head));
|
||||
Assert.That(events[0].Time, Is.EqualTo(start_time));
|
||||
|
||||
Assert.That(events[1].Type, Is.EqualTo(SliderEventType.Tick));
|
||||
Assert.That(events[1].Time, Is.EqualTo(300));
|
||||
|
||||
Assert.That(events[2].Type, Is.EqualTo(SliderEventType.Tick));
|
||||
Assert.That(events[2].Time, Is.EqualTo(600));
|
||||
|
||||
Assert.That(events[3].Type, Is.EqualTo(SliderEventType.Tick));
|
||||
Assert.That(events[3].Time, Is.EqualTo(900));
|
||||
|
||||
Assert.That(events[4].Type, Is.EqualTo(SliderEventType.Repeat));
|
||||
Assert.That(events[4].Time, Is.EqualTo(span_duration));
|
||||
|
||||
Assert.That(events[5].Type, Is.EqualTo(SliderEventType.Tick));
|
||||
Assert.That(events[5].Time, Is.EqualTo(1100));
|
||||
|
||||
Assert.That(events[6].Type, Is.EqualTo(SliderEventType.Tick));
|
||||
Assert.That(events[6].Time, Is.EqualTo(1400));
|
||||
|
||||
Assert.That(events[7].Type, Is.EqualTo(SliderEventType.Tick));
|
||||
Assert.That(events[7].Time, Is.EqualTo(1700));
|
||||
|
||||
Assert.That(events[9].Type, Is.EqualTo(SliderEventType.Tail));
|
||||
Assert.That(events[9].Time, Is.EqualTo(2 * span_duration));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLegacyLastTickOffset()
|
||||
{
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, 1, span_duration / 2, span_duration, 1, 100).ToArray();
|
||||
|
||||
Assert.That(events[2].Type, Is.EqualTo(SliderEventType.LegacyLastTick));
|
||||
Assert.That(events[2].Time, Is.EqualTo(900));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMinimumTickDistance()
|
||||
{
|
||||
const double velocity = 5;
|
||||
const double min_distance = velocity * 10;
|
||||
|
||||
var events = SliderEventGenerator.Generate(start_time, span_duration, velocity, velocity, span_duration, 2, 0).ToArray();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
int tickIndex = -1;
|
||||
|
||||
while (++tickIndex < events.Length)
|
||||
{
|
||||
if (events[tickIndex].Type != SliderEventType.Tick)
|
||||
continue;
|
||||
|
||||
Assert.That(events[tickIndex].Time, Is.LessThan(span_duration - min_distance).Or.GreaterThan(span_duration + min_distance));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,6 @@ using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@ -51,26 +50,14 @@ namespace osu.Game.Tests.Visual.Background
|
||||
private DummySongSelect songSelect;
|
||||
private TestPlayerLoader playerLoader;
|
||||
private TestPlayer player;
|
||||
private DatabaseContextFactory factory;
|
||||
private BeatmapManager manager;
|
||||
private RulesetStore rulesets;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
{
|
||||
factory = new DatabaseContextFactory(LocalStorage);
|
||||
factory.ResetDatabase();
|
||||
|
||||
using (var usage = factory.Get())
|
||||
usage.Migrate();
|
||||
|
||||
factory.ResetDatabase();
|
||||
|
||||
using (var usage = factory.Get())
|
||||
usage.Migrate();
|
||||
|
||||
Dependencies.Cache(rulesets = new RulesetStore(factory));
|
||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, Beatmap.Default));
|
||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
|
||||
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
||||
|
||||
manager.Import(TestResources.GetTestBeatmapForImport()).Wait();
|
||||
|
@ -1,145 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinReloadable : OsuTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestInitialLoad()
|
||||
{
|
||||
var secondarySource = new SecondarySource();
|
||||
SkinConsumer consumer = null;
|
||||
|
||||
AddStep("setup layout", () =>
|
||||
{
|
||||
Child = new SkinSourceContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new LocalSkinOverrideContainer(secondarySource)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
|
||||
AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOverride()
|
||||
{
|
||||
var secondarySource = new SecondarySource();
|
||||
|
||||
SkinConsumer consumer = null;
|
||||
Container target = null;
|
||||
|
||||
AddStep("setup layout", () =>
|
||||
{
|
||||
Child = new SkinSourceContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = target = new LocalSkinOverrideContainer(secondarySource)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)));
|
||||
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
|
||||
AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
|
||||
}
|
||||
|
||||
private class NamedBox : Container
|
||||
{
|
||||
public NamedBox(string name)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.Default.With(size: 40),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = name
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class SkinConsumer : SkinnableDrawable
|
||||
{
|
||||
public new Drawable Drawable => base.Drawable;
|
||||
public int SkinChangedCount { get; private set; }
|
||||
|
||||
public SkinConsumer(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, bool restrictSize = true)
|
||||
: base(name, defaultImplementation, allowFallback, restrictSize)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
SkinChangedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
private class BaseSourceBox : NamedBox
|
||||
{
|
||||
public BaseSourceBox()
|
||||
: base("Base Source")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class SecondarySourceBox : NamedBox
|
||||
{
|
||||
public SecondarySourceBox()
|
||||
: base("Secondary Source")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class SecondarySource : ISkin
|
||||
{
|
||||
public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox();
|
||||
|
||||
public Texture GetTexture(string componentName) => throw new NotImplementedException();
|
||||
|
||||
public SampleChannel GetSample(string sampleName) => throw new NotImplementedException();
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class SkinSourceContainer : Container, ISkin
|
||||
{
|
||||
public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox();
|
||||
|
||||
public Texture GetTexture(string componentName) => throw new NotImplementedException();
|
||||
|
||||
public SampleChannel GetSample(string sampleName) => throw new NotImplementedException();
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
283
osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableDrawable.cs
Normal file
@ -0,0 +1,283 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinnableDrawable : OsuTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestConfineScaleDown()
|
||||
{
|
||||
FillFlowContainer<ExposedSkinnableDrawable> fill = null;
|
||||
|
||||
AddStep("setup layout larger source", () =>
|
||||
{
|
||||
Child = new LocalSkinOverrideContainer(new SizedSource(50))
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = fill = new FillFlowContainer<ExposedSkinnableDrawable>
|
||||
{
|
||||
Size = new Vector2(30),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Spacing = new Vector2(10),
|
||||
Children = new[]
|
||||
{
|
||||
new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.ScaleToFit),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling)
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 30, 50 }));
|
||||
AddStep("adjust scale", () => fill.Scale = new Vector2(2));
|
||||
AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 30, 30, 30, 50 }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConfineScaleUp()
|
||||
{
|
||||
FillFlowContainer<ExposedSkinnableDrawable> fill = null;
|
||||
|
||||
AddStep("setup layout larger source", () =>
|
||||
{
|
||||
Child = new LocalSkinOverrideContainer(new SizedSource(30))
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = fill = new FillFlowContainer<ExposedSkinnableDrawable>
|
||||
{
|
||||
Size = new Vector2(50),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Spacing = new Vector2(10),
|
||||
Children = new[]
|
||||
{
|
||||
new ExposedSkinnableDrawable("default", _ => new DefaultBox(), _ => true),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.ScaleToFit),
|
||||
new ExposedSkinnableDrawable("available", _ => new DefaultBox(), _ => true, ConfineMode.NoScaling)
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
AddAssert("check sizes", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 30, 50, 30 }));
|
||||
AddStep("adjust scale", () => fill.Scale = new Vector2(2));
|
||||
AddAssert("check sizes unchanged by scale", () => fill.Children.Select(c => c.Drawable.DrawWidth).SequenceEqual(new float[] { 50, 30, 50, 30 }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInitialLoad()
|
||||
{
|
||||
var secondarySource = new SecondarySource();
|
||||
SkinConsumer consumer = null;
|
||||
|
||||
AddStep("setup layout", () =>
|
||||
{
|
||||
Child = new SkinSourceContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = new LocalSkinOverrideContainer(secondarySource)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
|
||||
AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOverride()
|
||||
{
|
||||
var secondarySource = new SecondarySource();
|
||||
|
||||
SkinConsumer consumer = null;
|
||||
Container target = null;
|
||||
|
||||
AddStep("setup layout", () =>
|
||||
{
|
||||
Child = new SkinSourceContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = target = new LocalSkinOverrideContainer(secondarySource)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("add permissive", () => target.Add(consumer = new SkinConsumer("test", name => new NamedBox("Default Implementation"), source => true)));
|
||||
AddAssert("consumer using override source", () => consumer.Drawable is SecondarySourceBox);
|
||||
AddAssert("skinchanged only called once", () => consumer.SkinChangedCount == 1);
|
||||
}
|
||||
|
||||
private class ExposedSkinnableDrawable : SkinnableDrawable
|
||||
{
|
||||
public new Drawable Drawable => base.Drawable;
|
||||
|
||||
public ExposedSkinnableDrawable(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
|
||||
: base(name, defaultImplementation, allowFallback, confineMode)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class DefaultBox : DrawWidthBox
|
||||
{
|
||||
public DefaultBox()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
}
|
||||
|
||||
private class DrawWidthBox : Container
|
||||
{
|
||||
private readonly OsuSpriteText text;
|
||||
|
||||
public DrawWidthBox()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Gray,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
text = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
text.Text = DrawWidth.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
private class NamedBox : Container
|
||||
{
|
||||
public NamedBox(string name)
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Black,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.Default.With(size: 40),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Text = name
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class SkinConsumer : SkinnableDrawable
|
||||
{
|
||||
public new Drawable Drawable => base.Drawable;
|
||||
public int SkinChangedCount { get; private set; }
|
||||
|
||||
public SkinConsumer(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null)
|
||||
: base(name, defaultImplementation, allowFallback)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
|
||||
{
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
SkinChangedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
private class BaseSourceBox : NamedBox
|
||||
{
|
||||
public BaseSourceBox()
|
||||
: base("Base Source")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class SecondarySourceBox : NamedBox
|
||||
{
|
||||
public SecondarySourceBox()
|
||||
: base("Secondary Source")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class SizedSource : ISkin
|
||||
{
|
||||
private readonly float size;
|
||||
|
||||
public SizedSource(float size)
|
||||
{
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public Drawable GetDrawableComponent(string componentName) =>
|
||||
componentName == "available"
|
||||
? new DrawWidthBox
|
||||
{
|
||||
Colour = Color4.Yellow,
|
||||
Size = new Vector2(size)
|
||||
}
|
||||
: null;
|
||||
|
||||
public Texture GetTexture(string componentName) => throw new NotImplementedException();
|
||||
|
||||
public SampleChannel GetSample(string sampleName) => throw new NotImplementedException();
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class SecondarySource : ISkin
|
||||
{
|
||||
public Drawable GetDrawableComponent(string componentName) => new SecondarySourceBox();
|
||||
|
||||
public Texture GetTexture(string componentName) => throw new NotImplementedException();
|
||||
|
||||
public SampleChannel GetSample(string sampleName) => throw new NotImplementedException();
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class SkinSourceContainer : Container, ISkin
|
||||
{
|
||||
public Drawable GetDrawableComponent(string componentName) => new BaseSourceBox();
|
||||
|
||||
public Texture GetTexture(string componentName) => throw new NotImplementedException();
|
||||
|
||||
public SampleChannel GetSample(string sampleName) => throw new NotImplementedException();
|
||||
|
||||
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
69
osu.Game.Tests/Visual/Menus/IntroTestScene.cs
Normal file
@ -0,0 +1,69 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
[TestFixture]
|
||||
public abstract class IntroTestScene : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(StartupScreen),
|
||||
typeof(IntroScreen),
|
||||
typeof(OsuScreen),
|
||||
typeof(IntroTestScene),
|
||||
};
|
||||
|
||||
[Cached]
|
||||
private OsuLogo logo;
|
||||
|
||||
protected IntroTestScene()
|
||||
{
|
||||
Drawable introStack = null;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Depth = float.MaxValue,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
logo = new OsuLogo
|
||||
{
|
||||
Alpha = 0,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Depth = float.MinValue,
|
||||
Position = new Vector2(0.5f),
|
||||
}
|
||||
};
|
||||
|
||||
AddStep("restart sequence", () =>
|
||||
{
|
||||
logo.FinishTransforms();
|
||||
logo.IsTracking = false;
|
||||
|
||||
introStack?.Expire();
|
||||
|
||||
Add(introStack = new OsuScreenStack(CreateScreen())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract IScreen CreateScreen();
|
||||
}
|
||||
}
|
15
osu.Game.Tests/Visual/Menus/TestSceneIntroCircles.cs
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Screens.Menu;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneIntroCircles : IntroTestScene
|
||||
{
|
||||
protected override IScreen CreateScreen() => new IntroCircles();
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneIntroSequence : OsuTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
typeof(OsuLogo),
|
||||
};
|
||||
|
||||
public TestSceneIntroSequence()
|
||||
{
|
||||
OsuLogo logo;
|
||||
|
||||
var rateAdjustClock = new StopwatchClock(true);
|
||||
var framedClock = new FramedClock(rateAdjustClock);
|
||||
framedClock.ProcessFrame();
|
||||
|
||||
Add(new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = framedClock,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
logo = new OsuLogo
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AddStep(@"Restart", logo.PlayIntro);
|
||||
AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v);
|
||||
}
|
||||
}
|
||||
}
|
@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
private TestChatOverlay chatOverlay;
|
||||
private ChannelManager channelManager;
|
||||
|
||||
private readonly Channel channel1 = new Channel(new User()) { Name = "test1" };
|
||||
private readonly Channel channel1 = new Channel(new User()) { Name = "test really long username" };
|
||||
private readonly Channel channel2 = new Channel(new User()) { Name = "test2" };
|
||||
|
||||
[SetUp]
|
||||
|
@ -9,6 +9,8 @@ using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Users;
|
||||
using osu.Framework.Bindables;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
@ -23,18 +25,25 @@ namespace osu.Game.Tests.Visual.Online
|
||||
public TestSceneProfileRulesetSelector()
|
||||
{
|
||||
ProfileRulesetSelector selector;
|
||||
Bindable<User> user = new Bindable<User>();
|
||||
|
||||
Child = selector = new ProfileRulesetSelector
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
User = { BindTarget = user }
|
||||
};
|
||||
|
||||
AddStep("set osu! as default", () => selector.SetDefaultRuleset(new OsuRuleset().RulesetInfo));
|
||||
AddStep("set mania as default", () => selector.SetDefaultRuleset(new ManiaRuleset().RulesetInfo));
|
||||
AddStep("set taiko as default", () => selector.SetDefaultRuleset(new TaikoRuleset().RulesetInfo));
|
||||
AddStep("set catch as default", () => selector.SetDefaultRuleset(new CatchRuleset().RulesetInfo));
|
||||
AddStep("select default ruleset", selector.SelectDefaultRuleset);
|
||||
|
||||
AddStep("User with osu as default", () => user.Value = new User { PlayMode = "osu" });
|
||||
AddStep("User with mania as default", () => user.Value = new User { PlayMode = "mania" });
|
||||
AddStep("User with taiko as default", () => user.Value = new User { PlayMode = "taiko" });
|
||||
AddStep("User with catch as default", () => user.Value = new User { PlayMode = "fruits" });
|
||||
AddStep("null user", () => user.Value = null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ using osu.Framework.MathUtils;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
@ -35,7 +34,6 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
private RulesetStore rulesets;
|
||||
|
||||
private WorkingBeatmap defaultBeatmap;
|
||||
private DatabaseContextFactory factory;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
@ -74,28 +72,11 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
private TestSongSelect songSelect;
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
factory.ResetDatabase();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, AudioManager audio)
|
||||
{
|
||||
factory = new DatabaseContextFactory(LocalStorage);
|
||||
factory.ResetDatabase();
|
||||
|
||||
using (var usage = factory.Get())
|
||||
usage.Migrate();
|
||||
|
||||
factory.ResetDatabase();
|
||||
|
||||
using (var usage = factory.Get())
|
||||
usage.Migrate();
|
||||
|
||||
Dependencies.Cache(rulesets = new RulesetStore(factory));
|
||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, factory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default));
|
||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default));
|
||||
|
||||
Beatmap.SetDefault();
|
||||
}
|
||||
|
@ -5,14 +5,13 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osu.Game.Tournament.Components;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osu.Game.Tournament.Screens.Ladder.Components;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Components
|
||||
{
|
||||
public class TestSceneDrawableTournamentMatch : OsuTestScene
|
||||
public class TestSceneDrawableTournamentMatch : TournamentTestScene
|
||||
{
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||
{
|
||||
|
@ -1,13 +1,14 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osu.Game.Tournament.Models;
|
||||
|
||||
namespace osu.Game.Tournament.Tests
|
||||
{
|
||||
public abstract class LadderTestScene : OsuTestScene
|
||||
[TestFixture]
|
||||
public abstract class LadderTestScene : TournamentTestScene
|
||||
{
|
||||
[Resolved]
|
||||
protected LadderInfo Ladder { get; private set; }
|
||||
|
@ -2,13 +2,12 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osu.Game.Tournament.Components;
|
||||
using osu.Game.Tournament.Screens.Gameplay;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public class TestSceneGameplayScreen : OsuTestScene
|
||||
public class TestSceneGameplayScreen : TournamentTestScene
|
||||
{
|
||||
[Cached]
|
||||
private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay();
|
||||
|
@ -2,12 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osu.Game.Tournament.Screens.Schedule;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public class TestSceneScheduleScreen : OsuTestScene
|
||||
public class TestSceneScheduleScreen : TournamentTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -2,12 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osu.Game.Tournament.Screens.Showcase;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public class TestSceneShowcaseScreen : OsuTestScene
|
||||
public class TestSceneShowcaseScreen : TournamentTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -3,11 +3,10 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tournament.Tests
|
||||
{
|
||||
public class TestSceneTournamentSceneManager : OsuTestScene
|
||||
public class TestSceneTournamentSceneManager : TournamentTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(Storage storage)
|
||||
|
28
osu.Game.Tournament.Tests/TournamentTestScene.cs
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tournament.Tests
|
||||
{
|
||||
public abstract class TournamentTestScene : OsuTestScene
|
||||
{
|
||||
protected override ITestSceneTestRunner CreateRunner() => new TournamentTestSceneTestRunner();
|
||||
|
||||
public class TournamentTestSceneTestRunner : TournamentGameBase, ITestSceneTestRunner
|
||||
{
|
||||
private TestSceneTestRunner.TestRunner runner;
|
||||
|
||||
protected override void LoadAsyncComplete()
|
||||
{
|
||||
// this has to be run here rather than LoadComplete because
|
||||
// TestScene.cs is checking the IsLoaded state (on another thread) and expects
|
||||
// the runner to be loaded at that point.
|
||||
Add(runner = new TestSceneTestRunner.TestRunner());
|
||||
}
|
||||
|
||||
public void RunTestBlocking(TestScene test) => runner.RunTestBlocking(test);
|
||||
}
|
||||
}
|
||||
}
|
@ -47,8 +47,8 @@ namespace osu.Game.Tournament.Screens.MapPool
|
||||
mapFlows = new FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>>
|
||||
{
|
||||
Y = 100,
|
||||
Spacing = new Vector2(10, 20),
|
||||
Padding = new MarginPadding(50),
|
||||
Spacing = new Vector2(10, 10),
|
||||
Padding = new MarginPadding(25),
|
||||
Direction = FillDirection.Vertical,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
@ -218,7 +218,7 @@ namespace osu.Game.Tournament.Screens.MapPool
|
||||
{
|
||||
mapFlows.Add(currentFlow = new FillFlowContainer<TournamentBeatmapPanel>
|
||||
{
|
||||
Spacing = new Vector2(10, 20),
|
||||
Spacing = new Vector2(10, 5),
|
||||
Direction = FillDirection.Full,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y
|
||||
|
@ -9,15 +9,20 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Tournament.IPC;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tournament
|
||||
@ -35,6 +40,8 @@ namespace osu.Game.Tournament
|
||||
private Bindable<Size> windowSize;
|
||||
private FileBasedIPC ipc;
|
||||
|
||||
private Drawable heightWarning;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
return dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
@ -53,6 +60,12 @@ namespace osu.Game.Tournament
|
||||
this.storage = storage;
|
||||
|
||||
windowSize = frameworkConfig.GetBindable<Size>(FrameworkSetting.WindowedSize);
|
||||
windowSize.BindValueChanged(size => ScheduleAfterChildren(() =>
|
||||
{
|
||||
var minWidth = (int)(size.NewValue.Height / 9f * 16 + 400);
|
||||
|
||||
heightWarning.Alpha = size.NewValue.Width < minWidth ? 1 : 0;
|
||||
}), true);
|
||||
|
||||
readBracket();
|
||||
|
||||
@ -61,16 +74,43 @@ namespace osu.Game.Tournament
|
||||
dependencies.CacheAs<MatchIPCInfo>(ipc = new FileBasedIPC());
|
||||
Add(ipc);
|
||||
|
||||
Add(new OsuButton
|
||||
AddRange(new[]
|
||||
{
|
||||
Text = "Save Changes",
|
||||
Width = 140,
|
||||
Height = 50,
|
||||
Depth = float.MinValue,
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Padding = new MarginPadding(10),
|
||||
Action = SaveChanges,
|
||||
new OsuButton
|
||||
{
|
||||
Text = "Save Changes",
|
||||
Width = 140,
|
||||
Height = 50,
|
||||
Depth = float.MinValue,
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Padding = new MarginPadding(10),
|
||||
Action = SaveChanges,
|
||||
},
|
||||
heightWarning = new Container
|
||||
{
|
||||
Masking = true,
|
||||
CornerRadius = 5,
|
||||
Depth = float.MinValue,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = Color4.Red,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new SpriteText
|
||||
{
|
||||
Text = "Please make the window wider",
|
||||
Font = OsuFont.Default.With(weight: "bold"),
|
||||
Colour = Color4.White,
|
||||
Padding = new MarginPadding(20)
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -195,18 +235,6 @@ namespace osu.Game.Tournament
|
||||
base.LoadComplete();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
var minWidth = (int)(windowSize.Value.Height / 9f * 16 + 400);
|
||||
|
||||
if (windowSize.Value.Width < minWidth)
|
||||
{
|
||||
// todo: can be removed after ppy/osu-framework#1975
|
||||
windowSize.Value = Host.Window.ClientSize = new Size(minWidth, windowSize.Value.Height);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SaveChanges()
|
||||
{
|
||||
foreach (var r in ladder.Rounds)
|
||||
|
@ -386,7 +386,7 @@ namespace osu.Game.Beatmaps
|
||||
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
|
||||
};
|
||||
|
||||
req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap}", e); };
|
||||
req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); };
|
||||
|
||||
// intentionally blocking to limit web request concurrency
|
||||
req.Perform(api);
|
||||
|
@ -89,6 +89,14 @@ namespace osu.Game.Beatmaps
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="IBeatmapConverter"/> to convert a <see cref="IBeatmap"/> for a specified <see cref="Ruleset"/>.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The <see cref="IBeatmap"/> to be converted.</param>
|
||||
/// <param name="ruleset">The <see cref="Ruleset"/> for which <paramref name="beatmap"/> should be converted.</param>
|
||||
/// <returns>The applicable <see cref="IBeatmapConverter"/>.</returns>
|
||||
protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap);
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>.
|
||||
/// <para>
|
||||
@ -104,7 +112,7 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
var rulesetInstance = ruleset.CreateInstance();
|
||||
|
||||
IBeatmapConverter converter = rulesetInstance.CreateBeatmapConverter(Beatmap);
|
||||
IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance);
|
||||
|
||||
// Check if the beatmap can be converted
|
||||
if (!converter.CanConvert)
|
||||
@ -141,6 +149,9 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
processor?.PostProcess();
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmap>())
|
||||
mod.ApplyToBeatmap(converted);
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
|
@ -253,7 +253,7 @@ namespace osu.Game.Database
|
||||
using (Stream s = reader.GetStream(file))
|
||||
s.CopyTo(hashable);
|
||||
|
||||
return hashable.ComputeSHA2Hash();
|
||||
return hashable.Length > 0 ? hashable.ComputeSHA2Hash() : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -27,11 +27,12 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
private bool shouldPerformRightMouseScroll(MouseButtonEvent e) => RightMouseScrollbar && e.Button == MouseButton.Right;
|
||||
|
||||
private void scrollToRelative(float value) => ScrollTo(Clamp((value - Scrollbar.DrawSize[ScrollDim] / 2) / Scrollbar.Size[ScrollDim]), true, DistanceDecayOnRightMouseScrollbar);
|
||||
private void scrollFromMouseEvent(MouseEvent e) =>
|
||||
ScrollTo(Clamp(ToLocalSpace(e.ScreenSpaceMousePosition)[ScrollDim] / DrawSize[ScrollDim]) * Content.DrawSize[ScrollDim], true, DistanceDecayOnRightMouseScrollbar);
|
||||
|
||||
private bool mouseScrollBarDragging;
|
||||
private bool rightMouseDragging;
|
||||
|
||||
protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging;
|
||||
protected override bool IsDragging => base.IsDragging || rightMouseDragging;
|
||||
|
||||
public OsuScrollContainer(Direction scrollDirection = Direction.Vertical)
|
||||
: base(scrollDirection)
|
||||
@ -42,7 +43,7 @@ namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
if (shouldPerformRightMouseScroll(e))
|
||||
{
|
||||
scrollToRelative(e.MousePosition[ScrollDim]);
|
||||
scrollFromMouseEvent(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -51,9 +52,9 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
protected override bool OnDrag(DragEvent e)
|
||||
{
|
||||
if (mouseScrollBarDragging)
|
||||
if (rightMouseDragging)
|
||||
{
|
||||
scrollToRelative(e.MousePosition[ScrollDim]);
|
||||
scrollFromMouseEvent(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -64,7 +65,7 @@ namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
if (shouldPerformRightMouseScroll(e))
|
||||
{
|
||||
mouseScrollBarDragging = true;
|
||||
rightMouseDragging = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -73,9 +74,9 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
protected override bool OnDragEnd(DragEndEvent e)
|
||||
{
|
||||
if (mouseScrollBarDragging)
|
||||
if (rightMouseDragging)
|
||||
{
|
||||
mouseScrollBarDragging = false;
|
||||
rightMouseDragging = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,8 @@ namespace osu.Game.Graphics
|
||||
|
||||
using (var image = await host.TakeScreenshotAsync())
|
||||
{
|
||||
Interlocked.Decrement(ref screenShotTasks);
|
||||
if (Interlocked.Decrement(ref screenShotTasks) == 0 && cursorVisibility.Value == false)
|
||||
cursorVisibility.Value = true;
|
||||
|
||||
var fileName = getFileName();
|
||||
if (fileName == null) return;
|
||||
@ -125,14 +126,6 @@ namespace osu.Game.Graphics
|
||||
}
|
||||
});
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (cursorVisibility.Value == false && Interlocked.CompareExchange(ref screenShotTasks, 0, 0) == 0)
|
||||
cursorVisibility.Value = true;
|
||||
}
|
||||
|
||||
private string getFileName()
|
||||
{
|
||||
var dt = DateTime.Now;
|
||||
|
44
osu.Game/Graphics/UserInterface/DimmedLoadingLayer.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
|
||||
namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
public class DimmedLoadingLayer : VisibilityContainer
|
||||
{
|
||||
private const float transition_duration = 250;
|
||||
|
||||
private readonly LoadingAnimation loading;
|
||||
|
||||
public DimmedLoadingLayer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
},
|
||||
loading = new LoadingAnimation(),
|
||||
};
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
this.FadeIn(transition_duration, Easing.OutQuint);
|
||||
loading.Show();
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
this.FadeOut(transition_duration, Easing.OutQuint);
|
||||
loading.Hide();
|
||||
}
|
||||
}
|
||||
}
|
@ -76,7 +76,12 @@ namespace osu.Game.Graphics.UserInterface
|
||||
{
|
||||
Masking = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = path = new SmoothPath { RelativeSizeAxes = Axes.Both, PathRadius = 1 }
|
||||
Child = path = new SmoothPath
|
||||
{
|
||||
AutoSizeAxes = Axes.None,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
PathRadius = 1
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Graphics.UserInterface
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
text = new OsuSpriteText { Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold) },
|
||||
text = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
|
||||
icon = new SpriteIcon
|
||||
{
|
||||
Size = new Vector2(14),
|
||||
@ -81,10 +81,15 @@ namespace osu.Game.Graphics.UserInterface
|
||||
Colour = Color4.White,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
}
|
||||
},
|
||||
new HoverClickSounds()
|
||||
};
|
||||
|
||||
Current.ValueChanged += selected => { icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle; };
|
||||
Current.ValueChanged += selected =>
|
||||
{
|
||||
icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle;
|
||||
text.Font = text.Font.With(weight: selected.NewValue ? FontWeight.Bold : FontWeight.Medium);
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -27,6 +27,7 @@ namespace osu.Game.Online.API.Requests
|
||||
{
|
||||
Favourite,
|
||||
RankedAndApproved,
|
||||
Loved,
|
||||
Unranked,
|
||||
Graveyard
|
||||
}
|
||||
|
@ -27,24 +27,25 @@ namespace osu.Game.Online.API.Requests
|
||||
}
|
||||
|
||||
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
|
||||
protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)searchCategory}&sort={sortCriteria.ToString().ToLowerInvariant()}_{directionString}";
|
||||
protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={searchCategory.ToString().ToLowerInvariant()}&sort={sortCriteria.ToString().ToLowerInvariant()}_{directionString}";
|
||||
}
|
||||
|
||||
public enum BeatmapSearchCategory
|
||||
{
|
||||
Any = 7,
|
||||
Any,
|
||||
|
||||
[Description("Ranked & Approved")]
|
||||
RankedApproved = 0,
|
||||
Qualified = 3,
|
||||
Loved = 8,
|
||||
Favourites = 2,
|
||||
[Description("Has Leaderboard")]
|
||||
Leaderboard,
|
||||
Ranked,
|
||||
Qualified,
|
||||
Loved,
|
||||
Favourites,
|
||||
|
||||
[Description("Pending & WIP")]
|
||||
PendingWIP = 4,
|
||||
Graveyard = 5,
|
||||
Pending,
|
||||
Graveyard,
|
||||
|
||||
[Description("My Maps")]
|
||||
MyMaps = 6,
|
||||
Mine,
|
||||
}
|
||||
}
|
||||
|
@ -48,22 +48,24 @@ namespace osu.Game.Online
|
||||
attachDownload(manager.GetExistingDownload(modelInfo.NewValue));
|
||||
}, true);
|
||||
|
||||
manager.DownloadBegan += download =>
|
||||
{
|
||||
if (download.Model.Equals(Model.Value))
|
||||
attachDownload(download);
|
||||
};
|
||||
|
||||
manager.DownloadFailed += download =>
|
||||
{
|
||||
if (download.Model.Equals(Model.Value))
|
||||
attachDownload(null);
|
||||
};
|
||||
|
||||
manager.DownloadBegan += downloadBegan;
|
||||
manager.DownloadFailed += downloadFailed;
|
||||
manager.ItemAdded += itemAdded;
|
||||
manager.ItemRemoved += itemRemoved;
|
||||
}
|
||||
|
||||
private void downloadBegan(ArchiveDownloadRequest<TModel> request)
|
||||
{
|
||||
if (request.Model.Equals(Model.Value))
|
||||
attachDownload(request);
|
||||
}
|
||||
|
||||
private void downloadFailed(ArchiveDownloadRequest<TModel> request)
|
||||
{
|
||||
if (request.Model.Equals(Model.Value))
|
||||
attachDownload(null);
|
||||
}
|
||||
|
||||
private ArchiveDownloadRequest<TModel> attachedRequest;
|
||||
|
||||
private void attachDownload(ArchiveDownloadRequest<TModel> request)
|
||||
@ -126,8 +128,10 @@ namespace osu.Game.Online
|
||||
|
||||
if (manager != null)
|
||||
{
|
||||
manager.DownloadBegan -= attachDownload;
|
||||
manager.DownloadBegan -= downloadBegan;
|
||||
manager.DownloadFailed -= downloadFailed;
|
||||
manager.ItemAdded -= itemAdded;
|
||||
manager.ItemRemoved -= itemRemoved;
|
||||
}
|
||||
|
||||
State.UnbindAll();
|
||||
|
@ -87,7 +87,8 @@ namespace osu.Game
|
||||
private BackButton backButton;
|
||||
|
||||
private MainMenu menuScreen;
|
||||
private Intro introScreen;
|
||||
|
||||
private IntroScreen introScreen;
|
||||
|
||||
private Bindable<int> configRuleset;
|
||||
|
||||
@ -264,7 +265,16 @@ namespace osu.Game
|
||||
{
|
||||
// The given ScoreInfo may have missing properties if it was retrieved from online data. Re-retrieve it from the database
|
||||
// to ensure all the required data for presenting a replay are present.
|
||||
var databasedScoreInfo = ScoreManager.Query(s => s.OnlineScoreID == score.OnlineScoreID);
|
||||
var databasedScoreInfo = score.OnlineScoreID != null
|
||||
? ScoreManager.Query(s => s.OnlineScoreID == score.OnlineScoreID)
|
||||
: ScoreManager.Query(s => s.Hash == score.Hash);
|
||||
|
||||
if (databasedScoreInfo == null)
|
||||
{
|
||||
Logger.Log("The requested score could not be found locally.", LoggingTarget.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var databasedScore = ScoreManager.GetScore(databasedScoreInfo);
|
||||
|
||||
if (databasedScore.Replay == null)
|
||||
@ -297,7 +307,9 @@ namespace osu.Game
|
||||
if (nextBeatmap?.Track != null)
|
||||
nextBeatmap.Track.Completed += currentTrackCompleted;
|
||||
|
||||
beatmap.OldValue?.Dispose();
|
||||
using (var oldBeatmap = beatmap.OldValue)
|
||||
if (oldBeatmap?.Track != null)
|
||||
oldBeatmap.Track.Completed -= currentTrackCompleted;
|
||||
|
||||
nextBeatmap?.LoadBeatmapAsync();
|
||||
}
|
||||
@ -589,7 +601,7 @@ namespace osu.Game
|
||||
{
|
||||
int recentLogCount = 0;
|
||||
|
||||
const double debounce = 5000;
|
||||
const double debounce = 60000;
|
||||
|
||||
Logger.NewEntry += entry =>
|
||||
{
|
||||
@ -761,7 +773,7 @@ namespace osu.Game
|
||||
if (introScreen == null)
|
||||
return true;
|
||||
|
||||
if (!introScreen.DidLoadMenu || !(screenStack.CurrentScreen is Intro))
|
||||
if (!introScreen.DidLoadMenu || !(screenStack.CurrentScreen is IntroScreen))
|
||||
{
|
||||
Scheduler.Add(introScreen.MakeCurrent);
|
||||
return true;
|
||||
@ -796,7 +808,7 @@ namespace osu.Game
|
||||
{
|
||||
switch (newScreen)
|
||||
{
|
||||
case Intro intro:
|
||||
case IntroScreen intro:
|
||||
introScreen = intro;
|
||||
break;
|
||||
|
||||
|
@ -13,6 +13,8 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
|
||||
public override bool IsSwitchable => false;
|
||||
|
||||
protected override bool IsBoldWhenActive => false;
|
||||
|
||||
public ChannelSelectorTabItem()
|
||||
: base(new ChannelSelectorTabChannel())
|
||||
{
|
||||
@ -22,7 +24,7 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
Icon.Alpha = 0;
|
||||
|
||||
Text.Font = Text.Font.With(size: 45);
|
||||
TextBold.Font = Text.Font.With(size: 45);
|
||||
Text.Truncate = false;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -29,7 +29,6 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
public override bool IsRemovable => !Pinned;
|
||||
|
||||
protected readonly SpriteText Text;
|
||||
protected readonly SpriteText TextBold;
|
||||
protected readonly ClickableContainer CloseButton;
|
||||
private readonly Box box;
|
||||
private readonly Box highlightBox;
|
||||
@ -88,20 +87,17 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
},
|
||||
Text = new OsuSpriteText
|
||||
{
|
||||
Margin = new MarginPadding(5),
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Text = value.ToString(),
|
||||
Font = OsuFont.GetFont(size: 18)
|
||||
},
|
||||
TextBold = new OsuSpriteText
|
||||
{
|
||||
Alpha = 0,
|
||||
Margin = new MarginPadding(5),
|
||||
Origin = Anchor.CentreLeft,
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Text = value.ToString(),
|
||||
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold)
|
||||
Font = OsuFont.GetFont(size: 18),
|
||||
Padding = new MarginPadding(5)
|
||||
{
|
||||
Left = LeftTextPadding,
|
||||
Right = RightTextPadding,
|
||||
},
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Truncate = true,
|
||||
},
|
||||
CloseButton = new TabCloseButton
|
||||
{
|
||||
@ -119,10 +115,16 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
};
|
||||
}
|
||||
|
||||
protected virtual float LeftTextPadding => 5;
|
||||
|
||||
protected virtual float RightTextPadding => IsRemovable ? 40 : 5;
|
||||
|
||||
protected virtual IconUsage DisplayIcon => FontAwesome.Solid.Hashtag;
|
||||
|
||||
protected virtual bool ShowCloseOnHover => true;
|
||||
|
||||
protected virtual bool IsBoldWhenActive => true;
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
{
|
||||
if (IsRemovable && ShowCloseOnHover)
|
||||
@ -203,8 +205,7 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
box.FadeColour(BackgroundActive, TRANSITION_LENGTH, Easing.OutQuint);
|
||||
highlightBox.FadeIn(TRANSITION_LENGTH, Easing.OutQuint);
|
||||
|
||||
Text.FadeOut(TRANSITION_LENGTH, Easing.OutQuint);
|
||||
TextBold.FadeIn(TRANSITION_LENGTH, Easing.OutQuint);
|
||||
if (IsBoldWhenActive) Text.Font = Text.Font.With(weight: FontWeight.Bold);
|
||||
}
|
||||
|
||||
protected virtual void FadeInactive()
|
||||
@ -216,8 +217,7 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
box.FadeColour(BackgroundInactive, TRANSITION_LENGTH, Easing.OutQuint);
|
||||
highlightBox.FadeOut(TRANSITION_LENGTH, Easing.OutQuint);
|
||||
|
||||
Text.FadeIn(TRANSITION_LENGTH, Easing.OutQuint);
|
||||
TextBold.FadeOut(TRANSITION_LENGTH, Easing.OutQuint);
|
||||
Text.Font = Text.Font.With(weight: FontWeight.Medium);
|
||||
}
|
||||
|
||||
protected override void OnActivated() => updateState();
|
||||
|
@ -62,11 +62,10 @@ namespace osu.Game.Overlays.Chat.Tabs
|
||||
});
|
||||
|
||||
avatar.OnLoadComplete += d => d.FadeInFromZero(300, Easing.OutQuint);
|
||||
|
||||
Text.X = ChatOverlay.TAB_AREA_HEIGHT;
|
||||
TextBold.X = ChatOverlay.TAB_AREA_HEIGHT;
|
||||
}
|
||||
|
||||
protected override float LeftTextPadding => base.LeftTextPadding + ChatOverlay.TAB_AREA_HEIGHT;
|
||||
|
||||
protected override bool ShowCloseOnHover => false;
|
||||
|
||||
protected override void FadeActive()
|
||||
|
@ -18,6 +18,7 @@ namespace osu.Game.Overlays.Direct
|
||||
|
||||
protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552");
|
||||
protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked;
|
||||
protected override BeatmapSearchCategory DefaultCategory => BeatmapSearchCategory.Leaderboard;
|
||||
|
||||
protected override Drawable CreateSupplementaryControls() => rulesetSelector = new DirectRulesetSelector();
|
||||
|
||||
|
@ -53,6 +53,6 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
User.BindValueChanged(user => updateFollowers(user.NewValue), true);
|
||||
}
|
||||
|
||||
private void updateFollowers(User user) => followerText.Text = user?.FollowerCount?.Length > 0 ? user.FollowerCount[0].ToString("#,##0") : "0";
|
||||
private void updateFollowers(User user) => followerText.Text = user?.FollowerCount.ToString("#,##0");
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,13 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -16,6 +18,8 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
{
|
||||
private Color4 accentColour = Color4.White;
|
||||
|
||||
public readonly Bindable<User> User = new Bindable<User>();
|
||||
|
||||
public ProfileRulesetSelector()
|
||||
{
|
||||
TabContainer.Masking = false;
|
||||
@ -32,24 +36,17 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
((ProfileRulesetTabItem)tabItem).AccentColour = accentColour;
|
||||
}
|
||||
|
||||
public void SetDefaultRuleset(RulesetInfo ruleset)
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
// Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value
|
||||
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
|
||||
((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID;
|
||||
base.LoadComplete();
|
||||
|
||||
User.BindValueChanged(u => SetDefaultRuleset(Rulesets.GetRuleset(u.NewValue?.PlayMode ?? "osu")), true);
|
||||
}
|
||||
|
||||
public void SelectDefaultRuleset()
|
||||
public void SetDefaultRuleset(RulesetInfo ruleset)
|
||||
{
|
||||
// Todo: This method shouldn't exist, but bindables don't provide the concept of observing a change to the default value
|
||||
foreach (TabItem<RulesetInfo> tabItem in TabContainer)
|
||||
{
|
||||
if (((ProfileRulesetTabItem)tabItem).IsDefault)
|
||||
{
|
||||
Current.Value = ((ProfileRulesetTabItem)tabItem).Value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
((ProfileRulesetTabItem)tabItem).IsDefault = ((ProfileRulesetTabItem)tabItem).Value.ID == ruleset.ID;
|
||||
}
|
||||
|
||||
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new ProfileRulesetTabItem(value)
|
||||
|
@ -80,7 +80,6 @@ namespace osu.Game.Overlays.Profile.Header.Components
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
background.Colour = colours.Pink;
|
||||
iconContainer.Colour = colours.GreySeafoam;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ namespace osu.Game.Overlays.Profile.Sections
|
||||
{
|
||||
new PaginatedBeatmapContainer(BeatmapSetType.Favourite, User, "Favourite Beatmaps"),
|
||||
new PaginatedBeatmapContainer(BeatmapSetType.RankedAndApproved, User, "Ranked & Approved Beatmaps"),
|
||||
new PaginatedBeatmapContainer(BeatmapSetType.Loved, User, "Loved Beatmaps"),
|
||||
new PaginatedBeatmapContainer(BeatmapSetType.Unranked, User, "Pending Beatmaps"),
|
||||
new PaginatedBeatmapContainer(BeatmapSetType.Graveyard, User, "Graveyarded Beatmaps"),
|
||||
};
|
||||
|