1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 17:43:05 +08:00

Merge branch 'refactor-osd' into music-controller-hotkeys

This commit is contained in:
Lucas A 2019-08-10 16:25:59 +02:00
commit 8c630b4f4c
218 changed files with 2825 additions and 1445 deletions

View File

@ -1,5 +1,5 @@
<p align="center"> <p align="center">
<img width="256" height="256" src="assets/lazer.png"> <img width="500px" src="assets/lazer.png">
</p> </p>
# osu! # osu!
@ -100,7 +100,7 @@ Before starting, please make sure you are familiar with the [development and tes
Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as pain-free as possible. Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. We welcome all feedback so we can make contributing to this project as pain-free as possible.
For those interested, we love to reward quality contributions via bounties, paid out via paypal or osu! supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project. For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via paypal or osu! supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project.
## Licence ## Licence

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 187 KiB

View File

@ -63,6 +63,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.704.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2019.726.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -29,29 +29,36 @@ namespace osu.Desktop
if (!host.IsPrimaryInstance) if (!host.IsPrimaryInstance)
{ {
var importer = new ArchiveImportIPCChannel(host); if (args.Length > 0 && args[0].Contains('.')) // easy way to check for a file import in args
// Restore the cwd so relative paths given at the command line work correctly
Directory.SetCurrentDirectory(cwd);
foreach (var file in args)
{ {
Console.WriteLine(@"Importing {0}", file); var importer = new ArchiveImportIPCChannel(host);
if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000)) // Restore the cwd so relative paths given at the command line work correctly
throw new TimeoutException(@"IPC took too long to send"); Directory.SetCurrentDirectory(cwd);
foreach (var file in args)
{
Console.WriteLine(@"Importing {0}", file);
if (!importer.ImportAsync(Path.GetFullPath(file)).Wait(3000))
throw new TimeoutException(@"IPC took too long to send");
}
return 0;
} }
// we want to allow multiple instances to be started when in debug.
if (!DebugUtils.IsDebugBuild)
return 0;
} }
else
{
switch (args.FirstOrDefault() ?? string.Empty)
{
default:
host.Run(new OsuGameDesktop(args));
break;
case "--tournament": switch (args.FirstOrDefault() ?? string.Empty)
host.Run(new TournamentGame()); {
break; default:
} host.Run(new OsuGameDesktop(args));
break;
case "--tournament":
host.Run(new TournamentGame());
break;
} }
return 0; return 0;

BIN
osu.Desktop/lazer.ico Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, "AppDelegate"); UIApplication.Main(args, "GameUIApplication", "AppDelegate");
} }
} }
} }

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -3,12 +3,10 @@
using System; using System;
using osuTK; using osuTK;
using osuTK.Graphics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch.Objects.Drawable namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
@ -60,16 +58,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss); ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss);
} }
protected override void SkinChanged(ISkinSource skin, bool allowFallback) protected override bool UseTransformStateManagement => false;
{
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
// TODO: update to use new state management.
using (BeginAbsoluteSequence(HitObject.StartTime - HitObject.TimePreempt)) using (BeginAbsoluteSequence(HitObject.StartTime - HitObject.TimePreempt))
this.FadeIn(200); this.FadeIn(200);

View File

@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces;
using osuTK; using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Objects.Drawable namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
@ -27,16 +26,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
private void load() private void load()
{ {
AddInternal(pulp = new Pulp { Size = Size }); AddInternal(pulp = new Pulp { Size = Size });
}
public override Color4 AccentColour AccentColour.BindValueChanged(colour => { pulp.AccentColour = colour.NewValue; }, true);
{
get => base.AccentColour;
set
{
base.AccentColour = value;
pulp.AccentColour = AccentColour;
}
} }
} }
} }

View File

@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
private void load() private void load()
{ {
// todo: this should come from the skin. // todo: this should come from the skin.
AccentColour = colourForRepresentation(HitObject.VisualRepresentation); AccentColour.Value = colourForRepresentation(HitObject.VisualRepresentation);
AddRangeInternal(new[] AddRangeInternal(new[]
{ {
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
Hollow = !HitObject.HyperDash, Hollow = !HitObject.HyperDash,
Type = EdgeEffectType.Glow, Type = EdgeEffectType.Glow,
Radius = 4 * radius_adjust, Radius = 4 * radius_adjust,
Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Darken(1).Opacity(0.6f) Colour = HitObject.HyperDash ? Color4.Red : AccentColour.Value.Darken(1).Opacity(0.6f)
}, },
Size = new Vector2(Height), Size = new Vector2(Height),
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
new Box new Box
{ {
AlwaysPresent = true, AlwaysPresent = true,
Colour = AccentColour, Colour = AccentColour.Value,
Alpha = 0, Alpha = 0,
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
} }
@ -115,32 +115,32 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(small_pulp), Size = new Vector2(small_pulp),
Y = -0.34f, Y = -0.34f,
}, },
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(large_pulp_4), Size = new Vector2(large_pulp_4),
Position = positionAt(0, distance_from_centre_4), Position = positionAt(0, distance_from_centre_4),
}, },
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(large_pulp_4), Size = new Vector2(large_pulp_4),
Position = positionAt(90, distance_from_centre_4), Position = positionAt(90, distance_from_centre_4),
}, },
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(large_pulp_4), Size = new Vector2(large_pulp_4),
Position = positionAt(180, distance_from_centre_4), Position = positionAt(180, distance_from_centre_4),
}, },
new Pulp new Pulp
{ {
Size = new Vector2(large_pulp_4), Size = new Vector2(large_pulp_4),
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Position = positionAt(270, distance_from_centre_4), Position = positionAt(270, distance_from_centre_4),
}, },
} }
@ -154,32 +154,32 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(small_pulp), Size = new Vector2(small_pulp),
Y = -0.3f, Y = -0.3f,
}, },
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(large_pulp_4), Size = new Vector2(large_pulp_4),
Position = positionAt(45, distance_from_centre_4), Position = positionAt(45, distance_from_centre_4),
}, },
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(large_pulp_4), Size = new Vector2(large_pulp_4),
Position = positionAt(135, distance_from_centre_4), Position = positionAt(135, distance_from_centre_4),
}, },
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(large_pulp_4), Size = new Vector2(large_pulp_4),
Position = positionAt(225, distance_from_centre_4), Position = positionAt(225, distance_from_centre_4),
}, },
new Pulp new Pulp
{ {
Size = new Vector2(large_pulp_4), Size = new Vector2(large_pulp_4),
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Position = positionAt(315, distance_from_centre_4), Position = positionAt(315, distance_from_centre_4),
}, },
} }
@ -193,26 +193,26 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(small_pulp), Size = new Vector2(small_pulp),
Y = -0.33f, Y = -0.33f,
}, },
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(large_pulp_3), Size = new Vector2(large_pulp_3),
Position = positionAt(60, distance_from_centre_3), Position = positionAt(60, distance_from_centre_3),
}, },
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(large_pulp_3), Size = new Vector2(large_pulp_3),
Position = positionAt(180, distance_from_centre_3), Position = positionAt(180, distance_from_centre_3),
}, },
new Pulp new Pulp
{ {
Size = new Vector2(large_pulp_3), Size = new Vector2(large_pulp_3),
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Position = positionAt(300, distance_from_centre_3), Position = positionAt(300, distance_from_centre_3),
}, },
} }
@ -226,26 +226,26 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(small_pulp), Size = new Vector2(small_pulp),
Y = -0.25f, Y = -0.25f,
}, },
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(large_pulp_3), Size = new Vector2(large_pulp_3),
Position = positionAt(0, distance_from_centre_3), Position = positionAt(0, distance_from_centre_3),
}, },
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(large_pulp_3), Size = new Vector2(large_pulp_3),
Position = positionAt(120, distance_from_centre_3), Position = positionAt(120, distance_from_centre_3),
}, },
new Pulp new Pulp
{ {
Size = new Vector2(large_pulp_3), Size = new Vector2(large_pulp_3),
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Position = positionAt(240, distance_from_centre_3), Position = positionAt(240, distance_from_centre_3),
}, },
} }
@ -259,13 +259,13 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
{ {
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(small_pulp), Size = new Vector2(small_pulp),
Y = -0.3f Y = -0.3f
}, },
new Pulp new Pulp
{ {
AccentColour = AccentColour, AccentColour = AccentColour.Value,
Size = new Vector2(large_pulp_4 * 0.8f, large_pulp_4 * 2.5f), Size = new Vector2(large_pulp_4 * 0.8f, large_pulp_4 * 2.5f),
Y = 0.05f, Y = 0.05f,
}, },

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Mania.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, "AppDelegate"); UIApplication.Main(args, "GameUIApplication", "AppDelegate");
} }
} }
} }

View File

@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Mania.Tests
Child = drawableObject = new DrawableHoldNote(holdNote) Child = drawableObject = new DrawableHoldNote(holdNote)
{ {
Height = 300, Height = 300,
AccentColour = OsuColour.Gray(0.3f) AccentColour = { Value = OsuColour.Gray(0.3f) }
} }
}; };
} }

View File

@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Tests
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Child = new NoteContainer(direction, $"note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}") Child = new NoteContainer(direction, $"note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}")
{ {
Child = hitObject = new DrawableNote(note) { AccentColour = Color4.OrangeRed } Child = hitObject = new DrawableNote(note) { AccentColour = { Value = Color4.OrangeRed } }
} }
}; };
} }
@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Mania.Tests
Child = hitObject = new DrawableHoldNote(note) Child = hitObject = new DrawableHoldNote(note)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
AccentColour = Color4.OrangeRed, AccentColour = { Value = Color4.OrangeRed },
} }
} }
}; };

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -6,7 +6,6 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osuTK.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -36,11 +35,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary> /// </summary>
private bool hasBroken; private bool hasBroken;
private readonly Container<DrawableHoldNoteTick> tickContainer;
public DrawableHoldNote(HoldNote hitObject) public DrawableHoldNote(HoldNote hitObject)
: base(hitObject) : base(hitObject)
{ {
Container<DrawableHoldNoteTick> tickContainer;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
AddRangeInternal(new Drawable[] AddRangeInternal(new Drawable[]
@ -74,6 +72,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
AddNested(Head); AddNested(Head);
AddNested(Tail); AddNested(Tail);
AccentColour.BindValueChanged(colour =>
{
bodyPiece.AccentColour = colour.NewValue;
Head.AccentColour.Value = colour.NewValue;
Tail.AccentColour.Value = colour.NewValue;
tickContainer.ForEach(t => t.AccentColour.Value = colour.NewValue);
}, true);
} }
protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e) protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
@ -83,20 +89,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft; bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
} }
public override Color4 AccentColour
{
get => base.AccentColour;
set
{
base.AccentColour = value;
bodyPiece.AccentColour = value;
Head.AccentColour = value;
Tail.AccentColour = value;
tickContainer.ForEach(t => t.AccentColour = value);
}
}
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (Tail.AllJudged) if (Tail.AllJudged)

View File

@ -3,7 +3,6 @@
using System; using System;
using osuTK; using osuTK;
using osuTK.Graphics;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -23,11 +22,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// </summary> /// </summary>
public Func<double?> HoldStartTime; public Func<double?> HoldStartTime;
private readonly Container glowContainer;
public DrawableHoldNoteTick(HoldNoteTick hitObject) public DrawableHoldNoteTick(HoldNoteTick hitObject)
: base(hitObject) : base(hitObject)
{ {
Container glowContainer;
Anchor = Anchor.TopCentre; Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;
@ -53,23 +52,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
} }
} }
}); });
}
public override Color4 AccentColour AccentColour.BindValueChanged(colour =>
{
get => base.AccentColour;
set
{ {
base.AccentColour = value;
glowContainer.EdgeEffect = new EdgeEffectParameters glowContainer.EdgeEffect = new EdgeEffectParameters
{ {
Type = EdgeEffectType.Glow, Type = EdgeEffectType.Glow,
Radius = 2f, Radius = 2f,
Roundness = 15f, Roundness = 15f,
Colour = value.Opacity(0.3f) Colour = colour.NewValue.Opacity(0.3f)
}; };
} }, true);
} }
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)

View File

@ -58,8 +58,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
HitObject = hitObject; HitObject = hitObject;
} }
protected override bool UseTransformStateManagement => false;
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
// TODO: update to use new state management.
switch (state) switch (state)
{ {
case ArmedState.Miss: case ArmedState.Miss:

View File

@ -3,7 +3,6 @@
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osuTK.Graphics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
@ -30,6 +29,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Masking = true; Masking = true;
AddInternal(headPiece = new NotePiece()); AddInternal(headPiece = new NotePiece());
AccentColour.BindValueChanged(colour =>
{
headPiece.AccentColour = colour.NewValue;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = colour.NewValue.Lighten(1f).Opacity(0.6f),
Radius = 10,
};
}, true);
} }
protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e) protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
@ -39,23 +50,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
headPiece.Anchor = headPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre; headPiece.Anchor = headPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
} }
public override Color4 AccentColour
{
get => base.AccentColour;
set
{
base.AccentColour = value;
headPiece.AccentColour = AccentColour;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = AccentColour.Lighten(1f).Opacity(0.6f),
Radius = 10,
};
}
}
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (!userTriggered) if (!userTriggered)

