mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 11:02:54 +08:00
Merge branch 'master' into cinema-mod
This commit is contained in:
commit
a61f8cc2c3
@ -1,5 +1,6 @@
|
|||||||
M:System.Object.Equals(System.Object,System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
M:System.Object.Equals(System.Object,System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
||||||
M:System.Object.Equals(System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
M:System.Object.Equals(System.Object)~System.Boolean;Don't use object.Equals. Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
||||||
M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(Fallbacks to ValueType). Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
M:System.ValueType.Equals(System.Object)~System.Boolean;Don't use object.Equals(Fallbacks to ValueType). Use IEquatable<T> or EqualityComparer<T>.Default instead.
|
||||||
|
M:System.Nullable`1.Equals(System.Object)~System.Boolean;Use == instead.
|
||||||
T:System.IComparable;Don't use non-generic IComparable. Use generic version instead.
|
T:System.IComparable;Don't use non-generic IComparable. Use generic version instead.
|
||||||
M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
|
M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
<EmbeddedResource Include="Resources\**\*.*" />
|
<EmbeddedResource Include="Resources\**\*.*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Code Analysis">
|
<ItemGroup Label="Code Analysis">
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="2.9.7" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="2.9.8" PrivateAssets="All" />
|
||||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
<AdditionalFiles Include="$(MSBuildThisFileDirectory)CodeAnalysis\BannedSymbols.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Documentation">
|
<PropertyGroup Label="Documentation">
|
||||||
|
@ -54,6 +54,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1126.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1210.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
20
osu.Desktop/app.manifest
Normal file
20
osu.Desktop/app.manifest
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<assemblyIdentity version="1.0.0.0" name="osu!" />
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||||
|
</requestedPrivileges>
|
||||||
|
<applicationRequestMinimum>
|
||||||
|
<defaultAssemblyRequest permissionSetReference="Custom" />
|
||||||
|
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
|
||||||
|
</applicationRequestMinimum>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
<asmv3:application>
|
||||||
|
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||||
|
<dpiAware>true</dpiAware>
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
|
</asmv1:assembly>
|
@ -8,6 +8,7 @@
|
|||||||
<Title>osu!lazer</Title>
|
<Title>osu!lazer</Title>
|
||||||
<Product>osu!lazer</Product>
|
<Product>osu!lazer</Product>
|
||||||
<ApplicationIcon>lazer.ico</ApplicationIcon>
|
<ApplicationIcon>lazer.ico</ApplicationIcon>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
<Version>0.0.0</Version>
|
<Version>0.0.0</Version>
|
||||||
<FileVersion>0.0.0</FileVersion>
|
<FileVersion>0.0.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@ -23,11 +24,11 @@
|
|||||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="System.IO.Packaging" Version="4.6.0" />
|
<PackageReference Include="System.IO.Packaging" Version="4.7.0" />
|
||||||
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.4" />
|
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.4" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.6.0" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Resources">
|
<ItemGroup Label="Resources">
|
||||||
<EmbeddedResource Include="lazer.ico" />
|
<EmbeddedResource Include="lazer.ico" />
|
||||||
|
@ -34,12 +34,10 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
{
|
{
|
||||||
mods = Score.Mods;
|
mods = Score.Mods;
|
||||||
|
|
||||||
var legacyScore = Score as LegacyScoreInfo;
|
fruitsHit = Score?.GetCount300() ?? Score.Statistics[HitResult.Perfect];
|
||||||
|
ticksHit = Score?.GetCount100() ?? 0;
|
||||||
fruitsHit = legacyScore?.Count300 ?? Score.Statistics[HitResult.Perfect];
|
tinyTicksHit = Score?.GetCount50() ?? 0;
|
||||||
ticksHit = legacyScore?.Count100 ?? 0;
|
tinyTicksMissed = Score?.GetCountKatu() ?? 0;
|
||||||
tinyTicksHit = legacyScore?.Count50 ?? 0;
|
|
||||||
tinyTicksMissed = legacyScore?.CountKatu ?? 0;
|
|
||||||
misses = Score.Statistics[HitResult.Miss];
|
misses = Score.Statistics[HitResult.Miss];
|
||||||
|
|
||||||
// Don't count scores made with supposedly unranked mods
|
// Don't count scores made with supposedly unranked mods
|
||||||
|
@ -1,12 +1,48 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModRelax : ModRelax
|
public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override string Description => @"Use the mouse to control the catcher.";
|
public override string Description => @"Use the mouse to control the catcher.";
|
||||||
|
|
||||||
|
public void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
|
||||||
|
{
|
||||||
|
drawableRuleset.Cursor.Add(new MouseInputHelper((CatchPlayfield)drawableRuleset.Playfield));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MouseInputHelper : Drawable, IKeyBindingHandler<CatchAction>, IRequireHighFrequencyMousePosition
|
||||||
|
{
|
||||||
|
private readonly CatcherArea.Catcher catcher;
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||||
|
|
||||||
|
public MouseInputHelper(CatchPlayfield playfield)
|
||||||
|
{
|
||||||
|
catcher = playfield.CatcherArea.MovableCatcher;
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
//disable keyboard controls
|
||||||
|
public bool OnPressed(CatchAction action) => true;
|
||||||
|
public bool OnReleased(CatchAction action) => true;
|
||||||
|
|
||||||
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
|
{
|
||||||
|
catcher.UpdatePosition(e.MousePosition.X / DrawSize.X);
|
||||||
|
return base.OnMouseMove(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,23 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public double Duration => EndTime - StartTime;
|
public double Duration => EndTime - StartTime;
|
||||||
|
|
||||||
public SliderPath Path { get; set; }
|
private readonly SliderPath path = new SliderPath();
|
||||||
|
|
||||||
|
public SliderPath Path
|
||||||
|
{
|
||||||
|
get => path;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
path.ControlPoints.Clear();
|
||||||
|
path.ExpectedDistance.Value = null;
|
||||||
|
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
path.ControlPoints.AddRange(value.ControlPoints);
|
||||||
|
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public double Distance => Path.Distance;
|
public double Distance => Path.Distance;
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.Objects.Drawable;
|
|||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
@ -19,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
internal readonly CatcherArea CatcherArea;
|
internal readonly CatcherArea CatcherArea;
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || CatcherArea.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation)
|
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> createDrawableRepresentation)
|
||||||
{
|
{
|
||||||
Container explodingFruitContainer;
|
Container explodingFruitContainer;
|
||||||
|
@ -377,8 +377,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
double dashModifier = Dashing ? 1 : 0.5;
|
double dashModifier = Dashing ? 1 : 0.5;
|
||||||
double speed = BASE_SPEED * dashModifier * hyperDashModifier;
|
double speed = BASE_SPEED * dashModifier * hyperDashModifier;
|
||||||
|
|
||||||
Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y);
|
UpdatePosition((float)(X + direction * Clock.ElapsedFrameTime * speed));
|
||||||
X = (float)Math.Clamp(X + direction * Clock.ElapsedFrameTime * speed, 0, 1);
|
|
||||||
|
|
||||||
// Correct overshooting.
|
// Correct overshooting.
|
||||||
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
||||||
@ -452,6 +451,17 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
fruit.LifetimeStart = Time.Current;
|
fruit.LifetimeStart = Time.Current;
|
||||||
fruit.Expire();
|
fruit.Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdatePosition(float position)
|
||||||
|
{
|
||||||
|
position = Math.Clamp(position, 0, 1);
|
||||||
|
|
||||||
|
if (position == X)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Scale = new Vector2(Math.Abs(Scale.X) * (position > X ? 1 : -1), Scale.Y);
|
||||||
|
X = position;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChild = new SkinnableSprite("Gameplay/catch/fruit-catcher-idle")
|
InternalChild = new SkinnableSprite("Gameplay/catch/fruit-catcher-idle", confineMode: ConfineMode.ScaleDownToFit)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
|
@ -37,12 +37,12 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
{
|
{
|
||||||
mods = Score.Mods;
|
mods = Score.Mods;
|
||||||
scaledScore = Score.TotalScore;
|
scaledScore = Score.TotalScore;
|
||||||
countPerfect = Convert.ToInt32(Score.Statistics[HitResult.Perfect]);
|
countPerfect = Score.Statistics[HitResult.Perfect];
|
||||||
countGreat = Convert.ToInt32(Score.Statistics[HitResult.Great]);
|
countGreat = Score.Statistics[HitResult.Great];
|
||||||
countGood = Convert.ToInt32(Score.Statistics[HitResult.Good]);
|
countGood = Score.Statistics[HitResult.Good];
|
||||||
countOk = Convert.ToInt32(Score.Statistics[HitResult.Ok]);
|
countOk = Score.Statistics[HitResult.Ok];
|
||||||
countMeh = Convert.ToInt32(Score.Statistics[HitResult.Meh]);
|
countMeh = Score.Statistics[HitResult.Meh];
|
||||||
countMiss = Convert.ToInt32(Score.Statistics[HitResult.Miss]);
|
countMiss = Score.Statistics[HitResult.Miss];
|
||||||
|
|
||||||
if (mods.Any(m => !m.Ranked))
|
if (mods.Any(m => !m.Ranked))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||||
|
|
||||||
[TestCase(6.931145117263422, "diffcalc-test")]
|
[TestCase(6.9311451172608853d, "diffcalc-test")]
|
||||||
[TestCase(1.0736587013228804d, "zero-length-sliders")]
|
[TestCase(1.0736587013228804d, "zero-length-sliders")]
|
||||||
public void Test(double expected, string name)
|
public void Test(double expected, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expected, name);
|
||||||
|
@ -196,7 +196,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
AddStep($"move mouse to control point {index}", () =>
|
AddStep($"move mouse to control point {index}", () =>
|
||||||
{
|
{
|
||||||
Vector2 position = slider.Position + slider.Path.ControlPoints[index];
|
Vector2 position = slider.Position + slider.Path.ControlPoints[index].Position.Value;
|
||||||
InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position));
|
InputManager.MoveMouseTo(drawableObject.Parent.ToScreenSpace(position));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -45,10 +45,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
mods = Score.Mods;
|
mods = Score.Mods;
|
||||||
accuracy = Score.Accuracy;
|
accuracy = Score.Accuracy;
|
||||||
scoreMaxCombo = Score.MaxCombo;
|
scoreMaxCombo = Score.MaxCombo;
|
||||||
countGreat = Convert.ToInt32(Score.Statistics[HitResult.Great]);
|
countGreat = Score.Statistics[HitResult.Great];
|
||||||
countGood = Convert.ToInt32(Score.Statistics[HitResult.Good]);
|
countGood = Score.Statistics[HitResult.Good];
|
||||||
countMeh = Convert.ToInt32(Score.Statistics[HitResult.Meh]);
|
countMeh = Score.Statistics[HitResult.Meh];
|
||||||
countMiss = Convert.ToInt32(Score.Statistics[HitResult.Miss]);
|
countMiss = Score.Statistics[HitResult.Miss];
|
||||||
|
|
||||||
// Don't count scores made with supposedly unranked mods
|
// Don't count scores made with supposedly unranked mods
|
||||||
if (mods.Any(m => !m.Ranked))
|
if (mods.Any(m => !m.Ranked))
|
||||||
|
@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
public class PathControlPointPiece : BlueprintPiece<Slider>
|
public class PathControlPointPiece : BlueprintPiece<Slider>
|
||||||
{
|
{
|
||||||
public Action<int, MouseButtonEvent> RequestSelection;
|
public Action<int, MouseButtonEvent> RequestSelection;
|
||||||
public Action<Vector2[]> ControlPointsChanged;
|
|
||||||
|
|
||||||
public readonly BindableBool IsSelected = new BindableBool();
|
public readonly BindableBool IsSelected = new BindableBool();
|
||||||
public readonly int Index;
|
public readonly int Index;
|
||||||
@ -90,7 +89,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
Position = slider.StackedPosition + slider.Path.ControlPoints[Index];
|
Position = slider.StackedPosition + slider.Path.ControlPoints[Index].Position.Value;
|
||||||
|
|
||||||
updateMarkerDisplay();
|
updateMarkerDisplay();
|
||||||
updateConnectingPath();
|
updateConnectingPath();
|
||||||
@ -103,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
markerRing.Alpha = IsSelected.Value ? 1 : 0;
|
markerRing.Alpha = IsSelected.Value ? 1 : 0;
|
||||||
|
|
||||||
Color4 colour = isSegmentSeparator ? colours.Red : colours.Yellow;
|
Color4 colour = slider.Path.ControlPoints[Index].Type.Value.HasValue ? colours.Red : colours.Yellow;
|
||||||
if (IsHovered || IsSelected.Value)
|
if (IsHovered || IsSelected.Value)
|
||||||
colour = Color4.White;
|
colour = Color4.White;
|
||||||
marker.Colour = colour;
|
marker.Colour = colour;
|
||||||
@ -116,10 +115,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
path.ClearVertices();
|
path.ClearVertices();
|
||||||
|
|
||||||
if (Index != slider.Path.ControlPoints.Length - 1)
|
if (Index != slider.Path.ControlPoints.Count - 1)
|
||||||
{
|
{
|
||||||
path.AddVertex(Vector2.Zero);
|
path.AddVertex(Vector2.Zero);
|
||||||
path.AddVertex(slider.Path.ControlPoints[Index + 1] - slider.Path.ControlPoints[Index]);
|
path.AddVertex(slider.Path.ControlPoints[Index + 1].Position.Value - slider.Path.ControlPoints[Index].Position.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
|
||||||
@ -156,8 +155,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
protected override bool OnDrag(DragEvent e)
|
protected override bool OnDrag(DragEvent e)
|
||||||
{
|
{
|
||||||
var newControlPoints = slider.Path.ControlPoints.ToArray();
|
|
||||||
|
|
||||||
if (Index == 0)
|
if (Index == 0)
|
||||||
{
|
{
|
||||||
// Special handling for the head control point - the position of the slider changes which means the snapped position and time have to be taken into account
|
// Special handling for the head control point - the position of the slider changes which means the snapped position and time have to be taken into account
|
||||||
@ -168,29 +165,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
slider.StartTime = snappedTime;
|
slider.StartTime = snappedTime;
|
||||||
|
|
||||||
// Since control points are relative to the position of the slider, they all need to be offset backwards by the delta
|
// Since control points are relative to the position of the slider, they all need to be offset backwards by the delta
|
||||||
for (int i = 1; i < newControlPoints.Length; i++)
|
for (int i = 1; i < slider.Path.ControlPoints.Count; i++)
|
||||||
newControlPoints[i] -= movementDelta;
|
slider.Path.ControlPoints[i].Position.Value -= movementDelta;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
newControlPoints[Index] += e.Delta;
|
slider.Path.ControlPoints[Index].Position.Value += e.Delta;
|
||||||
|
|
||||||
if (isSegmentSeparatorWithNext)
|
|
||||||
newControlPoints[Index + 1] = newControlPoints[Index];
|
|
||||||
|
|
||||||
if (isSegmentSeparatorWithPrevious)
|
|
||||||
newControlPoints[Index - 1] = newControlPoints[Index];
|
|
||||||
|
|
||||||
ControlPointsChanged?.Invoke(newControlPoints);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnDragEnd(DragEndEvent e) => true;
|
protected override bool OnDragEnd(DragEndEvent e) => true;
|
||||||
|
|
||||||
private bool isSegmentSeparator => isSegmentSeparatorWithNext || isSegmentSeparatorWithPrevious;
|
|
||||||
|
|
||||||
private bool isSegmentSeparatorWithNext => Index < slider.Path.ControlPoints.Length - 1 && slider.Path.ControlPoints[Index + 1] == slider.Path.ControlPoints[Index];
|
|
||||||
|
|
||||||
private bool isSegmentSeparatorWithPrevious => Index > 0 && slider.Path.ControlPoints[Index - 1] == slider.Path.ControlPoints[Index];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
@ -14,6 +13,7 @@ using osu.Framework.Input;
|
|||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit.Compose;
|
using osu.Game.Screens.Edit.Compose;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -23,8 +23,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
|
public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler<PlatformAction>, IHasContextMenu
|
||||||
{
|
{
|
||||||
public Action<Vector2[]> ControlPointsChanged;
|
|
||||||
|
|
||||||
internal readonly Container<PathControlPointPiece> Pieces;
|
internal readonly Container<PathControlPointPiece> Pieces;
|
||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
private readonly bool allowSelection;
|
private readonly bool allowSelection;
|
||||||
@ -55,12 +53,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
while (slider.Path.ControlPoints.Length > Pieces.Count)
|
while (slider.Path.ControlPoints.Count > Pieces.Count)
|
||||||
{
|
{
|
||||||
var piece = new PathControlPointPiece(slider, Pieces.Count)
|
var piece = new PathControlPointPiece(slider, Pieces.Count);
|
||||||
{
|
|
||||||
ControlPointsChanged = c => ControlPointsChanged?.Invoke(c),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (allowSelection)
|
if (allowSelection)
|
||||||
piece.RequestSelection = selectPiece;
|
piece.RequestSelection = selectPiece;
|
||||||
@ -68,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
Pieces.Add(piece);
|
Pieces.Add(piece);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (slider.Path.ControlPoints.Length < Pieces.Count)
|
while (slider.Path.ControlPoints.Count < Pieces.Count)
|
||||||
Pieces.Remove(Pieces[Pieces.Count - 1]);
|
Pieces.Remove(Pieces[Pieces.Count - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,38 +100,40 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
private bool deleteSelected()
|
private bool deleteSelected()
|
||||||
{
|
{
|
||||||
var newControlPoints = new List<Vector2>();
|
List<PathControlPoint> toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => slider.Path.ControlPoints[p.Index]).ToList();
|
||||||
|
|
||||||
foreach (var piece in Pieces)
|
|
||||||
{
|
|
||||||
if (!piece.IsSelected.Value)
|
|
||||||
newControlPoints.Add(slider.Path.ControlPoints[piece.Index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that there are any points to be deleted
|
// Ensure that there are any points to be deleted
|
||||||
if (newControlPoints.Count == slider.Path.ControlPoints.Length)
|
if (toRemove.Count == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If there are 0 remaining control points, treat the slider as being deleted
|
foreach (var c in toRemove)
|
||||||
if (newControlPoints.Count == 0)
|
{
|
||||||
|
// The first control point in the slider must have a type, so take it from the previous "first" one
|
||||||
|
// Todo: Should be handled within SliderPath itself
|
||||||
|
if (c == slider.Path.ControlPoints[0] && slider.Path.ControlPoints.Count > 1 && slider.Path.ControlPoints[1].Type.Value == null)
|
||||||
|
slider.Path.ControlPoints[1].Type.Value = slider.Path.ControlPoints[0].Type.Value;
|
||||||
|
|
||||||
|
slider.Path.ControlPoints.Remove(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are 0 or 1 remaining control points, the slider is in a degenerate (single point) form and should be deleted
|
||||||
|
if (slider.Path.ControlPoints.Count <= 1)
|
||||||
{
|
{
|
||||||
placementHandler?.Delete(slider);
|
placementHandler?.Delete(slider);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make control points relative
|
// The path will have a non-zero offset if the head is removed, but sliders don't support this behaviour since the head is positioned at the slider's position
|
||||||
Vector2 first = newControlPoints[0];
|
// So the slider needs to be offset by this amount instead, and all control points offset backwards such that the path is re-positioned at (0, 0)
|
||||||
for (int i = 0; i < newControlPoints.Count; i++)
|
Vector2 first = slider.Path.ControlPoints[0].Position.Value;
|
||||||
newControlPoints[i] = newControlPoints[i] - first;
|
foreach (var c in slider.Path.ControlPoints)
|
||||||
|
c.Position.Value -= first;
|
||||||
// The slider's position defines the position of the first control point, and all further control points are relative to that point
|
|
||||||
slider.Position += first;
|
slider.Position += first;
|
||||||
|
|
||||||
// Since pieces are re-used, they will not point to the deleted control points while remaining selected
|
// Since pieces are re-used, they will not point to the deleted control points while remaining selected
|
||||||
foreach (var piece in Pieces)
|
foreach (var piece in Pieces)
|
||||||
piece.IsSelected.Value = false;
|
piece.IsSelected.Value = false;
|
||||||
|
|
||||||
ControlPointsChanged?.Invoke(newControlPoints.ToArray());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +151,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
return new MenuItem[]
|
return new MenuItem[]
|
||||||
{
|
{
|
||||||
new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints)}", MenuItemType.Destructive, () => deleteSelected())
|
new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints, selectedPoints > 1 ? ShowQuantityAs.Numeric : ShowQuantityAs.None)}", MenuItemType.Destructive, () => deleteSelected())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
@ -27,11 +24,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
private HitCirclePiece headCirclePiece;
|
private HitCirclePiece headCirclePiece;
|
||||||
private HitCirclePiece tailCirclePiece;
|
private HitCirclePiece tailCirclePiece;
|
||||||
|
|
||||||
private readonly List<Segment> segments = new List<Segment>();
|
|
||||||
private Vector2 cursor;
|
|
||||||
private InputManager inputManager;
|
private InputManager inputManager;
|
||||||
|
|
||||||
private PlacementState state;
|
private PlacementState state;
|
||||||
|
private PathControlPoint segmentStart;
|
||||||
|
private PathControlPoint cursor;
|
||||||
|
private int currentSegmentLength;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private HitObjectComposer composer { get; set; }
|
private HitObjectComposer composer { get; set; }
|
||||||
@ -40,7 +38,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
: base(new Objects.Slider())
|
: base(new Objects.Slider())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
segments.Add(new Segment(Vector2.Zero));
|
|
||||||
|
HitObject.Path.ControlPoints.Add(segmentStart = new PathControlPoint(Vector2.Zero, PathType.Linear));
|
||||||
|
currentSegmentLength = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
bodyPiece = new SliderBodyPiece(),
|
bodyPiece = new SliderBodyPiece(),
|
||||||
headCirclePiece = new HitCirclePiece(),
|
headCirclePiece = new HitCirclePiece(),
|
||||||
tailCirclePiece = new HitCirclePiece(),
|
tailCirclePiece = new HitCirclePiece(),
|
||||||
new PathControlPointVisualiser(HitObject, false) { ControlPointsChanged = _ => updateSlider() },
|
new PathControlPointVisualiser(HitObject, false)
|
||||||
};
|
};
|
||||||
|
|
||||||
setState(PlacementState.Initial);
|
setState(PlacementState.Initial);
|
||||||
@ -72,9 +72,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PlacementState.Body:
|
case PlacementState.Body:
|
||||||
|
ensureCursor();
|
||||||
|
|
||||||
// The given screen-space position may have been externally snapped, but the unsnapped position from the input manager
|
// The given screen-space position may have been externally snapped, but the unsnapped position from the input manager
|
||||||
// is used instead since snapping control points doesn't make much sense
|
// is used instead since snapping control points doesn't make much sense
|
||||||
cursor = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position;
|
cursor.Position.Value = ToLocalSpace(inputManager.CurrentState.Mouse.Position) - HitObject.Position;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +93,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
switch (e.Button)
|
switch (e.Button)
|
||||||
{
|
{
|
||||||
case MouseButton.Left:
|
case MouseButton.Left:
|
||||||
segments.Last().ControlPoints.Add(cursor);
|
ensureCursor();
|
||||||
|
|
||||||
|
// Detatch the cursor
|
||||||
|
cursor = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +115,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
protected override bool OnDoubleClick(DoubleClickEvent e)
|
protected override bool OnDoubleClick(DoubleClickEvent e)
|
||||||
{
|
{
|
||||||
segments.Add(new Segment(segments[segments.Count - 1].ControlPoints.Last()));
|
// Todo: This should all not occur on double click, but rather if the previous control point is hovered.
|
||||||
|
segmentStart = HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 1];
|
||||||
|
segmentStart.Type.Value = PathType.Linear;
|
||||||
|
|
||||||
|
currentSegmentLength = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,14 +141,39 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
updateSlider();
|
updateSlider();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updatePathType()
|
||||||
|
{
|
||||||
|
switch (currentSegmentLength)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
segmentStart.Type.Value = PathType.Linear;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
segmentStart.Type.Value = PathType.PerfectCurve;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
segmentStart.Type.Value = PathType.Bezier;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureCursor()
|
||||||
|
{
|
||||||
|
if (cursor == null)
|
||||||
|
{
|
||||||
|
HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = { Value = Vector2.Zero } });
|
||||||
|
currentSegmentLength++;
|
||||||
|
|
||||||
|
updatePathType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateSlider()
|
private void updateSlider()
|
||||||
{
|
{
|
||||||
Vector2[] newControlPoints = segments.SelectMany(s => s.ControlPoints).Concat(cursor.Yield()).ToArray();
|
HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance;
|
||||||
|
|
||||||
var unsnappedPath = new SliderPath(newControlPoints.Length > 2 ? PathType.Bezier : PathType.Linear, newControlPoints);
|
|
||||||
var snappedDistance = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)unsnappedPath.Distance) ?? (float)unsnappedPath.Distance;
|
|
||||||
|
|
||||||
HitObject.Path = new SliderPath(unsnappedPath.Type, newControlPoints, snappedDistance);
|
|
||||||
|
|
||||||
bodyPiece.UpdateFrom(HitObject);
|
bodyPiece.UpdateFrom(HitObject);
|
||||||
headCirclePiece.UpdateFrom(HitObject.HeadCircle);
|
headCirclePiece.UpdateFrom(HitObject.HeadCircle);
|
||||||
@ -156,15 +190,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
Initial,
|
Initial,
|
||||||
Body,
|
Body,
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Segment
|
|
||||||
{
|
|
||||||
public readonly List<Vector2> ControlPoints = new List<Vector2>();
|
|
||||||
|
|
||||||
public Segment(Vector2 offset)
|
|
||||||
{
|
|
||||||
ControlPoints.Add(offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
@ -11,7 +11,6 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
@ -40,10 +39,20 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
BodyPiece = new SliderBodyPiece(),
|
BodyPiece = new SliderBodyPiece(),
|
||||||
HeadBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.Start),
|
HeadBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.Start),
|
||||||
TailBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.End),
|
TailBlueprint = CreateCircleSelectionBlueprint(slider, SliderPosition.End),
|
||||||
ControlPointVisualiser = new PathControlPointVisualiser(sliderObject, true) { ControlPointsChanged = onNewControlPoints },
|
ControlPointVisualiser = new PathControlPointVisualiser(sliderObject, true)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IBindable<int> pathVersion;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
pathVersion = HitObject.Path.Version.GetBoundCopy();
|
||||||
|
pathVersion.BindValueChanged(_ => updatePath());
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
@ -77,12 +86,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
{
|
{
|
||||||
Debug.Assert(placementControlPointIndex != null);
|
Debug.Assert(placementControlPointIndex != null);
|
||||||
|
|
||||||
Vector2 position = e.MousePosition - HitObject.Position;
|
HitObject.Path.ControlPoints[placementControlPointIndex.Value].Position.Value = e.MousePosition - HitObject.Position;
|
||||||
|
|
||||||
var controlPoints = HitObject.Path.ControlPoints.ToArray();
|
|
||||||
controlPoints[placementControlPointIndex.Value] = position;
|
|
||||||
|
|
||||||
onNewControlPoints(controlPoints);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -97,15 +101,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
{
|
{
|
||||||
position -= HitObject.Position;
|
position -= HitObject.Position;
|
||||||
|
|
||||||
var controlPoints = new Vector2[HitObject.Path.ControlPoints.Length + 1];
|
|
||||||
HitObject.Path.ControlPoints.CopyTo(controlPoints);
|
|
||||||
|
|
||||||
int insertionIndex = 0;
|
int insertionIndex = 0;
|
||||||
float minDistance = float.MaxValue;
|
float minDistance = float.MaxValue;
|
||||||
|
|
||||||
for (int i = 0; i < controlPoints.Length - 2; i++)
|
for (int i = 0; i < HitObject.Path.ControlPoints.Count - 1; i++)
|
||||||
{
|
{
|
||||||
float dist = new Line(controlPoints[i], controlPoints[i + 1]).DistanceToPoint(position);
|
float dist = new Line(HitObject.Path.ControlPoints[i].Position.Value, HitObject.Path.ControlPoints[i + 1].Position.Value).DistanceToPoint(position);
|
||||||
|
|
||||||
if (dist < minDistance)
|
if (dist < minDistance)
|
||||||
{
|
{
|
||||||
@ -115,21 +116,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Move the control points from the insertion index onwards to make room for the insertion
|
// Move the control points from the insertion index onwards to make room for the insertion
|
||||||
Array.Copy(controlPoints, insertionIndex, controlPoints, insertionIndex + 1, controlPoints.Length - insertionIndex - 1);
|
HitObject.Path.ControlPoints.Insert(insertionIndex, new PathControlPoint { Position = { Value = position } });
|
||||||
controlPoints[insertionIndex] = position;
|
|
||||||
|
|
||||||
onNewControlPoints(controlPoints);
|
|
||||||
|
|
||||||
return insertionIndex;
|
return insertionIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onNewControlPoints(Vector2[] controlPoints)
|
private void updatePath()
|
||||||
{
|
{
|
||||||
var unsnappedPath = new SliderPath(controlPoints.Length > 2 ? PathType.Bezier : PathType.Linear, controlPoints);
|
HitObject.Path.ExpectedDistance.Value = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance;
|
||||||
var snappedDistance = composer?.GetSnappedDistanceFromDistance(HitObject.StartTime, (float)unsnappedPath.Distance) ?? (float)unsnappedPath.Distance;
|
|
||||||
|
|
||||||
HitObject.Path = new SliderPath(unsnappedPath.Type, controlPoints, snappedDistance);
|
|
||||||
|
|
||||||
UpdateHitObject();
|
UpdateHitObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,11 +28,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||||
slider.NestedHitObjects.OfType<RepeatPoint>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
slider.NestedHitObjects.OfType<RepeatPoint>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||||
|
|
||||||
var newControlPoints = new Vector2[slider.Path.ControlPoints.Length];
|
foreach (var point in slider.Path.ControlPoints)
|
||||||
for (int i = 0; i < slider.Path.ControlPoints.Length; i++)
|
point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);
|
||||||
newControlPoints[i] = new Vector2(slider.Path.ControlPoints[i].X, -slider.Path.ControlPoints[i].Y);
|
|
||||||
|
|
||||||
slider.Path = new SliderPath(slider.Path.Type, newControlPoints, slider.Path.ExpectedDistance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
||||||
private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
|
private readonly IBindable<int> stackHeightBindable = new Bindable<int>();
|
||||||
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
private readonly IBindable<float> scaleBindable = new Bindable<float>();
|
||||||
private readonly IBindable<SliderPath> pathBindable = new Bindable<SliderPath>();
|
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private OsuRulesetConfigManager config { get; set; }
|
private OsuRulesetConfigManager config { get; set; }
|
||||||
@ -84,9 +84,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
positionBindable.BindTo(HitObject.PositionBindable);
|
positionBindable.BindTo(HitObject.PositionBindable);
|
||||||
stackHeightBindable.BindTo(HitObject.StackHeightBindable);
|
stackHeightBindable.BindTo(HitObject.StackHeightBindable);
|
||||||
scaleBindable.BindTo(HitObject.ScaleBindable);
|
scaleBindable.BindTo(HitObject.ScaleBindable);
|
||||||
pathBindable.BindTo(slider.PathBindable);
|
pathVersion.BindTo(slider.Path.Version);
|
||||||
|
|
||||||
pathBindable.BindValueChanged(_ => Body.Refresh());
|
pathVersion.BindValueChanged(_ => Body.Refresh());
|
||||||
|
|
||||||
AccentColour.BindValueChanged(colour =>
|
AccentColour.BindValueChanged(colour =>
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -13,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
public class DrawableSliderHead : DrawableHitCircle
|
public class DrawableSliderHead : DrawableHitCircle
|
||||||
{
|
{
|
||||||
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
||||||
private readonly IBindable<SliderPath> pathBindable = new Bindable<SliderPath>();
|
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
||||||
|
|
||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
|
|
||||||
@ -27,10 +26,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
positionBindable.BindTo(HitObject.PositionBindable);
|
positionBindable.BindTo(HitObject.PositionBindable);
|
||||||
pathBindable.BindTo(slider.PathBindable);
|
pathVersion.BindTo(slider.Path.Version);
|
||||||
|
|
||||||
positionBindable.BindValueChanged(_ => updatePosition());
|
positionBindable.BindValueChanged(_ => updatePosition());
|
||||||
pathBindable.BindValueChanged(_ => updatePosition(), true);
|
pathVersion.BindValueChanged(_ => updatePosition(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Objects;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -21,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
public bool Tracking { get; set; }
|
public bool Tracking { get; set; }
|
||||||
|
|
||||||
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
||||||
private readonly IBindable<SliderPath> pathBindable = new Bindable<SliderPath>();
|
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
||||||
|
|
||||||
public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
|
public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
|
||||||
: base(hitCircle)
|
: base(hitCircle)
|
||||||
@ -36,10 +35,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
AlwaysPresent = true;
|
AlwaysPresent = true;
|
||||||
|
|
||||||
positionBindable.BindTo(hitCircle.PositionBindable);
|
positionBindable.BindTo(hitCircle.PositionBindable);
|
||||||
pathBindable.BindTo(slider.PathBindable);
|
pathVersion.BindTo(slider.Path.Version);
|
||||||
|
|
||||||
positionBindable.BindValueChanged(_ => updatePosition());
|
positionBindable.BindValueChanged(_ => updatePosition());
|
||||||
pathBindable.BindValueChanged(_ => updatePosition(), true);
|
pathVersion.BindValueChanged(_ => updatePosition(), true);
|
||||||
|
|
||||||
// TODO: This has no drawable content. Support for skins should be added.
|
// TODO: This has no drawable content. Support for skins should be added.
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ using osu.Game.Rulesets.Objects.Types;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -28,17 +27,21 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);
|
public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);
|
||||||
|
|
||||||
public readonly Bindable<SliderPath> PathBindable = new Bindable<SliderPath>();
|
private readonly SliderPath path = new SliderPath();
|
||||||
|
|
||||||
public SliderPath Path
|
public SliderPath Path
|
||||||
{
|
{
|
||||||
get => PathBindable.Value;
|
get => path;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
PathBindable.Value = value;
|
path.ControlPoints.Clear();
|
||||||
endPositionCache.Invalidate();
|
path.ExpectedDistance.Value = null;
|
||||||
|
|
||||||
updateNestedPositions();
|
if (value != null)
|
||||||
|
{
|
||||||
|
path.ControlPoints.AddRange(value.ControlPoints);
|
||||||
|
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,8 +53,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
base.Position = value;
|
base.Position = value;
|
||||||
endPositionCache.Invalidate();
|
|
||||||
|
|
||||||
updateNestedPositions();
|
updateNestedPositions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,6 +113,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
SamplesBindable.ItemsAdded += _ => updateNestedSamples();
|
SamplesBindable.ItemsAdded += _ => updateNestedSamples();
|
||||||
SamplesBindable.ItemsRemoved += _ => updateNestedSamples();
|
SamplesBindable.ItemsRemoved += _ => updateNestedSamples();
|
||||||
|
Path.Version.ValueChanged += _ => updateNestedPositions();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||||
@ -189,6 +191,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
private void updateNestedPositions()
|
private void updateNestedPositions()
|
||||||
{
|
{
|
||||||
|
endPositionCache.Invalidate();
|
||||||
|
|
||||||
if (HeadCircle != null)
|
if (HeadCircle != null)
|
||||||
HeadCircle.Position = Position;
|
HeadCircle.Position = Position;
|
||||||
|
|
||||||
|
@ -15,12 +15,12 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class SliderTailCircle : SliderCircle
|
public class SliderTailCircle : SliderCircle
|
||||||
{
|
{
|
||||||
private readonly IBindable<SliderPath> pathBindable = new Bindable<SliderPath>();
|
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
||||||
|
|
||||||
public SliderTailCircle(Slider slider)
|
public SliderTailCircle(Slider slider)
|
||||||
{
|
{
|
||||||
pathBindable.BindTo(slider.PathBindable);
|
pathVersion.BindTo(slider.Path.Version);
|
||||||
pathBindable.BindValueChanged(_ => Position = slider.EndPosition);
|
pathVersion.BindValueChanged(_ => Position = slider.EndPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new OsuSliderTailJudgement();
|
public override Judgement CreateJudgement() => new OsuSliderTailJudgement();
|
||||||
|
@ -11,6 +11,9 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
public class LegacyCursor : CompositeDrawable
|
public class LegacyCursor : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
private NonPlayfieldSprite cursor;
|
||||||
|
private bool spin;
|
||||||
|
|
||||||
public LegacyCursor()
|
public LegacyCursor()
|
||||||
{
|
{
|
||||||
Size = new Vector2(50);
|
Size = new Vector2(50);
|
||||||
@ -22,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin)
|
private void load(ISkinSource skin)
|
||||||
{
|
{
|
||||||
|
spin = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorRotate)?.Value ?? true;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
new NonPlayfieldSprite
|
new NonPlayfieldSprite
|
||||||
@ -30,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
new NonPlayfieldSprite
|
cursor = new NonPlayfieldSprite
|
||||||
{
|
{
|
||||||
Texture = skin.GetTexture("cursor"),
|
Texture = skin.GetTexture("cursor"),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -38,5 +43,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
if (spin)
|
||||||
|
cursor.Spin(10000, RotationDirection.Clockwise);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
return this.GetAnimation(component.LookupName, true, false);
|
return this.GetAnimation(component.LookupName, true, false);
|
||||||
|
|
||||||
case OsuSkinComponents.SliderFollowCircle:
|
case OsuSkinComponents.SliderFollowCircle:
|
||||||
return this.GetAnimation("sliderfollowcircle", true, true);
|
var followCircle = this.GetAnimation("sliderfollowcircle", true, true);
|
||||||
|
if (followCircle != null)
|
||||||
|
// follow circles are 2x the hitcircle resolution in legacy skins (since they are scaled down from >1x
|
||||||
|
followCircle.Scale *= 0.5f;
|
||||||
|
return followCircle;
|
||||||
|
|
||||||
case OsuSkinComponents.SliderBall:
|
case OsuSkinComponents.SliderBall:
|
||||||
var sliderBallContent = this.GetAnimation("sliderb", true, true, "");
|
var sliderBallContent = this.GetAnimation("sliderb", true, true, "");
|
||||||
|
@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
SliderPathRadius,
|
SliderPathRadius,
|
||||||
AllowSliderBallTint,
|
AllowSliderBallTint,
|
||||||
CursorExpand,
|
CursorExpand,
|
||||||
|
CursorRotate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ using osu.Game.Rulesets.Osu.Scoring;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.UI
|
namespace osu.Game.Rulesets.Osu.UI
|
||||||
{
|
{
|
||||||
@ -30,6 +31,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; // always show the gameplay cursor
|
||||||
|
|
||||||
public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this);
|
public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this);
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new OsuPlayfield();
|
protected override Playfield CreatePlayfield() => new OsuPlayfield();
|
||||||
|
@ -31,10 +31,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
|
||||||
{
|
{
|
||||||
mods = Score.Mods;
|
mods = Score.Mods;
|
||||||
countGreat = Convert.ToInt32(Score.Statistics[HitResult.Great]);
|
countGreat = Score.Statistics[HitResult.Great];
|
||||||
countGood = Convert.ToInt32(Score.Statistics[HitResult.Good]);
|
countGood = Score.Statistics[HitResult.Good];
|
||||||
countMeh = Convert.ToInt32(Score.Statistics[HitResult.Meh]);
|
countMeh = Score.Statistics[HitResult.Meh];
|
||||||
countMiss = Convert.ToInt32(Score.Statistics[HitResult.Miss]);
|
countMiss = Score.Statistics[HitResult.Miss];
|
||||||
|
|
||||||
// Don't count scores made with supposedly unranked mods
|
// Don't count scores made with supposedly unranked mods
|
||||||
if (mods.Any(m => !m.Ranked))
|
if (mods.Any(m => !m.Ranked))
|
||||||
|
73
osu.Game.Tests/Scores/IO/TestScoreEquality.cs
Normal file
73
osu.Game.Tests/Scores/IO/TestScoreEquality.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// 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.Game.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Scores.IO
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestScoreEquality
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestNonMatchingByReference()
|
||||||
|
{
|
||||||
|
ScoreInfo score1 = new ScoreInfo();
|
||||||
|
ScoreInfo score2 = new ScoreInfo();
|
||||||
|
|
||||||
|
Assert.That(score1, Is.Not.EqualTo(score2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMatchingByReference()
|
||||||
|
{
|
||||||
|
ScoreInfo score = new ScoreInfo();
|
||||||
|
|
||||||
|
Assert.That(score, Is.EqualTo(score));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNonMatchingByPrimaryKey()
|
||||||
|
{
|
||||||
|
ScoreInfo score1 = new ScoreInfo { ID = 1 };
|
||||||
|
ScoreInfo score2 = new ScoreInfo { ID = 2 };
|
||||||
|
|
||||||
|
Assert.That(score1, Is.Not.EqualTo(score2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMatchingByPrimaryKey()
|
||||||
|
{
|
||||||
|
ScoreInfo score1 = new ScoreInfo { ID = 1 };
|
||||||
|
ScoreInfo score2 = new ScoreInfo { ID = 1 };
|
||||||
|
|
||||||
|
Assert.That(score1, Is.EqualTo(score2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNonMatchingByHash()
|
||||||
|
{
|
||||||
|
ScoreInfo score1 = new ScoreInfo { Hash = "a" };
|
||||||
|
ScoreInfo score2 = new ScoreInfo { Hash = "b" };
|
||||||
|
|
||||||
|
Assert.That(score1, Is.Not.EqualTo(score2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMatchingByHash()
|
||||||
|
{
|
||||||
|
ScoreInfo score1 = new ScoreInfo { Hash = "a" };
|
||||||
|
ScoreInfo score2 = new ScoreInfo { Hash = "a" };
|
||||||
|
|
||||||
|
Assert.That(score1, Is.EqualTo(score2));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNonMatchingByNull()
|
||||||
|
{
|
||||||
|
ScoreInfo score = new ScoreInfo();
|
||||||
|
|
||||||
|
Assert.That(score, Is.Not.EqualTo(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -223,9 +223,10 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
public void TransitionTest()
|
public void TransitionTest()
|
||||||
{
|
{
|
||||||
performFullSetup();
|
performFullSetup();
|
||||||
var results = new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } });
|
FadeAccessibleResults results = null;
|
||||||
AddStep("Transition to Results", () => player.Push(results));
|
AddStep("Transition to Results", () => player.Push(results =
|
||||||
AddUntilStep("Wait for results is current", results.IsCurrentScreen);
|
new FadeAccessibleResults(new ScoreInfo { User = new User { Username = "osu!" } })));
|
||||||
|
AddUntilStep("Wait for results is current", () => results.IsCurrentScreen());
|
||||||
waitForDim();
|
waitForDim();
|
||||||
AddAssert("Screen is undimmed, original background retained", () =>
|
AddAssert("Screen is undimmed, original background retained", () =>
|
||||||
songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect());
|
songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect());
|
||||||
|
@ -13,6 +13,8 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -25,6 +27,7 @@ namespace osu.Game.Tests.Visual.Editor
|
|||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
typeof(TimelineArea),
|
typeof(TimelineArea),
|
||||||
|
typeof(TimelineHitObjectDisplay),
|
||||||
typeof(Timeline),
|
typeof(Timeline),
|
||||||
typeof(TimelineButton),
|
typeof(TimelineButton),
|
||||||
typeof(CentreMarker)
|
typeof(CentreMarker)
|
||||||
@ -35,6 +38,8 @@ namespace osu.Game.Tests.Visual.Editor
|
|||||||
{
|
{
|
||||||
Beatmap.Value = new WaveformTestBeatmap(audio);
|
Beatmap.Value = new WaveformTestBeatmap(audio);
|
||||||
|
|
||||||
|
var editorBeatmap = new EditorBeatmap<HitObject>((Beatmap<HitObject>)Beatmap.Value.Beatmap);
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
@ -50,6 +55,7 @@ namespace osu.Game.Tests.Visual.Editor
|
|||||||
},
|
},
|
||||||
new TimelineArea
|
new TimelineArea
|
||||||
{
|
{
|
||||||
|
Child = new TimelineHitObjectDisplay(editorBeatmap),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
|
@ -115,8 +115,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestExitTooSoon()
|
public void TestExitTooSoon()
|
||||||
{
|
{
|
||||||
pauseAndConfirm();
|
AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000));
|
||||||
|
|
||||||
|
pauseAndConfirm();
|
||||||
resume();
|
resume();
|
||||||
|
|
||||||
AddStep("exit too soon", () => Player.Exit());
|
AddStep("exit too soon", () => Player.Exit());
|
||||||
@ -176,7 +177,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public void TestExitFromGameplay()
|
public void TestExitFromGameplay()
|
||||||
{
|
{
|
||||||
AddStep("exit", () => Player.Exit());
|
AddStep("exit", () => Player.Exit());
|
||||||
|
confirmPaused();
|
||||||
|
|
||||||
|
AddStep("exit", () => Player.Exit());
|
||||||
confirmExited();
|
confirmExited();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,6 +217,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestRestartAfterResume()
|
public void TestRestartAfterResume()
|
||||||
{
|
{
|
||||||
|
AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000));
|
||||||
|
|
||||||
pauseAndConfirm();
|
pauseAndConfirm();
|
||||||
resumeAndConfirm();
|
resumeAndConfirm();
|
||||||
restart();
|
restart();
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
@ -19,6 +20,7 @@ using osu.Game.Overlays;
|
|||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
@ -55,6 +57,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
beforeLoadAction?.Invoke();
|
beforeLoadAction?.Invoke();
|
||||||
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
|
foreach (var mod in Mods.Value.OfType<IApplicableToTrack>())
|
||||||
|
mod.ApplyToTrack(Beatmap.Value.Track);
|
||||||
|
|
||||||
InputManager.Child = container = new TestPlayerLoaderContainer(
|
InputManager.Child = container = new TestPlayerLoaderContainer(
|
||||||
loader = new TestPlayerLoader(() =>
|
loader = new TestPlayerLoader(() =>
|
||||||
{
|
{
|
||||||
@ -63,6 +68,24 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When <see cref="PlayerLoader"/> exits early, it has to wait for the player load task
|
||||||
|
/// to complete before running disposal on player. This previously caused an issue where mod
|
||||||
|
/// speed adjustments were undone too late, causing cross-screen pollution.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestEarlyExit()
|
||||||
|
{
|
||||||
|
AddStep("load dummy beatmap", () => ResetPlayer(false, () => Mods.Value = new[] { new OsuModNightcore() }));
|
||||||
|
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
|
||||||
|
AddAssert("mod rate applied", () => Beatmap.Value.Track.Rate != 1);
|
||||||
|
AddStep("exit loader", () => loader.Exit());
|
||||||
|
AddUntilStep("wait for not current", () => !loader.IsCurrentScreen());
|
||||||
|
AddAssert("player did not load", () => !player.IsLoaded);
|
||||||
|
AddUntilStep("player disposed", () => loader.DisposalTask?.IsCompleted == true);
|
||||||
|
AddAssert("mod rate still applied", () => Beatmap.Value.Track.Rate != 1);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBlockLoadViaMouseMovement()
|
public void TestBlockLoadViaMouseMovement()
|
||||||
{
|
{
|
||||||
@ -196,6 +219,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
public new VisualSettings VisualSettings => base.VisualSettings;
|
public new VisualSettings VisualSettings => base.VisualSettings;
|
||||||
|
|
||||||
|
public new Task DisposalTask => base.DisposalTask;
|
||||||
|
|
||||||
public TestPlayerLoader(Func<Player> createPlayer)
|
public TestPlayerLoader(Func<Player> createPlayer)
|
||||||
: base(createPlayer)
|
: base(createPlayer)
|
||||||
{
|
{
|
||||||
|
@ -5,11 +5,12 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Online;
|
using osu.Game.Online;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Rulesets.Osu;
|
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Screens.Ranking.Pages;
|
using osu.Game.Screens.Ranking.Pages;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
@ -17,6 +18,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneReplayDownloadButton : OsuTestScene
|
public class TestSceneReplayDownloadButton : OsuTestScene
|
||||||
{
|
{
|
||||||
|
[Resolved]
|
||||||
|
private RulesetStore rulesets { get; set; }
|
||||||
|
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
typeof(ReplayDownloadButton)
|
typeof(ReplayDownloadButton)
|
||||||
@ -49,16 +53,15 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
return new APILegacyScoreInfo
|
return new APILegacyScoreInfo
|
||||||
{
|
{
|
||||||
ID = 1,
|
|
||||||
OnlineScoreID = 2553163309,
|
OnlineScoreID = 2553163309,
|
||||||
Ruleset = new OsuRuleset().RulesetInfo,
|
OnlineRulesetID = 0,
|
||||||
Replay = replayAvailable,
|
Replay = replayAvailable,
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 39828,
|
Id = 39828,
|
||||||
Username = @"WubWoofWolf",
|
Username = @"WubWoofWolf",
|
||||||
}
|
}
|
||||||
};
|
}.CreateScoreInfo(rulesets);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestReplayDownloadButton : ReplayDownloadButton
|
private class TestReplayDownloadButton : ReplayDownloadButton
|
||||||
|
193
osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs
Normal file
193
osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Lines;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public class TestSceneSliderPath : OsuTestScene
|
||||||
|
{
|
||||||
|
private readonly SmoothPath drawablePath;
|
||||||
|
private SliderPath path;
|
||||||
|
|
||||||
|
public TestSceneSliderPath()
|
||||||
|
{
|
||||||
|
Child = drawablePath = new SmoothPath
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup() => Schedule(() =>
|
||||||
|
{
|
||||||
|
path = new SliderPath();
|
||||||
|
});
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
if (path != null)
|
||||||
|
{
|
||||||
|
List<Vector2> vertices = new List<Vector2>();
|
||||||
|
path.GetPathToProgress(vertices, 0, 1);
|
||||||
|
|
||||||
|
drawablePath.Vertices = vertices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEmptyPath()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(PathType.Linear)]
|
||||||
|
[TestCase(PathType.Bezier)]
|
||||||
|
[TestCase(PathType.Catmull)]
|
||||||
|
[TestCase(PathType.PerfectCurve)]
|
||||||
|
public void TestSingleSegment(PathType type)
|
||||||
|
=> AddStep("create path", () => path.ControlPoints.AddRange(createSegment(type, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||||
|
|
||||||
|
[TestCase(PathType.Linear)]
|
||||||
|
[TestCase(PathType.Bezier)]
|
||||||
|
[TestCase(PathType.Catmull)]
|
||||||
|
[TestCase(PathType.PerfectCurve)]
|
||||||
|
public void TestMultipleSegment(PathType type)
|
||||||
|
{
|
||||||
|
AddStep("create path", () =>
|
||||||
|
{
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero));
|
||||||
|
path.ControlPoints.AddRange(createSegment(type, new Vector2(0, 100), new Vector2(100), Vector2.Zero));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddControlPoint()
|
||||||
|
{
|
||||||
|
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100))));
|
||||||
|
AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = { Value = new Vector2(100) } }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestInsertControlPoint()
|
||||||
|
{
|
||||||
|
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100))));
|
||||||
|
AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = { Value = new Vector2(0, 100) } }));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRemoveControlPoint()
|
||||||
|
{
|
||||||
|
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||||
|
AddStep("remove second point", () => path.ControlPoints.RemoveAt(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestChangePathType()
|
||||||
|
{
|
||||||
|
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||||
|
AddStep("change type to bezier", () => path.ControlPoints[0].Type.Value = PathType.Bezier);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestAddSegmentByChangingType()
|
||||||
|
{
|
||||||
|
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))));
|
||||||
|
AddStep("change second point type to bezier", () => path.ControlPoints[1].Type.Value = PathType.Bezier);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRemoveSegmentByChangingType()
|
||||||
|
{
|
||||||
|
AddStep("create path", () =>
|
||||||
|
{
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||||
|
path.ControlPoints[1].Type.Value = PathType.Bezier;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("change second point type to null", () => path.ControlPoints[1].Type.Value = null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRemoveSegmentByRemovingControlPoint()
|
||||||
|
{
|
||||||
|
AddStep("create path", () =>
|
||||||
|
{
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||||
|
path.ControlPoints[1].Type.Value = PathType.Bezier;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("remove second point", () => path.ControlPoints.RemoveAt(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(2)]
|
||||||
|
[TestCase(4)]
|
||||||
|
public void TestPerfectCurveFallbackScenarios(int points)
|
||||||
|
{
|
||||||
|
AddStep("create path", () =>
|
||||||
|
{
|
||||||
|
switch (points)
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLengthenLastSegment()
|
||||||
|
{
|
||||||
|
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||||
|
AddStep("lengthen last segment", () => path.ExpectedDistance.Value = 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShortenLastSegment()
|
||||||
|
{
|
||||||
|
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||||
|
AddStep("shorten last segment", () => path.ExpectedDistance.Value = 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShortenFirstSegment()
|
||||||
|
{
|
||||||
|
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||||
|
AddStep("shorten first segment", () => path.ExpectedDistance.Value = 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShortenToZeroLength()
|
||||||
|
{
|
||||||
|
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||||
|
AddStep("shorten to 0 length", () => path.ExpectedDistance.Value = 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestShortenToNegativeLength()
|
||||||
|
{
|
||||||
|
AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100))));
|
||||||
|
AddStep("shorten to -10 length", () => path.ExpectedDistance.Value = -10);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PathControlPoint> createSegment(PathType type, params Vector2[] controlPoints)
|
||||||
|
{
|
||||||
|
var points = controlPoints.Select(p => new PathControlPoint { Position = { Value = p } }).ToList();
|
||||||
|
points[0].Type.Value = type;
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard
|
private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard
|
||||||
{
|
{
|
||||||
protected override APIRequest FetchScores(Action<IEnumerable<APIRoomScoreInfo>> scoresCallback)
|
protected override APIRequest FetchScores(Action<IEnumerable<APIUserScoreAggregate>> scoresCallback)
|
||||||
{
|
{
|
||||||
var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray();
|
var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray();
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private APIRoomScoreInfo createRoomScore(int id) => new APIRoomScoreInfo
|
private APIUserScoreAggregate createRoomScore(int id) => new APIUserScoreAggregate
|
||||||
{
|
{
|
||||||
User = new User { Id = id, Username = $"User {id}" },
|
User = new User { Id = id, Username = $"User {id}" },
|
||||||
Accuracy = 0.98,
|
Accuracy = 0.98,
|
||||||
|
129
osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs
Normal file
129
osu.Game.Tests/Visual/Online/TestSceneRankingsTables.cs
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// 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.Game.Overlays.Rankings.Tables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Online.API.Requests;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using System.Threading;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
|
using osu.Game.Rulesets.Taiko;
|
||||||
|
using osu.Game.Rulesets.Catch;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public class TestSceneRankingsTables : OsuTestScene
|
||||||
|
{
|
||||||
|
protected override bool UseOnlineAPI => true;
|
||||||
|
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(PerformanceTable),
|
||||||
|
typeof(ScoresTable),
|
||||||
|
typeof(CountriesTable),
|
||||||
|
typeof(TableRowBackground),
|
||||||
|
typeof(UserBasedTable),
|
||||||
|
typeof(RankingsTable<>)
|
||||||
|
};
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
|
private readonly BasicScrollContainer scrollFlow;
|
||||||
|
private readonly DimmedLoadingLayer loading;
|
||||||
|
private CancellationTokenSource cancellationToken;
|
||||||
|
private APIRequest request;
|
||||||
|
|
||||||
|
public TestSceneRankingsTables()
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
scrollFlow = new BasicScrollContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Width = 0.8f,
|
||||||
|
},
|
||||||
|
loading = new DimmedLoadingLayer(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
AddStep("Osu performance", () => createPerformanceTable(new OsuRuleset().RulesetInfo, null));
|
||||||
|
AddStep("Mania scores", () => createScoreTable(new ManiaRuleset().RulesetInfo));
|
||||||
|
AddStep("Taiko country scores", () => createCountryTable(new TaikoRuleset().RulesetInfo));
|
||||||
|
AddStep("Catch US performance page 10", () => createPerformanceTable(new CatchRuleset().RulesetInfo, "US", 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCountryTable(RulesetInfo ruleset, int page = 1)
|
||||||
|
{
|
||||||
|
onLoadStarted();
|
||||||
|
|
||||||
|
request = new GetCountryRankingsRequest(ruleset, page);
|
||||||
|
((GetCountryRankingsRequest)request).Success += rankings => Schedule(() =>
|
||||||
|
{
|
||||||
|
var table = new CountriesTable(page, rankings.Countries);
|
||||||
|
loadTable(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
api.Queue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createPerformanceTable(RulesetInfo ruleset, string country, int page = 1)
|
||||||
|
{
|
||||||
|
onLoadStarted();
|
||||||
|
|
||||||
|
request = new GetUserRankingsRequest(ruleset, country: country, page: page);
|
||||||
|
((GetUserRankingsRequest)request).Success += rankings => Schedule(() =>
|
||||||
|
{
|
||||||
|
var table = new PerformanceTable(page, rankings.Users);
|
||||||
|
loadTable(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
api.Queue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createScoreTable(RulesetInfo ruleset, int page = 1)
|
||||||
|
{
|
||||||
|
onLoadStarted();
|
||||||
|
|
||||||
|
request = new GetUserRankingsRequest(ruleset, UserRankingsType.Score, page);
|
||||||
|
((GetUserRankingsRequest)request).Success += rankings => Schedule(() =>
|
||||||
|
{
|
||||||
|
var table = new ScoresTable(page, rankings.Users);
|
||||||
|
loadTable(table);
|
||||||
|
});
|
||||||
|
|
||||||
|
api.Queue(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onLoadStarted()
|
||||||
|
{
|
||||||
|
loading.Show();
|
||||||
|
request?.Cancel();
|
||||||
|
cancellationToken?.Cancel();
|
||||||
|
cancellationToken = new CancellationTokenSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadTable(Drawable table)
|
||||||
|
{
|
||||||
|
LoadComponentAsync(table, t =>
|
||||||
|
{
|
||||||
|
scrollFlow.Clear();
|
||||||
|
scrollFlow.Add(t);
|
||||||
|
loading.Hide();
|
||||||
|
}, cancellationToken.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -9,9 +9,7 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays.BeatmapSet.Scores;
|
using osu.Game.Overlays.BeatmapSet.Scores;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -66,12 +64,12 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
FlagName = @"ES",
|
FlagName = @"ES",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mods = new Mod[]
|
Mods = new[]
|
||||||
{
|
{
|
||||||
new OsuModDoubleTime(),
|
new OsuModDoubleTime().Acronym,
|
||||||
new OsuModHidden(),
|
new OsuModHidden().Acronym,
|
||||||
new OsuModFlashlight(),
|
new OsuModFlashlight().Acronym,
|
||||||
new OsuModHardRock(),
|
new OsuModHardRock().Acronym,
|
||||||
},
|
},
|
||||||
Rank = ScoreRank.XH,
|
Rank = ScoreRank.XH,
|
||||||
PP = 200,
|
PP = 200,
|
||||||
@ -91,11 +89,11 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
FlagName = @"BR",
|
FlagName = @"BR",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mods = new Mod[]
|
Mods = new[]
|
||||||
{
|
{
|
||||||
new OsuModDoubleTime(),
|
new OsuModDoubleTime().Acronym,
|
||||||
new OsuModHidden(),
|
new OsuModHidden().Acronym,
|
||||||
new OsuModFlashlight(),
|
new OsuModFlashlight().Acronym,
|
||||||
},
|
},
|
||||||
Rank = ScoreRank.S,
|
Rank = ScoreRank.S,
|
||||||
PP = 190,
|
PP = 190,
|
||||||
@ -115,10 +113,10 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
FlagName = @"JP",
|
FlagName = @"JP",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mods = new Mod[]
|
Mods = new[]
|
||||||
{
|
{
|
||||||
new OsuModDoubleTime(),
|
new OsuModDoubleTime().Acronym,
|
||||||
new OsuModHidden(),
|
new OsuModHidden().Acronym,
|
||||||
},
|
},
|
||||||
Rank = ScoreRank.B,
|
Rank = ScoreRank.B,
|
||||||
PP = 180,
|
PP = 180,
|
||||||
@ -138,9 +136,9 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
FlagName = @"CA",
|
FlagName = @"CA",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mods = new Mod[]
|
Mods = new[]
|
||||||
{
|
{
|
||||||
new OsuModDoubleTime(),
|
new OsuModDoubleTime().Acronym,
|
||||||
},
|
},
|
||||||
Rank = ScoreRank.C,
|
Rank = ScoreRank.C,
|
||||||
PP = 170,
|
PP = 170,
|
||||||
@ -208,12 +206,12 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
FlagName = @"ES",
|
FlagName = @"ES",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mods = new Mod[]
|
Mods = new[]
|
||||||
{
|
{
|
||||||
new OsuModDoubleTime(),
|
new OsuModDoubleTime().Acronym,
|
||||||
new OsuModHidden(),
|
new OsuModHidden().Acronym,
|
||||||
new OsuModFlashlight(),
|
new OsuModFlashlight().Acronym,
|
||||||
new OsuModHardRock(),
|
new OsuModHardRock().Acronym,
|
||||||
},
|
},
|
||||||
Rank = ScoreRank.XH,
|
Rank = ScoreRank.XH,
|
||||||
PP = 200,
|
PP = 200,
|
||||||
@ -226,10 +224,13 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
foreach (var s in allScores.Scores)
|
foreach (var s in allScores.Scores)
|
||||||
{
|
{
|
||||||
s.Statistics.Add(HitResult.Great, RNG.Next(2000));
|
s.Statistics = new Dictionary<string, int>
|
||||||
s.Statistics.Add(HitResult.Good, RNG.Next(2000));
|
{
|
||||||
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
|
{ "count_300", RNG.Next(2000) },
|
||||||
s.Statistics.Add(HitResult.Miss, RNG.Next(2000));
|
{ "count_100", RNG.Next(2000) },
|
||||||
|
{ "count_50", RNG.Next(2000) },
|
||||||
|
{ "count_miss", RNG.Next(2000) }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
AddStep("Load all scores", () =>
|
AddStep("Load all scores", () =>
|
||||||
|
65
osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs
Normal file
65
osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Settings
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneSettingsSource : OsuTestScene
|
||||||
|
{
|
||||||
|
public TestSceneSettingsSource()
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(20),
|
||||||
|
Width = 0.5f,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Padding = new MarginPadding(50),
|
||||||
|
ChildrenEnumerable = new TestTargetClass().CreateSettingsControls()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestTargetClass
|
||||||
|
{
|
||||||
|
[SettingSource("Sample bool", "Clicking this changes a setting")]
|
||||||
|
public BindableBool TickBindable { get; } = new BindableBool();
|
||||||
|
|
||||||
|
[SettingSource("Sample float", "Change something for a mod")]
|
||||||
|
public BindableFloat SliderBindable { get; } = new BindableFloat
|
||||||
|
{
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = 10,
|
||||||
|
Default = 5,
|
||||||
|
Value = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Sample enum", "Change something for a mod")]
|
||||||
|
public Bindable<TestEnum> EnumBindable { get; } = new Bindable<TestEnum>
|
||||||
|
{
|
||||||
|
Default = TestEnum.Value1,
|
||||||
|
Value = TestEnum.Value2
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Sample string", "Change something for a mod")]
|
||||||
|
public Bindable<string> StringBindable { get; } = new Bindable<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TestEnum
|
||||||
|
{
|
||||||
|
Value1,
|
||||||
|
Value2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Online.Leaderboards;
|
using osu.Game.Online.Leaderboards;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Select.Leaderboards;
|
using osu.Game.Screens.Select.Leaderboards;
|
||||||
@ -62,7 +61,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 1,
|
Accuracy = 1,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new[] { new OsuModHidden().Acronym, new OsuModHardRock().Acronym, },
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 6602580,
|
Id = 6602580,
|
||||||
|
@ -95,6 +95,42 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
AddAssert("filter count is 1", () => songSelect.FilterCount == 1);
|
AddAssert("filter count is 1", () => songSelect.FilterCount == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoFilterOnSimpleResume()
|
||||||
|
{
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
|
||||||
|
createSongSelect();
|
||||||
|
|
||||||
|
AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child")));
|
||||||
|
AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
|
||||||
|
|
||||||
|
AddStep("return", () => songSelect.MakeCurrent());
|
||||||
|
AddUntilStep("wait for current", () => songSelect.IsCurrentScreen());
|
||||||
|
AddAssert("filter count is 1", () => songSelect.FilterCount == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFilterOnResumeAfterChange()
|
||||||
|
{
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
|
||||||
|
AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, false));
|
||||||
|
|
||||||
|
createSongSelect();
|
||||||
|
|
||||||
|
AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child")));
|
||||||
|
AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
|
||||||
|
|
||||||
|
AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true));
|
||||||
|
|
||||||
|
AddStep("return", () => songSelect.MakeCurrent());
|
||||||
|
AddUntilStep("wait for current", () => songSelect.IsCurrentScreen());
|
||||||
|
AddAssert("filter count is 2", () => songSelect.FilterCount == 2);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAudioResuming()
|
public void TestAudioResuming()
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,6 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Screens.Select.Leaderboards;
|
using osu.Game.Screens.Select.Leaderboards;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -52,7 +51,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Accuracy = 1,
|
Accuracy = 1,
|
||||||
MaxCombo = 244,
|
MaxCombo = 244,
|
||||||
TotalScore = 1707827,
|
TotalScore = 1707827,
|
||||||
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
|
Mods = new[] { new OsuModHidden().Acronym, new OsuModHardRock().Acronym, },
|
||||||
User = new User
|
User = new User
|
||||||
{
|
{
|
||||||
Id = 6602580,
|
Id = 6602580,
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
AddAssert("Parallax is off", () => stack.ParallaxAmount == 0);
|
AddAssert("Parallax is off", () => stack.ParallaxAmount == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestScreen : ScreenWithBeatmapBackground
|
public class TestScreen : ScreenWithBeatmapBackground
|
||||||
{
|
{
|
||||||
private readonly string screenText;
|
private readonly string screenText;
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays.Dialog;
|
using osu.Game.Overlays.Dialog;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.UserInterface
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
@ -11,13 +14,22 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestScenePopupDialog : OsuTestScene
|
public class TestScenePopupDialog : OsuTestScene
|
||||||
{
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(PopupDialogOkButton),
|
||||||
|
typeof(PopupDialogCancelButton),
|
||||||
|
typeof(PopupDialogButton),
|
||||||
|
typeof(DialogButton),
|
||||||
|
};
|
||||||
|
|
||||||
public TestScenePopupDialog()
|
public TestScenePopupDialog()
|
||||||
{
|
{
|
||||||
Add(new TestPopupDialog
|
AddStep("new popup", () =>
|
||||||
{
|
Add(new TestPopupDialog
|
||||||
RelativeSizeAxes = Axes.Both,
|
{
|
||||||
State = { Value = Framework.Graphics.Containers.Visibility.Visible },
|
RelativeSizeAxes = Axes.Both,
|
||||||
});
|
State = { Value = Framework.Graphics.Containers.Visibility.Visible },
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestPopupDialog : PopupDialog
|
private class TestPopupDialog : PopupDialog
|
||||||
|
@ -54,8 +54,8 @@ namespace osu.Game.Tournament
|
|||||||
{
|
{
|
||||||
Resources.AddStore(new DllResourceStore(@"osu.Game.Tournament.dll"));
|
Resources.AddStore(new DllResourceStore(@"osu.Game.Tournament.dll"));
|
||||||
|
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Resources/Fonts/Aquatico-Regular"));
|
AddFont(Resources, @"Resources/Fonts/Aquatico-Regular");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Resources/Fonts/Aquatico-Light"));
|
AddFont(Resources, @"Resources/Fonts/Aquatico-Light");
|
||||||
|
|
||||||
Textures.AddStore(new TextureLoaderStore(new ResourceStore<byte[]>(new StorageBackedResourceStore(storage))));
|
Textures.AddStore(new TextureLoaderStore(new ResourceStore<byte[]>(new StorageBackedResourceStore(storage))));
|
||||||
|
|
||||||
@ -228,7 +228,7 @@ namespace osu.Game.Tournament
|
|||||||
if (b.BeatmapInfo == null && b.ID > 0)
|
if (b.BeatmapInfo == null && b.ID > 0)
|
||||||
{
|
{
|
||||||
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
|
var req = new GetBeatmapRequest(new BeatmapInfo { OnlineBeatmapID = b.ID });
|
||||||
req.Perform(API);
|
API.Perform(req);
|
||||||
b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore);
|
b.BeatmapInfo = req.Result?.ToBeatmap(RulesetStore);
|
||||||
|
|
||||||
addedInfo = true;
|
addedInfo = true;
|
||||||
|
@ -9,6 +9,6 @@
|
|||||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.6.0" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="4.7.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -398,7 +398,7 @@ namespace osu.Game.Beatmaps
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// intentionally blocking to limit web request concurrency
|
// intentionally blocking to limit web request concurrency
|
||||||
req.Perform(api);
|
api.Perform(req);
|
||||||
|
|
||||||
var res = req.Result;
|
var res = req.Result;
|
||||||
|
|
||||||
|
@ -25,6 +25,6 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// <returns>Whether equivalent.</returns>
|
/// <returns>Whether equivalent.</returns>
|
||||||
public abstract bool EquivalentTo(ControlPoint other);
|
public abstract bool EquivalentTo(ControlPoint other);
|
||||||
|
|
||||||
public bool Equals(ControlPoint other) => Time.Equals(other?.Time) && EquivalentTo(other);
|
public bool Equals(ControlPoint other) => Time == other?.Time && EquivalentTo(other);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
|
|
||||||
difficultyName.Text = beatmap.Version;
|
difficultyName.Text = beatmap.Version;
|
||||||
starRating.Text = $"{beatmap.StarDifficulty:0.##}";
|
starRating.Text = $"{beatmap.StarDifficulty:0.##}";
|
||||||
difficultyFlow.Colour = colours.ForDifficultyRating(beatmap.DifficultyRating);
|
difficultyFlow.Colour = colours.ForDifficultyRating(beatmap.DifficultyRating, true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -9,8 +10,8 @@ using osu.Game.Rulesets;
|
|||||||
|
|
||||||
namespace osu.Game.Configuration
|
namespace osu.Game.Configuration
|
||||||
{
|
{
|
||||||
public abstract class DatabasedConfigManager<T> : ConfigManager<T>
|
public abstract class DatabasedConfigManager<TLookup> : ConfigManager<TLookup>
|
||||||
where T : struct
|
where TLookup : struct, Enum
|
||||||
{
|
{
|
||||||
private readonly SettingsStore settings;
|
private readonly SettingsStore settings;
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
private readonly List<DatabasedSetting> dirtySettings = new List<DatabasedSetting>();
|
private readonly List<DatabasedSetting> dirtySettings = new List<DatabasedSetting>();
|
||||||
|
|
||||||
protected override void AddBindable<TBindable>(T lookup, Bindable<TBindable> bindable)
|
protected override void AddBindable<TBindable>(TLookup lookup, Bindable<TBindable> bindable)
|
||||||
{
|
{
|
||||||
base.AddBindable(lookup, bindable);
|
base.AddBindable(lookup, bindable);
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
|
|
||||||
namespace osu.Game.Configuration
|
namespace osu.Game.Configuration
|
||||||
{
|
{
|
||||||
public class InMemoryConfigManager<T> : ConfigManager<T>
|
public class InMemoryConfigManager<TLookup> : ConfigManager<TLookup>
|
||||||
where T : struct
|
where TLookup : struct, Enum
|
||||||
{
|
{
|
||||||
public InMemoryConfigManager()
|
public InMemoryConfigManager()
|
||||||
{
|
{
|
||||||
|
110
osu.Game/Configuration/SettingSourceAttribute.cs
Normal file
110
osu.Game/Configuration/SettingSourceAttribute.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// 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.Reflection;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
|
||||||
|
namespace osu.Game.Configuration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An attribute to mark a bindable as being exposed to the user via settings controls.
|
||||||
|
/// Can be used in conjunction with <see cref="SettingSourceExtensions.CreateSettingsControls"/> to automatically create UI controls.
|
||||||
|
/// </summary>
|
||||||
|
[MeansImplicitUse]
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
|
public class SettingSourceAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string Label { get; }
|
||||||
|
|
||||||
|
public string Description { get; }
|
||||||
|
|
||||||
|
public SettingSourceAttribute(string label, string description = null)
|
||||||
|
{
|
||||||
|
Label = label ?? string.Empty;
|
||||||
|
Description = description ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SettingSourceExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<Drawable> CreateSettingsControls(this object obj)
|
||||||
|
{
|
||||||
|
foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance))
|
||||||
|
{
|
||||||
|
var attr = property.GetCustomAttribute<SettingSourceAttribute>(true);
|
||||||
|
|
||||||
|
if (attr == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var prop = property.GetValue(obj);
|
||||||
|
|
||||||
|
switch (prop)
|
||||||
|
{
|
||||||
|
case BindableNumber<float> bNumber:
|
||||||
|
yield return new SettingsSlider<float>
|
||||||
|
{
|
||||||
|
LabelText = attr.Label,
|
||||||
|
Bindable = bNumber
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BindableNumber<double> bNumber:
|
||||||
|
yield return new SettingsSlider<double>
|
||||||
|
{
|
||||||
|
LabelText = attr.Label,
|
||||||
|
Bindable = bNumber
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BindableNumber<int> bNumber:
|
||||||
|
yield return new SettingsSlider<int>
|
||||||
|
{
|
||||||
|
LabelText = attr.Label,
|
||||||
|
Bindable = bNumber
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Bindable<bool> bBool:
|
||||||
|
yield return new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = attr.Label,
|
||||||
|
Bindable = bBool
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Bindable<string> bString:
|
||||||
|
yield return new SettingsTextBox
|
||||||
|
{
|
||||||
|
LabelText = attr.Label,
|
||||||
|
Bindable = bString
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IBindable bindable:
|
||||||
|
var dropdownType = typeof(SettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]);
|
||||||
|
var dropdown = (Drawable)Activator.CreateInstance(dropdownType);
|
||||||
|
|
||||||
|
dropdown.GetType().GetProperty(nameof(IHasCurrentValue<object>.Current))?.SetValue(dropdown, obj);
|
||||||
|
|
||||||
|
yield return dropdown;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} was attached to an unsupported type ({prop})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -99,17 +99,7 @@ namespace osu.Game.Database
|
|||||||
currentDownloads.Add(request);
|
currentDownloads.Add(request);
|
||||||
PostNotification?.Invoke(notification);
|
PostNotification?.Invoke(notification);
|
||||||
|
|
||||||
Task.Factory.StartNew(() =>
|
api.PerformAsync(request);
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
request.Perform(api);
|
|
||||||
}
|
|
||||||
catch (Exception error)
|
|
||||||
{
|
|
||||||
triggerFailure(error);
|
|
||||||
}
|
|
||||||
}, TaskCreationOptions.LongRunning);
|
|
||||||
|
|
||||||
DownloadBegan?.Invoke(request);
|
DownloadBegan?.Invoke(request);
|
||||||
return true;
|
return true;
|
||||||
|
@ -16,6 +16,8 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Background : CompositeDrawable
|
public class Background : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
private const float blur_scale = 0.5f;
|
||||||
|
|
||||||
public readonly Sprite Sprite;
|
public readonly Sprite Sprite;
|
||||||
|
|
||||||
private readonly string textureName;
|
private readonly string textureName;
|
||||||
@ -43,7 +45,7 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
Sprite.Texture = textures.Get(textureName);
|
Sprite.Texture = textures.Get(textureName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2 BlurSigma => bufferedContainer?.BlurSigma ?? Vector2.Zero;
|
public Vector2 BlurSigma => bufferedContainer?.BlurSigma / blur_scale ?? Vector2.Zero;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Smoothly adjusts <see cref="IBufferedContainer.BlurSigma"/> over time.
|
/// Smoothly adjusts <see cref="IBufferedContainer.BlurSigma"/> over time.
|
||||||
@ -64,7 +66,10 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferedContainer?.BlurTo(newBlurSigma, duration, easing);
|
if (bufferedContainer != null)
|
||||||
|
bufferedContainer.FrameBufferScale = newBlurSigma == Vector2.Zero ? Vector2.One : new Vector2(blur_scale);
|
||||||
|
|
||||||
|
bufferedContainer?.BlurTo(newBlurSigma * blur_scale, duration, easing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,33 +67,21 @@ namespace osu.Game.Graphics.Containers
|
|||||||
// receive input outside our bounds so we can trigger a close event on ourselves.
|
// receive input outside our bounds so we can trigger a close event on ourselves.
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
private bool closeOnMouseUp;
|
||||||
{
|
|
||||||
if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition))
|
|
||||||
Hide();
|
|
||||||
|
|
||||||
return base.OnClick(e);
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
closeOnMouseUp = !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition);
|
||||||
|
|
||||||
|
return base.OnMouseDown(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool closeOnDragEnd;
|
protected override bool OnMouseUp(MouseUpEvent e)
|
||||||
|
|
||||||
protected override bool OnDragStart(DragStartEvent e)
|
|
||||||
{
|
{
|
||||||
if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition))
|
if (closeOnMouseUp && !base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition))
|
||||||
closeOnDragEnd = true;
|
|
||||||
|
|
||||||
return base.OnDragStart(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool OnDragEnd(DragEndEvent e)
|
|
||||||
{
|
|
||||||
if (closeOnDragEnd)
|
|
||||||
{
|
|
||||||
Hide();
|
Hide();
|
||||||
closeOnDragEnd = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.OnDragEnd(e);
|
return base.OnMouseUp(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool OnPressed(GlobalAction action)
|
public virtual bool OnPressed(GlobalAction action)
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Graphics
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||||
public static TransformSequence<T> FadeAccent<T>(this T accentedDrawable, Color4 newColour, double duration = 0, Easing easing = Easing.None)
|
public static TransformSequence<T> FadeAccent<T>(this T accentedDrawable, Color4 newColour, double duration = 0, Easing easing = Easing.None)
|
||||||
where T : IHasAccentColour
|
where T : class, IHasAccentColour
|
||||||
=> accentedDrawable.TransformTo(nameof(accentedDrawable.AccentColour), newColour, duration, easing);
|
=> accentedDrawable.TransformTo(nameof(accentedDrawable.AccentColour), newColour, duration, easing);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Graphics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Color4 ForDifficultyRating(DifficultyRating difficulty)
|
public Color4 ForDifficultyRating(DifficultyRating difficulty, bool useLighterColour = false)
|
||||||
{
|
{
|
||||||
switch (difficulty)
|
switch (difficulty)
|
||||||
{
|
{
|
||||||
@ -56,10 +56,10 @@ namespace osu.Game.Graphics
|
|||||||
return Pink;
|
return Pink;
|
||||||
|
|
||||||
case DifficultyRating.Expert:
|
case DifficultyRating.Expert:
|
||||||
return Purple;
|
return useLighterColour ? PurpleLight : Purple;
|
||||||
|
|
||||||
case DifficultyRating.ExpertPlus:
|
case DifficultyRating.ExpertPlus:
|
||||||
return Gray0;
|
return useLighterColour ? Gray9 : Gray0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,9 +20,10 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
public class DialogButton : OsuClickableContainer
|
public class DialogButton : OsuClickableContainer
|
||||||
{
|
{
|
||||||
|
private const float idle_width = 0.8f;
|
||||||
private const float hover_width = 0.9f;
|
private const float hover_width = 0.9f;
|
||||||
|
|
||||||
private const float hover_duration = 500;
|
private const float hover_duration = 500;
|
||||||
private const float glow_fade_duration = 250;
|
|
||||||
private const float click_duration = 200;
|
private const float click_duration = 200;
|
||||||
|
|
||||||
public readonly BindableBool Selected = new BindableBool();
|
public readonly BindableBool Selected = new BindableBool();
|
||||||
@ -99,7 +100,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Width = 0.8f,
|
Width = idle_width,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
MaskingSmoothness = 2,
|
MaskingSmoothness = 2,
|
||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
@ -199,26 +200,50 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
|
private bool clickAnimating;
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In);
|
var flash = new Box
|
||||||
flash();
|
|
||||||
|
|
||||||
this.Delay(click_duration).Schedule(delegate
|
|
||||||
{
|
{
|
||||||
colourContainer.ResizeTo(new Vector2(0.8f, 1f));
|
RelativeSizeAxes = Axes.Both,
|
||||||
spriteText.Spacing = Vector2.Zero;
|
Colour = ButtonColour,
|
||||||
glowContainer.FadeOut();
|
Blending = BlendingParameters.Additive,
|
||||||
});
|
Alpha = 0.05f
|
||||||
|
};
|
||||||
|
|
||||||
|
colourContainer.Add(flash);
|
||||||
|
flash.FadeOutFromOne(100).Expire();
|
||||||
|
|
||||||
|
clickAnimating = true;
|
||||||
|
colourContainer.ResizeWidthTo(colourContainer.Width * 1.05f, 100, Easing.OutQuint)
|
||||||
|
.OnComplete(_ =>
|
||||||
|
{
|
||||||
|
clickAnimating = false;
|
||||||
|
Selected.TriggerChange();
|
||||||
|
});
|
||||||
|
|
||||||
return base.OnClick(e);
|
return base.OnClick(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
|
{
|
||||||
|
colourContainer.ResizeWidthTo(hover_width * 0.98f, click_duration * 4, Easing.OutQuad);
|
||||||
|
return base.OnMouseDown(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseUp(MouseUpEvent e)
|
||||||
|
{
|
||||||
|
if (Selected.Value)
|
||||||
|
colourContainer.ResizeWidthTo(hover_width, click_duration, Easing.In);
|
||||||
|
return base.OnMouseUp(e);
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
{
|
{
|
||||||
base.OnHover(e);
|
base.OnHover(e);
|
||||||
|
|
||||||
Selected.Value = true;
|
Selected.Value = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,36 +255,23 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
|
|
||||||
private void selectionChanged(ValueChangedEvent<bool> args)
|
private void selectionChanged(ValueChangedEvent<bool> args)
|
||||||
{
|
{
|
||||||
|
if (clickAnimating)
|
||||||
|
return;
|
||||||
|
|
||||||
if (args.NewValue)
|
if (args.NewValue)
|
||||||
{
|
{
|
||||||
spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic);
|
spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic);
|
||||||
colourContainer.ResizeTo(new Vector2(hover_width, 1f), hover_duration, Easing.OutElastic);
|
colourContainer.ResizeWidthTo(hover_width, hover_duration, Easing.OutElastic);
|
||||||
glowContainer.FadeIn(glow_fade_duration, Easing.Out);
|
glowContainer.FadeIn(hover_duration, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
colourContainer.ResizeTo(new Vector2(0.8f, 1f), hover_duration, Easing.OutElastic);
|
colourContainer.ResizeWidthTo(idle_width, hover_duration, Easing.OutElastic);
|
||||||
spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic);
|
spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic);
|
||||||
glowContainer.FadeOut(glow_fade_duration, Easing.Out);
|
glowContainer.FadeOut(hover_duration, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flash()
|
|
||||||
{
|
|
||||||
var flash = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both
|
|
||||||
};
|
|
||||||
|
|
||||||
colourContainer.Add(flash);
|
|
||||||
|
|
||||||
flash.Colour = ButtonColour;
|
|
||||||
flash.Blending = BlendingParameters.Additive;
|
|
||||||
flash.Alpha = 0.3f;
|
|
||||||
flash.FadeOutFromOne(click_duration);
|
|
||||||
flash.Expire();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateGlow()
|
private void updateGlow()
|
||||||
{
|
{
|
||||||
leftGlow.Colour = ColourInfo.GradientHorizontal(new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f), ButtonColour);
|
leftGlow.Colour = ColourInfo.GradientHorizontal(new Color4(ButtonColour.R, ButtonColour.G, ButtonColour.B, 0f), ButtonColour);
|
||||||
|
@ -6,12 +6,10 @@ using System;
|
|||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class OsuEnumDropdown<T> : OsuDropdown<T>
|
public class OsuEnumDropdown<T> : OsuDropdown<T>
|
||||||
|
where T : struct, Enum
|
||||||
{
|
{
|
||||||
public OsuEnumDropdown()
|
public OsuEnumDropdown()
|
||||||
{
|
{
|
||||||
if (!typeof(T).IsEnum)
|
|
||||||
throw new InvalidOperationException("OsuEnumDropdown only supports enums as the generic type argument");
|
|
||||||
|
|
||||||
Items = (T[])Enum.GetValues(typeof(T));
|
Items = (T[])Enum.GetValues(typeof(T));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,18 +151,18 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
private void updateTooltipText(T value)
|
private void updateTooltipText(T value)
|
||||||
{
|
{
|
||||||
if (CurrentNumber.IsInteger)
|
if (CurrentNumber.IsInteger)
|
||||||
TooltipText = ((int)Convert.ChangeType(value, typeof(int))).ToString("N0");
|
TooltipText = value.ToInt32(NumberFormatInfo.InvariantInfo).ToString("N0");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
double floatValue = (double)Convert.ChangeType(value, typeof(double));
|
double floatValue = value.ToDouble(NumberFormatInfo.InvariantInfo);
|
||||||
double floatMinValue = (double)Convert.ChangeType(CurrentNumber.MinValue, typeof(double));
|
double floatMinValue = CurrentNumber.MinValue.ToDouble(NumberFormatInfo.InvariantInfo);
|
||||||
double floatMaxValue = (double)Convert.ChangeType(CurrentNumber.MaxValue, typeof(double));
|
double floatMaxValue = CurrentNumber.MaxValue.ToDouble(NumberFormatInfo.InvariantInfo);
|
||||||
|
|
||||||
if (floatMaxValue == 1 && floatMinValue >= -1)
|
if (floatMaxValue == 1 && floatMinValue >= -1)
|
||||||
TooltipText = floatValue.ToString("P0");
|
TooltipText = floatValue.ToString("P0");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var decimalPrecision = normalise((decimal)Convert.ChangeType(CurrentNumber.Precision, typeof(decimal)), max_decimal_digits);
|
var decimalPrecision = normalise(CurrentNumber.Precision.ToDecimal(NumberFormatInfo.InvariantInfo), max_decimal_digits);
|
||||||
|
|
||||||
// Find the number of significant digits (we could have less than 5 after normalize())
|
// Find the number of significant digits (we could have less than 5 after normalize())
|
||||||
var significantDigits = findPrecision(decimalPrecision);
|
var significantDigits = findPrecision(decimalPrecision);
|
||||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
public override bool OnPressed(PlatformAction action)
|
public override bool OnPressed(PlatformAction action)
|
||||||
{
|
{
|
||||||
// Shift+delete is handled via PlatformAction on macOS. this is not so useful in the context of a SearchTextBox
|
// Shift+delete is handled via PlatformAction on macOS. this is not so useful in the context of a SearchTextBox
|
||||||
// as we do not allow arrow key navigation in the first place (ie. the care should always be at the end of text)
|
// as we do not allow arrow key navigation in the first place (ie. the caret should always be at the end of text)
|
||||||
// Avoid handling it here to allow other components to potentially consume the shortcut.
|
// Avoid handling it here to allow other components to potentially consume the shortcut.
|
||||||
if (action.ActionType == PlatformActionType.CharNext && action.ActionMethod == PlatformActionMethod.Delete)
|
if (action.ActionType == PlatformActionType.CharNext && action.ActionMethod == PlatformActionMethod.Delete)
|
||||||
return false;
|
return false;
|
||||||
|
@ -7,6 +7,7 @@ using System.Diagnostics;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -198,6 +199,22 @@ namespace osu.Game.Online.API
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Perform(APIRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
request.Perform(this);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// todo: fix exception handling
|
||||||
|
request.Fail(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task PerformAsync(APIRequest request) =>
|
||||||
|
Task.Factory.StartNew(() => Perform(request), TaskCreationOptions.LongRunning);
|
||||||
|
|
||||||
public void Login(string username, string password)
|
public void Login(string username, string password)
|
||||||
{
|
{
|
||||||
Debug.Assert(State == APIState.Offline);
|
Debug.Assert(State == APIState.Offline);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
@ -56,6 +57,10 @@ namespace osu.Game.Online.API
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Perform(APIRequest request) { }
|
||||||
|
|
||||||
|
public Task PerformAsync(APIRequest request) => Task.CompletedTask;
|
||||||
|
|
||||||
public void Register(IOnlineComponent component)
|
public void Register(IOnlineComponent component)
|
||||||
{
|
{
|
||||||
Scheduler.Add(delegate { components.Add(component); });
|
Scheduler.Add(delegate { components.Add(component); });
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
@ -42,6 +43,24 @@ namespace osu.Game.Online.API
|
|||||||
/// <param name="request">The request to perform.</param>
|
/// <param name="request">The request to perform.</param>
|
||||||
void Queue(APIRequest request);
|
void Queue(APIRequest request);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a request immediately, bypassing any API state checks.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Can be used to run requests as a guest user.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="request">The request to perform.</param>
|
||||||
|
void Perform(APIRequest request);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform a request immediately, bypassing any API state checks.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Can be used to run requests as a guest user.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="request">The request to perform.</param>
|
||||||
|
Task PerformAsync(APIRequest request);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register a component to receive state changes.
|
/// Register a component to receive state changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
15
osu.Game/Online/API/Requests/GetCountriesResponse.cs
Normal file
15
osu.Game/Online/API/Requests/GetCountriesResponse.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 System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests
|
||||||
|
{
|
||||||
|
public class GetCountriesResponse : ResponseWithCursor
|
||||||
|
{
|
||||||
|
[JsonProperty("ranking")]
|
||||||
|
public List<CountryStatistics> Countries;
|
||||||
|
}
|
||||||
|
}
|
17
osu.Game/Online/API/Requests/GetCountryRankingsRequest.cs
Normal file
17
osu.Game/Online/API/Requests/GetCountryRankingsRequest.cs
Normal file
@ -0,0 +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.Game.Rulesets;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests
|
||||||
|
{
|
||||||
|
public class GetCountryRankingsRequest : GetRankingsRequest<GetCountriesResponse>
|
||||||
|
{
|
||||||
|
public GetCountryRankingsRequest(RulesetInfo ruleset, int page = 1)
|
||||||
|
: base(ruleset, page)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string TargetPostfix() => "country";
|
||||||
|
}
|
||||||
|
}
|
33
osu.Game/Online/API/Requests/GetRankingsRequest.cs
Normal file
33
osu.Game/Online/API/Requests/GetRankingsRequest.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 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.IO.Network;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests
|
||||||
|
{
|
||||||
|
public abstract class GetRankingsRequest<TModel> : APIRequest<TModel>
|
||||||
|
{
|
||||||
|
private readonly RulesetInfo ruleset;
|
||||||
|
private readonly int page;
|
||||||
|
|
||||||
|
protected GetRankingsRequest(RulesetInfo ruleset, int page = 1)
|
||||||
|
{
|
||||||
|
this.ruleset = ruleset;
|
||||||
|
this.page = page;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override WebRequest CreateWebRequest()
|
||||||
|
{
|
||||||
|
var req = base.CreateWebRequest();
|
||||||
|
|
||||||
|
req.AddParameter("page", page.ToString());
|
||||||
|
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string Target => $"rankings/{ruleset.ShortName}/{TargetPostfix()}";
|
||||||
|
|
||||||
|
protected abstract string TargetPostfix();
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ using osu.Game.Online.API.Requests.Responses;
|
|||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
namespace osu.Game.Online.API.Requests
|
||||||
{
|
{
|
||||||
public class GetRoomScoresRequest : APIRequest<List<APIRoomScoreInfo>>
|
public class GetRoomScoresRequest : APIRequest<List<APIUserScoreAggregate>>
|
||||||
{
|
{
|
||||||
private readonly int roomId;
|
private readonly int roomId;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ using osu.Game.Online.API.Requests.Responses;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
namespace osu.Game.Online.API.Requests
|
||||||
{
|
{
|
||||||
@ -37,10 +38,12 @@ namespace osu.Game.Online.API.Requests
|
|||||||
|
|
||||||
private void onSuccess(APILegacyScores r)
|
private void onSuccess(APILegacyScores r)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(ruleset.ID != null, "ruleset.ID != null");
|
||||||
|
|
||||||
foreach (APILegacyScoreInfo score in r.Scores)
|
foreach (APILegacyScoreInfo score in r.Scores)
|
||||||
{
|
{
|
||||||
score.Beatmap = beatmap;
|
score.Beatmap = beatmap;
|
||||||
score.Ruleset = ruleset;
|
score.OnlineRulesetID = ruleset.ID.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var userScore = r.UserScore;
|
var userScore = r.UserScore;
|
||||||
@ -48,7 +51,7 @@ namespace osu.Game.Online.API.Requests
|
|||||||
if (userScore != null)
|
if (userScore != null)
|
||||||
{
|
{
|
||||||
userScore.Score.Beatmap = beatmap;
|
userScore.Score.Beatmap = beatmap;
|
||||||
userScore.Score.Ruleset = ruleset;
|
userScore.Score.OnlineRulesetID = ruleset.ID.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
osu.Game/Online/API/Requests/GetUserRankingsRequest.cs
Normal file
39
osu.Game/Online/API/Requests/GetUserRankingsRequest.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 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.IO.Network;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests
|
||||||
|
{
|
||||||
|
public class GetUserRankingsRequest : GetRankingsRequest<GetUsersResponse>
|
||||||
|
{
|
||||||
|
private readonly string country;
|
||||||
|
private readonly UserRankingsType type;
|
||||||
|
|
||||||
|
public GetUserRankingsRequest(RulesetInfo ruleset, UserRankingsType type = UserRankingsType.Performance, int page = 1, string country = null)
|
||||||
|
: base(ruleset, page)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
this.country = country;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override WebRequest CreateWebRequest()
|
||||||
|
{
|
||||||
|
var req = base.CreateWebRequest();
|
||||||
|
|
||||||
|
if (country != null)
|
||||||
|
req.AddParameter("country", country);
|
||||||
|
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override string TargetPostfix() => type.ToString().ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum UserRankingsType
|
||||||
|
{
|
||||||
|
Performance,
|
||||||
|
Score
|
||||||
|
}
|
||||||
|
}
|
@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
namespace osu.Game.Online.API.Requests
|
||||||
{
|
{
|
||||||
public class GetUsersResponse : ResponseWithCursor
|
public class GetUsersResponse : ResponseWithCursor
|
||||||
{
|
{
|
||||||
[JsonProperty("ranking")]
|
[JsonProperty("ranking")]
|
||||||
public List<APIUser> Users;
|
public List<UserStatistics> Users;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,56 +5,106 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Scoring.Legacy;
|
using osu.Game.Scoring.Legacy;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
|
||||||
namespace osu.Game.Online.API.Requests.Responses
|
namespace osu.Game.Online.API.Requests.Responses
|
||||||
{
|
{
|
||||||
public class APILegacyScoreInfo : LegacyScoreInfo
|
public class APILegacyScoreInfo
|
||||||
{
|
{
|
||||||
[JsonProperty(@"score")]
|
public ScoreInfo CreateScoreInfo(RulesetStore rulesets)
|
||||||
private int totalScore
|
|
||||||
{
|
{
|
||||||
set => TotalScore = value;
|
var ruleset = rulesets.GetRuleset(OnlineRulesetID);
|
||||||
|
|
||||||
|
var mods = Mods != null ? ruleset.CreateInstance().GetAllMods().Where(mod => Mods.Contains(mod.Acronym)).ToArray() : Array.Empty<Mod>();
|
||||||
|
|
||||||
|
var scoreInfo = new ScoreInfo
|
||||||
|
{
|
||||||
|
TotalScore = TotalScore,
|
||||||
|
MaxCombo = MaxCombo,
|
||||||
|
User = User,
|
||||||
|
Accuracy = Accuracy,
|
||||||
|
OnlineScoreID = OnlineScoreID,
|
||||||
|
Date = Date,
|
||||||
|
PP = PP,
|
||||||
|
Beatmap = Beatmap,
|
||||||
|
RulesetID = OnlineRulesetID,
|
||||||
|
Hash = Replay ? "online" : string.Empty, // todo: temporary?
|
||||||
|
Rank = Rank,
|
||||||
|
Ruleset = ruleset,
|
||||||
|
Mods = mods,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Statistics != null)
|
||||||
|
{
|
||||||
|
foreach (var kvp in Statistics)
|
||||||
|
{
|
||||||
|
switch (kvp.Key)
|
||||||
|
{
|
||||||
|
case @"count_geki":
|
||||||
|
scoreInfo.SetCountGeki(kvp.Value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case @"count_300":
|
||||||
|
scoreInfo.SetCount300(kvp.Value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case @"count_katu":
|
||||||
|
scoreInfo.SetCountKatu(kvp.Value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case @"count_100":
|
||||||
|
scoreInfo.SetCount100(kvp.Value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case @"count_50":
|
||||||
|
scoreInfo.SetCount50(kvp.Value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case @"count_miss":
|
||||||
|
scoreInfo.SetCountMiss(kvp.Value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scoreInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonProperty(@"score")]
|
||||||
|
public int TotalScore { get; set; }
|
||||||
|
|
||||||
[JsonProperty(@"max_combo")]
|
[JsonProperty(@"max_combo")]
|
||||||
private int maxCombo
|
public int MaxCombo { get; set; }
|
||||||
{
|
|
||||||
set => MaxCombo = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty(@"user")]
|
[JsonProperty(@"user")]
|
||||||
private User user
|
public User User { get; set; }
|
||||||
{
|
|
||||||
set => User = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty(@"id")]
|
[JsonProperty(@"id")]
|
||||||
private long onlineScoreID
|
public long OnlineScoreID { get; set; }
|
||||||
{
|
|
||||||
set => OnlineScoreID = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty(@"replay")]
|
[JsonProperty(@"replay")]
|
||||||
public bool Replay { get; set; }
|
public bool Replay { get; set; }
|
||||||
|
|
||||||
[JsonProperty(@"created_at")]
|
[JsonProperty(@"created_at")]
|
||||||
private DateTimeOffset date
|
public DateTimeOffset Date { get; set; }
|
||||||
{
|
|
||||||
set => Date = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty(@"beatmap")]
|
[JsonProperty(@"beatmap")]
|
||||||
private BeatmapInfo beatmap
|
public BeatmapInfo Beatmap { get; set; }
|
||||||
{
|
|
||||||
set => Beatmap = value;
|
[JsonProperty("accuracy")]
|
||||||
}
|
public double Accuracy { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(@"pp")]
|
||||||
|
public double? PP { get; set; }
|
||||||
|
|
||||||
[JsonProperty(@"beatmapset")]
|
[JsonProperty(@"beatmapset")]
|
||||||
private BeatmapMetadata metadata
|
public BeatmapMetadata Metadata
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
@ -67,68 +117,16 @@ namespace osu.Game.Online.API.Requests.Responses
|
|||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty(@"statistics")]
|
[JsonProperty(@"statistics")]
|
||||||
private Dictionary<string, int> jsonStats
|
public Dictionary<string, int> Statistics { get; set; }
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
foreach (var kvp in value)
|
|
||||||
{
|
|
||||||
switch (kvp.Key)
|
|
||||||
{
|
|
||||||
case @"count_geki":
|
|
||||||
CountGeki = kvp.Value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case @"count_300":
|
|
||||||
Count300 = kvp.Value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case @"count_katu":
|
|
||||||
CountKatu = kvp.Value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case @"count_100":
|
|
||||||
Count100 = kvp.Value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case @"count_50":
|
|
||||||
Count50 = kvp.Value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case @"count_miss":
|
|
||||||
CountMiss = kvp.Value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty(@"mode_int")]
|
[JsonProperty(@"mode_int")]
|
||||||
public int OnlineRulesetID
|
public int OnlineRulesetID { get; set; }
|
||||||
{
|
|
||||||
get => RulesetID;
|
|
||||||
set => RulesetID = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonProperty(@"mods")]
|
[JsonProperty(@"mods")]
|
||||||
private string[] modStrings { get; set; }
|
public string[] Mods { get; set; }
|
||||||
|
|
||||||
public override RulesetInfo Ruleset
|
[JsonProperty("rank")]
|
||||||
{
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
get => base.Ruleset;
|
public ScoreRank Rank { get; set; }
|
||||||
set
|
|
||||||
{
|
|
||||||
base.Ruleset = value;
|
|
||||||
|
|
||||||
if (modStrings != null)
|
|
||||||
{
|
|
||||||
// Evaluate the mod string
|
|
||||||
Mods = Ruleset.CreateInstance().GetAllMods().Where(mod => modStrings.Contains(mod.Acronym)).ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +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 Newtonsoft.Json;
|
|
||||||
using osu.Game.Scoring;
|
|
||||||
|
|
||||||
namespace osu.Game.Online.API.Requests.Responses
|
|
||||||
{
|
|
||||||
public class APIRoomScoreInfo : ScoreInfo
|
|
||||||
{
|
|
||||||
[JsonProperty("attempts")]
|
|
||||||
public int TotalAttempts { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("completed")]
|
|
||||||
public int CompletedBeatmaps { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,45 @@
|
|||||||
|
// 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 Newtonsoft.Json;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests.Responses
|
||||||
|
{
|
||||||
|
public class APIUserScoreAggregate
|
||||||
|
{
|
||||||
|
[JsonProperty("attempts")]
|
||||||
|
public int TotalAttempts { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("completed")]
|
||||||
|
public int CompletedBeatmaps { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("accuracy")]
|
||||||
|
public double Accuracy { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(@"pp")]
|
||||||
|
public double? PP { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(@"room_id")]
|
||||||
|
public int RoomID { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("total_score")]
|
||||||
|
public long TotalScore { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(@"user_id")]
|
||||||
|
public long UserID { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("user")]
|
||||||
|
public User User { get; set; }
|
||||||
|
|
||||||
|
public ScoreInfo CreateScoreInfo() =>
|
||||||
|
new ScoreInfo
|
||||||
|
{
|
||||||
|
Accuracy = Accuracy,
|
||||||
|
PP = PP,
|
||||||
|
TotalScore = TotalScore,
|
||||||
|
User = User,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,7 @@ namespace osu.Game.Online
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Bindable<DownloadState> State = new Bindable<DownloadState>();
|
protected readonly Bindable<DownloadState> State = new Bindable<DownloadState>();
|
||||||
|
|
||||||
protected readonly Bindable<double> Progress = new Bindable<double>();
|
protected readonly BindableNumber<double> Progress = new BindableNumber<double> { MinValue = 0, MaxValue = 1 };
|
||||||
|
|
||||||
protected DownloadTrackingComposite(TModel model = null)
|
protected DownloadTrackingComposite(TModel model = null)
|
||||||
{
|
{
|
||||||
|
@ -406,11 +406,11 @@ namespace osu.Game
|
|||||||
nextBeatmap?.LoadBeatmapAsync();
|
nextBeatmap?.LoadBeatmapAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void currentTrackCompleted()
|
private void currentTrackCompleted() => Schedule(() =>
|
||||||
{
|
{
|
||||||
if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled)
|
if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled)
|
||||||
musicController.NextTrack();
|
musicController.NextTrack();
|
||||||
}
|
});
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -131,29 +131,29 @@ namespace osu.Game
|
|||||||
dependencies.CacheAs(this);
|
dependencies.CacheAs(this);
|
||||||
dependencies.Cache(LocalConfig);
|
dependencies.Cache(LocalConfig);
|
||||||
|
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont"));
|
AddFont(Resources, @"Fonts/osuFont");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Medium"));
|
AddFont(Resources, @"Fonts/Exo2.0-Medium");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-MediumItalic"));
|
AddFont(Resources, @"Fonts/Exo2.0-MediumItalic");
|
||||||
|
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-Basic"));
|
AddFont(Resources, @"Fonts/Noto-Basic");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-Hangul"));
|
AddFont(Resources, @"Fonts/Noto-Hangul");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-CJK-Basic"));
|
AddFont(Resources, @"Fonts/Noto-CJK-Basic");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Noto-CJK-Compatibility"));
|
AddFont(Resources, @"Fonts/Noto-CJK-Compatibility");
|
||||||
|
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Regular"));
|
AddFont(Resources, @"Fonts/Exo2.0-Regular");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-RegularItalic"));
|
AddFont(Resources, @"Fonts/Exo2.0-RegularItalic");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-SemiBold"));
|
AddFont(Resources, @"Fonts/Exo2.0-SemiBold");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-SemiBoldItalic"));
|
AddFont(Resources, @"Fonts/Exo2.0-SemiBoldItalic");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Bold"));
|
AddFont(Resources, @"Fonts/Exo2.0-Bold");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-BoldItalic"));
|
AddFont(Resources, @"Fonts/Exo2.0-BoldItalic");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Light"));
|
AddFont(Resources, @"Fonts/Exo2.0-Light");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-LightItalic"));
|
AddFont(Resources, @"Fonts/Exo2.0-LightItalic");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Black"));
|
AddFont(Resources, @"Fonts/Exo2.0-Black");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-BlackItalic"));
|
AddFont(Resources, @"Fonts/Exo2.0-BlackItalic");
|
||||||
|
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera"));
|
AddFont(Resources, @"Fonts/Venera");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Light"));
|
AddFont(Resources, @"Fonts/Venera-Light");
|
||||||
Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Medium"));
|
AddFont(Resources, @"Fonts/Venera-Medium");
|
||||||
|
|
||||||
runMigrations();
|
runMigrations();
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
for (int i = 0; i < value.Count; i++)
|
for (int i = 0; i < value.Count; i++)
|
||||||
backgroundFlow.Add(new ScoreTableRowBackground(i));
|
backgroundFlow.Add(new ScoreTableRowBackground(i, value[i]));
|
||||||
|
|
||||||
Columns = createHeaders(value[0]);
|
Columns = createHeaders(value[0]);
|
||||||
Content = value.Select((s, i) => createContent(i, s)).ToArray().ToRectangular();
|
Content = value.Select((s, i) => createContent(i, s)).ToArray().ToRectangular();
|
||||||
|
@ -7,6 +7,8 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.BeatmapSet.Scores
|
namespace osu.Game.Overlays.BeatmapSet.Scores
|
||||||
{
|
{
|
||||||
@ -17,8 +19,14 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
private readonly Box hoveredBackground;
|
private readonly Box hoveredBackground;
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
|
|
||||||
public ScoreTableRowBackground(int index)
|
private readonly int index;
|
||||||
|
private readonly ScoreInfo score;
|
||||||
|
|
||||||
|
public ScoreTableRowBackground(int index, ScoreInfo score)
|
||||||
{
|
{
|
||||||
|
this.index = index;
|
||||||
|
this.score = score;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = 25;
|
Height = 25;
|
||||||
|
|
||||||
@ -37,16 +45,21 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (index % 2 != 0)
|
|
||||||
background.Alpha = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours, IAPIProvider api)
|
||||||
{
|
{
|
||||||
hoveredBackground.Colour = colours.Gray4;
|
var isOwnScore = api.LocalUser.Value.Id == score.UserID;
|
||||||
background.Colour = colours.Gray3;
|
|
||||||
|
if (isOwnScore)
|
||||||
|
background.Colour = colours.GreenDarker;
|
||||||
|
else if (index % 2 == 0)
|
||||||
|
background.Colour = colours.Gray3;
|
||||||
|
else
|
||||||
|
background.Alpha = 0;
|
||||||
|
|
||||||
|
hoveredBackground.Colour = isOwnScore ? colours.GreenDark : colours.Gray4;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
@ -41,6 +41,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private IAPIProvider api { get; set; }
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private RulesetStore rulesets { get; set; }
|
||||||
|
|
||||||
private GetScoresRequest getScoresRequest;
|
private GetScoresRequest getScoresRequest;
|
||||||
|
|
||||||
protected APILegacyScores Scores
|
protected APILegacyScores Scores
|
||||||
@ -56,16 +59,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoreTable.Scores = value.Scores;
|
var scoreInfos = value.Scores.Select(s => s.CreateScoreInfo(rulesets)).ToList();
|
||||||
|
|
||||||
|
scoreTable.Scores = scoreInfos;
|
||||||
scoreTable.Show();
|
scoreTable.Show();
|
||||||
|
|
||||||
var topScore = value.Scores.First();
|
var topScore = scoreInfos.First();
|
||||||
var userScore = value.UserScore;
|
var userScore = value.UserScore;
|
||||||
|
var userScoreInfo = userScore?.Score.CreateScoreInfo(rulesets);
|
||||||
|
|
||||||
topScoresContainer.Add(new DrawableTopScore(topScore));
|
topScoresContainer.Add(new DrawableTopScore(topScore));
|
||||||
|
|
||||||
if (userScore != null && userScore.Score.OnlineScoreID != topScore.OnlineScoreID)
|
if (userScoreInfo != null && userScoreInfo.OnlineScoreID != topScore.OnlineScoreID)
|
||||||
topScoresContainer.Add(new DrawableTopScore(userScore.Score, userScore.Position));
|
topScoresContainer.Add(new DrawableTopScore(userScoreInfo, userScore.Position));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -43,18 +42,7 @@ namespace osu.Game.Overlays.Changelog
|
|||||||
};
|
};
|
||||||
req.Failure += _ => complete = true;
|
req.Failure += _ => complete = true;
|
||||||
|
|
||||||
// This is done on a separate thread to support cancellation below
|
api.PerformAsync(req);
|
||||||
Task.Run(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
req.Perform(api);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
complete = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
while (!complete)
|
while (!complete)
|
||||||
{
|
{
|
||||||
|
@ -191,15 +191,7 @@ namespace osu.Game.Overlays
|
|||||||
tcs.SetResult(false);
|
tcs.SetResult(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
await API.PerformAsync(req);
|
||||||
{
|
|
||||||
req.Perform(API);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
initialFetchTask = null;
|
|
||||||
tcs.SetResult(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
await tcs.Task;
|
await tcs.Task;
|
||||||
});
|
});
|
||||||
|
@ -16,6 +16,7 @@ using osu.Game.Users;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.MedalSplash
|
namespace osu.Game.Overlays.MedalSplash
|
||||||
{
|
{
|
||||||
|
[LongRunningLoad]
|
||||||
public class DrawableMedal : Container, IStateful<DisplayState>
|
public class DrawableMedal : Container, IStateful<DisplayState>
|
||||||
{
|
{
|
||||||
private const float scale_when_unlocked = 0.76f;
|
private const float scale_when_unlocked = 0.76f;
|
||||||
|
@ -167,10 +167,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
switch (e.Button)
|
switch (e.Button)
|
||||||
{
|
{
|
||||||
case MouseButton.Left:
|
|
||||||
SelectNext(1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MouseButton.Right:
|
case MouseButton.Right:
|
||||||
SelectNext(-1);
|
SelectNext(-1);
|
||||||
break;
|
break;
|
||||||
@ -180,6 +176,13 @@ namespace osu.Game.Overlays.Mods
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e)
|
||||||
|
{
|
||||||
|
SelectNext(1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Select the next available mod in a specified direction.
|
/// Select the next available mod in a specified direction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -261,8 +261,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
if (allowRateAdjustments)
|
if (allowRateAdjustments)
|
||||||
{
|
{
|
||||||
foreach (var mod in mods.Value.OfType<IApplicableToClock>())
|
foreach (var mod in mods.Value.OfType<IApplicableToTrack>())
|
||||||
mod.ApplyToClock(track);
|
mod.ApplyToTrack(track);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Profile.Header.Components
|
namespace osu.Game.Overlays.Profile.Header.Components
|
||||||
{
|
{
|
||||||
|
[LongRunningLoad]
|
||||||
public class DrawableBadge : CompositeDrawable, IHasTooltip
|
public class DrawableBadge : CompositeDrawable, IHasTooltip
|
||||||
{
|
{
|
||||||
public static readonly Vector2 DRAWABLE_BADGE_SIZE = new Vector2(86, 40);
|
public static readonly Vector2 DRAWABLE_BADGE_SIZE = new Vector2(86, 40);
|
||||||
|
@ -19,8 +19,8 @@ namespace osu.Game.Overlays.Profile.Sections
|
|||||||
private const int fade_duration = 200;
|
private const int fade_duration = 200;
|
||||||
|
|
||||||
private Box underscoreLine;
|
private Box underscoreLine;
|
||||||
private readonly Box coloredBackground;
|
private Box coloredBackground;
|
||||||
private readonly Container background;
|
private Container background;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A visual element displayed to the left of <see cref="LeftFlowContainer"/> content.
|
/// A visual element displayed to the left of <see cref="LeftFlowContainer"/> content.
|
||||||
@ -36,6 +36,19 @@ namespace osu.Game.Overlays.Profile.Sections
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = 60;
|
Height = 60;
|
||||||
|
|
||||||
|
Content = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Width = 0.97f,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(OsuColour colour)
|
||||||
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
background = new Container
|
background = new Container
|
||||||
@ -53,21 +66,7 @@ namespace osu.Game.Overlays.Profile.Sections
|
|||||||
},
|
},
|
||||||
Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both }
|
Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both }
|
||||||
},
|
},
|
||||||
Content = new Container
|
Content,
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre,
|
|
||||||
Width = 0.97f,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
|
||||||
private void load(OsuColour colour)
|
|
||||||
{
|
|
||||||
AddRange(new Drawable[]
|
|
||||||
{
|
|
||||||
underscoreLine = new Box
|
underscoreLine = new Box
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.BottomCentre,
|
||||||
@ -101,7 +100,7 @@ namespace osu.Game.Overlays.Profile.Sections
|
|||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
|
||||||
coloredBackground.Colour = underscoreLine.Colour = colour.Gray4;
|
coloredBackground.Colour = underscoreLine.Colour = colour.Gray4;
|
||||||
}
|
}
|
||||||
|
@ -29,14 +29,6 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
|||||||
ItemsContainer.Direction = FillDirection.Vertical;
|
ItemsContainer.Direction = FillDirection.Vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateItems(List<APILegacyScoreInfo> items)
|
|
||||||
{
|
|
||||||
foreach (var item in items)
|
|
||||||
item.Ruleset = Rulesets.GetRuleset(item.RulesetID);
|
|
||||||
|
|
||||||
base.UpdateItems(items);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override APIRequest<List<APILegacyScoreInfo>> CreateRequest() =>
|
protected override APIRequest<List<APILegacyScoreInfo>> CreateRequest() =>
|
||||||
new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage);
|
new GetUserScoresRequest(User.Value.Id, type, VisiblePages++, ItemsPerPage);
|
||||||
|
|
||||||
@ -45,10 +37,10 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
return new DrawablePerformanceScore(model, includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null);
|
return new DrawablePerformanceScore(model.CreateScoreInfo(Rulesets), includeWeight ? Math.Pow(0.95, ItemsContainer.Count) : (double?)null);
|
||||||
|
|
||||||
case ScoreType.Recent:
|
case ScoreType.Recent:
|
||||||
return new DrawableTotalScore(model);
|
return new DrawableTotalScore(model.CreateScoreInfo(Rulesets));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,11 +66,14 @@ namespace osu.Game.Overlays.Profile.Sections.Recent
|
|||||||
};
|
};
|
||||||
|
|
||||||
case RecentActivityType.Achievement:
|
case RecentActivityType.Achievement:
|
||||||
return new MedalIcon(activity.Achievement.Slug)
|
return new DelayedLoadWrapper(new MedalIcon(activity.Achievement.Slug)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
FillMode = FillMode.Fit,
|
||||||
|
})
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Width = 60,
|
Width = 60,
|
||||||
FillMode = FillMode.Fit,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics.Textures;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Profile.Sections.Recent
|
namespace osu.Game.Overlays.Profile.Sections.Recent
|
||||||
{
|
{
|
||||||
|
[LongRunningLoad]
|
||||||
public class MedalIcon : Container
|
public class MedalIcon : Container
|
||||||
{
|
{
|
||||||
private readonly string slug;
|
private readonly string slug;
|
||||||
|
67
osu.Game/Overlays/Rankings/Tables/CountriesTable.cs
Normal file
67
osu.Game/Overlays/Rankings/Tables/CountriesTable.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
// 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.Graphics.Containers;
|
||||||
|
using System;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Rankings.Tables
|
||||||
|
{
|
||||||
|
public class CountriesTable : RankingsTable<CountryStatistics>
|
||||||
|
{
|
||||||
|
public CountriesTable(int page, IReadOnlyList<CountryStatistics> rankings)
|
||||||
|
: base(page, rankings)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TableColumn[] CreateAdditionalHeaders() => new[]
|
||||||
|
{
|
||||||
|
new TableColumn("Active Users", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
new TableColumn("Avg. Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
new TableColumn("Avg. Perf.", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override Country GetCountry(CountryStatistics item) => item.Country;
|
||||||
|
|
||||||
|
protected override Drawable CreateFlagContent(CountryStatistics item) => new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.GetFont(size: TEXT_SIZE),
|
||||||
|
Text = $@"{item.Country.FullName}",
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override Drawable[] CreateAdditionalContent(CountryStatistics item) => new Drawable[]
|
||||||
|
{
|
||||||
|
new ColoredRowText
|
||||||
|
{
|
||||||
|
Text = $@"{item.ActiveUsers:N0}",
|
||||||
|
},
|
||||||
|
new ColoredRowText
|
||||||
|
{
|
||||||
|
Text = $@"{item.PlayCount:N0}",
|
||||||
|
},
|
||||||
|
new ColoredRowText
|
||||||
|
{
|
||||||
|
Text = $@"{item.RankedScore:N0}",
|
||||||
|
},
|
||||||
|
new ColoredRowText
|
||||||
|
{
|
||||||
|
Text = $@"{item.RankedScore / Math.Max(item.ActiveUsers, 1):N0}",
|
||||||
|
},
|
||||||
|
new RowText
|
||||||
|
{
|
||||||
|
Text = $@"{item.Performance:N0}",
|
||||||
|
},
|
||||||
|
new ColoredRowText
|
||||||
|
{
|
||||||
|
Text = $@"{item.Performance / Math.Max(item.ActiveUsers, 1):N0}",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
28
osu.Game/Overlays/Rankings/Tables/PerformanceTable.cs
Normal file
28
osu.Game/Overlays/Rankings/Tables/PerformanceTable.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 System.Collections.Generic;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Rankings.Tables
|
||||||
|
{
|
||||||
|
public class PerformanceTable : UserBasedTable
|
||||||
|
{
|
||||||
|
public PerformanceTable(int page, IReadOnlyList<UserStatistics> rankings)
|
||||||
|
: base(page, rankings)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TableColumn[] CreateUniqueHeaders() => new[]
|
||||||
|
{
|
||||||
|
new TableColumn("Performance", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[]
|
||||||
|
{
|
||||||
|
new RowText { Text = $@"{item.PP:N0}", }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
140
osu.Game/Overlays/Rankings/Tables/RankingsTable.cs
Normal file
140
osu.Game/Overlays/Rankings/Tables/RankingsTable.cs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// 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.Graphics.Containers;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Users.Drawables;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Rankings.Tables
|
||||||
|
{
|
||||||
|
public abstract class RankingsTable<TModel> : TableContainer
|
||||||
|
{
|
||||||
|
protected const int TEXT_SIZE = 14;
|
||||||
|
private const float horizontal_inset = 20;
|
||||||
|
private const float row_height = 25;
|
||||||
|
private const int items_per_page = 50;
|
||||||
|
|
||||||
|
private readonly int page;
|
||||||
|
private readonly IReadOnlyList<TModel> rankings;
|
||||||
|
|
||||||
|
protected RankingsTable(int page, IReadOnlyList<TModel> rankings)
|
||||||
|
{
|
||||||
|
this.page = page;
|
||||||
|
this.rankings = rankings;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
Padding = new MarginPadding { Horizontal = horizontal_inset };
|
||||||
|
RowSize = new Dimension(GridSizeMode.Absolute, row_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
FillFlowContainer backgroundFlow;
|
||||||
|
|
||||||
|
AddInternal(backgroundFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Depth = 1f,
|
||||||
|
Margin = new MarginPadding { Top = row_height }
|
||||||
|
});
|
||||||
|
|
||||||
|
rankings.ForEach(_ => backgroundFlow.Add(new TableRowBackground()));
|
||||||
|
|
||||||
|
Columns = mainHeaders.Concat(CreateAdditionalHeaders()).ToArray();
|
||||||
|
Content = rankings.Select((s, i) => createContent((page - 1) * items_per_page + i, s)).ToArray().ToRectangular();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable[] createContent(int index, TModel item) => new Drawable[] { createIndexDrawable(index), createMainContent(item) }.Concat(CreateAdditionalContent(item)).ToArray();
|
||||||
|
|
||||||
|
private static TableColumn[] mainHeaders => new[]
|
||||||
|
{
|
||||||
|
new TableColumn(string.Empty, Anchor.Centre, new Dimension(GridSizeMode.Absolute, 50)), // place
|
||||||
|
new TableColumn(string.Empty, Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed)), // flag and username (country name)
|
||||||
|
};
|
||||||
|
|
||||||
|
protected abstract TableColumn[] CreateAdditionalHeaders();
|
||||||
|
|
||||||
|
protected abstract Drawable[] CreateAdditionalContent(TModel item);
|
||||||
|
|
||||||
|
protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty, HighlightedColumn());
|
||||||
|
|
||||||
|
protected abstract Country GetCountry(TModel item);
|
||||||
|
|
||||||
|
protected abstract Drawable CreateFlagContent(TModel item);
|
||||||
|
|
||||||
|
private OsuSpriteText createIndexDrawable(int index) => new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = $"#{index + 1}",
|
||||||
|
Font = OsuFont.GetFont(size: TEXT_SIZE, weight: FontWeight.Bold)
|
||||||
|
};
|
||||||
|
|
||||||
|
private FillFlowContainer createMainContent(TModel item) => new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(7, 0),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new UpdateableFlag(GetCountry(item))
|
||||||
|
{
|
||||||
|
Size = new Vector2(20, 13),
|
||||||
|
ShowPlaceholderOnNull = false,
|
||||||
|
},
|
||||||
|
CreateFlagContent(item)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected virtual string HighlightedColumn() => @"Performance";
|
||||||
|
|
||||||
|
private class HeaderText : OsuSpriteText
|
||||||
|
{
|
||||||
|
private readonly string highlighted;
|
||||||
|
|
||||||
|
public HeaderText(string text, string highlighted)
|
||||||
|
{
|
||||||
|
this.highlighted = highlighted;
|
||||||
|
|
||||||
|
Text = text;
|
||||||
|
Font = OsuFont.GetFont(size: 12);
|
||||||
|
Margin = new MarginPadding { Horizontal = 10 };
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
if (Text != highlighted)
|
||||||
|
Colour = colours.GreySeafoamLighter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class RowText : OsuSpriteText
|
||||||
|
{
|
||||||
|
public RowText()
|
||||||
|
{
|
||||||
|
Font = OsuFont.GetFont(size: TEXT_SIZE);
|
||||||
|
Margin = new MarginPadding { Horizontal = 10 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class ColoredRowText : RowText
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
Colour = colours.GreySeafoamLighter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
osu.Game/Overlays/Rankings/Tables/ScoresTable.cs
Normal file
38
osu.Game/Overlays/Rankings/Tables/ScoresTable.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Rankings.Tables
|
||||||
|
{
|
||||||
|
public class ScoresTable : UserBasedTable
|
||||||
|
{
|
||||||
|
public ScoresTable(int page, IReadOnlyList<UserStatistics> rankings)
|
||||||
|
: base(page, rankings)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TableColumn[] CreateUniqueHeaders() => new[]
|
||||||
|
{
|
||||||
|
new TableColumn("Total Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
new TableColumn("Ranked Score", Anchor.Centre, new Dimension(GridSizeMode.AutoSize))
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override Drawable[] CreateUniqueContent(UserStatistics item) => new Drawable[]
|
||||||
|
{
|
||||||
|
new ColoredRowText
|
||||||
|
{
|
||||||
|
Text = $@"{item.TotalScore:N0}",
|
||||||
|
},
|
||||||
|
new RowText
|
||||||
|
{
|
||||||
|
Text = $@"{item.RankedScore:N0}",
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override string HighlightedColumn() => @"Ranked Score";
|
||||||
|
}
|
||||||
|
}
|
56
osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs
Normal file
56
osu.Game/Overlays/Rankings/Tables/TableRowBackground.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// 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.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Rankings.Tables
|
||||||
|
{
|
||||||
|
public class TableRowBackground : CompositeDrawable
|
||||||
|
{
|
||||||
|
private const int fade_duration = 100;
|
||||||
|
|
||||||
|
private readonly Box background;
|
||||||
|
|
||||||
|
private Color4 idleColour;
|
||||||
|
private Color4 hoverColour;
|
||||||
|
|
||||||
|
public TableRowBackground()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Height = 25;
|
||||||
|
|
||||||
|
CornerRadius = 3;
|
||||||
|
Masking = true;
|
||||||
|
|
||||||
|
InternalChild = background = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
background.Colour = idleColour = colours.GreySeafoam;
|
||||||
|
hoverColour = colours.GreySeafoamLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
background.FadeColour(hoverColour, fade_duration, Easing.OutQuint);
|
||||||
|
return base.OnHover(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e)
|
||||||
|
{
|
||||||
|
background.FadeColour(idleColour, fade_duration, Easing.OutQuint);
|
||||||
|
base.OnHoverLost(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs
Normal file
56
osu.Game/Overlays/Rankings/Tables/UserBasedTable.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Users;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Rankings.Tables
|
||||||
|
{
|
||||||
|
public abstract class UserBasedTable : RankingsTable<UserStatistics>
|
||||||
|
{
|
||||||
|
protected UserBasedTable(int page, IReadOnlyList<UserStatistics> rankings)
|
||||||
|
: base(page, rankings)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TableColumn[] CreateAdditionalHeaders() => new[]
|
||||||
|
{
|
||||||
|
new TableColumn("Accuracy", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
new TableColumn("Play Count", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
}.Concat(CreateUniqueHeaders()).Concat(new[]
|
||||||
|
{
|
||||||
|
new TableColumn("SS", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
new TableColumn("S", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
new TableColumn("A", Anchor.Centre, new Dimension(GridSizeMode.AutoSize)),
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
protected sealed override Country GetCountry(UserStatistics item) => item.User.Country;
|
||||||
|
|
||||||
|
protected sealed override Drawable CreateFlagContent(UserStatistics item)
|
||||||
|
{
|
||||||
|
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: TEXT_SIZE)) { AutoSizeAxes = Axes.Both };
|
||||||
|
username.AddUserLink(item.User);
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected sealed override Drawable[] CreateAdditionalContent(UserStatistics item) => new[]
|
||||||
|
{
|
||||||
|
new ColoredRowText { Text = $@"{item.Accuracy:F2}%", },
|
||||||
|
new ColoredRowText { Text = $@"{item.PlayCount:N0}", },
|
||||||
|
}.Concat(CreateUniqueContent(item)).Concat(new[]
|
||||||
|
{
|
||||||
|
new ColoredRowText { Text = $@"{item.GradesCount.SS + item.GradesCount.SSPlus:N0}", },
|
||||||
|
new ColoredRowText { Text = $@"{item.GradesCount.S + item.GradesCount.SPlus:N0}", },
|
||||||
|
new ColoredRowText { Text = $@"{item.GradesCount.A:N0}", }
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
protected abstract TableColumn[] CreateUniqueHeaders();
|
||||||
|
|
||||||
|
protected abstract Drawable[] CreateUniqueContent(UserStatistics item);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -11,6 +12,7 @@ using osu.Game.Graphics.Containers;
|
|||||||
namespace osu.Game.Overlays.SearchableList
|
namespace osu.Game.Overlays.SearchableList
|
||||||
{
|
{
|
||||||
public class DisplayStyleControl<T> : Container
|
public class DisplayStyleControl<T> : Container
|
||||||
|
where T : struct, Enum
|
||||||
{
|
{
|
||||||
public readonly SlimEnumDropdown<T> Dropdown;
|
public readonly SlimEnumDropdown<T> Dropdown;
|
||||||
public readonly Bindable<PanelDisplayStyle> DisplayStyle = new Bindable<PanelDisplayStyle>();
|
public readonly Bindable<PanelDisplayStyle> DisplayStyle = new Bindable<PanelDisplayStyle>();
|
||||||
|
@ -13,7 +13,9 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.SearchableList
|
namespace osu.Game.Overlays.SearchableList
|
||||||
{
|
{
|
||||||
public abstract class SearchableListFilterControl<T, U> : Container
|
public abstract class SearchableListFilterControl<TTab, TCategory> : Container
|
||||||
|
where TTab : struct, Enum
|
||||||
|
where TCategory : struct, Enum
|
||||||
{
|
{
|
||||||
private const float padding = 10;
|
private const float padding = 10;
|
||||||
|
|
||||||
@ -21,12 +23,12 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
private readonly Box tabStrip;
|
private readonly Box tabStrip;
|
||||||
|
|
||||||
public readonly SearchTextBox Search;
|
public readonly SearchTextBox Search;
|
||||||
public readonly PageTabControl<T> Tabs;
|
public readonly PageTabControl<TTab> Tabs;
|
||||||
public readonly DisplayStyleControl<U> DisplayStyleControl;
|
public readonly DisplayStyleControl<TCategory> DisplayStyleControl;
|
||||||
|
|
||||||
protected abstract Color4 BackgroundColour { get; }
|
protected abstract Color4 BackgroundColour { get; }
|
||||||
protected abstract T DefaultTab { get; }
|
protected abstract TTab DefaultTab { get; }
|
||||||
protected abstract U DefaultCategory { get; }
|
protected abstract TCategory DefaultCategory { get; }
|
||||||
protected virtual Drawable CreateSupplementaryControls() => null;
|
protected virtual Drawable CreateSupplementaryControls() => null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -36,9 +38,6 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
|
|
||||||
protected SearchableListFilterControl()
|
protected SearchableListFilterControl()
|
||||||
{
|
{
|
||||||
if (!typeof(T).IsEnum)
|
|
||||||
throw new InvalidOperationException("SearchableListFilterControl's sort tabs only support enums as the generic type argument");
|
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
var controls = CreateSupplementaryControls();
|
var controls = CreateSupplementaryControls();
|
||||||
@ -90,7 +89,7 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Right = 225 },
|
Padding = new MarginPadding { Right = 225 },
|
||||||
Child = Tabs = new PageTabControl<T>
|
Child = Tabs = new PageTabControl<TTab>
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
},
|
},
|
||||||
@ -105,7 +104,7 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DisplayStyleControl = new DisplayStyleControl<U>
|
DisplayStyleControl = new DisplayStyleControl<TCategory>
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user