View File

@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Mania.UI
/// <param name="hitObject">The DrawableHitObject to add.</param> /// <param name="hitObject">The DrawableHitObject to add.</param>
public override void Add(DrawableHitObject hitObject) public override void Add(DrawableHitObject hitObject)
{ {
hitObject.AccentColour = AccentColour; hitObject.AccentColour.Value = AccentColour;
hitObject.OnNewResult += OnNewResult; hitObject.OnNewResult += OnNewResult;
HitObjectContainer.Add(hitObject); HitObjectContainer.Add(hitObject);

View File

@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mania.UI
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
{ {
Type = EdgeEffectType.Glow, Type = EdgeEffectType.Glow,
Colour = Interpolation.ValueAt(0.1f, judgedObject.AccentColour, Color4.White, 0, 1), Colour = Interpolation.ValueAt(0.1f, judgedObject.AccentColour.Value, Color4.White, 0, 1),
Radius = 100, Radius = 100,
}, },
Child = new Box Child = new Box

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, "AppDelegate"); UIApplication.Main(args, "GameUIApplication", "AppDelegate");
} }
} }
} }

View File

@ -15,6 +15,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.931145117263422, "diffcalc-test")]
[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);

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
this.hitCircle = hitCircle; this.hitCircle = hitCircle;
Origin = Anchor.Centre; Origin = Anchor.Centre;
Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2); Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Scale = new Vector2(hitCircle.Scale); Scale = new Vector2(hitCircle.Scale);
CornerRadius = Size.X / 2; CornerRadius = Size.X / 2;

View File

@ -24,7 +24,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
InternalChild = body = new ManualSliderBody InternalChild = body = new ManualSliderBody
{ {
AccentColour = Color4.Transparent, AccentColour = Color4.Transparent,
PathRadius = slider.Scale * 64
}; };
} }
@ -34,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
body.BorderColour = colours.Yellow; body.BorderColour = colours.Yellow;
PositionBindable.BindValueChanged(_ => updatePosition(), true); PositionBindable.BindValueChanged(_ => updatePosition(), true);
ScaleBindable.BindValueChanged(scale => body.PathRadius = scale.NewValue * 64, true); ScaleBindable.BindValueChanged(scale => body.PathRadius = scale.NewValue * OsuHitObject.OBJECT_RADIUS, true);
} }
private void updatePosition() => Position = slider.StackedPosition; private void updatePosition() => Position = slider.StackedPosition;

View File

@ -13,13 +13,11 @@ using static osu.Game.Input.Handlers.ReplayInputHandler;
namespace osu.Game.Rulesets.Osu.Mods namespace osu.Game.Rulesets.Osu.Mods
{ {
public class OsuModRelax : ModRelax, IApplicableFailOverride, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject> public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject>
{ {
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things."; public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
public bool AllowFail => false;
public void Update(Playfield playfield) public void Update(Playfield playfield)
{ {
bool requiresHold = false; bool requiresHold = false;

View File

@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Acronym => "TD"; public override string Acronym => "TD";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;
public override ModType Type => ModType.System;
public override bool Ranked => true; public override bool Ranked => true;
} }
} }

View File

@ -10,7 +10,6 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using osuTK; using osuTK;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
@ -98,19 +97,14 @@ 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);
}
public override Color4 AccentColour AccentColour.BindValueChanged(colour =>
{
get => base.AccentColour;
set
{ {
base.AccentColour = value; explode.Colour = colour.NewValue;
explode.Colour = AccentColour; glow.Colour = colour.NewValue;
glow.Colour = AccentColour; circle.Colour = colour.NewValue;
circle.Colour = AccentColour; ApproachCircle.Colour = colour.NewValue;
ApproachCircle.Colour = AccentColour; }, true);
}
} }
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
@ -134,16 +128,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ApplyResult(r => r.Type = result); ApplyResult(r => r.Type = result);
} }
protected override void UpdatePreemptState() protected override void UpdateInitialTransforms()
{ {
base.UpdatePreemptState(); base.UpdateInitialTransforms();
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt)); ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt));
ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt); ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt);
ApproachCircle.Expire(true); ApproachCircle.Expire(true);
} }
protected override void UpdateCurrentState(ArmedState state) protected override void UpdateStateTransforms(ArmedState state)
{ {
glow.FadeOut(400); glow.FadeOut(400);

View File

@ -1,15 +1,10 @@
// 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.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osuTK.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
@ -29,6 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ShakeDuration = 30, ShakeDuration = 30,
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}); });
Alpha = 0; Alpha = 0;
} }
@ -38,47 +34,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void ClearInternal(bool disposeChildren = true) => shakeContainer.Clear(disposeChildren); protected override void ClearInternal(bool disposeChildren = true) => shakeContainer.Clear(disposeChildren);
protected override bool RemoveInternal(Drawable drawable) => shakeContainer.Remove(drawable); protected override bool RemoveInternal(Drawable drawable) => shakeContainer.Remove(drawable);
protected sealed override void UpdateState(ArmedState state) protected sealed override double InitialLifetimeOffset => HitObject.TimePreempt;
{
double transformTime = HitObject.StartTime - HitObject.TimePreempt;
base.ApplyTransformsAt(transformTime, true); protected override void UpdateInitialTransforms() => this.FadeIn(HitObject.TimeFadeIn);
base.ClearTransformsAfter(transformTime, true);
using (BeginAbsoluteSequence(transformTime, true))
{
UpdatePreemptState();
var judgementOffset = Math.Min(HitObject.HitWindows.HalfWindowFor(HitResult.Miss), Result?.TimeOffset ?? 0);
using (BeginDelayedSequence(HitObject.TimePreempt + judgementOffset, true))
UpdateCurrentState(state);
}
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn);
protected virtual void UpdateCurrentState(ArmedState state)
{
}
// Todo: At some point we need to move these to DrawableHitObject after ensuring that all other Rulesets apply
// transforms in the same way and don't rely on them not being cleared
public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
{
}
public override void ApplyTransformsAt(double time, bool propagateChildren = false)
{
}
private OsuInputManager osuActionInputManager; private OsuInputManager osuActionInputManager;
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager); internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);

View File

@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? HitResult.Great : HitResult.Miss); ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? HitResult.Great : HitResult.Miss);
} }
protected override void UpdatePreemptState() protected override void UpdateInitialTransforms()
{ {
animDuration = Math.Min(150, repeatPoint.SpanDuration / 2); animDuration = Math.Min(150, repeatPoint.SpanDuration / 2);
@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
); );
} }
protected override void UpdateCurrentState(ArmedState state) protected override void UpdateStateTransforms(ArmedState state)
{ {
switch (state) switch (state)
{ {

View File

@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
Body = new SnakingSliderBody(s) Body = new SnakingSliderBody(s)
{ {
PathRadius = s.Scale * 64, PathRadius = s.Scale * OsuHitObject.OBJECT_RADIUS,
}, },
ticks = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both }, ticks = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
repeatPoints = new Container<DrawableRepeatPoint> { RelativeSizeAxes = Axes.Both }, repeatPoints = new Container<DrawableRepeatPoint> { RelativeSizeAxes = Axes.Both },
@ -114,20 +114,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
pathBindable.BindTo(slider.PathBindable); pathBindable.BindTo(slider.PathBindable);
pathBindable.BindValueChanged(_ => Body.Refresh()); pathBindable.BindValueChanged(_ => Body.Refresh());
}
public override Color4 AccentColour AccentColour.BindValueChanged(colour =>
{
get => base.AccentColour;
set
{ {
base.AccentColour = value; Body.AccentColour = colour.NewValue;
Body.AccentColour = AccentColour; Ball.AccentColour = colour.NewValue;
Ball.AccentColour = AccentColour;
foreach (var drawableHitObject in NestedHitObjects) foreach (var drawableHitObject in NestedHitObjects)
drawableHitObject.AccentColour = AccentColour; drawableHitObject.AccentColour.Value = colour.NewValue;
} }, true);
} }
public readonly Bindable<bool> Tracking = new Bindable<bool>(); public readonly Bindable<bool> Tracking = new Bindable<bool>();
@ -156,14 +151,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
} }
} }
public override void OnKilled()
{
base.OnKilled();
Body.RecyclePath();
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback) protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{ {
base.SkinChanged(skin, allowFallback); base.SkinChanged(skin, allowFallback);
Body.BorderSize = skin.GetValue<SkinConfiguration, float?>(s => s.SliderBorderSize) ?? SliderBody.DEFAULT_BORDER_SIZE; Body.BorderSize = skin.GetValue<SkinConfiguration, float?>(s => s.SliderBorderSize) ?? SliderBody.DEFAULT_BORDER_SIZE;
Body.AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour; Body.AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : (Color4?)null) ?? AccentColour.Value;
Body.BorderColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White; Body.BorderColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : (Color4?)null) ?? Color4.White;
Ball.AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? AccentColour; Ball.AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : (Color4?)null) ?? AccentColour.Value;
} }
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
@ -189,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}); });
} }
protected override void UpdateCurrentState(ArmedState state) protected override void UpdateStateTransforms(ArmedState state)
{ {
Ball.FadeIn(); Ball.FadeIn();
Ball.ScaleTo(HitObject.Scale); Ball.ScaleTo(HitObject.Scale);

View File

@ -34,14 +34,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre, Origin = Anchor.Centre,
CornerRadius = Size.X / 2, CornerRadius = Size.X / 2,
BorderThickness = 2, BorderThickness = 2,
BorderColour = Color4.White, BorderColour = Color4.White,
Child = new Box Child = new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = AccentColour, Colour = AccentColour.Value,
Alpha = 0.3f, Alpha = 0.3f,
} }
}, restrictSize: false) }, restrictSize: false)
@ -54,13 +52,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss); ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
} }
protected override void UpdatePreemptState() protected override void UpdateInitialTransforms()
{ {
this.FadeOut().FadeIn(ANIM_DURATION); this.FadeOut().FadeIn(ANIM_DURATION);
this.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf); this.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf);
} }
protected override void UpdateCurrentState(ArmedState state) protected override void UpdateStateTransforms(ArmedState state)
{ {
switch (state) switch (state)
{ {

View File

@ -196,9 +196,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
symbol.RotateTo(Disc.Rotation / 2, 500, Easing.OutQuint); symbol.RotateTo(Disc.Rotation / 2, 500, Easing.OutQuint);
} }
protected override void UpdatePreemptState() protected override void UpdateInitialTransforms()
{ {
base.UpdatePreemptState(); base.UpdateInitialTransforms();
circleContainer.ScaleTo(Spinner.Scale * 0.3f); circleContainer.ScaleTo(Spinner.Scale * 0.3f);
circleContainer.ScaleTo(Spinner.Scale, HitObject.TimePreempt / 1.4f, Easing.OutQuint); circleContainer.ScaleTo(Spinner.Scale, HitObject.TimePreempt / 1.4f, Easing.OutQuint);
@ -213,7 +213,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
.ScaleTo(1, 500, Easing.OutQuint); .ScaleTo(1, 500, Easing.OutQuint);
} }
protected override void UpdateCurrentState(ArmedState state) protected override void UpdateStateTransforms(ArmedState state)
{ {
var sequence = this.Delay(Spinner.Duration).FadeOut(160); var sequence = this.Delay(Spinner.Duration).FadeOut(160);

View File

@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public CirclePiece() public CirclePiece()
{ {
Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2); Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Masking = true; Masking = true;
CornerRadius = Size.X / 2; CornerRadius = Size.X / 2;

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public ExplodePiece() public ExplodePiece()
{ {
Size = new Vector2(128); Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;

View File

@ -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 osu.Framework.Graphics; using osu.Framework.Graphics;
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public FlashPiece() public FlashPiece()
{ {
Size = new Vector2(128); Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;

View File

@ -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 osu.Framework.Graphics; using osu.Framework.Graphics;
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public RingPiece() public RingPiece()
{ {
Size = new Vector2(128); Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;

View File

@ -17,8 +17,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition public class SliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition
{ {
private const float width = 128;
private Color4 accentColour = Color4.Black; private Color4 accentColour = Color4.Black;
public Func<OsuAction?> GetInitialHitAction; public Func<OsuAction?> GetInitialHitAction;
@ -57,8 +55,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Width = width, Width = OsuHitObject.OBJECT_RADIUS * 2,
Height = width, Height = OsuHitObject.OBJECT_RADIUS * 2,
Alpha = 0, Alpha = 0,
Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new CircularContainer Child = new SkinnableDrawable("Play/osu/sliderfollowcircle", _ => new CircularContainer
{ {
@ -84,8 +82,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Alpha = 1, Alpha = 1,
Child = new Container Child = new Container
{ {
Width = width, Width = OsuHitObject.OBJECT_RADIUS * 2,
Height = width, Height = OsuHitObject.OBJECT_RADIUS * 2,
// TODO: support skin filename animation (sliderb0, sliderb1...) // TODO: support skin filename animation (sliderb0, sliderb1...)
Child = new SkinnableDrawable("Play/osu/sliderb", _ => new CircularContainer Child = new SkinnableDrawable("Play/osu/sliderb", _ => new CircularContainer
{ {

View File

@ -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 osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Lines;
@ -13,7 +14,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public const float DEFAULT_BORDER_SIZE = 1; public const float DEFAULT_BORDER_SIZE = 1;
private readonly SliderPath path; private SliderPath path;
protected Path Path => path; protected Path Path => path;
public float PathRadius public float PathRadius
@ -74,7 +76,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
protected SliderBody() protected SliderBody()
{ {
InternalChild = path = new SliderPath(); RecyclePath();
}
/// <summary>
/// Initialises a new <see cref="SliderPath"/>, releasing all resources retained by the old one.
/// </summary>
public virtual void RecyclePath()
{
InternalChild = path = new SliderPath
{
Position = path?.Position ?? Vector2.Zero,
PathRadius = path?.PathRadius ?? 10,
AccentColour = path?.AccentColour ?? Color4.White,
BorderColour = path?.BorderColour ?? Color4.White,
BorderSize = path?.BorderSize ?? DEFAULT_BORDER_SIZE,
Vertices = path?.Vertices ?? Array.Empty<Vector2>()
};
} }
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos); public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osuTK; using osuTK;
@ -78,9 +79,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
slider.Path.GetPathToProgress(CurrentCurve, 0, 1); slider.Path.GetPathToProgress(CurrentCurve, 0, 1);
SetVertices(CurrentCurve); SetVertices(CurrentCurve);
// The body is sized to the full path size to avoid excessive autosize computations // Force the body to be the final path size to avoid excessive autosize computations
Path.AutoSizeAxes = Axes.Both;
Size = Path.Size; Size = Path.Size;
updatePathSize();
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero); snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]); snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
@ -93,6 +97,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
setRange(lastSnakedStart, lastSnakedEnd); setRange(lastSnakedStart, lastSnakedEnd);
} }
public override void RecyclePath()
{
base.RecyclePath();
updatePathSize();
}
private void updatePathSize()
{
// Force the path to its final size to avoid excessive framebuffer resizes
Path.AutoSizeAxes = Axes.None;
Path.Size = Size;
}
private void setRange(double p0, double p1) private void setRange(double p0, double p1)
{ {
if (p0 > p1) if (p0 > p1)

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Objects
{ {
public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition
{ {
public const double OBJECT_RADIUS = 64; public const float OBJECT_RADIUS = 64;
public double TimePreempt = 600; public double TimePreempt = 600;
public double TimeFadeIn = 400; public double TimeFadeIn = 400;

View File

@ -138,6 +138,12 @@ namespace osu.Game.Rulesets.Osu
new MultiMod(new ModWindUp<OsuHitObject>(), new ModWindDown<OsuHitObject>()), new MultiMod(new ModWindUp<OsuHitObject>(), new ModWindDown<OsuHitObject>()),
}; };
case ModType.System:
return new Mod[]
{
new OsuModTouchDevice(),
};
default: default:
return new Mod[] { }; return new Mod[] { };
} }

View File

@ -0,0 +1,28 @@
osu file format v14
[Difficulty]
HPDrainRate:3
CircleSize:3
OverallDifficulty:3
ApproachRate:4.5
SliderMultiplier:0.799999999999999
SliderTickRate:1
[TimingPoints]
800,260.869565217391,3,2,10,60,1,0
[HitObjects]
// Linear
78,193,2365,2,0,L|330:193,1,0
78,193,3669,2,0,L|330:193,1,0
78,193,4973,2,0,L|330:193,1,0
// Perfect-curve
151,206,6278,2,0,P|293:75|345:204,1,0
151,206,8104,2,0,P|293:75|345:204,1,0
151,206,9930,2,0,P|293:75|345:204,1,0
// Bezier
76,191,11756,2,0,B|176:59|358:340|438:190,1,0
76,191,13582,2,0,B|176:59|358:340|438:190,1,0
76,191,15408,2,0,B|176:59|358:340|438:190,1,0

View File

@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private readonly TrailPart[] parts = new TrailPart[max_sprites]; private readonly TrailPart[] parts = new TrailPart[max_sprites];
private Vector2 size; private Vector2 size;
private readonly VertexBatch<TexturedTrailVertex> vertexBatch = new QuadBatch<TexturedTrailVertex>(max_sprites, 1); private readonly TrailBatch vertexBatch = new TrailBatch(max_sprites, 1);
public TrailDrawNode(CursorTrail source) public TrailDrawNode(CursorTrail source)
: base(source) : base(source)
@ -196,21 +196,16 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
for (int i = 0; i < parts.Length; ++i) for (int i = 0; i < parts.Length; ++i)
{ {
vertexBatch.DrawTime = parts[i].Time;
Vector2 pos = parts[i].Position; Vector2 pos = parts[i].Position;
float localTime = parts[i].Time;
DrawQuad( DrawQuad(
texture, texture,
new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y), new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y),
DrawColourInfo.Colour, DrawColourInfo.Colour,
null, null,
v => vertexBatch.Add(new TexturedTrailVertex vertexBatch.AddAction);
{
Position = v.Position,
TexturePosition = v.TexturePosition,
Time = localTime + 1,
Colour = v.Colour,
}));
} }
shader.Unbind(); shader.Unbind();
@ -222,6 +217,25 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
vertexBatch.Dispose(); vertexBatch.Dispose();
} }
// Todo: This shouldn't exist, but is currently used to reduce allocations by caching variable-capturing closures.
private class TrailBatch : QuadBatch<TexturedTrailVertex>
{
public new readonly Action<TexturedVertex2D> AddAction;
public float DrawTime;
public TrailBatch(int size, int maxBuffers)
: base(size, maxBuffers)
{
AddAction = v => Add(new TexturedTrailVertex
{
Position = v.Position,
TexturePosition = v.TexturePosition,
Time = DrawTime + 1,
Colour = v.Colour,
});
}
}
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]

View File

@ -12,6 +12,7 @@ using osu.Game.Rulesets.UI;
using System.Linq; using System.Linq;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.UI namespace osu.Game.Rulesets.Osu.UI
{ {
@ -39,7 +40,13 @@ namespace osu.Game.Rulesets.Osu.UI
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Depth = 1, Depth = 1,
}, },
HitObjectContainer, // Todo: This should not exist, but currently helps to reduce LOH allocations due to unbinding skin source events on judgement disposal
// Todo: Remove when hitobjects are properly pooled
new LocalSkinOverrideContainer(null)
{
RelativeSizeAxes = Axes.Both,
Child = HitObjectContainer,
},
approachCircles = new ApproachCircleProxyContainer approachCircles = new ApproachCircleProxyContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, "AppDelegate"); UIApplication.Main(args, "GameUIApplication", "AppDelegate");
} }
} }
} }

View File

@ -2,7 +2,7 @@
<Import Project="..\osu.TestProject.props" /> <Import Project="..\osu.TestProject.props" />
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -94,6 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
// TODO: update to use new state management.
var circlePiece = MainPiece as CirclePiece; var circlePiece = MainPiece as CirclePiece;
circlePiece?.FlashBox.FinishTransforms(); circlePiece?.FlashBox.FinishTransforms();

View File

@ -121,6 +121,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
} }
} }
protected override bool UseTransformStateManagement => false;
// Normal and clap samples are handled by the drum // Normal and clap samples are handled by the drum
protected override IEnumerable<HitSampleInfo> GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP); protected override IEnumerable<HitSampleInfo> GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP);

View File

@ -9,7 +9,7 @@ namespace osu.Game.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, null, "AppDelegate"); UIApplication.Main(args, "GameUIApplication", "AppDelegate");
} }
} }
} }

View File

@ -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 NUnit.Framework; using NUnit.Framework;
using osu.Game.Replays; using osu.Game.Replays;
@ -9,7 +10,7 @@ using osu.Game.Rulesets.Replays;
namespace osu.Game.Tests.NonVisual namespace osu.Game.Tests.NonVisual
{ {
[TestFixture] [TestFixture]
public class FramedReplayinputHandlerTest public class FramedReplayInputHandlerTest
{ {
private Replay replay; private Replay replay;
private TestInputHandler handler; private TestInputHandler handler;
@ -160,10 +161,7 @@ namespace osu.Game.Tests.NonVisual
[Test] [Test]
public void TestRewindInsideImportantSection() public void TestRewindInsideImportantSection()
{ {
// fast forward to important section fastForwardToPoint(3000);
while (handler.SetFrameFromTime(3000) != null)
{
}
setTime(4000, 4000); setTime(4000, 4000);
confirmCurrentFrame(4); confirmCurrentFrame(4);
@ -205,10 +203,7 @@ namespace osu.Game.Tests.NonVisual
[Test] [Test]
public void TestRewindOutOfImportantSection() public void TestRewindOutOfImportantSection()
{ {
// fast forward to important section fastForwardToPoint(3500);
while (handler.SetFrameFromTime(3500) != null)
{
}
confirmCurrentFrame(3); confirmCurrentFrame(3);
confirmNextFrame(4); confirmNextFrame(4);
@ -227,6 +222,15 @@ namespace osu.Game.Tests.NonVisual
confirmNextFrame(2); confirmNextFrame(2);
} }
private void fastForwardToPoint(double destination)
{
for (int i = 0; i < 1000; i++)
if (handler.SetFrameFromTime(destination) == null)
return;
throw new TimeoutException("Seek was never fulfilled");
}
private void setTime(double set, double? expect) private void setTime(double set, double? expect)
{ {
Assert.AreEqual(expect, handler.SetFrameFromTime(set)); Assert.AreEqual(expect, handler.SetFrameFromTime(set));
@ -274,6 +278,7 @@ namespace osu.Game.Tests.NonVisual
public TestInputHandler(Replay replay) public TestInputHandler(Replay replay)
: base(replay) : base(replay)
{ {
FrameAccuratePlayback = true;
} }
protected override double AllowedImportantTimeSpan => 1000; protected override double AllowedImportantTimeSpan => 1000;

View File

@ -10,6 +10,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Input.States; using osu.Framework.Input.States;
using osu.Framework.Platform; using osu.Framework.Platform;
@ -36,7 +37,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Background namespace osu.Game.Tests.Visual.Background
{ {
[TestFixture] [TestFixture]
public class TestSceneBackgroundScreenBeatmap : ManualInputManagerTestScene public class TestSceneUserDimContainer : ManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
@ -137,14 +138,14 @@ namespace osu.Game.Tests.Visual.Background
player.StoryboardEnabled.Value = true; player.StoryboardEnabled.Value = true;
}); });
waitForDim(); waitForDim();
AddAssert("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible()); AddAssert("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible);
AddStep("Storyboard Disabled", () => AddStep("Storyboard Disabled", () =>
{ {
player.ReplacesBackground.Value = false; player.ReplacesBackground.Value = false;
player.StoryboardEnabled.Value = false; player.StoryboardEnabled.Value = false;
}); });
waitForDim(); waitForDim();
AddAssert("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && player.IsStoryboardInvisible()); AddAssert("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && !player.IsStoryboardVisible);
} }
/// <summary> /// <summary>
@ -241,14 +242,15 @@ namespace osu.Game.Tests.Visual.Background
{ {
player.StoryboardEnabled.Value = false; player.StoryboardEnabled.Value = false;
player.ReplacesBackground.Value = false; player.ReplacesBackground.Value = false;
player.CurrentStoryboardContainer.Add(new OsuSpriteText player.DimmableStoryboard.Add(new OsuSpriteText
{ {
Size = new Vector2(250, 50), Size = new Vector2(500, 50),
Alpha = 1, Alpha = 1,
Colour = Color4.Tomato, Colour = Color4.White,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Text = "THIS IS A STORYBOARD", Text = "THIS IS A STORYBOARD",
Font = new FontUsage(size: 50)
}); });
}); });
@ -300,7 +302,7 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White; public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White;
public bool IsUserBlurApplied() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2((float)BlurLevel.Value * 25); public bool IsUserBlurApplied() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR);
public bool IsUserBlurDisabled() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(0); public bool IsUserBlurDisabled() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(0);
@ -333,17 +335,7 @@ namespace osu.Game.Tests.Visual.Background
{ {
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value); protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
protected override UserDimContainer CreateStoryboardContainer() public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard;
{
return new TestUserDimContainer(true)
{
RelativeSizeAxes = Axes.Both,
Alpha = 1,
EnableUserDim = { Value = true }
};
}
public UserDimContainer CurrentStoryboardContainer => StoryboardContainer;
// Whether or not the player should be allowed to load. // Whether or not the player should be allowed to load.
public bool BlockLoad; public bool BlockLoad;
@ -357,9 +349,7 @@ namespace osu.Game.Tests.Visual.Background
{ {
} }
public bool IsStoryboardVisible() => ((TestUserDimContainer)CurrentStoryboardContainer).CurrentAlpha == 1; public bool IsStoryboardVisible => DimmableStoryboard.ContentDisplayed;
public bool IsStoryboardInvisible() => ((TestUserDimContainer)CurrentStoryboardContainer).CurrentAlpha <= 1;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config, CancellationToken token) private void load(OsuConfigManager config, CancellationToken token)
@ -392,15 +382,15 @@ namespace osu.Game.Tests.Visual.Background
private class FadeAccessibleBackground : BackgroundScreenBeatmap private class FadeAccessibleBackground : BackgroundScreenBeatmap
{ {
protected override UserDimContainer CreateFadeContainer() => fadeContainer = new TestUserDimContainer { RelativeSizeAxes = Axes.Both }; protected override DimmableBackground CreateFadeContainer() => dimmable = new TestDimmableBackground { RelativeSizeAxes = Axes.Both };
public Color4 CurrentColour => fadeContainer.CurrentColour; public Color4 CurrentColour => dimmable.CurrentColour;
public float CurrentAlpha => fadeContainer.CurrentAlpha; public float CurrentAlpha => dimmable.CurrentAlpha;
public Vector2 CurrentBlur => Background.BlurSigma; public Vector2 CurrentBlur => Background.BlurSigma;
private TestUserDimContainer fadeContainer; private TestDimmableBackground dimmable;
public FadeAccessibleBackground(WorkingBeatmap beatmap) public FadeAccessibleBackground(WorkingBeatmap beatmap)
: base(beatmap) : base(beatmap)
@ -408,15 +398,10 @@ namespace osu.Game.Tests.Visual.Background
} }
} }
private class TestUserDimContainer : UserDimContainer private class TestDimmableBackground : BackgroundScreenBeatmap.DimmableBackground
{ {
public Color4 CurrentColour => DimContainer.Colour; public Color4 CurrentColour => Content.Colour;
public float CurrentAlpha => DimContainer.Alpha; public float CurrentAlpha => Content.Alpha;
public TestUserDimContainer(bool isStoryboard = false)
: base(isStoryboard)
{
}
} }
} }
} }

View File

@ -9,6 +9,7 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
@ -16,12 +17,13 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens; using osu.Game.Screens;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Screens.Play.PlayerSettings;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestScenePlayerLoader : ManualInputManagerTestScene public class TestScenePlayerLoader : ManualInputManagerTestScene
{ {
private PlayerLoader loader; private TestPlayerLoader loader;
private OsuScreenStack stack; private OsuScreenStack stack;
[SetUp] [SetUp]
@ -31,19 +33,29 @@ namespace osu.Game.Tests.Visual.Gameplay
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
}); });
[Test]
public void TestBlockLoadViaMouseMovement()
{
AddStep("load dummy beatmap", () => stack.Push(loader = new TestPlayerLoader(() => new TestPlayer(false, false))));
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
AddRepeatStep("move mouse", () => InputManager.MoveMouseTo(loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft + (loader.VisualSettings.ScreenSpaceDrawQuad.BottomRight - loader.VisualSettings.ScreenSpaceDrawQuad.TopLeft) * RNG.NextSingle()), 20);
AddAssert("loader still active", () => loader.IsCurrentScreen());
AddUntilStep("loads after idle", () => !loader.IsCurrentScreen());
}
[Test] [Test]
public void TestLoadContinuation() public void TestLoadContinuation()
{ {
Player player = null; Player player = null;
SlowLoadPlayer slowPlayer = null; SlowLoadPlayer slowPlayer = null;
AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => player = new TestPlayer(false, false)))); AddStep("load dummy beatmap", () => stack.Push(loader = new TestPlayerLoader(() => player = new TestPlayer(false, false))));
AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddUntilStep("wait for current", () => loader.IsCurrentScreen());
AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for player to be current", () => player.IsCurrentScreen()); AddUntilStep("wait for player to be current", () => player.IsCurrentScreen());
AddStep("load slow dummy beatmap", () => AddStep("load slow dummy beatmap", () =>
{ {
stack.Push(loader = new PlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false))); stack.Push(loader = new TestPlayerLoader(() => slowPlayer = new SlowLoadPlayer(false, false)));
Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000); Scheduler.AddDelayed(() => slowPlayer.AllowLoad.Set(), 5000);
}); });
@ -61,7 +73,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("load player", () => AddStep("load player", () =>
{ {
Mods.Value = new[] { gameMod = new TestMod() }; Mods.Value = new[] { gameMod = new TestMod() };
stack.Push(loader = new PlayerLoader(() => player = new TestPlayer())); stack.Push(loader = new TestPlayerLoader(() => player = new TestPlayer()));
}); });
AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen()); AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen());
@ -85,6 +97,16 @@ namespace osu.Game.Tests.Visual.Gameplay
AddAssert("player mods applied", () => playerMod2.Applied); AddAssert("player mods applied", () => playerMod2.Applied);
} }
private class TestPlayerLoader : PlayerLoader
{
public new VisualSettings VisualSettings => base.VisualSettings;
public TestPlayerLoader(Func<Player> createPlayer)
: base(createPlayer)
{
}
}
private class TestMod : Mod, IApplicableToScoreProcessor private class TestMod : Mod, IApplicableToScoreProcessor
{ {
public override string Name => string.Empty; public override string Name => string.Empty;

View File

@ -1,19 +1,88 @@
// 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.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[TestFixture] [TestFixture]
public class TestSceneSkipOverlay : OsuTestScene public class TestSceneSkipOverlay : ManualInputManagerTestScene
{ {
protected override void LoadComplete() private SkipOverlay skip;
{ private int requestCount;
base.LoadComplete();
Add(new SkipOverlay(Clock.CurrentTime + 5000)); [SetUp]
public void SetUp() => Schedule(() =>
{
requestCount = 0;
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Clock = new FramedOffsetClock(Clock)
{
Offset = -Clock.CurrentTime,
},
Children = new Drawable[]
{
skip = new SkipOverlay(6000)
{
RequestSeek = _ => requestCount++
}
},
};
});
[Test]
public void TestFadeOnIdle()
{
AddStep("move mouse", () => InputManager.MoveMouseTo(Vector2.Zero));
AddUntilStep("fully visible", () => skip.Children.First().Alpha == 1);
AddUntilStep("wait for fade", () => skip.Children.First().Alpha < 1);
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
AddUntilStep("fully visible", () => skip.Children.First().Alpha == 1);
AddUntilStep("wait for fade", () => skip.Children.First().Alpha < 1);
} }
[Test]
public void TestClickableAfterFade()
{
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for fade", () => skip.Children.First().Alpha == 0);
AddStep("click", () => InputManager.Click(MouseButton.Left));
checkRequestCount(1);
}
[Test]
public void TestClickOnlyActuatesOnce()
{
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
AddStep("click", () => InputManager.Click(MouseButton.Left));
AddStep("click", () => InputManager.Click(MouseButton.Left));
AddStep("click", () => InputManager.Click(MouseButton.Left));
AddStep("click", () => InputManager.Click(MouseButton.Left));
checkRequestCount(1);
}
[Test]
public void TestDoesntFadeOnMouseDown()
{
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
AddStep("button down", () => InputManager.PressButton(MouseButton.Left));
AddUntilStep("wait for overlay disapper", () => !skip.IsAlive);
AddAssert("ensure button didn't disappear", () => skip.Children.First().Alpha > 0);
AddStep("button up", () => InputManager.ReleaseButton(MouseButton.Left));
checkRequestCount(0);
}
private void checkRequestCount(int expected) =>
AddAssert($"request count is {expected}", () => requestCount == expected);
} }
} }

View File

@ -108,6 +108,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
StarDifficulty = 9.99, StarDifficulty = 9.99,
Version = @"TEST", Version = @"TEST",
Length = 456000,
Ruleset = maniaRuleset, Ruleset = maniaRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
@ -118,7 +119,6 @@ namespace osu.Game.Tests.Visual.Online
}, },
OnlineInfo = new BeatmapOnlineInfo OnlineInfo = new BeatmapOnlineInfo
{ {
Length = 456000,
CircleCount = 111, CircleCount = 111,
SliderCount = 12, SliderCount = 12,
PlayCount = 222, PlayCount = 222,
@ -181,6 +181,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
StarDifficulty = 5.67, StarDifficulty = 5.67,
Version = @"ANOTHER TEST", Version = @"ANOTHER TEST",
Length = 123000,
Ruleset = taikoRuleset, Ruleset = taikoRuleset,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
@ -191,7 +192,6 @@ namespace osu.Game.Tests.Visual.Online
}, },
OnlineInfo = new BeatmapOnlineInfo OnlineInfo = new BeatmapOnlineInfo
{ {
Length = 123000,
CircleCount = 123, CircleCount = 123,
SliderCount = 45, SliderCount = 45,
PlayCount = 567, PlayCount = 567,

View File

@ -24,6 +24,7 @@ namespace osu.Game.Tests.Visual.Online
typeof(ChangelogListing), typeof(ChangelogListing),
typeof(ChangelogSingleBuild), typeof(ChangelogSingleBuild),
typeof(ChangelogBuild), typeof(ChangelogBuild),
typeof(Comments),
}; };
protected override void LoadComplete() protected override void LoadComplete()

View File

@ -70,7 +70,7 @@ namespace osu.Game.Tests.Visual.Online
}); });
channelTabControl.OnRequestLeave += channel => channelTabControl.RemoveChannel(channel); channelTabControl.OnRequestLeave += channel => channelTabControl.RemoveChannel(channel);
channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.NewValue.ToString(); channelTabControl.Current.ValueChanged += channel => currentText.Text = "Currently selected channel: " + channel.NewValue;
AddStep("Add random private channel", addRandomPrivateChannel); AddStep("Add random private channel", addRandomPrivateChannel);
AddAssert("There is only one channels", () => channelTabControl.Items.Count() == 2); AddAssert("There is only one channels", () => channelTabControl.Items.Count() == 2);

View File

@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
typeof(HistoricalSection), typeof(HistoricalSection),
typeof(PaginatedMostPlayedBeatmapContainer), typeof(PaginatedMostPlayedBeatmapContainer),
typeof(DrawableMostPlayedRow), typeof(DrawableMostPlayedBeatmap),
typeof(DrawableProfileRow) typeof(DrawableProfileRow)
}; };

View File

@ -3,19 +3,18 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Graphics; 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.Mods;
using osu.Game.Rulesets.Osu.Mods; 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.Users; using osu.Game.Users;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
@ -30,11 +29,9 @@ namespace osu.Game.Tests.Visual.Online
typeof(ScoreTableRowBackground), typeof(ScoreTableRowBackground),
}; };
private readonly Box background;
public TestSceneScoresContainer() public TestSceneScoresContainer()
{ {
ScoresContainer scoresContainer; TestScoresContainer scoresContainer;
Child = new Container Child = new Container
{ {
@ -44,108 +41,137 @@ namespace osu.Game.Tests.Visual.Online
Width = 0.8f, Width = 0.8f,
Children = new Drawable[] Children = new Drawable[]
{ {
background = new Box { RelativeSizeAxes = Axes.Both }, new Box
scoresContainer = new ScoresContainer(), {
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
scoresContainer = new TestScoresContainer(),
} }
}; };
var scores = new List<ScoreInfo> var allScores = new APILegacyScores
{ {
new ScoreInfo Scores = new List<APILegacyScoreInfo>
{ {
User = new User new APILegacyScoreInfo
{ {
Id = 6602580, User = new User
Username = @"waaiiru",
Country = new Country
{ {
FullName = @"Spain", Id = 6602580,
FlagName = @"ES", Username = @"waaiiru",
Country = new Country
{
FullName = @"Spain",
FlagName = @"ES",
},
}, },
}, Mods = new Mod[]
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
new OsuModFlashlight(),
new OsuModHardRock(),
},
Rank = ScoreRank.XH,
PP = 200,
MaxCombo = 1234,
TotalScore = 1234567890,
Accuracy = 1,
},
new ScoreInfo
{
User = new User
{
Id = 4608074,
Username = @"Skycries",
Country = new Country
{ {
FullName = @"Brazil", new OsuModDoubleTime(),
FlagName = @"BR", new OsuModHidden(),
new OsuModFlashlight(),
new OsuModHardRock(),
}, },
Rank = ScoreRank.XH,
PP = 200,
MaxCombo = 1234,
TotalScore = 1234567890,
Accuracy = 1,
}, },
Mods = new Mod[] new APILegacyScoreInfo
{ {
new OsuModDoubleTime(), User = new User
new OsuModHidden(),
new OsuModFlashlight(),
},
Rank = ScoreRank.S,
PP = 190,
MaxCombo = 1234,
TotalScore = 1234789,
Accuracy = 0.9997,
},
new ScoreInfo
{
User = new User
{
Id = 1014222,
Username = @"eLy",
Country = new Country
{ {
FullName = @"Japan", Id = 4608074,
FlagName = @"JP", Username = @"Skycries",
Country = new Country
{
FullName = @"Brazil",
FlagName = @"BR",
},
}, },
}, Mods = new Mod[]
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
},
Rank = ScoreRank.B,
PP = 180,
MaxCombo = 1234,
TotalScore = 12345678,
Accuracy = 0.9854,
},
new ScoreInfo
{
User = new User
{
Id = 1541390,
Username = @"Toukai",
Country = new Country
{ {
FullName = @"Canada", new OsuModDoubleTime(),
FlagName = @"CA", new OsuModHidden(),
new OsuModFlashlight(),
}, },
Rank = ScoreRank.S,
PP = 190,
MaxCombo = 1234,
TotalScore = 1234789,
Accuracy = 0.9997,
}, },
Mods = new Mod[] new APILegacyScoreInfo
{ {
new OsuModDoubleTime(), User = new User
{
Id = 1014222,
Username = @"eLy",
Country = new Country
{
FullName = @"Japan",
FlagName = @"JP",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
},
Rank = ScoreRank.B,
PP = 180,
MaxCombo = 1234,
TotalScore = 12345678,
Accuracy = 0.9854,
}, },
Rank = ScoreRank.C, new APILegacyScoreInfo
PP = 170, {
MaxCombo = 1234, User = new User
TotalScore = 1234567, {
Accuracy = 0.8765, Id = 1541390,
}, Username = @"Toukai",
new ScoreInfo Country = new Country
{
FullName = @"Canada",
FlagName = @"CA",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
},
Rank = ScoreRank.C,
PP = 170,
MaxCombo = 1234,
TotalScore = 1234567,
Accuracy = 0.8765,
},
new APILegacyScoreInfo
{
User = new User
{
Id = 7151382,
Username = @"Mayuri Hana",
Country = new Country
{
FullName = @"Thailand",
FlagName = @"TH",
},
},
Rank = ScoreRank.D,
PP = 160,
MaxCombo = 1234,
TotalScore = 123456,
Accuracy = 0.6543,
},
}
};
var myBestScore = new APILegacyUserTopScoreInfo
{
Score = new APILegacyScoreInfo
{ {
User = new User User = new User
{ {
@ -163,9 +189,42 @@ namespace osu.Game.Tests.Visual.Online
TotalScore = 123456, TotalScore = 123456,
Accuracy = 0.6543, Accuracy = 0.6543,
}, },
Position = 1337,
}; };
foreach (var s in scores) var oneScore = new APILegacyScores
{
Scores = new List<APILegacyScoreInfo>
{
new APILegacyScoreInfo
{
User = new User
{
Id = 6602580,
Username = @"waaiiru",
Country = new Country
{
FullName = @"Spain",
FlagName = @"ES",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
new OsuModFlashlight(),
new OsuModHardRock(),
},
Rank = ScoreRank.XH,
PP = 200,
MaxCombo = 1234,
TotalScore = 1234567890,
Accuracy = 1,
}
}
};
foreach (var s in allScores.Scores)
{ {
s.Statistics.Add(HitResult.Great, RNG.Next(2000)); s.Statistics.Add(HitResult.Great, RNG.Next(2000));
s.Statistics.Add(HitResult.Good, RNG.Next(2000)); s.Statistics.Add(HitResult.Good, RNG.Next(2000));
@ -173,15 +232,26 @@ namespace osu.Game.Tests.Visual.Online
s.Statistics.Add(HitResult.Miss, RNG.Next(2000)); s.Statistics.Add(HitResult.Miss, RNG.Next(2000));
} }
AddStep("Load all scores", () => scoresContainer.Scores = scores); AddStep("Load all scores", () =>
{
allScores.UserScore = null;
scoresContainer.Scores = allScores;
});
AddStep("Load null scores", () => scoresContainer.Scores = null); AddStep("Load null scores", () => scoresContainer.Scores = null);
AddStep("Load only one score", () => scoresContainer.Scores = new[] { scores.First() }); AddStep("Load only one score", () => scoresContainer.Scores = oneScore);
AddStep("Load scores with my best", () =>
{
allScores.UserScore = myBestScore;
scoresContainer.Scores = allScores;
});
} }
[BackgroundDependencyLoader] private class TestScoresContainer : ScoresContainer
private void load(OsuColour colours)
{ {
background.Colour = colours.Gray2; public new APILegacyScores Scores
{
set => base.Scores = value;
}
} }
} }
} }

View File

@ -4,12 +4,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Online.Leaderboards; using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Select.Leaderboards; using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users; using osu.Game.Users;
@ -27,8 +24,6 @@ namespace osu.Game.Tests.Visual.SongSelect
typeof(RetrievalFailurePlaceholder), typeof(RetrievalFailurePlaceholder),
}; };
private RulesetStore rulesets;
private readonly FailableLeaderboard leaderboard; private readonly FailableLeaderboard leaderboard;
public TestSceneLeaderboard() public TestSceneLeaderboard()
@ -47,13 +42,8 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter)); AddStep(@"No supporter", () => leaderboard.SetRetrievalState(PlaceholderState.NotSupporter));
AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn)); AddStep(@"Not logged in", () => leaderboard.SetRetrievalState(PlaceholderState.NotLoggedIn));
AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable)); AddStep(@"Unavailable", () => leaderboard.SetRetrievalState(PlaceholderState.Unavailable));
AddStep(@"Real beatmap", realBeatmap); foreach (BeatmapSetOnlineStatus status in Enum.GetValues(typeof(BeatmapSetOnlineStatus)))
} AddStep($"{status} beatmap", () => showBeatmapWithStatus(status));
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
} }
private void newScores() private void newScores()
@ -245,34 +235,12 @@ namespace osu.Game.Tests.Visual.SongSelect
leaderboard.Scores = scores; leaderboard.Scores = scores;
} }
private void realBeatmap() private void showBeatmapWithStatus(BeatmapSetOnlineStatus status)
{ {
leaderboard.Beatmap = new BeatmapInfo leaderboard.Beatmap = new BeatmapInfo
{ {
StarDifficulty = 1.36,
Version = @"BASIC",
OnlineBeatmapID = 1113057, OnlineBeatmapID = 1113057,
Ruleset = rulesets.GetRuleset(0), Status = status,
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 4,
DrainRate = 6.5f,
OverallDifficulty = 6.5f,
ApproachRate = 5,
},
OnlineInfo = new BeatmapOnlineInfo
{
Length = 115000,
CircleCount = 265,
SliderCount = 71,
PlayCount = 47906,
PassCount = 19899,
},
Metrics = new BeatmapMetrics
{
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
}; };
} }

View File

@ -133,6 +133,9 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; }); AddStep(@"Sort by Artist", delegate { songSelect.FilterControl.Sort = SortMode.Artist; });
AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; }); AddStep(@"Sort by Title", delegate { songSelect.FilterControl.Sort = SortMode.Title; });
AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; }); AddStep(@"Sort by Author", delegate { songSelect.FilterControl.Sort = SortMode.Author; });
AddStep(@"Sort by DateAdded", delegate { songSelect.FilterControl.Sort = SortMode.DateAdded; });
AddStep(@"Sort by BPM", delegate { songSelect.FilterControl.Sort = SortMode.BPM; });
AddStep(@"Sort by Length", delegate { songSelect.FilterControl.Sort = SortMode.Length; });
AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; }); AddStep(@"Sort by Difficulty", delegate { songSelect.FilterControl.Sort = SortMode.Difficulty; });
} }
@ -265,16 +268,21 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
int beatmapId = setId * 10 + i; int beatmapId = setId * 10 + i;
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
beatmaps.Add(new BeatmapInfo beatmaps.Add(new BeatmapInfo
{ {
Ruleset = getRuleset(), Ruleset = getRuleset(),
OnlineBeatmapID = beatmapId, OnlineBeatmapID = beatmapId,
Path = "normal.osu", Path = "normal.osu",
Version = $"{beatmapId}", Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
Length = length,
BPM = bpm,
BaseDifficulty = new BeatmapDifficulty BaseDifficulty = new BeatmapDifficulty
{ {
OverallDifficulty = 3.5f, OverallDifficulty = 3.5f,
} },
}); });
} }
@ -286,10 +294,11 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
// Create random metadata, then we can check if sorting works based on these // Create random metadata, then we can check if sorting works based on these
Artist = "Some Artist " + RNG.Next(0, 9), Artist = "Some Artist " + RNG.Next(0, 9),
Title = $"Some Song (set id {setId})", Title = $"Some Song (set id {setId}, max bpm {beatmaps.Max(b => b.BPM):0.#})",
AuthorString = "Some Guy " + RNG.Next(0, 9), AuthorString = "Some Guy " + RNG.Next(0, 9),
}, },
Beatmaps = beatmaps Beatmaps = beatmaps,
DateAdded = DateTimeOffset.UtcNow,
}; };
} }
} }

View File

@ -14,8 +14,6 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneBackButton : OsuTestScene public class TestSceneBackButton : OsuTestScene
{ {
private readonly BackButton button;
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
typeof(TwoLayerButton) typeof(TwoLayerButton)
@ -23,6 +21,8 @@ namespace osu.Game.Tests.Visual.UserInterface
public TestSceneBackButton() public TestSceneBackButton()
{ {
BackButton button;
Child = new Container Child = new Container
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
@ -40,11 +40,12 @@ namespace osu.Game.Tests.Visual.UserInterface
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Action = () => button.Hide(),
} }
} }
}; };
button.Action = () => button.Hide();
AddStep("show button", () => button.Show()); AddStep("show button", () => button.Show());
AddStep("hide button", () => button.Hide()); AddStep("hide button", () => button.Hide());
} }

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Screens.Menu; using osu.Game.Screens.Menu;
using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
@ -23,11 +24,12 @@ namespace osu.Game.Tests.Visual.UserInterface
typeof(Button) typeof(Button)
}; };
public TestSceneButtonSystem() private OsuLogo logo;
{ private ButtonSystem buttons;
OsuLogo logo;
ButtonSystem buttons;
[SetUp]
public void SetUp() => Schedule(() =>
{
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new Box
@ -36,13 +38,47 @@ namespace osu.Game.Tests.Visual.UserInterface
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
buttons = new ButtonSystem(), buttons = new ButtonSystem(),
logo = new OsuLogo { RelativePositionAxes = Axes.Both } logo = new OsuLogo
{
RelativePositionAxes = Axes.Both,
Position = new Vector2(0.5f)
}
}; };
buttons.SetOsuLogo(logo); buttons.SetOsuLogo(logo);
});
[Test]
public void TestAllStates()
{
foreach (var s in Enum.GetValues(typeof(ButtonSystemState)).OfType<ButtonSystemState>().Skip(1)) foreach (var s in Enum.GetValues(typeof(ButtonSystemState)).OfType<ButtonSystemState>().Skip(1))
AddStep($"State to {s}", () => buttons.State = s); AddStep($"State to {s}", () => buttons.State = s);
AddStep("Enter mode", performEnterMode);
AddStep("Return to menu", () =>
{
buttons.State = ButtonSystemState.Play;
buttons.FadeIn(MainMenu.FADE_IN_DURATION, Easing.OutQuint);
buttons.MoveTo(new Vector2(0), MainMenu.FADE_IN_DURATION, Easing.OutQuint);
logo.FadeColour(Color4.White, 100, Easing.OutQuint);
logo.FadeIn(100, Easing.OutQuint);
});
}
[Test]
public void TestSmoothExit()
{
AddStep("Enter mode", performEnterMode);
}
private void performEnterMode()
{
buttons.State = ButtonSystemState.EnteringMode;
buttons.FadeOut(MainMenu.FADE_OUT_DURATION, Easing.InSine);
buttons.MoveTo(new Vector2(-800, 0), MainMenu.FADE_OUT_DURATION, Easing.InSine);
logo.FadeOut(300, Easing.InSine)
.ScaleTo(0.2f, 300, Easing.InSine);
} }
} }
} }

View File

@ -48,7 +48,7 @@ namespace osu.Game.Tests.Visual.UserInterface
}, },
}; };
breadcrumbs.Current.ValueChanged += screen => titleText.Text = $"Changed to {screen.NewValue.ToString()}"; breadcrumbs.Current.ValueChanged += screen => titleText.Text = $"Changed to {screen.NewValue}";
breadcrumbs.Current.TriggerChange(); breadcrumbs.Current.TriggerChange();
waitForCurrent(); waitForCurrent();

View File

@ -3,7 +3,7 @@
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="DeepEqual" Version="2.0.0" /> <PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.1.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />

View File

@ -5,9 +5,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -159,7 +159,7 @@ namespace osu.Game.Tournament.Components
} }
var bpm = beatmap.BeatmapSet.OnlineInfo.BPM; var bpm = beatmap.BeatmapSet.OnlineInfo.BPM;
var length = beatmap.OnlineInfo.Length; var length = beatmap.Length;
string hardRockExtra = ""; string hardRockExtra = "";
string srExtra = ""; string srExtra = "";
@ -180,7 +180,7 @@ namespace osu.Game.Tournament.Components
panelContents.Children = new Drawable[] panelContents.Children = new Drawable[]
{ {
new DiffPiece(("Length", TimeSpan.FromSeconds(length).ToString(@"mm\:ss"))) new DiffPiece(("Length", TimeSpan.FromMilliseconds(length).ToString(@"mm\:ss")))
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,

View File

@ -47,8 +47,8 @@ namespace osu.Game.Tournament.Screens.MapPool
mapFlows = new FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>> mapFlows = new FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>>
{ {
Y = 100, Y = 100,
Spacing = new Vector2(10, 20), Spacing = new Vector2(10, 10),
Padding = new MarginPadding(50), Padding = new MarginPadding(25),
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
@ -218,7 +218,7 @@ namespace osu.Game.Tournament.Screens.MapPool
{ {
mapFlows.Add(currentFlow = new FillFlowContainer<TournamentBeatmapPanel> mapFlows.Add(currentFlow = new FillFlowContainer<TournamentBeatmapPanel>
{ {
Spacing = new Vector2(10, 20), Spacing = new Vector2(10, 5),
Direction = FillDirection.Full, Direction = FillDirection.Full,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y AutoSizeAxes = Axes.Y

View File

@ -10,7 +10,7 @@ namespace osu.Game.Tournament.Screens.Showcase
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
AddInternal(new TournamentLogo()); AddInternal(new TournamentLogo(false));
} }
} }
} }

View File

@ -45,7 +45,7 @@ namespace osu.Game.Tournament.Screens.TeamWin
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Loop = true, Loop = true,
}, },
new TournamentLogo new TournamentLogo(false)
{ {
Y = 40, Y = 40,
}, },

View File

@ -51,6 +51,16 @@ namespace osu.Game.Beatmaps
[NotMapped] [NotMapped]
public BeatmapOnlineInfo OnlineInfo { get; set; } public BeatmapOnlineInfo OnlineInfo { get; set; }
/// <summary>
/// The playable length in milliseconds of this beatmap.
/// </summary>
public double Length { get; set; }
/// <summary>
/// The most common BPM of this beatmap.
/// </summary>
public double BPM { get; set; }
public string Path { get; set; } public string Path { get; set; }
[JsonProperty("file_sha2")] [JsonProperty("file_sha2")]

View File

@ -23,6 +23,7 @@ using osu.Game.IO.Archives;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
@ -180,19 +181,18 @@ namespace osu.Game.Beatmaps
lock (workingCache) lock (workingCache)
{ {
var cached = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID); var working = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID);
if (cached != null) if (working == null)
return cached; {
if (beatmapInfo.Metadata == null)
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
if (beatmapInfo.Metadata == null) workingCache.Add(working = new BeatmapManagerWorkingBeatmap(Files.Store,
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager));
}
WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager);
previous?.TransferTo(working); previous?.TransferTo(working);
workingCache.Add(working);
return working; return working;
} }
} }
@ -302,6 +302,8 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo.Ruleset = ruleset; beatmap.BeatmapInfo.Ruleset = ruleset;
// TODO: this should be done in a better place once we actually need to dynamically update it. // TODO: this should be done in a better place once we actually need to dynamically update it.
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0; beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance().CreateDifficultyCalculator(new DummyConversionBeatmap(beatmap)).Calculate().StarRating ?? 0;
beatmap.BeatmapInfo.Length = calculateLength(beatmap);
beatmap.BeatmapInfo.BPM = beatmap.ControlPointInfo.BPMMode;
beatmapInfos.Add(beatmap.BeatmapInfo); beatmapInfos.Add(beatmap.BeatmapInfo);
} }
@ -310,6 +312,19 @@ namespace osu.Game.Beatmaps
return beatmapInfos; return beatmapInfos;
} }
private double calculateLength(IBeatmap b)
{
if (!b.HitObjects.Any())
return 0;
var lastObject = b.HitObjects.Last();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime;
double startTime = b.HitObjects.First().StartTime;
return endTime - startTime;
}
/// <summary> /// <summary>
/// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation. /// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation.
/// </summary> /// </summary>
@ -371,7 +386,7 @@ namespace osu.Game.Beatmaps
beatmap.OnlineBeatmapID = res.OnlineBeatmapID; beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
}; };
req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap}", e); }; req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); };
// intentionally blocking to limit web request concurrency // intentionally blocking to limit web request concurrency
req.Perform(api); req.Perform(api);

View File

@ -8,11 +8,6 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
public class BeatmapOnlineInfo public class BeatmapOnlineInfo
{ {
/// <summary>
/// The length in milliseconds of this beatmap's song.
/// </summary>
public double Length { get; set; }
/// <summary> /// <summary>
/// The amount of circles in this beatmap. /// The amount of circles in this beatmap.
/// </summary> /// </summary>

View File

@ -35,8 +35,21 @@ namespace osu.Game.Beatmaps
[NotMapped] [NotMapped]
public BeatmapSetMetrics Metrics { get; set; } public BeatmapSetMetrics Metrics { get; set; }
/// <summary>
/// The maximum star difficulty of all beatmaps in this set.
/// </summary>
public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0; public double MaxStarDifficulty => Beatmaps?.Max(b => b.StarDifficulty) ?? 0;
/// <summary>
/// The maximum playable length in milliseconds of all beatmaps in this set.
/// </summary>
public double MaxLength => Beatmaps?.Max(b => b.Length) ?? 0;
/// <summary>
/// The maximum BPM of all beatmaps in this set.
/// </summary>
public double MaxBPM => Beatmaps?.Max(b => b.BPM) ?? 0;
[NotMapped] [NotMapped]
public bool DeletePending { get; set; } public bool DeletePending { get; set; }

View File

@ -66,6 +66,11 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
public int FavouriteCount { get; set; } public int FavouriteCount { get; set; }
/// <summary>
/// Whether this beatmap set has been favourited by the current user.
/// </summary>
public bool HasFavourited { get; set; }
/// <summary> /// <summary>
/// The availability of this beatmap set. /// The availability of this beatmap set.
/// </summary> /// </summary>

View File

@ -35,15 +35,15 @@ namespace osu.Game.Beatmaps.Drawables
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad) protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad)
=> new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, UnloadDelay); => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, UnloadDelay);
protected override double TransformDuration => 400;
protected override Drawable CreateDrawable(BeatmapInfo model) protected override Drawable CreateDrawable(BeatmapInfo model)
{ {
Drawable drawable = getDrawableForModel(model); var drawable = getDrawableForModel(model);
drawable.RelativeSizeAxes = Axes.Both; drawable.RelativeSizeAxes = Axes.Both;
drawable.Anchor = Anchor.Centre; drawable.Anchor = Anchor.Centre;
drawable.Origin = Anchor.Centre; drawable.Origin = Anchor.Centre;
drawable.FillMode = FillMode.Fill; drawable.FillMode = FillMode.Fill;
drawable.OnLoadComplete += d => d.FadeInFromZero(400);
return drawable; return drawable;
} }

View File

@ -247,7 +247,7 @@ namespace osu.Game.Beatmaps
// cancelling the beatmap load is safe for now since the retrieval is a synchronous // cancelling the beatmap load is safe for now since the retrieval is a synchronous
// operation. if we add an async retrieval method this may need to be reconsidered. // operation. if we add an async retrieval method this may need to be reconsidered.
beatmapCancellation.Cancel(); beatmapCancellation?.Cancel();
total_count.Value--; total_count.Value--;
} }

View File

@ -77,6 +77,7 @@ namespace osu.Game.Configuration
Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01);
Set(OsuSetting.ShowInterface, true); Set(OsuSetting.ShowInterface, true);
Set(OsuSetting.ShowHealthDisplayWhenCantFail, true);
Set(OsuSetting.KeyOverlay, false); Set(OsuSetting.KeyOverlay, false);
Set(OsuSetting.FloatingComments, false); Set(OsuSetting.FloatingComments, false);
@ -131,6 +132,7 @@ namespace osu.Game.Configuration
KeyOverlay, KeyOverlay,
FloatingComments, FloatingComments,
ShowInterface, ShowInterface,
ShowHealthDisplayWhenCantFail,
MouseDisableButtons, MouseDisableButtons,
MouseDisableWheel, MouseDisableWheel,
AudioOffset, AudioOffset,

View File

@ -41,6 +41,9 @@ namespace osu.Game.Database
{ {
// required to initialise native SQLite libraries on some platforms. // required to initialise native SQLite libraries on some platforms.
SQLitePCL.Batteries_V2.Init(); SQLitePCL.Batteries_V2.Init();
// https://github.com/aspnet/EntityFrameworkCore/issues/9994#issuecomment-508588678
SQLitePCL.raw.sqlite3_config(2 /*SQLITE_CONFIG_MULTITHREAD*/);
} }
/// <summary> /// <summary>

View File

@ -16,7 +16,7 @@ namespace osu.Game.Graphics.Backgrounds
/// </summary> /// </summary>
public class Background : CompositeDrawable public class Background : CompositeDrawable
{ {
public Sprite Sprite; public readonly Sprite Sprite;
private readonly string textureName; private readonly string textureName;
@ -51,7 +51,7 @@ namespace osu.Game.Graphics.Backgrounds
/// <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 void BlurTo(Vector2 newBlurSigma, double duration = 0, Easing easing = Easing.None) public void BlurTo(Vector2 newBlurSigma, double duration = 0, Easing easing = Easing.None)
{ {
if (bufferedContainer == null) if (bufferedContainer == null && newBlurSigma != Vector2.Zero)
{ {
RemoveInternal(Sprite); RemoveInternal(Sprite);
@ -63,7 +63,7 @@ namespace osu.Game.Graphics.Backgrounds
}); });
} }
bufferedContainer.BlurTo(newBlurSigma, duration, easing); bufferedContainer?.BlurTo(newBlurSigma, duration, easing);
} }
} }
} }

View File

@ -8,7 +8,6 @@ using osuTK.Graphics;
using System; using System;
using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osuTK.Graphics.ES30;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -137,11 +136,13 @@ namespace osu.Game.Graphics.Backgrounds
} }
} }
protected int AimCount;
private void addTriangles(bool randomY) private void addTriangles(bool randomY)
{ {
int aimTriangleCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); AimCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio);
for (int i = 0; i < aimTriangleCount - parts.Count; i++) for (int i = 0; i < AimCount - parts.Count; i++)
parts.Add(createTriangle(randomY)); parts.Add(createTriangle(randomY));
} }
@ -190,7 +191,7 @@ namespace osu.Game.Graphics.Backgrounds
private readonly List<TriangleParticle> parts = new List<TriangleParticle>(); private readonly List<TriangleParticle> parts = new List<TriangleParticle>();
private Vector2 size; private Vector2 size;
private readonly LinearBatch<TexturedVertex2D> vertexBatch = new LinearBatch<TexturedVertex2D>(100 * 3, 10, PrimitiveType.Triangles); private TriangleBatch<TexturedVertex2D> vertexBatch;
public TrianglesDrawNode(Triangles source) public TrianglesDrawNode(Triangles source)
: base(source) : base(source)
@ -213,6 +214,12 @@ namespace osu.Game.Graphics.Backgrounds
{ {
base.Draw(vertexAction); base.Draw(vertexAction);
if (Source.AimCount > 0 && (vertexBatch == null || vertexBatch.Size != Source.AimCount))
{
vertexBatch?.Dispose();
vertexBatch = new TriangleBatch<TexturedVertex2D>(Source.AimCount, 1);
}
shader.Bind(); shader.Bind();
Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy; Vector2 localInflationAmount = edge_smoothness * DrawInfo.MatrixInverse.ExtractScale().Xy;
@ -246,7 +253,7 @@ namespace osu.Game.Graphics.Backgrounds
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);
vertexBatch.Dispose(); vertexBatch?.Dispose();
} }
} }

View File

@ -1,31 +1,26 @@
// 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.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Screens.Play;
using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Graphics.Containers namespace osu.Game.Graphics.Containers
{ {
/// <summary> /// <summary>
/// A container that applies user-configured visual settings to its contents. /// A container that applies user-configured visual settings to its contents.
/// This container specifies behavior that applies to both Storyboards and Backgrounds.
/// </summary> /// </summary>
public class UserDimContainer : Container public abstract class UserDimContainer : Container
{ {
private const float background_fade_duration = 800; protected const float BACKGROUND_FADE_DURATION = 800;
/// <summary> /// <summary>
/// Whether or not user-configured dim levels should be applied to the container. /// Whether or not user-configured dim levels should be applied to the container.
/// </summary> /// </summary>
public readonly Bindable<bool> EnableUserDim = new Bindable<bool>(); public readonly Bindable<bool> EnableUserDim = new Bindable<bool>(true);
/// <summary> /// <summary>
/// Whether or not the storyboard loaded should completely hide the background behind it. /// Whether or not the storyboard loaded should completely hide the background behind it.
@ -33,103 +28,58 @@ namespace osu.Game.Graphics.Containers
public readonly Bindable<bool> StoryboardReplacesBackground = new Bindable<bool>(); public readonly Bindable<bool> StoryboardReplacesBackground = new Bindable<bool>();
/// <summary> /// <summary>
/// The amount of blur to be applied to the background in addition to user-specified blur. /// Whether the content of this container is currently being displayed.
/// </summary> /// </summary>
/// <remarks> public bool ContentDisplayed { get; private set; }
/// Used in contexts where there can potentially be both user and screen-specified blurring occuring at the same time, such as in <see cref="PlayerLoader"/>
/// </remarks>
public readonly Bindable<float> BlurAmount = new Bindable<float>();
private Bindable<double> userDimLevel { get; set; } protected Bindable<double> UserDimLevel { get; private set; }
private Bindable<double> userBlurLevel { get; set; } protected Bindable<bool> ShowStoryboard { get; private set; }
private Bindable<bool> showStoryboard { get; set; } protected override Container<Drawable> Content => dimContent;
protected Container DimContainer { get; } private Container dimContent { get; }
protected override Container<Drawable> Content => DimContainer;
private readonly bool isStoryboard;
/// <summary>
/// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs.
/// </summary>
private Vector2 blurTarget => EnableUserDim.Value
? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * 25)
: new Vector2(BlurAmount.Value);
/// <summary> /// <summary>
/// Creates a new <see cref="UserDimContainer"/>. /// Creates a new <see cref="UserDimContainer"/>.
/// </summary> /// </summary>
/// <param name="isStoryboard"> Whether or not this instance contains a storyboard. protected UserDimContainer()
/// <remarks>
/// While both backgrounds and storyboards allow user dim levels to be applied, storyboards can be toggled via <see cref="showStoryboard"/>
/// and can cause backgrounds to become hidden via <see cref="StoryboardReplacesBackground"/>. Storyboards are also currently unable to be blurred.
/// </remarks>
/// </param>
public UserDimContainer(bool isStoryboard = false)
{ {
this.isStoryboard = isStoryboard; AddInternal(dimContent = new Container { RelativeSizeAxes = Axes.Both });
AddInternal(DimContainer = new Container { RelativeSizeAxes = Axes.Both });
}
private Background background;
public Background Background
{
get => background;
set
{
base.Add(background = value);
background.BlurTo(blurTarget, 0, Easing.OutQuint);
}
}
public override void Add(Drawable drawable)
{
if (drawable is Background)
throw new InvalidOperationException($"Use {nameof(Background)} to set a background.");
base.Add(drawable);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
userDimLevel = config.GetBindable<double>(OsuSetting.DimLevel); UserDimLevel = config.GetBindable<double>(OsuSetting.DimLevel);
userBlurLevel = config.GetBindable<double>(OsuSetting.BlurLevel); ShowStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
showStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
EnableUserDim.ValueChanged += _ => updateVisuals(); EnableUserDim.ValueChanged += _ => UpdateVisuals();
userDimLevel.ValueChanged += _ => updateVisuals(); UserDimLevel.ValueChanged += _ => UpdateVisuals();
userBlurLevel.ValueChanged += _ => updateVisuals(); ShowStoryboard.ValueChanged += _ => UpdateVisuals();
showStoryboard.ValueChanged += _ => updateVisuals(); StoryboardReplacesBackground.ValueChanged += _ => UpdateVisuals();
StoryboardReplacesBackground.ValueChanged += _ => updateVisuals();
BlurAmount.ValueChanged += _ => updateVisuals();
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
updateVisuals(); UpdateVisuals();
} }
private void updateVisuals() /// <summary>
/// Whether the content of this container should currently be visible.
/// </summary>
protected virtual bool ShowDimContent => true;
/// <summary>
/// Should be invoked when any dependent dim level or user setting is changed and bring the visual state up-to-date.
/// </summary>
protected virtual void UpdateVisuals()
{ {
if (isStoryboard) ContentDisplayed = ShowDimContent;
{
DimContainer.FadeTo(!showStoryboard.Value || userDimLevel.Value == 1 ? 0 : 1, background_fade_duration, Easing.OutQuint);
}
else
{
// The background needs to be hidden in the case of it being replaced by the storyboard
DimContainer.FadeTo(showStoryboard.Value && StoryboardReplacesBackground.Value ? 0 : 1, background_fade_duration, Easing.OutQuint);
Background?.BlurTo(blurTarget, background_fade_duration, Easing.OutQuint); dimContent.FadeTo((ContentDisplayed) ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
} dimContent.FadeColour(EnableUserDim.Value ? OsuColour.Gray(1 - (float)UserDimLevel.Value) : Color4.White, BACKGROUND_FADE_DURATION, Easing.OutQuint);
DimContainer.FadeColour(EnableUserDim.Value ? OsuColour.Gray(1 - (float)userDimLevel.Value) : Color4.White, background_fade_duration, Easing.OutQuint);
} }
} }
} }

View File

@ -0,0 +1,26 @@
// 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.Runtime.InteropServices;
using osu.Framework.Graphics.OpenGL.Vertices;
using osuTK;
using osuTK.Graphics;
using osuTK.Graphics.ES30;
namespace osu.Game.Graphics.OpenGL.Vertices
{
[StructLayout(LayoutKind.Sequential)]
public struct PositionAndColourVertex : IEquatable<PositionAndColourVertex>, IVertex
{
[VertexMember(2, VertexAttribPointerType.Float)]
public Vector2 Position;
[VertexMember(4, VertexAttribPointerType.Float)]
public Color4 Colour;
public bool Equals(PositionAndColourVertex other)
=> Position.Equals(other.Position)
&& Colour.Equals(other.Colour);
}
}

View File

@ -0,0 +1,80 @@
// 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.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osuTK;
namespace osu.Game.Graphics.Sprites
{
public class GlowingSpriteText : Container, IHasText
{
private readonly OsuSpriteText spriteText, blurredText;
public string Text
{
get => spriteText.Text;
set => blurredText.Text = spriteText.Text = value;
}
public FontUsage Font
{
get => spriteText.Font;
set => blurredText.Font = spriteText.Font = value.With(fixedWidth: true);
}
public Vector2 TextSize
{
get => spriteText.Size;
set => blurredText.Size = spriteText.Size = value;
}
public ColourInfo TextColour
{
get => spriteText.Colour;
set => spriteText.Colour = value;
}
public ColourInfo GlowColour
{
get => blurredText.Colour;
set => blurredText.Colour = value;
}
public GlowingSpriteText()
{
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new BufferedContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BlurSigma = new Vector2(4),
CacheDrawnFrameBuffer = true,
RelativeSizeAxes = Axes.Both,
Blending = BlendingMode.Additive,
Size = new Vector2(3f),
Children = new[]
{
blurredText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Shadow = false,
},
},
},
spriteText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Shadow = false,
},
};
}
}
}

View File

@ -76,7 +76,12 @@ namespace osu.Game.Graphics.UserInterface
{ {
Masking = true, Masking = true,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = path = new SmoothPath { RelativeSizeAxes = Axes.Both, PathRadius = 1 } Child = path = new SmoothPath
{
AutoSizeAxes = Axes.None,
RelativeSizeAxes = Axes.Both,
PathRadius = 1
}
}); });
} }

View File

@ -64,7 +64,7 @@ namespace osu.Game.Graphics.UserInterface
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
Children = new Drawable[] Children = new Drawable[]
{ {
text = new OsuSpriteText { Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold) }, text = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
icon = new SpriteIcon icon = new SpriteIcon
{ {
Size = new Vector2(14), Size = new Vector2(14),
@ -84,7 +84,11 @@ namespace osu.Game.Graphics.UserInterface
} }
}; };
Current.ValueChanged += selected => { icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle; }; Current.ValueChanged += selected =>
{
icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle;
text.Font = text.Font.With(weight: selected.NewValue ? FontWeight.Bold : FontWeight.Medium);
};
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -3,6 +3,7 @@
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osuTK; using osuTK;
using osuTK.Input; using osuTK.Input;
@ -33,6 +34,17 @@ namespace osu.Game.Graphics.UserInterface
PlaceholderText = "type to search"; PlaceholderText = "type to search";
} }
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
// as we do not allow arrow key navigation in the first place (ie. the care should always be at the end of text)
// Avoid handling it here to allow other components to potentially consume the shortcut.
if (action.ActionType == PlatformActionType.CharNext && action.ActionMethod == PlatformActionMethod.Delete)
return false;
return base.OnPressed(action);
}
protected override bool OnKeyDown(KeyDownEvent e) protected override bool OnKeyDown(KeyDownEvent e)
{ {
if (!e.ControlPressed && !e.ShiftPressed) if (!e.ControlPressed && !e.ShiftPressed)

View File

@ -39,7 +39,7 @@ namespace osu.Game.Input.Bindings
new KeyBinding(InputKey.F4, GlobalAction.ToggleMute), new KeyBinding(InputKey.F4, GlobalAction.ToggleMute),
new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.Escape, GlobalAction.Back),
new KeyBinding(InputKey.MouseButton1, GlobalAction.Back), new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back),
new KeyBinding(InputKey.Space, GlobalAction.Select), new KeyBinding(InputKey.Space, GlobalAction.Select),
new KeyBinding(InputKey.Enter, GlobalAction.Select), new KeyBinding(InputKey.Enter, GlobalAction.Select),

View File

@ -0,0 +1,504 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
{
[DbContext(typeof(OsuDbContext))]
[Migration("20190708070844_AddBPMAndLengthColumns")]
partial class AddBPMAndLengthColumns
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<float>("ApproachRate");
b.Property<float>("CircleSize");
b.Property<float>("DrainRate");
b.Property<float>("OverallDifficulty");
b.Property<double>("SliderMultiplier");
b.Property<double>("SliderTickRate");
b.HasKey("ID");
b.ToTable("BeatmapDifficulty");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("AudioLeadIn");
b.Property<double>("BPM");
b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor");
b.Property<int>("BeatmapSetInfoID");
b.Property<bool>("Countdown");
b.Property<double>("DistanceSpacing");
b.Property<int>("GridSize");
b.Property<string>("Hash");
b.Property<bool>("Hidden");
b.Property<double>("Length");
b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapID");
b.Property<string>("Path");
b.Property<int>("RulesetID");
b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency");
b.Property<double>("StarDifficulty");
b.Property<int>("Status");
b.Property<string>("StoredBookmarks");
b.Property<double>("TimelineZoom");
b.Property<string>("Version");
b.Property<bool>("WidescreenStoryboard");
b.HasKey("ID");
b.HasIndex("BaseDifficultyID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("Hash");
b.HasIndex("MD5Hash");
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("BeatmapInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Artist");
b.Property<string>("ArtistUnicode");
b.Property<string>("AudioFile");
b.Property<string>("AuthorString")
.HasColumnName("Author");
b.Property<string>("BackgroundFile");
b.Property<int>("PreviewTime");
b.Property<string>("Source");
b.Property<string>("Tags");
b.Property<string>("Title");
b.Property<string>("TitleUnicode");
b.HasKey("ID");
b.ToTable("BeatmapMetadata");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("BeatmapSetInfoID");
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.HasKey("ID");
b.HasIndex("BeatmapSetInfoID");
b.HasIndex("FileInfoID");
b.ToTable("BeatmapSetFileInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset>("DateAdded");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int?>("MetadataID");
b.Property<int?>("OnlineBeatmapSetID");
b.Property<bool>("Protected");
b.Property<int>("Status");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("MetadataID");
b.HasIndex("OnlineBeatmapSetID")
.IsUnique();
b.ToTable("BeatmapSetInfo");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Key")
.HasColumnName("Key");
b.Property<int?>("RulesetID");
b.Property<int?>("SkinInfoID");
b.Property<string>("StringValue")
.HasColumnName("Value");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("SkinInfoID");
b.HasIndex("RulesetID", "Variant");
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("IntAction")
.HasColumnName("Action");
b.Property<string>("KeysString")
.HasColumnName("Keys");
b.Property<int?>("RulesetID");
b.Property<int?>("Variant");
b.HasKey("ID");
b.HasIndex("IntAction");
b.HasIndex("RulesetID", "Variant");
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
.ValueGeneratedOnAdd();
b.Property<bool>("Available");
b.Property<string>("InstantiationInfo");
b.Property<string>("Name");
b.Property<string>("ShortName");
b.HasKey("ID");
b.HasIndex("Available");
b.HasIndex("ShortName")
.IsUnique();
b.ToTable("RulesetInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int?>("ScoreInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("ScoreInfoID");
b.ToTable("ScoreFileInfo");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<double>("Accuracy")
.HasColumnType("DECIMAL(1,4)");
b.Property<int>("BeatmapInfoID");
b.Property<int>("Combo");
b.Property<DateTimeOffset>("Date");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<int>("MaxCombo");
b.Property<string>("ModsJson")
.HasColumnName("Mods");
b.Property<long?>("OnlineScoreID");
b.Property<double?>("PP");
b.Property<int>("Rank");
b.Property<int>("RulesetID");
b.Property<string>("StatisticsJson")
.HasColumnName("Statistics");
b.Property<long>("TotalScore");
b.Property<long?>("UserID")
.HasColumnName("UserID");
b.Property<string>("UserString")
.HasColumnName("User");
b.HasKey("ID");
b.HasIndex("BeatmapInfoID");
b.HasIndex("OnlineScoreID")
.IsUnique();
b.HasIndex("RulesetID");
b.ToTable("ScoreInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<int>("FileInfoID");
b.Property<string>("Filename")
.IsRequired();
b.Property<int>("SkinInfoID");
b.HasKey("ID");
b.HasIndex("FileInfoID");
b.HasIndex("SkinInfoID");
b.ToTable("SkinFileInfo");
});
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Creator");
b.Property<bool>("DeletePending");
b.Property<string>("Hash");
b.Property<string>("Name");
b.HasKey("ID");
b.HasIndex("DeletePending");
b.HasIndex("Hash")
.IsUnique();
b.ToTable("SkinInfo");
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
.WithMany()
.HasForeignKey("BaseDifficultyID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
.WithMany("Beatmaps")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("Beatmaps")
.HasForeignKey("MetadataID");
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
.WithMany("Files")
.HasForeignKey("BeatmapSetInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
.WithMany("BeatmapSets")
.HasForeignKey("MetadataID");
});
modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
{
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Settings")
.HasForeignKey("SkinInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Scoring.ScoreInfo")
.WithMany("Files")
.HasForeignKey("ScoreInfoID");
});
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany("Scores")
.HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
.WithMany()
.HasForeignKey("RulesetID")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
{
b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
.WithMany()
.HasForeignKey("FileInfoID")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("osu.Game.Skinning.SkinInfo")
.WithMany("Files")
.HasForeignKey("SkinInfoID")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,33 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace osu.Game.Migrations
{
public partial class AddBPMAndLengthColumns : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<double>(
name: "BPM",
table: "BeatmapInfo",
nullable: false,
defaultValue: 0.0);
migrationBuilder.AddColumn<double>(
name: "Length",
table: "BeatmapInfo",
nullable: false,
defaultValue: 0.0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "BPM",
table: "BeatmapInfo");
migrationBuilder.DropColumn(
name: "Length",
table: "BeatmapInfo");
}
}
}

View File

@ -45,6 +45,8 @@ namespace osu.Game.Migrations
b.Property<int>("AudioLeadIn"); b.Property<int>("AudioLeadIn");
b.Property<double>("BPM");
b.Property<int>("BaseDifficultyID"); b.Property<int>("BaseDifficultyID");
b.Property<int>("BeatDivisor"); b.Property<int>("BeatDivisor");
@ -61,6 +63,8 @@ namespace osu.Game.Migrations
b.Property<bool>("Hidden"); b.Property<bool>("Hidden");
b.Property<double>("Length");
b.Property<bool>("LetterboxInBreaks"); b.Property<bool>("LetterboxInBreaks");
b.Property<string>("MD5Hash"); b.Property<string>("MD5Hash");

View File

@ -5,8 +5,10 @@ using System;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Screens.Select.Leaderboards; using osu.Game.Screens.Select.Leaderboards;
using osu.Framework.IO.Network;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Mods;
using System.Text;
using System.Collections.Generic;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
@ -15,8 +17,9 @@ namespace osu.Game.Online.API.Requests
private readonly BeatmapInfo beatmap; private readonly BeatmapInfo beatmap;
private readonly BeatmapLeaderboardScope scope; private readonly BeatmapLeaderboardScope scope;
private readonly RulesetInfo ruleset; private readonly RulesetInfo ruleset;
private readonly IEnumerable<Mod> mods;
public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global) public GetScoresRequest(BeatmapInfo beatmap, RulesetInfo ruleset, BeatmapLeaderboardScope scope = BeatmapLeaderboardScope.Global, IEnumerable<Mod> mods = null)
{ {
if (!beatmap.OnlineBeatmapID.HasValue) if (!beatmap.OnlineBeatmapID.HasValue)
throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}."); throw new InvalidOperationException($"Cannot lookup a beatmap's scores without having a populated {nameof(BeatmapInfo.OnlineBeatmapID)}.");
@ -27,6 +30,7 @@ namespace osu.Game.Online.API.Requests
this.beatmap = beatmap; this.beatmap = beatmap;
this.scope = scope; this.scope = scope;
this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset)); this.ruleset = ruleset ?? throw new ArgumentNullException(nameof(ruleset));
this.mods = mods ?? Array.Empty<Mod>();
Success += onSuccess; Success += onSuccess;
} }
@ -38,19 +42,29 @@ namespace osu.Game.Online.API.Requests
score.Beatmap = beatmap; score.Beatmap = beatmap;
score.Ruleset = ruleset; score.Ruleset = ruleset;
} }
var userScore = r.UserScore;
if (userScore != null)
{
userScore.Score.Beatmap = beatmap;
userScore.Score.Ruleset = ruleset;
}
} }
protected override WebRequest CreateWebRequest() protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores{createQueryParameters()}";
private string createQueryParameters()
{ {
var req = base.CreateWebRequest(); StringBuilder query = new StringBuilder(@"?");
req.Timeout = 30000; query.Append($@"type={scope.ToString().ToLowerInvariant()}");
req.AddParameter(@"type", scope.ToString().ToLowerInvariant()); query.Append($@"&mode={ruleset.ShortName}");
req.AddParameter(@"mode", ruleset.ShortName);
return req; foreach (var mod in mods)
query.Append($@"&mods[]={mod.Acronym}");
return query.ToString();
} }
protected override string Target => $@"beatmaps/{beatmap.OnlineBeatmapID}/scores";
} }
} }

View File

@ -7,21 +7,20 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class GetUserBeatmapsRequest : APIRequest<List<APIBeatmapSet>> public class GetUserBeatmapsRequest : PaginatedAPIRequest<List<APIBeatmapSet>>
{ {
private readonly long userId; private readonly long userId;
private readonly int offset;
private readonly BeatmapSetType type; private readonly BeatmapSetType type;
public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int page = 0, int itemsPerPage = 6)
: base(page, itemsPerPage)
{ {
this.userId = userId; this.userId = userId;
this.offset = offset;
this.type = type; this.type = type;
} }
// ReSharper disable once ImpureMethodCallOnReadonlyValueField protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}";
protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}";
} }
public enum BeatmapSetType public enum BeatmapSetType

View File

@ -6,17 +6,16 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class GetUserMostPlayedBeatmapsRequest : APIRequest<List<APIUserMostPlayedBeatmap>> public class GetUserMostPlayedBeatmapsRequest : PaginatedAPIRequest<List<APIUserMostPlayedBeatmap>>
{ {
private readonly long userId; private readonly long userId;
private readonly int offset;
public GetUserMostPlayedBeatmapsRequest(long userId, int offset = 0) public GetUserMostPlayedBeatmapsRequest(long userId, int page = 0, int itemsPerPage = 5)
: base(page, itemsPerPage)
{ {
this.userId = userId; this.userId = userId;
this.offset = offset;
} }
protected override string Target => $@"users/{userId}/beatmapsets/most_played?offset={offset}"; protected override string Target => $@"users/{userId}/beatmapsets/most_played";
} }
} }

View File

@ -6,18 +6,17 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class GetUserRecentActivitiesRequest : APIRequest<List<APIRecentActivity>> public class GetUserRecentActivitiesRequest : PaginatedAPIRequest<List<APIRecentActivity>>
{ {
private readonly long userId; private readonly long userId;
private readonly int offset;
public GetUserRecentActivitiesRequest(long userId, int offset = 0) public GetUserRecentActivitiesRequest(long userId, int page = 0, int itemsPerPage = 5)
: base(page, itemsPerPage)
{ {
this.userId = userId; this.userId = userId;
this.offset = offset;
} }
protected override string Target => $"users/{userId}/recent_activity?offset={offset}"; protected override string Target => $"users/{userId}/recent_activity";
} }
public enum RecentActivityType public enum RecentActivityType

View File

@ -6,21 +6,19 @@ using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class GetUserScoresRequest : APIRequest<List<APILegacyScoreInfo>> public class GetUserScoresRequest : PaginatedAPIRequest<List<APILegacyScoreInfo>>
{ {
private readonly long userId; private readonly long userId;
private readonly ScoreType type; private readonly ScoreType type;
private readonly int offset;
public GetUserScoresRequest(long userId, ScoreType type, int offset = 0) public GetUserScoresRequest(long userId, ScoreType type, int page = 0, int itemsPerPage = 5)
: base(page, itemsPerPage)
{ {
this.userId = userId; this.userId = userId;
this.type = type; this.type = type;
this.offset = offset;
} }
// ReSharper disable once ImpureMethodCallOnReadonlyValueField protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLowerInvariant()}";
protected override string Target => $@"users/{userId}/scores/{type.ToString().ToLowerInvariant()}?offset={offset}";
} }
public enum ScoreType public enum ScoreType

View File

@ -0,0 +1,30 @@
// 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.Globalization;
using osu.Framework.IO.Network;
namespace osu.Game.Online.API.Requests
{
public abstract class PaginatedAPIRequest<T> : APIRequest<T>
{
private readonly int page;
private readonly int itemsPerPage;
protected PaginatedAPIRequest(int page, int itemsPerPage)
{
this.page = page;
this.itemsPerPage = itemsPerPage;
}
protected override WebRequest CreateWebRequest()
{
var req = base.CreateWebRequest();
req.AddParameter("offset", (page * itemsPerPage).ToString(CultureInfo.InvariantCulture));
req.AddParameter("limit", itemsPerPage.ToString(CultureInfo.InvariantCulture));
return req;
}
}
}

View File

@ -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 Newtonsoft.Json; using Newtonsoft.Json;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -71,6 +72,7 @@ namespace osu.Game.Online.API.Requests.Responses
StarDifficulty = starDifficulty, StarDifficulty = starDifficulty,
OnlineBeatmapID = OnlineBeatmapID, OnlineBeatmapID = OnlineBeatmapID,
Version = version, Version = version,
Length = TimeSpan.FromSeconds(length).TotalMilliseconds,
Status = Status, Status = Status,
BeatmapSet = set, BeatmapSet = set,
Metrics = metrics, Metrics = metrics,
@ -85,7 +87,6 @@ namespace osu.Game.Online.API.Requests.Responses
{ {
PlayCount = playCount, PlayCount = playCount,
PassCount = passCount, PassCount = passCount,
Length = length,
CircleCount = circleCount, CircleCount = circleCount,
SliderCount = sliderCount, SliderCount = sliderCount,
}, },

View File

@ -30,6 +30,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"preview_url")] [JsonProperty(@"preview_url")]
private string preview { get; set; } private string preview { get; set; }
[JsonProperty(@"has_favourited")]
private bool hasFavourited { get; set; }
[JsonProperty(@"play_count")] [JsonProperty(@"play_count")]
private int playCount { get; set; } private int playCount { get; set; }
@ -91,6 +94,7 @@ namespace osu.Game.Online.API.Requests.Responses
Ranked = ranked, Ranked = ranked,
LastUpdated = lastUpdated, LastUpdated = lastUpdated,
Availability = availability, Availability = availability,
HasFavourited = hasFavourited,
}, },
Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(), Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(),
}; };

Some files were not shown because too many files have changed in this diff Show More