1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-15 06:22:34 +08:00

Compare commits

...

218 Commits

206 changed files with 2612 additions and 1426 deletions
+4 -2
View File
@@ -1,5 +1,5 @@
#addin "nuget:?package=CodeFileSanity&version=0.0.21"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.2"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.3.4"
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
@@ -46,7 +46,9 @@ Task("InspectCode")
OutputFile = "inspectcodereport.xml",
});
StartProcess(nVikaToolPath, @"parsereport ""inspectcodereport.xml"" --treatwarningsaserrors");
int returnCode = StartProcess(nVikaToolPath, $@"parsereport ""inspectcodereport.xml"" --treatwarningsaserrors");
if (returnCode != 0)
throw new Exception($"inspectcode failed with return code {returnCode}");
});
Task("CodeFileSanity")
+1 -1
View File
@@ -110,7 +110,7 @@ namespace osu.Desktop.Overlays
public UpdateCompleteNotification(string version, Action<string> openUrl = null)
{
Text = $"You are now running osu!lazer {version}.\nClick to see what's new!";
Icon = FontAwesome.CheckSquare;
Icon = FontAwesome.Solid.CheckSquare;
Activated = delegate
{
openUrl?.Invoke($"https://osu.ppy.sh/home/changelog/lazer/{version}");
+1 -1
View File
@@ -54,7 +54,7 @@ namespace osu.Desktop.Updater
{
Text = $"A newer release of osu! has been found ({version} → {latest.TagName}).\n\n"
+ "Click here to download the new version, which can be installed over the top of your existing installation",
Icon = FontAwesome.Upload,
Icon = FontAwesome.Solid.Upload,
Activated = () =>
{
host.OpenUrlExternally(getBestUrl(latest));
+1 -1
View File
@@ -159,7 +159,7 @@ namespace osu.Desktop.Updater
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.Upload,
Icon = FontAwesome.Solid.Upload,
Colour = Color4.White,
Size = new Vector2(20),
}
@@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Tests
protected override Player CreatePlayer(Ruleset ruleset)
{
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return base.CreatePlayer(ruleset);
}
}
@@ -23,19 +23,19 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{
Name = @"Fruit Count",
Content = fruits.ToString(),
Icon = FontAwesome.CircleOutline
Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Juice Stream Count",
Content = juiceStreams.ToString(),
Icon = FontAwesome.Circle
Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Banana Shower Count",
Content = bananaShowers.ToString(),
Icon = FontAwesome.Circle
Icon = FontAwesome.Regular.Circle
}
};
}
+1 -1
View File
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch
{
public class CatchRuleset : Ruleset
{
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableCatchRuleset(this, beatmap);
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableCatchRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
@@ -10,6 +11,7 @@ using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Catch.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@@ -23,8 +25,8 @@ namespace osu.Game.Rulesets.Catch.UI
protected override bool UserScrollSpeedAdjustment => false;
public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
public DrawableCatchRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap, mods)
{
Direction.Value = ScrollingDirection.Down;
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -8,6 +10,7 @@ using osu.Framework.Timing;
using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual;
@@ -21,6 +24,9 @@ namespace osu.Game.Rulesets.Mania.Tests
{
private readonly Column column;
[Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
protected ManiaPlacementBlueprintTestCase()
{
Add(column = new Column(0)
@@ -13,6 +13,7 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mania.UI.Components;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual;
using osuTK;
@@ -31,6 +32,9 @@ namespace osu.Game.Rulesets.Mania.Tests
typeof(ColumnHitObjectArea)
};
[Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
private readonly List<Column> columns = new List<Column>();
public TestCaseColumn()
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
@@ -13,6 +14,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual;
using osuTK;
@@ -24,6 +26,9 @@ namespace osu.Game.Rulesets.Mania.Tests
{
private const int columns = 4;
[Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
private readonly List<ManiaStage> stages = new List<ManiaStage>();
private FillFlowContainer<ScrollingTestContainer> fill;
@@ -42,13 +42,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
{
Name = @"Note Count",
Content = notes.ToString(),
Icon = FontAwesome.CircleOutline
Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Hold Note Count",
Content = holdnotes.ToString(),
Icon = FontAwesome.Circle
Icon = FontAwesome.Regular.Circle
},
};
}
@@ -1,10 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Graphics;
using osuTK;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
@@ -14,8 +16,8 @@ namespace osu.Game.Rulesets.Mania.Edit
{
public new IScrollingInfo ScrollingInfo => base.ScrollingInfo;
public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
public DrawableManiaEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap, mods)
{
}
@@ -11,6 +11,7 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
@@ -41,9 +42,9 @@ namespace osu.Game.Rulesets.Mania.Edit
public int TotalColumns => ((ManiaPlayfield)DrawableRuleset.Playfield).TotalColumns;
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap)
protected override DrawableRuleset<ManiaHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
{
DrawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap);
DrawableRuleset = new DrawableManiaEditRuleset(ruleset, beatmap, mods);
// This is the earliest we can cache the scrolling info to ourselves, before masks are added to the hierarchy and inject it
dependencies.CacheAs(DrawableRuleset.ScrollingInfo);
+1 -1
View File
@@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Mania
{
public class ManiaRuleset : Ruleset
{
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableManiaRuleset(this, beatmap);
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableManiaRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score);
@@ -18,6 +18,7 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
@@ -39,8 +40,8 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly Bindable<ManiaScrollingDirection> configDirection = new Bindable<ManiaScrollingDirection>();
public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
public DrawableManiaRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap, mods)
{
// Generate the bar lines
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
+3 -1
View File
@@ -1,11 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
using Decoder = osu.Game.Beatmaps.Formats.Decoder;
@@ -22,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Tests
using (var reader = new StreamReader(stream))
{
var beatmap = Decoder.GetDecoder<Beatmap>(reader).Decode(reader);
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo);
var converted = new TestWorkingBeatmap(beatmap).GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty<Mod>());
var objects = converted.HitObjects.ToList();
@@ -30,7 +30,6 @@ namespace osu.Game.Rulesets.Osu.Tests
protected override Container<Drawable> Content => content;
private int depthIndex;
protected readonly List<Mod> Mods = new List<Mod>();
public TestCaseHitCircle()
{
@@ -68,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Depth = depthIndex++
};
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>())
foreach (var mod in Mods.Value.OfType<IApplicableToDrawableHitObjects>())
mod.ApplyToDrawableHitObjects(new[] { drawable });
Add(drawable);
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public TestCaseHitCircleHidden()
{
Mods.Add(new OsuModHidden());
Mods.Value = new[] { new OsuModHidden() };
}
}
}
@@ -0,0 +1,70 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestCaseResumeOverlay : ManualInputManagerTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuResumeOverlay),
};
public TestCaseResumeOverlay()
{
ManualOsuInputManager osuInputManager;
CursorContainer cursor;
ResumeOverlay resume;
bool resumeFired = false;
Child = osuInputManager = new ManualOsuInputManager(new OsuRuleset().RulesetInfo)
{
Children = new Drawable[]
{
cursor = new CursorContainer(),
resume = new OsuResumeOverlay
{
GameplayCursor = cursor
},
}
};
resume.ResumeAction = () => resumeFired = true;
AddStep("move mouse to center", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre));
AddStep("show", () => resume.Show());
AddStep("move mouse away", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.TopLeft));
AddStep("click", () => osuInputManager.GameClick());
AddAssert("not dismissed", () => !resumeFired && resume.State == Visibility.Visible);
AddStep("move mouse back", () => InputManager.MoveMouseTo(ScreenSpaceDrawQuad.Centre));
AddStep("click", () => osuInputManager.GameClick());
AddAssert("dismissed", () => resumeFired && resume.State == Visibility.Hidden);
}
private class ManualOsuInputManager : OsuInputManager
{
public ManualOsuInputManager(RulesetInfo ruleset)
: base(ruleset)
{
}
public void GameClick()
{
KeyBindingContainer.TriggerPressed(OsuAction.LeftButton);
KeyBindingContainer.TriggerReleased(OsuAction.LeftButton);
}
}
}
}
@@ -44,7 +44,6 @@ namespace osu.Game.Rulesets.Osu.Tests
protected override Container<Drawable> Content => content;
private int depthIndex;
protected readonly List<Mod> Mods = new List<Mod>();
public TestCaseSlider()
{
@@ -292,7 +291,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Depth = depthIndex++
};
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>())
foreach (var mod in Mods.Value.OfType<IApplicableToDrawableHitObjects>())
mod.ApplyToDrawableHitObjects(new[] { drawable });
drawable.OnNewResult += onNewResult;
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public TestCaseSliderHidden()
{
Mods.Add(new OsuModHidden());
Mods.Value = new[] { new OsuModHidden() };
}
}
}
@@ -31,7 +31,6 @@ namespace osu.Game.Rulesets.Osu.Tests
protected override Container<Drawable> Content => content;
private int depthIndex;
protected readonly List<Mod> Mods = new List<Mod>();
public TestCaseSpinner()
{
@@ -57,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests
Depth = depthIndex++
};
foreach (var mod in Mods.OfType<IApplicableToDrawableHitObjects>())
foreach (var mod in Mods.Value.OfType<IApplicableToDrawableHitObjects>())
mod.ApplyToDrawableHitObjects(new[] { drawable });
Add(drawable);
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public TestCaseSpinnerHidden()
{
Mods.Add(new OsuModHidden());
Mods.Value = new[] { new OsuModHidden() };
}
}
}
+3 -3
View File
@@ -23,19 +23,19 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
{
Name = @"Circle Count",
Content = circles.ToString(),
Icon = FontAwesome.CircleOutline
Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Slider Count",
Content = sliders.ToString(),
Icon = FontAwesome.Circle
Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Spinner Count",
Content = spinners.ToString(),
Icon = FontAwesome.Circle
Icon = FontAwesome.Regular.Circle
}
};
}
@@ -16,15 +16,16 @@ namespace osu.Game.Rulesets.Osu.Configuration
protected override void InitialiseDefaults()
{
base.InitialiseDefaults();
Set(OsuRulesetSetting.SnakingInSliders, true);
Set(OsuRulesetSetting.SnakingOutSliders, true);
Set(OsuRulesetSetting.ShowCursorTrail, true);
}
}
public enum OsuRulesetSetting
{
SnakingInSliders,
SnakingOutSliders
SnakingOutSliders,
ShowCursorTrail
}
}
@@ -1,7 +1,9 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI;
using osuTK;
@@ -10,8 +12,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{
public class DrawableOsuEditRuleset : DrawableOsuRuleset
{
public DrawableOsuEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
public DrawableOsuEditRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap, mods)
{
}
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
@@ -23,8 +24,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{
}
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap)
=> new DrawableOsuEditRuleset(ruleset, beatmap);
protected override DrawableRuleset<OsuHitObject> CreateDrawableRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
=> new DrawableOsuEditRuleset(ruleset, beatmap, mods);
protected override IReadOnlyList<HitObjectCompositionTool> CompositionTools => new HitObjectCompositionTool[]
{
+1 -1
View File
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Description => "Play with blinds on your screen.";
public override string Acronym => "BL";
public override IconUsage Icon => FontAwesome.Adjust;
public override IconUsage Icon => FontAwesome.Solid.Adjust;
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => false;
+1 -1
View File
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override string Acronym => "GR";
public override IconUsage Icon => FontAwesome.ArrowsV;
public override IconUsage Icon => FontAwesome.Solid.ArrowsAltV;
public override ModType Type => ModType.Fun;
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Transform";
public override string Acronym => "TR";
public override IconUsage Icon => FontAwesome.Arrows;
public override IconUsage Icon => FontAwesome.Solid.ArrowsAlt;
public override ModType Type => ModType.Fun;
public override string Description => "Everything rotates. EVERYTHING.";
public override double ScoreMultiplier => 1;
+1 -1
View File
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{
public override string Name => "Wiggle";
public override string Acronym => "WG";
public override IconUsage Icon => FontAwesome.Certificate;
public override IconUsage Icon => FontAwesome.Solid.Certificate;
public override ModType Type => ModType.Fun;
public override string Description => "They just won't stay still...";
public override double ScoreMultiplier => 1;
@@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon
{
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.ChevronRight
Icon = FontAwesome.Solid.ChevronRight
}, restrictSize: false)
};
}
@@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(48),
Icon = FontAwesome.Asterisk,
Icon = FontAwesome.Solid.Asterisk,
Shadow = false,
},
}
+1 -1
View File
@@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu
{
public class OsuRuleset : Ruleset
{
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableOsuRuleset(this, beatmap);
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableOsuRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);
@@ -292,7 +292,6 @@ namespace osu.Game.Rulesets.Osu.Replays
{
// We add intermediate frames for spinning / following a slider here.
case Spinner spinner:
{
Vector2 difference = startPosition - SPINNER_CENTRE;
float radius = difference.Length;
@@ -315,9 +314,7 @@ namespace osu.Game.Rulesets.Osu.Replays
endFrame.Position = endPosition;
break;
}
case Slider slider:
{
for (double j = FrameDelay; j < slider.Duration; j += FrameDelay)
{
Vector2 pos = slider.StackedPositionAt(j / slider.Duration);
@@ -326,7 +323,6 @@ namespace osu.Game.Rulesets.Osu.Replays
AddFrameToReplay(new OsuReplayFrame(slider.EndTime, new Vector2(slider.StackedEndPosition.X, slider.StackedEndPosition.Y), action));
break;
}
}
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!
@@ -0,0 +1,148 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.UI.Cursor
{
public class OsuCursor : SkinReloadableDrawable
{
private bool cursorExpand;
private Bindable<double> cursorScale;
private Bindable<bool> autoCursorScale;
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private Container expandTarget;
private Drawable scaleTarget;
public OsuCursor()
{
Origin = Anchor.Centre;
Size = new Vector2(28);
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
cursorExpand = skin.GetValue<SkinConfiguration, bool>(s => s.CursorExpand ?? true);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, IBindable<WorkingBeatmap> beatmap)
{
InternalChild = expandTarget = new Container
{
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Child = scaleTarget = new SkinnableDrawable("cursor", _ => new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 6,
BorderColour = Color4.White,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Pink.Opacity(0.5f),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 3,
BorderColour = Color4.White.Opacity(0.5f),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
},
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.1f),
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
},
},
}
}, restrictSize: false)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
}
};
this.beatmap.BindTo(beatmap);
this.beatmap.ValueChanged += _ => calculateScale();
cursorScale = config.GetBindable<double>(OsuSetting.GameplayCursorSize);
cursorScale.ValueChanged += _ => calculateScale();
autoCursorScale = config.GetBindable<bool>(OsuSetting.AutoCursorSize);
autoCursorScale.ValueChanged += _ => calculateScale();
calculateScale();
}
private void calculateScale()
{
float scale = (float)cursorScale.Value;
if (autoCursorScale.Value && beatmap.Value != null)
{
// if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
}
scaleTarget.Scale = new Vector2(scale);
}
private const float pressed_scale = 1.2f;
private const float released_scale = 1f;
public void Expand()
{
if (!cursorExpand) return;
expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad);
}
public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad);
}
}
@@ -3,16 +3,10 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu.UI.Cursor
@@ -25,6 +19,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private readonly Container<Drawable> fadeContainer;
private readonly Bindable<bool> showTrail = new Bindable<bool>(true);
private readonly CursorTrail cursorTrail;
public OsuCursorContainer()
{
InternalChild = fadeContainer = new Container
@@ -32,11 +30,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new CursorTrail { Depth = 1 }
cursorTrail = new CursorTrail { Depth = 1 }
}
};
}
[BackgroundDependencyLoader(true)]
private void load(OsuRulesetConfigManager config)
{
config?.BindWith(OsuRulesetSetting.ShowCursorTrail, showTrail);
showTrail.BindValueChanged(v => cursorTrail.FadeTo(v.NewValue ? 1 : 0, 200), true);
}
private int downCount;
private void updateExpandedState()
@@ -88,136 +94,5 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
}
public class OsuCursor : SkinReloadableDrawable
{
private bool cursorExpand;
private Bindable<double> cursorScale;
private Bindable<bool> autoCursorScale;
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private Container expandTarget;
private Drawable scaleTarget;
public OsuCursor()
{
Origin = Anchor.Centre;
Size = new Vector2(28);
}
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
cursorExpand = skin.GetValue<SkinConfiguration, bool>(s => s.CursorExpand ?? true);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, IBindable<WorkingBeatmap> beatmap)
{
InternalChild = expandTarget = new Container
{
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Child = scaleTarget = new SkinnableDrawable("cursor", _ => new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 6,
BorderColour = Color4.White,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Pink.Opacity(0.5f),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 3,
BorderColour = Color4.White.Opacity(0.5f),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
},
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.1f),
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
},
},
}
}, restrictSize: false)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
}
};
this.beatmap.BindTo(beatmap);
this.beatmap.ValueChanged += _ => calculateScale();
cursorScale = config.GetBindable<double>(OsuSetting.GameplayCursorSize);
cursorScale.ValueChanged += _ => calculateScale();
autoCursorScale = config.GetBindable<bool>(OsuSetting.AutoCursorSize);
autoCursorScale.ValueChanged += _ => calculateScale();
calculateScale();
}
private void calculateScale()
{
float scale = (float)cursorScale.Value;
if (autoCursorScale.Value && beatmap.Value != null)
{
// if we have a beatmap available, let's get its circle size to figure out an automatic cursor scale modifier.
scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
}
scaleTarget.Scale = new Vector2(scale);
}
private const float pressed_scale = 1.2f;
private const float released_scale = 1f;
public void Expand()
{
if (!cursorExpand) return;
expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad);
}
public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad);
}
}
}
@@ -1,11 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Input.Handlers;
using osu.Game.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Objects;
@@ -14,6 +16,7 @@ using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
namespace osu.Game.Rulesets.Osu.UI
{
@@ -21,8 +24,8 @@ namespace osu.Game.Rulesets.Osu.UI
{
protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config;
public DrawableOsuRuleset(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
public DrawableOsuRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap, mods)
{
}
@@ -34,6 +37,8 @@ namespace osu.Game.Rulesets.Osu.UI
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer();
protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay();
public override DrawableHitObject<OsuHitObject> CreateDrawableRepresentation(OsuHitObject h)
{
switch (h)
@@ -0,0 +1,109 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Play;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.UI
{
public class OsuResumeOverlay : ResumeOverlay
{
private OsuClickToResumeCursor clickToResumeCursor;
private GameplayCursorContainer localCursorContainer;
public override CursorContainer LocalCursor => State == Visibility.Visible ? localCursorContainer : null;
protected override string Message => "Click the orange cursor to resume";
[BackgroundDependencyLoader]
private void load()
{
Add(clickToResumeCursor = new OsuClickToResumeCursor { ResumeRequested = Resume });
}
public override void Show()
{
base.Show();
clickToResumeCursor.ShowAt(GameplayCursor.ActiveCursor.Position);
if (localCursorContainer == null)
Add(localCursorContainer = new OsuCursorContainer());
}
public override void Hide()
{
localCursorContainer?.Expire();
localCursorContainer = null;
base.Hide();
}
protected override bool OnHover(HoverEvent e) => true;
public class OsuClickToResumeCursor : OsuCursor, IKeyBindingHandler<OsuAction>
{
public override bool HandlePositionalInput => true;
public Action ResumeRequested;
public OsuClickToResumeCursor()
{
RelativePositionAxes = Axes.Both;
}
protected override bool OnHover(HoverEvent e)
{
updateColour();
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateColour();
base.OnHoverLost(e);
}
public bool OnPressed(OsuAction action)
{
switch (action)
{
case OsuAction.LeftButton:
case OsuAction.RightButton:
if (!IsHovered) return false;
this.ScaleTo(new Vector2(2), TRANSITION_TIME, Easing.OutQuint);
ResumeRequested?.Invoke();
return true;
}
return false;
}
public bool OnReleased(OsuAction action) => false;
public void ShowAt(Vector2 activeCursorPosition) => Schedule(() =>
{
updateColour();
this.MoveTo(activeCursorPosition);
this.ScaleTo(new Vector2(4)).Then().ScaleTo(Vector2.One, 1000, Easing.OutQuint);
});
private void updateColour()
{
this.FadeColour(IsHovered ? Color4.White : Color4.Orange, 400, Easing.OutQuint);
}
}
}
}
@@ -34,6 +34,11 @@ namespace osu.Game.Rulesets.Osu.UI
LabelText = "Snaking out sliders",
Bindable = config.GetBindable<bool>(OsuRulesetSetting.SnakingOutSliders)
},
new SettingsCheckbox
{
LabelText = "Cursor trail",
Bindable = config.GetBindable<bool>(OsuRulesetSetting.ShowCursorTrail)
},
};
}
}
@@ -11,6 +11,7 @@ using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
@@ -86,7 +87,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = 768,
Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap) }
Children = new[] { drawableRuleset = new DrawableTaikoRuleset(new TaikoRuleset(), beatmap, Array.Empty<Mod>()) }
});
}
@@ -23,19 +23,19 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
{
Name = @"Hit Count",
Content = hits.ToString(),
Icon = FontAwesome.CircleOutline
Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Drumroll Count",
Content = drumrolls.ToString(),
Icon = FontAwesome.Circle
Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Swell Count",
Content = swells.ToString(),
Icon = FontAwesome.Circle
Icon = FontAwesome.Regular.Circle
}
};
}
@@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
new SpriteIcon
{
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.Asterisk,
Icon = FontAwesome.Solid.Asterisk,
Shadow = false
}
};
+1 -1
View File
@@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko
{
public class TaikoRuleset : Ruleset
{
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap) => new DrawableTaikoRuleset(this, beatmap);
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods) => new DrawableTaikoRuleset(this, beatmap, mods);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
@@ -16,6 +17,7 @@ using osu.Framework.Input;
using osu.Game.Configuration;
using osu.Game.Input.Handlers;
using osu.Game.Replays;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Taiko.UI
@@ -26,8 +28,8 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override bool UserScrollSpeedAdjustment => false;
public DrawableTaikoRuleset(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
public DrawableTaikoRuleset(Ruleset ruleset, WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
: base(ruleset, beatmap, mods)
{
Direction.Value = ScrollingDirection.Left;
TimeRange.Value = 7000;
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.IO;
using NUnit.Framework;
using osuTK;
@@ -13,6 +14,7 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Osu;
@@ -39,7 +41,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(6, working.BeatmapInfo.BeatmapVersion);
Assert.AreEqual(6, working.Beatmap.BeatmapInfo.BeatmapVersion);
Assert.AreEqual(6, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapVersion);
Assert.AreEqual(6, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo, Array.Empty<Mod>()).BeatmapInfo.BeatmapVersion);
}
}
@@ -267,7 +267,7 @@ namespace osu.Game.Tests.Visual.Background
AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null);
AddStep("Set default user settings", () =>
{
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { new OsuModNoFail() });
Mods.Value = Mods.Value.Concat(new[] { new OsuModNoFail() }).ToArray();
songSelect.DimLevel.Value = 0.7f;
songSelect.BlurLevel.Value = 0.4f;
});
@@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
protected override Player CreatePlayer(Ruleset ruleset)
{
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
return new ScoreAccessiblePlayer();
}
@@ -1,13 +1,19 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Cursor;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
@@ -15,14 +21,52 @@ namespace osu.Game.Tests.Visual.Gameplay
{
protected new PausePlayer Player => (PausePlayer)base.Player;
private readonly Container content;
protected override Container<Drawable> Content => content;
public TestCasePause()
: base(new OsuRuleset())
{
base.Content.Add(content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both });
}
[Test]
public void TestPauseResume()
{
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
pauseAndConfirm();
resumeAndConfirm();
}
[Test]
public void TestResumeWithResumeOverlay()
{
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1);
pauseAndConfirm();
resume();
confirmClockRunning(false);
confirmPauseOverlayShown(false);
AddStep("click to resume", () =>
{
InputManager.PressButton(MouseButton.Left);
InputManager.ReleaseButton(MouseButton.Left);
});
confirmClockRunning(true);
}
[Test]
public void TestResumeWithResumeOverlaySkipped()
{
AddStep("move cursor to button", () =>
InputManager.MoveMouseTo(Player.HUDOverlay.HoldToQuit.Children.OfType<HoldToConfirmContainer>().First().ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1);
pauseAndConfirm();
resumeAndConfirm();
}
@@ -30,6 +74,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestPauseTooSoon()
{
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
pauseAndConfirm();
resumeAndConfirm();
@@ -144,9 +190,16 @@ namespace osu.Game.Tests.Visual.Gameplay
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
public bool FailOverlayVisible => FailOverlay.State == Visibility.Visible;
public bool PauseOverlayVisible => PauseOverlay.State == Visibility.Visible;
public PausePlayer()
{
PauseOnFocusLost = false;
}
}
}
}
@@ -1,11 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play;
@@ -21,23 +26,15 @@ namespace osu.Game.Tests.Visual.Gameplay
InputManager.Add(stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both });
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
[Test]
public void TestLoadContinuation()
{
Beatmap.Value = new DummyWorkingBeatmap(game);
AddStep("load dummy beatmap", () => stack.Push(loader = new PlayerLoader(() => new Player(false, false))));
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen());
AddStep("exit loader", () => loader.Exit());
AddUntilStep("wait for no longer alive", () => !loader.IsAlive);
AddStep("load slow dummy beatmap", () =>
{
SlowLoadPlayer slow = null;
@@ -50,6 +47,81 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for no longer current", () => !loader.IsCurrentScreen());
}
[Test]
public void TestModReinstantiation()
{
TestPlayer player = null;
TestMod gameMod = null;
TestMod playerMod1 = null;
TestMod playerMod2 = null;
AddStep("load player", () =>
{
Mods.Value = new[] { gameMod = new TestMod() };
InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre);
stack.Push(new PlayerLoader(() => player = new TestPlayer()));
});
AddUntilStep("wait for player to become current", () =>
{
if (player.IsCurrentScreen())
{
playerMod1 = (TestMod)player.Mods.Value.Single();
return true;
}
return false;
});
AddAssert("game mods not applied", () => gameMod.Applied == false);
AddAssert("player mods applied", () => playerMod1.Applied);
AddStep("restart player", () =>
{
player = null;
player.Restart();
});
AddUntilStep("wait for player to become current", () =>
{
if (player.IsCurrentScreen())
{
playerMod2 = (TestMod)player.Mods.Value.Single();
return true;
}
return false;
});
AddAssert("game mods not applied", () => gameMod.Applied == false);
AddAssert("player has different mods", () => playerMod1 != playerMod2);
AddAssert("player mods applied", () => playerMod2.Applied);
}
private class TestMod : Mod, IApplicableToScoreProcessor
{
public override string Name => string.Empty;
public override string Acronym => string.Empty;
public override double ScoreMultiplier => 1;
public bool Applied { get; private set; }
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{
Applied = true;
}
}
private class TestPlayer : Player
{
public new Bindable<IReadOnlyList<Mod>> Mods => base.Mods;
public TestPlayer()
: base(false, false)
{
}
}
protected class SlowLoadPlayer : Player
{
public bool Ready;
@@ -1,9 +1,11 @@
// 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.ComponentModel;
using System.Linq;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
@@ -15,7 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
protected override Player CreatePlayer(Ruleset ruleset)
{
var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo);
var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty<Mod>());
return new ScoreAccessibleReplayPlayer(ruleset.GetAutoplayMod().CreateReplayScore(beatmap));
}
@@ -4,11 +4,13 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Timing;
@@ -23,6 +25,9 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(Playfield) };
[Cached(typeof(IReadOnlyList<Mod>))]
private IReadOnlyList<Mod> mods { get; set; } = Array.Empty<Mod>();
private readonly ScrollingTestContainer[] scrollContainers = new ScrollingTestContainer[4];
private readonly TestPlayfield[] playfields = new TestPlayfield[4];
@@ -1,9 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
@@ -13,6 +10,9 @@ using osu.Game.Overlays.BeatmapSet.Buttons;
using osu.Game.Overlays.BeatmapSet.Scores;
using osu.Game.Rulesets;
using osu.Game.Users;
using System;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Tests.Visual.Online
{
@@ -24,8 +24,8 @@ namespace osu.Game.Tests.Visual.Online
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Header),
typeof(ClickableUsername),
typeof(DrawableScore),
typeof(ScoreTable),
typeof(ScoreTableRowBackground),
typeof(DrawableTopScore),
typeof(ScoresContainer),
typeof(AuthorInfo),
@@ -0,0 +1,185 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Overlays.BeatmapSet.Scores;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Online
{
public class TestCaseScoresContainer : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(DrawableTopScore),
typeof(TopScoreUserSection),
typeof(TopScoreStatisticsSection),
typeof(ScoreTable),
typeof(ScoreTableRowBackground),
};
private readonly Box background;
public TestCaseScoresContainer()
{
ScoresContainer scoresContainer;
Child = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Width = 0.8f,
Children = new Drawable[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
scoresContainer = new ScoresContainer(),
}
};
var scores = new List<ScoreInfo>
{
new ScoreInfo
{
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,
},
new ScoreInfo
{
User = new User
{
Id = 4608074,
Username = @"Skycries",
Country = new Country
{
FullName = @"Brazil",
FlagName = @"BR",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
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",
FlagName = @"JP",
},
},
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",
FlagName = @"CA",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
},
Rank = ScoreRank.C,
PP = 170,
MaxCombo = 1234,
TotalScore = 1234567,
Accuracy = 0.8765,
},
new ScoreInfo
{
User = new User
{
Id = 7151382,
Username = @"Mayuri Hana",
Country = new Country
{
FullName = @"Thailand",
FlagName = @"TH",
},
},
Rank = ScoreRank.F,
PP = 160,
MaxCombo = 1234,
TotalScore = 123456,
Accuracy = 0.6543,
},
};
foreach (var s in scores)
{
s.Statistics.Add(HitResult.Great, RNG.Next(2000));
s.Statistics.Add(HitResult.Good, RNG.Next(2000));
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
s.Statistics.Add(HitResult.Miss, RNG.Next(2000));
}
scoresContainer.Scores = scores;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray2;
}
}
}
@@ -16,10 +16,10 @@ namespace osu.Game.Tests.Visual.SongSelect
{
var overlay = new BeatmapOptionsOverlay();
overlay.AddButton(@"Remove", @"from unplayed", FontAwesome.TimesCircleOutline, Color4.Purple, null, Key.Number1);
overlay.AddButton(@"Clear", @"local scores", FontAwesome.Eraser, Color4.Purple, null, Key.Number2);
overlay.AddButton(@"Edit", @"Beatmap", FontAwesome.Pencil, Color4.Yellow, null, Key.Number3);
overlay.AddButton(@"Delete", @"Beatmap", FontAwesome.Trash, Color4.Pink, null, Key.Number4, float.MaxValue);
overlay.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, Color4.Purple, null, Key.Number1);
overlay.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, Color4.Purple, null, Key.Number2);
overlay.AddButton(@"Edit", @"Beatmap", FontAwesome.Solid.PencilAlt, Color4.Yellow, null, Key.Number3);
overlay.AddButton(@"Delete", @"Beatmap", FontAwesome.Solid.Trash, Color4.Pink, null, Key.Number4, float.MaxValue);
Add(overlay);
@@ -1,312 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Overlays.BeatmapSet.Scores;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.SongSelect
{
[System.ComponentModel.Description("in BeatmapOverlay")]
public class TestCaseBeatmapScoresContainer : OsuTestCase
{
private readonly Box background;
public TestCaseBeatmapScoresContainer()
{
Container container;
ScoresContainer scoresContainer;
Child = container = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Width = 0.8f,
Children = new Drawable[]
{
background = new Box { RelativeSizeAxes = Axes.Both },
scoresContainer = new ScoresContainer(),
}
};
IEnumerable<ScoreInfo> scores = new[]
{
new ScoreInfo
{
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,
TotalScore = 1234567890,
Accuracy = 1,
},
new ScoreInfo
{
User = new User
{
Id = 4608074,
Username = @"Skycries",
Country = new Country
{
FullName = @"Brazil",
FlagName = @"BR",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
new OsuModFlashlight(),
},
Rank = ScoreRank.S,
TotalScore = 1234789,
Accuracy = 0.9997,
},
new ScoreInfo
{
User = new User
{
Id = 1014222,
Username = @"eLy",
Country = new Country
{
FullName = @"Japan",
FlagName = @"JP",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
},
Rank = ScoreRank.B,
TotalScore = 12345678,
Accuracy = 0.9854,
},
new ScoreInfo
{
User = new User
{
Id = 1541390,
Username = @"Toukai",
Country = new Country
{
FullName = @"Canada",
FlagName = @"CA",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
},
Rank = ScoreRank.C,
TotalScore = 1234567,
Accuracy = 0.8765,
},
new ScoreInfo
{
User = new User
{
Id = 7151382,
Username = @"Mayuri Hana",
Country = new Country
{
FullName = @"Thailand",
FlagName = @"TH",
},
},
Rank = ScoreRank.F,
TotalScore = 123456,
Accuracy = 0.6543,
},
};
foreach (var s in scores)
{
s.Statistics.Add(HitResult.Great, RNG.Next(2000));
s.Statistics.Add(HitResult.Good, RNG.Next(2000));
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
}
IEnumerable<ScoreInfo> anotherScores = new[]
{
new ScoreInfo
{
User = new User
{
Id = 4608074,
Username = @"Skycries",
Country = new Country
{
FullName = @"Brazil",
FlagName = @"BR",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
new OsuModFlashlight(),
},
Rank = ScoreRank.S,
TotalScore = 1234789,
Accuracy = 0.9997,
},
new ScoreInfo
{
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,
TotalScore = 1234567890,
Accuracy = 1,
},
new ScoreInfo
{
User = new User
{
Id = 7151382,
Username = @"Mayuri Hana",
Country = new Country
{
FullName = @"Thailand",
FlagName = @"TH",
},
},
Rank = ScoreRank.F,
TotalScore = 123456,
Accuracy = 0.6543,
},
new ScoreInfo
{
User = new User
{
Id = 1014222,
Username = @"eLy",
Country = new Country
{
FullName = @"Japan",
FlagName = @"JP",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModHidden(),
},
Rank = ScoreRank.B,
TotalScore = 12345678,
Accuracy = 0.9854,
},
new ScoreInfo
{
User = new User
{
Id = 1541390,
Username = @"Toukai",
Country = new Country
{
FullName = @"Canada",
FlagName = @"CA",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
},
Rank = ScoreRank.C,
TotalScore = 1234567,
Accuracy = 0.8765,
},
};
foreach (var s in anotherScores)
{
s.Statistics.Add(HitResult.Great, RNG.Next(2000));
s.Statistics.Add(HitResult.Good, RNG.Next(2000));
s.Statistics.Add(HitResult.Meh, RNG.Next(2000));
}
var topScoreInfo = new ScoreInfo
{
User = new User
{
Id = 2705430,
Username = @"Mooha",
Country = new Country
{
FullName = @"France",
FlagName = @"FR",
},
},
Mods = new Mod[]
{
new OsuModDoubleTime(),
new OsuModFlashlight(),
new OsuModHardRock(),
},
Rank = ScoreRank.B,
TotalScore = 987654321,
Accuracy = 0.8487,
};
topScoreInfo.Statistics.Add(HitResult.Great, RNG.Next(2000));
topScoreInfo.Statistics.Add(HitResult.Good, RNG.Next(2000));
topScoreInfo.Statistics.Add(HitResult.Meh, RNG.Next(2000));
AddStep("scores pack 1", () => scoresContainer.Scores = scores);
AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores);
AddStep("only top score", () => scoresContainer.Scores = new[] { topScoreInfo });
AddStep("remove scores", () => scoresContainer.Scores = null);
AddStep("resize to big", () => container.ResizeWidthTo(1, 300));
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray2;
}
}
}
@@ -35,10 +35,6 @@ namespace osu.Game.Tests.Visual.SongSelect
private WorkingBeatmap defaultBeatmap;
private DatabaseContextFactory factory;
[Cached]
[Cached(Type = typeof(IBindable<IEnumerable<Mod>>))]
private readonly Bindable<IEnumerable<Mod>> selectedMods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Screens.Select.SongSelect),
@@ -175,19 +171,19 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("change ruleset", () =>
{
songSelect.CurrentBeatmap.Mods.ValueChanged += onModChange;
Mods.ValueChanged += onModChange;
songSelect.Ruleset.ValueChanged += onRulesetChange;
Ruleset.Value = new TaikoRuleset().RulesetInfo;
songSelect.CurrentBeatmap.Mods.ValueChanged -= onModChange;
Mods.ValueChanged -= onModChange;
songSelect.Ruleset.ValueChanged -= onRulesetChange;
});
AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex);
AddAssert("empty mods", () => !selectedMods.Value.Any());
AddAssert("empty mods", () => !Mods.Value.Any());
void onModChange(ValueChangedEvent<IEnumerable<Mod>> e) => modChangeIndex = actionIndex++;
void onModChange(ValueChangedEvent<IReadOnlyList<Mod>> e) => modChangeIndex = actionIndex++;
void onRulesetChange(ValueChangedEvent<RulesetInfo> e) => rulesetChangeIndex = actionIndex--;
}
@@ -218,7 +214,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private static int importId;
private int getImportId() => ++importId;
private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => selectedMods.Value = mods);
private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => Mods.Value = mods);
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));
@@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.UserInterface
RelativeSizeAxes = Axes.Both,
},
buttons = new ButtonSystem(),
logo = new OsuLogo()
logo = new OsuLogo { RelativePositionAxes = Axes.Both }
};
buttons.SetOsuLogo(logo);
@@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("dialog #1", () => overlay.Push(new PopupDialog
{
Icon = FontAwesome.TrashOutline,
Icon = FontAwesome.Regular.TrashAlt,
HeaderText = @"Confirm deletion of",
BodyText = @"Ayase Rie - Yuima-ru*World TVver.",
Buttons = new PopupDialogButton[]
@@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("dialog #2", () => overlay.Push(new PopupDialog
{
Icon = FontAwesome.Gear,
Icon = FontAwesome.Solid.Cog,
HeaderText = @"What do you want to do with",
BodyText = "Camellia as \"Bang Riot\" - Blastix Riotz",
Buttons = new PopupDialogButton[]
@@ -0,0 +1,299 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.MathUtils;
using osu.Framework.Testing;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestCaseLogoTrackingContainer : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(PlayerLoader),
typeof(Player),
typeof(LogoTrackingContainer),
typeof(ButtonSystem),
typeof(ButtonSystemState),
typeof(Menu),
typeof(MainMenu)
};
private OsuLogo logo;
private TestLogoTrackingContainer trackingContainer;
private Container transferContainer;
private Box visualBox;
private Box transferContainerBox;
private Drawable logoFacade;
private bool randomPositions;
private const float visual_box_size = 72;
[SetUpSteps]
public void SetUpSteps()
{
AddStep("Clear facades", () =>
{
Clear();
Add(logo = new OsuLogo { Scale = new Vector2(0.15f), RelativePositionAxes = Axes.Both });
trackingContainer = null;
transferContainer = null;
});
}
/// <summary>
/// Move the facade to 0,0, then move it to a random new location while the logo is still transforming to it.
/// Check if the logo is still tracking the facade.
/// </summary>
[Test]
public void TestMoveFacade()
{
AddToggleStep("Toggle move continuously", b => randomPositions = b);
AddStep("Add tracking containers", addFacadeContainers);
AddStep("Move facade to random position", moveLogoFacade);
waitForMove();
AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking);
}
/// <summary>
/// Check if the facade is removed from the container, the logo stops tracking.
/// </summary>
[Test]
public void TestRemoveFacade()
{
AddStep("Add tracking containers", addFacadeContainers);
AddStep("Move facade to random position", moveLogoFacade);
AddStep("Remove facade from FacadeContainer", removeFacade);
waitForMove();
AddAssert("Logo is not tracking", () => !trackingContainer.IsLogoTracking);
}
/// <summary>
/// Check if the facade gets added to a new container, tracking starts on the new facade.
/// </summary>
[Test]
public void TestTransferFacade()
{
AddStep("Add tracking containers", addFacadeContainers);
AddStep("Move facade to random position", moveLogoFacade);
AddStep("Remove facade from FacadeContainer", removeFacade);
AddStep("Transfer facade to a new container", () =>
{
transferContainer.Add(logoFacade);
transferContainerBox.Colour = Color4.Tomato;
moveLogoFacade();
});
waitForMove();
AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking);
}
/// <summary>
/// Add a facade to a flow container, move the logo to the center of the screen, then start tracking on the facade.
/// </summary>
[Test]
public void TestFlowContainer()
{
FillFlowContainer flowContainer;
AddStep("Create new flow container with facade", () =>
{
Add(trackingContainer = new TestLogoTrackingContainer
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Child = flowContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Direction = FillDirection.Vertical,
}
});
flowContainer.Children = new Drawable[]
{
new Box
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Colour = Color4.Azure,
Size = new Vector2(visual_box_size)
},
new Container
{
Alpha = 0.35f,
RelativeSizeAxes = Axes.None,
Size = new Vector2(visual_box_size),
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Children = new Drawable[]
{
visualBox = new Box
{
Colour = Color4.White,
RelativeSizeAxes = Axes.Both,
},
trackingContainer.LogoFacade,
}
},
new Box
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Colour = Color4.Azure,
Size = new Vector2(visual_box_size)
},
};
});
AddStep("Perform logo movements", () =>
{
trackingContainer.StopTracking();
logo.MoveTo(new Vector2(0.5f), 500, Easing.InOutExpo);
visualBox.Colour = Color4.White;
Scheduler.AddDelayed(() =>
{
trackingContainer.StartTracking(logo, 1000, Easing.InOutExpo);
visualBox.Colour = Color4.Tomato;
}, 700);
});
waitForMove(8);
AddAssert("Logo is tracking", () => trackingContainer.IsLogoTracking);
}
[Test]
public void TestSetFacadeSize()
{
bool failed = false;
AddStep("Set up scenario", () =>
{
failed = false;
addFacadeContainers();
});
AddStep("Try setting facade size", () =>
{
try
{
logoFacade.Size = new Vector2(0, 0);
}
catch (Exception e)
{
if (e is InvalidOperationException)
failed = true;
}
});
AddAssert("Exception thrown", () => failed);
}
[Test]
public void TestSetMultipleContainers()
{
bool failed = false;
LogoTrackingContainer newContainer = new LogoTrackingContainer();
AddStep("Set up scenario", () =>
{
failed = false;
newContainer = new LogoTrackingContainer();
addFacadeContainers();
moveLogoFacade();
});
AddStep("Try tracking new container", () =>
{
try
{
newContainer.StartTracking(logo);
}
catch (Exception e)
{
if (e is InvalidOperationException)
failed = true;
}
});
AddAssert("Exception thrown", () => failed);
}
private void addFacadeContainers()
{
AddRange(new Drawable[]
{
trackingContainer = new TestLogoTrackingContainer
{
Alpha = 0.35f,
RelativeSizeAxes = Axes.None,
Size = new Vector2(visual_box_size),
Child = visualBox = new Box
{
Colour = Color4.Tomato,
RelativeSizeAxes = Axes.Both,
}
},
transferContainer = new Container
{
Alpha = 0.35f,
RelativeSizeAxes = Axes.None,
Size = new Vector2(visual_box_size),
Child = transferContainerBox = new Box
{
Colour = Color4.White,
RelativeSizeAxes = Axes.Both,
}
},
});
trackingContainer.Add(logoFacade = trackingContainer.LogoFacade);
trackingContainer.StartTracking(logo, 1000);
}
private void waitForMove(int count = 5) => AddWaitStep("Wait for transforms to finish", count);
private void removeFacade()
{
trackingContainer.Remove(logoFacade);
visualBox.Colour = Color4.White;
moveLogoFacade();
}
private void moveLogoFacade()
{
if (logoFacade?.Transforms.Count == 0 && transferContainer?.Transforms.Count == 0)
{
Random random = new Random();
trackingContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300);
transferContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300);
}
if (randomPositions)
Schedule(moveLogoFacade);
}
private class TestLogoTrackingContainer : LogoTrackingContainer
{
/// <summary>
/// Check that the logo is tracking the position of the facade, with an acceptable precision lenience.
/// </summary>
public bool IsLogoTracking => Precision.AlmostEquals(Logo.Position, ComputeLogoTrackingPosition());
}
}
}
@@ -253,7 +253,7 @@ namespace osu.Game.Tests.Visual.UserInterface
private class TestModSelectOverlay : ModSelectOverlay
{
public new Bindable<IEnumerable<Mod>> SelectedMods => base.SelectedMods;
public new Bindable<IReadOnlyList<Mod>> SelectedMods => base.SelectedMods;
public ModButton GetModButton(Mod mod)
{
@@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
RelativeSizeAxes = Axes.Both,
State = Framework.Graphics.Containers.Visibility.Visible,
Icon = FontAwesome.AssistiveListeningSystems,
Icon = FontAwesome.Solid.AssistiveListeningSystems,
HeaderText = @"This is a test popup",
BodyText = "I can say lots of stuff and even wrap my words!",
Buttons = new PopupDialogButton[]
@@ -60,7 +60,7 @@ namespace osu.Game.Beatmaps.Drawables
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
// the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment)
Icon = ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.QuestionCircleOutline }
Icon = ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }
}
};
}
+1 -1
View File
@@ -52,7 +52,7 @@ namespace osu.Game.Beatmaps
{
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[] { };
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap)
public override DrawableRuleset CreateDrawableRulesetWith(WorkingBeatmap beatmap, IReadOnlyList<Mod> mods)
{
throw new NotImplementedException();
}
+6 -28
View File
@@ -11,7 +11,6 @@ using osu.Framework.IO.File;
using System.IO;
using System.Linq;
using System.Threading;
using osu.Framework.Bindables;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
@@ -28,16 +27,12 @@ namespace osu.Game.Beatmaps
public readonly BeatmapMetadata Metadata;
public readonly Bindable<IEnumerable<Mod>> Mods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
protected WorkingBeatmap(BeatmapInfo beatmapInfo)
{
BeatmapInfo = beatmapInfo;
BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
Mods.ValueChanged += _ => applyRateAdjustments();
beatmap = new RecyclableLazy<IBeatmap>(() =>
{
var b = GetBeatmap() ?? new Beatmap();
@@ -51,14 +46,7 @@ namespace osu.Game.Beatmaps
return b;
});
track = new RecyclableLazy<Track>(() =>
{
// we want to ensure that we always have a track, even if it's a fake one.
var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap);
applyRateAdjustments(t);
return t;
});
track = new RecyclableLazy<Track>(() => GetTrack() ?? new VirtualBeatmapTrack(Beatmap));
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
waveform = new RecyclableLazy<Waveform>(GetWaveform);
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
@@ -87,7 +75,7 @@ namespace osu.Game.Beatmaps
/// <param name="ruleset">The <see cref="RulesetInfo"/> to create a playable <see cref="IBeatmap"/> for.</param>
/// <returns>The converted <see cref="IBeatmap"/>.</returns>
/// <exception cref="BeatmapInvalidForRulesetException">If <see cref="Beatmap"/> could not be converted to <paramref name="ruleset"/>.</exception>
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset)
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods)
{
var rulesetInstance = ruleset.CreateInstance();
@@ -98,19 +86,19 @@ namespace osu.Game.Beatmaps
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
// Apply conversion mods
foreach (var mod in Mods.Value.OfType<IApplicableToBeatmapConverter>())
foreach (var mod in mods.OfType<IApplicableToBeatmapConverter>())
mod.ApplyToBeatmapConverter(converter);
// Convert
IBeatmap converted = converter.Convert();
// Apply difficulty mods
if (Mods.Value.Any(m => m is IApplicableToDifficulty))
if (mods.Any(m => m is IApplicableToDifficulty))
{
converted.BeatmapInfo = converted.BeatmapInfo.Clone();
converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone();
foreach (var mod in Mods.Value.OfType<IApplicableToDifficulty>())
foreach (var mod in mods.OfType<IApplicableToDifficulty>())
mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
}
@@ -122,7 +110,7 @@ namespace osu.Game.Beatmaps
foreach (var obj in converted.HitObjects)
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
foreach (var mod in Mods.Value.OfType<IApplicableToHitObject>())
foreach (var mod in mods.OfType<IApplicableToHitObject>())
foreach (var obj in converted.HitObjects)
mod.ApplyToHitObject(obj);
@@ -188,16 +176,6 @@ namespace osu.Game.Beatmaps
/// </summary>
public void RecycleTrack() => track.Recycle();
private void applyRateAdjustments(Track t = null)
{
if (t == null && track.IsResultAvailable) t = Track;
if (t == null) return;
t.ResetSpeedAdjustments();
foreach (var mod in Mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(t);
}
public class RecyclableLazy<T>
{
private Lazy<T> lazy;
@@ -11,6 +11,7 @@ using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using osu.Game.Users;
namespace osu.Game.Graphics.Containers
{
@@ -35,7 +36,7 @@ namespace osu.Game.Graphics.Containers
showNotImplementedError = () => notifications?.Post(new SimpleNotification
{
Text = @"This link type is not yet supported!",
Icon = FontAwesome.LifeSaver,
Icon = FontAwesome.Solid.LifeRing,
});
}
@@ -75,6 +76,9 @@ namespace osu.Game.Graphics.Containers
return createLink(text, null, url, linkType, linkArgument, tooltipText);
}
public IEnumerable<Drawable> AddUserLink(User user, Action<SpriteText> creationParameters = null)
=> createLink(AddText(user.Username, creationParameters), user.Username, null, LinkAction.OpenUserProfile, user.Id.ToString(), "View profile");
private IEnumerable<Drawable> createLink(IEnumerable<Drawable> drawables, string text, string url = null, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action action = null)
{
AddInternal(new DrawableLinkCompiler(drawables.OfType<SpriteText>().ToList())
@@ -0,0 +1,158 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
using osu.Game.Screens.Menu;
using osuTK;
namespace osu.Game.Graphics.Containers
{
/// <summary>
/// A container that handles tracking of an <see cref="OsuLogo"/> through different layout scenarios.
/// </summary>
public class LogoTrackingContainer : Container
{
public Facade LogoFacade => facade;
protected OsuLogo Logo { get; private set; }
private readonly InternalFacade facade = new InternalFacade();
private Easing easing;
private Vector2? startPosition;
private double? startTime;
private double duration;
/// <summary>
/// Assign the logo that should track the facade's position, as well as how it should transform to its initial position.
/// </summary>
/// <param name="logo">The instance of the logo to be used for tracking.</param>
/// <param name="facadeScale">The scale of the facade. Does not actually affect the logo itself.</param>
/// <param name="duration">The duration of the initial transform. Default is instant.</param>
/// <param name="easing">The easing type of the initial transform.</param>
public void StartTracking(OsuLogo logo, double duration = 0, Easing easing = Easing.None)
{
if (logo == null)
throw new ArgumentNullException(nameof(logo));
if (logo.IsTracking && Logo == null)
throw new InvalidOperationException($"Cannot track an instance of {typeof(OsuLogo)} to multiple {typeof(LogoTrackingContainer)}s");
if (Logo != logo && Logo != null)
{
// If we're replacing the logo to be tracked, the old one no longer has a tracking container
Logo.IsTracking = false;
}
Logo = logo;
Logo.IsTracking = true;
this.duration = duration;
this.easing = easing;
startTime = null;
startPosition = null;
}
/// <summary>
/// Stops the logo assigned in <see cref="StartTracking"/> from tracking the facade's position.
/// </summary>
public void StopTracking()
{
if (Logo != null)
{
Logo.IsTracking = false;
Logo = null;
}
}
/// <summary>
/// Gets the position that the logo should move to with respect to the <see cref="LogoFacade"/>.
/// Manually performs a conversion of the Facade's position to the Logo's parent's relative space.
/// </summary>
/// <remarks>Will only be correct if the logo's <see cref="Drawable.RelativePositionAxes"/> are set to Axes.Both</remarks>
protected Vector2 ComputeLogoTrackingPosition()
{
var absolutePos = Logo.Parent.ToLocalSpace(LogoFacade.ScreenSpaceDrawQuad.Centre);
return new Vector2(absolutePos.X / Logo.Parent.RelativeToAbsoluteFactor.X,
absolutePos.Y / Logo.Parent.RelativeToAbsoluteFactor.Y);
}
protected override void Update()
{
base.Update();
if (Logo == null)
return;
if (Logo.RelativePositionAxes != Axes.Both)
throw new InvalidOperationException($"Tracking logo must have {nameof(RelativePositionAxes)} = Axes.Both");
// Account for the scale of the actual OsuLogo, as SizeForFlow only accounts for the sprite scale.
facade.SetSize(new Vector2(Logo.SizeForFlow * Logo.Scale.X));
var localPos = ComputeLogoTrackingPosition();
if (LogoFacade.Parent != null && Logo.Position != localPos)
{
// If this is our first update since tracking has started, initialize our starting values for interpolation
if (startTime == null || startPosition == null)
{
startTime = Time.Current;
startPosition = Logo.Position;
}
if (duration != 0)
{
double elapsedDuration = (double)(Time.Current - startTime);
var amount = (float)Interpolation.ApplyEasing(easing, Math.Min(elapsedDuration / duration, 1));
// Interpolate the position of the logo, where amount 0 is where the logo was when it first began interpolating, and amount 1 is the target location.
Logo.Position = Vector2.Lerp(startPosition.Value, localPos, amount);
}
else
{
Logo.Position = localPos;
}
}
}
protected override void Dispose(bool isDisposing)
{
if (Logo != null)
Logo.IsTracking = false;
base.Dispose(isDisposing);
}
private class InternalFacade : Facade
{
public void SetSize(Vector2 size)
{
base.SetSize(size);
}
}
/// <summary>
/// A dummy object used to denote another object's location.
/// </summary>
public abstract class Facade : Drawable
{
public override Vector2 Size
{
get => base.Size;
set => throw new InvalidOperationException($"Cannot set the Size of a {typeof(Facade)} outside of a {typeof(LogoTrackingContainer)}");
}
protected void SetSize(Vector2 size)
{
base.Size = size;
}
}
}
}
@@ -1,17 +1,19 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osuTK.Graphics;
using System.Collections.Generic;
namespace osu.Game.Graphics.Containers
{
public class OsuHoverContainer : OsuClickableContainer
{
protected const float FADE_DURATION = 500;
protected Color4 HoverColour;
protected Color4 IdleColour = Color4.White;
@@ -20,13 +22,13 @@ namespace osu.Game.Graphics.Containers
protected override bool OnHover(HoverEvent e)
{
EffectTargets.ForEach(d => d.FadeColour(HoverColour, 500, Easing.OutQuint));
EffectTargets.ForEach(d => d.FadeColour(HoverColour, FADE_DURATION, Easing.OutQuint));
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
EffectTargets.ForEach(d => d.FadeColour(IdleColour, 500, Easing.OutQuint));
EffectTargets.ForEach(d => d.FadeColour(IdleColour, FADE_DURATION, Easing.OutQuint));
base.OnHoverLost(e);
}
-3
View File
@@ -42,8 +42,6 @@ namespace osu.Game.Graphics
{
case Typeface.Exo:
return "Exo2.0";
case Typeface.FontAwesome:
return "FontAwesome";
case Typeface.Venera:
return "Venera";
}
@@ -101,7 +99,6 @@ namespace osu.Game.Graphics
public enum Typeface
{
Exo,
FontAwesome,
Venera,
}
+1 -1
View File
@@ -7,7 +7,7 @@ namespace osu.Game.Graphics
{
public static class OsuIcon
{
public static IconUsage Get(int icon) => new IconUsage((char)icon, "OsuFont");
public static IconUsage Get(int icon) => new IconUsage((char)icon, "osuFont");
// ruleset icons in circles
public static IconUsage RulesetOsu => Get(0xe000);
@@ -93,7 +93,7 @@ namespace osu.Game.Graphics.UserInterface
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreLeft,
Size = new Vector2(item_chevron_size),
Icon = FontAwesome.ChevronRight,
Icon = FontAwesome.Solid.ChevronRight,
Margin = new MarginPadding { Left = padding },
Alpha = 0f,
});
@@ -26,7 +26,7 @@ namespace osu.Game.Graphics.UserInterface
Size = new Vector2(12);
InternalChild = new SpriteIcon
{
Icon = FontAwesome.ExternalLink,
Icon = FontAwesome.Solid.ExternalLinkAlt,
RelativeSizeAxes = Axes.Both
};
}
@@ -37,14 +37,14 @@ namespace osu.Game.Graphics.UserInterface
Position = new Vector2(1, 1),
Colour = Color4.Black,
Alpha = 0.4f,
Icon = FontAwesome.CircleONotch
Icon = FontAwesome.Solid.CircleNotch
},
spinner = new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.CircleONotch
Icon = FontAwesome.Solid.CircleNotch
}
};
}
@@ -179,7 +179,7 @@ namespace osu.Game.Graphics.UserInterface
Chevron = new SpriteIcon
{
AlwaysPresent = true,
Icon = FontAwesome.ChevronRight,
Icon = FontAwesome.Solid.ChevronRight,
Colour = Color4.Black,
Alpha = 0.5f,
Size = new Vector2(8),
@@ -244,7 +244,7 @@ namespace osu.Game.Graphics.UserInterface
},
Icon = new SpriteIcon
{
Icon = FontAwesome.ChevronDown,
Icon = FontAwesome.Solid.ChevronDown,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Margin = new MarginPadding { Right = 4 },
@@ -108,7 +108,7 @@ namespace osu.Game.Graphics.UserInterface
public CapsWarning()
{
Icon = FontAwesome.Warning;
Icon = FontAwesome.Solid.ExclamationTriangle;
}
[BackgroundDependencyLoader]
@@ -254,7 +254,7 @@ namespace osu.Game.Graphics.UserInterface
{
new SpriteIcon
{
Icon = FontAwesome.EllipsisH,
Icon = FontAwesome.Solid.EllipsisH,
Size = new Vector2(14),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
@@ -99,7 +99,7 @@ namespace osu.Game.Graphics.UserInterface
icon = new SpriteIcon
{
Size = new Vector2(14),
Icon = FontAwesome.CircleOutline,
Icon = FontAwesome.Regular.Circle,
Shadow = true,
},
},
@@ -120,12 +120,12 @@ namespace osu.Game.Graphics.UserInterface
if (selected.NewValue)
{
fadeIn();
icon.Icon = FontAwesome.CheckCircleOutline;
icon.Icon = FontAwesome.Regular.CheckCircle;
}
else
{
fadeOut();
icon.Icon = FontAwesome.CircleOutline;
icon.Icon = FontAwesome.Regular.Circle;
}
};
}
@@ -22,7 +22,7 @@ namespace osu.Game.Graphics.UserInterface
{
new SpriteIcon
{
Icon = FontAwesome.Search,
Icon = FontAwesome.Solid.Search,
Origin = Anchor.CentreRight,
Anchor = Anchor.CentreRight,
Margin = new MarginPadding { Right = 10 },
@@ -143,7 +143,7 @@ namespace osu.Game.Graphics.UserInterface
Child = Icon = new SpriteIcon
{
Size = new Vector2(star_size),
Icon = FontAwesome.Star,
Icon = FontAwesome.Solid.Star,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
@@ -9,6 +9,6 @@ namespace osu.Game.Online.API.Requests.Responses
public class APILegacyScores
{
[JsonProperty(@"scores")]
public IEnumerable<APILegacyScoreInfo> Scores;
public List<APILegacyScoreInfo> Scores;
}
}
@@ -258,8 +258,8 @@ namespace osu.Game.Online.Leaderboards
protected virtual IEnumerable<LeaderboardScoreStatistic> GetStatistics(ScoreInfo model) => new[]
{
new LeaderboardScoreStatistic(FontAwesome.Link, "Max Combo", model.MaxCombo.ToString()),
new LeaderboardScoreStatistic(FontAwesome.Crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy))
new LeaderboardScoreStatistic(FontAwesome.Solid.Link, "Max Combo", model.MaxCombo.ToString()),
new LeaderboardScoreStatistic(FontAwesome.Solid.Crosshairs, "Accuracy", string.Format(model.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", model.Accuracy))
};
protected override bool OnHover(HoverEvent e)
@@ -353,7 +353,7 @@ namespace osu.Game.Online.Leaderboards
Size = new Vector2(icon_size),
Rotation = 45,
Colour = OsuColour.FromHex(@"3087ac"),
Icon = FontAwesome.Square,
Icon = FontAwesome.Solid.Square,
Shadow = true,
},
new SpriteIcon
@@ -12,7 +12,7 @@ namespace osu.Game.Online.Leaderboards
public MessagePlaceholder(string message)
{
AddIcon(FontAwesome.ExclamationCircle, cp =>
AddIcon(FontAwesome.Solid.ExclamationCircle, cp =>
{
cp.Font = cp.Font.With(size: TEXT_SIZE);
cp.Padding = new MarginPadding { Right = 10 };
@@ -41,7 +41,7 @@ namespace osu.Game.Online.Leaderboards
Action = () => Action?.Invoke(),
Child = icon = new SpriteIcon
{
Icon = FontAwesome.Refresh,
Icon = FontAwesome.Solid.Sync,
Size = new Vector2(TEXT_SIZE),
Shadow = true,
},
@@ -18,7 +18,7 @@ namespace osu.Game.Online.Multiplayer.GameTypes
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.Refresh,
Icon = FontAwesome.Solid.Sync,
Size = new Vector2(size),
Colour = colours.Blue,
Shadow = false,
@@ -26,14 +26,14 @@ namespace osu.Game.Online.Multiplayer.GameTypes
{
new SpriteIcon
{
Icon = FontAwesome.Refresh,
Icon = FontAwesome.Solid.Sync,
Size = new Vector2(size * 0.75f),
Colour = colours.Blue,
Shadow = false,
},
new SpriteIcon
{
Icon = FontAwesome.Refresh,
Icon = FontAwesome.Solid.Sync,
Size = new Vector2(size * 0.75f),
Colour = colours.Pink,
Shadow = false,
@@ -16,7 +16,7 @@ namespace osu.Game.Online.Multiplayer.GameTypes
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.ClockOutline,
Icon = FontAwesome.Regular.Clock,
Size = new Vector2(size),
Colour = colours.Blue,
Shadow = false
+23 -17
View File
@@ -112,8 +112,8 @@ namespace osu.Game
// todo: move this to SongSelect once Screen has the ability to unsuspend.
[Cached]
[Cached(Type = typeof(IBindable<IEnumerable<Mod>>))]
private readonly Bindable<IEnumerable<Mod>> selectedMods = new Bindable<IEnumerable<Mod>>(new Mod[] { });
[Cached(typeof(IBindable<IReadOnlyList<Mod>>))]
private readonly Bindable<IReadOnlyList<Mod>> mods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
public OsuGame(string[] args = null)
{
@@ -254,6 +254,12 @@ namespace osu.Game
if (menuScreen.IsCurrentScreen())
menuScreen.LoadToSolo();
// we might even already be at the song
if (Beatmap.Value.BeatmapSetInfo.Hash == databasedSet.Hash)
{
return;
}
// Use first beatmap available for current ruleset, else switch ruleset.
var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
@@ -287,9 +293,8 @@ namespace osu.Game
performFromMainMenu(() =>
{
ruleset.Value = databasedScoreInfo.Ruleset;
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap);
Beatmap.Value.Mods.Value = databasedScoreInfo.Mods;
mods.Value = databasedScoreInfo.Mods;
menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore)));
}, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true);
@@ -393,7 +398,8 @@ namespace osu.Game
}
},
overlayContent = new Container { RelativeSizeAxes = Axes.Both },
floatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both },
rightFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both },
leftFloatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both },
topMostOverlayContent = new Container { RelativeSizeAxes = Axes.Both },
idleTracker = new GameIdleTracker(6000)
});
@@ -421,15 +427,15 @@ namespace osu.Game
},
}, topMostOverlayContent.Add);
loadComponentSingleFile(volume = new VolumeOverlay(), floatingOverlayContent.Add);
loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add);
loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
loadComponentSingleFile(loginOverlay = new LoginOverlay
loadComponentSingleFile(notifications = new NotificationOverlay
{
GetToolbarHeight = () => ToolbarOffset,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
}, floatingOverlayContent.Add);
}, rightFloatingOverlayContent.Add);
loadComponentSingleFile(screenshotManager, Add);
@@ -438,28 +444,26 @@ namespace osu.Game
loadComponentSingleFile(social = new SocialOverlay(), overlayContent.Add);
loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal);
loadComponentSingleFile(chatOverlay = new ChatOverlay(), overlayContent.Add);
loadComponentSingleFile(settings = new MainSettings { GetToolbarHeight = () => ToolbarOffset }, floatingOverlayContent.Add);
loadComponentSingleFile(settings = new MainSettings { GetToolbarHeight = () => ToolbarOffset }, leftFloatingOverlayContent.Add);
loadComponentSingleFile(userProfile = new UserProfileOverlay(), overlayContent.Add);
loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay(), overlayContent.Add);
loadComponentSingleFile(notifications = new NotificationOverlay
loadComponentSingleFile(loginOverlay = new LoginOverlay
{
GetToolbarHeight = () => ToolbarOffset,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
}, floatingOverlayContent.Add);
}, rightFloatingOverlayContent.Add);
loadComponentSingleFile(musicController = new MusicController
{
GetToolbarHeight = () => ToolbarOffset,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
}, floatingOverlayContent.Add);
}, rightFloatingOverlayContent.Add);
loadComponentSingleFile(accountCreation = new AccountCreationOverlay(), topMostOverlayContent.Add);
loadComponentSingleFile(dialogOverlay = new DialogOverlay(), topMostOverlayContent.Add);
loadComponentSingleFile(externalLinkOpener = new ExternalLinkOpener(), topMostOverlayContent.Add);
dependencies.CacheAs(idleTracker);
@@ -580,7 +584,7 @@ namespace osu.Game
{
Schedule(() => notifications.Post(new SimpleNotification
{
Icon = entry.Level == LogLevel.Important ? FontAwesome.ExclamationCircle : FontAwesome.Bomb,
Icon = entry.Level == LogLevel.Important ? FontAwesome.Solid.ExclamationCircle : FontAwesome.Solid.Bomb,
Text = entry.Message + (entry.Exception != null && IsDeployedBuild ? "\n\nThis error has been automatically reported to the devs." : string.Empty),
}));
}
@@ -588,7 +592,7 @@ namespace osu.Game
{
Schedule(() => notifications.Post(new SimpleNotification
{
Icon = FontAwesome.EllipsisH,
Icon = FontAwesome.Solid.EllipsisH,
Text = "Subsequent messages have been logged. Click to view log files.",
Activated = () =>
{
@@ -705,7 +709,9 @@ namespace osu.Game
private Container overlayContent;
private Container floatingOverlayContent;
private Container rightFloatingOverlayContent;
private Container leftFloatingOverlayContent;
private Container topMostOverlayContent;
+5 -5
View File
@@ -75,10 +75,10 @@ namespace osu.Game.Overlays.BeatmapSet
Direction = FillDirection.Horizontal,
Children = new[]
{
length = new Statistic(FontAwesome.ClockOutline, "Length") { Width = 0.25f },
bpm = new Statistic(FontAwesome.Circle, "BPM") { Width = 0.25f },
circleCount = new Statistic(FontAwesome.CircleOutline, "Circle Count") { Width = 0.25f },
sliderCount = new Statistic(FontAwesome.Circle, "Slider Count") { Width = 0.25f },
length = new Statistic(FontAwesome.Regular.Clock, "Length") { Width = 0.25f },
bpm = new Statistic(FontAwesome.Regular.Circle, "BPM") { Width = 0.25f },
circleCount = new Statistic(FontAwesome.Regular.Circle, "Circle Count") { Width = 0.25f },
sliderCount = new Statistic(FontAwesome.Regular.Circle, "Slider Count") { Width = 0.25f },
},
};
}
@@ -121,7 +121,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
Icon = FontAwesome.Square,
Icon = FontAwesome.Solid.Square,
Size = new Vector2(13),
Rotation = 45,
Colour = OsuColour.FromHex(@"441288"),
@@ -131,8 +131,8 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Top = 5 },
Children = new[]
{
plays = new Statistic(FontAwesome.PlayCircle),
favourites = new Statistic(FontAwesome.Heart),
plays = new Statistic(FontAwesome.Solid.PlayCircle),
favourites = new Statistic(FontAwesome.Solid.Heart),
},
},
},
@@ -78,7 +78,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
Depth = -1,
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Icon = FontAwesome.Download,
Icon = FontAwesome.Solid.Download,
Size = new Vector2(16),
Margin = new MarginPadding { Right = 5 },
},
@@ -48,7 +48,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = FontAwesome.HeartOutline,
Icon = FontAwesome.Regular.Heart,
Size = new Vector2(18),
Shadow = false,
},
@@ -59,12 +59,12 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
if (favourited.NewValue)
{
pink.FadeIn(200);
icon.Icon = FontAwesome.Heart;
icon.Icon = FontAwesome.Solid.Heart;
}
else
{
pink.FadeOut(200);
icon.Icon = FontAwesome.HeartOutline;
icon.Icon = FontAwesome.Regular.Heart;
}
};
@@ -1,58 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class ClickableUsername : OsuHoverContainer
{
private readonly OsuSpriteText text;
private UserProfileOverlay profile;
private User user;
public User User
{
get => user;
set
{
if (user == value) return;
user = value;
text.Text = user.Username;
}
}
public float TextSize
{
get => text.Font.Size;
set => text.Font = text.Font.With(size: value);
}
public ClickableUsername()
{
AutoSizeAxes = Axes.Both;
Child = text = new OsuSpriteText { Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true) };
}
[BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profile)
{
this.profile = profile;
}
protected override bool OnClick(ClickEvent e)
{
profile?.ShowUser(user);
return true;
}
}
}
@@ -1,141 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osuTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Users;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class DrawableScore : Container
{
private const int fade_duration = 100;
private const float side_margin = 20;
private readonly Box background;
public DrawableScore(int index, ScoreInfo score)
{
ScoreModsContainer modsContainer;
RelativeSizeAxes = Axes.X;
Height = 30;
CornerRadius = 3;
Masking = true;
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Text = $"#{index + 1}",
Font = OsuFont.GetFont(weight: FontWeight.Regular, italics: true),
Margin = new MarginPadding { Left = side_margin }
},
new DrawableFlag(score.User.Country)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(30, 20),
Margin = new MarginPadding { Left = 60 }
},
new ClickableUsername
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
User = score.User,
Margin = new MarginPadding { Left = 100 }
},
modsContainer = new ScoreModsContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Width = 0.06f,
RelativePositionAxes = Axes.X,
X = 0.42f
},
new DrawableRank(score.Rank)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(30, 20),
FillMode = FillMode.Fit,
RelativePositionAxes = Axes.X,
X = 0.55f
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Text = $@"{score.TotalScore:N0}",
Font = OsuFont.Numeric.With(fixedWidth: true),
RelativePositionAxes = Axes.X,
X = 0.75f,
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreRight,
Text = $@"{score.Accuracy:P2}",
Font = OsuFont.GetFont(weight: FontWeight.Regular, italics: true),
RelativePositionAxes = Axes.X,
X = 0.85f
},
new OsuSpriteText
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Text = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}",
Font = OsuFont.GetFont(weight: FontWeight.Regular, italics: true),
Margin = new MarginPadding { Right = side_margin }
},
};
foreach (Mod mod in score.Mods)
modsContainer.Add(new ModIcon(mod)
{
AutoSizeAxes = Axes.Both,
Scale = new Vector2(0.35f),
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray4;
}
protected override bool OnHover(HoverEvent e)
{
background.FadeIn(fade_duration, Easing.OutQuint);
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
background.FadeOut(fade_duration, Easing.OutQuint);
base.OnHoverLost(e);
}
protected override bool OnClick(ClickEvent e) => true;
}
}
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osuTK;
using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -10,230 +8,117 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Overlays.Profile.Sections.Ranks;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class DrawableTopScore : Container
public class DrawableTopScore : CompositeDrawable
{
private const float fade_duration = 100;
private const float height = 200;
private const float avatar_size = 80;
private const float margin = 10;
private Color4 backgroundIdleColour;
private Color4 backgroundHoveredColour;
private readonly Box background;
private readonly Box bottomBackground;
private readonly Box middleLine;
private readonly UpdateableAvatar avatar;
private readonly DrawableFlag flag;
private readonly ClickableUsername username;
private readonly OsuSpriteText rankText;
private readonly OsuSpriteText date;
private readonly DrawableRank rank;
private readonly InfoColumn totalScore;
private readonly InfoColumn accuracy;
private readonly InfoColumn statistics;
private readonly ScoreModsContainer modsContainer;
private ScoreInfo score;
public ScoreInfo Score
{
get => score;
set
{
if (score == value) return;
score = value;
avatar.User = username.User = score.User;
flag.Country = score.User.Country;
date.Text = $@"achieved {score.Date:MMM d, yyyy}";
rank.UpdateRank(score.Rank);
totalScore.Value = $@"{score.TotalScore:N0}";
accuracy.Value = $@"{score.Accuracy:P2}";
statistics.Value = $"{score.Statistics[HitResult.Great]}/{score.Statistics[HitResult.Good]}/{score.Statistics[HitResult.Meh]}";
modsContainer.Clear();
foreach (Mod mod in score.Mods)
modsContainer.Add(new ModIcon(mod)
{
AutoSizeAxes = Axes.Both,
Scale = new Vector2(0.45f),
});
}
}
private readonly TopScoreUserSection userSection;
private readonly TopScoreStatisticsSection statisticsSection;
public DrawableTopScore()
{
RelativeSizeAxes = Axes.X;
Height = height;
CornerRadius = 5;
BorderThickness = 4;
AutoSizeAxes = Axes.Y;
Masking = true;
Children = new Drawable[]
CornerRadius = 10;
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.2f),
Radius = 1,
Offset = new Vector2(0, 1),
};
InternalChildren = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true, //used for correct border representation
},
avatar = new UpdateableAvatar
{
Size = new Vector2(avatar_size),
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.25f),
Offset = new Vector2(0, 2),
Radius = 1,
},
Margin = new MarginPadding { Top = margin, Left = margin }
},
flag = new DrawableFlag
{
Size = new Vector2(30, 20),
Position = new Vector2(margin * 2 + avatar_size, height / 4),
},
username = new ClickableUsername
{
Origin = Anchor.BottomLeft,
TextSize = 30,
Position = new Vector2(margin * 2 + avatar_size, height / 4),
Margin = new MarginPadding { Bottom = 4 }
},
rankText = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.BottomRight,
Text = "#1",
Font = OsuFont.GetFont(size: 40, weight: FontWeight.Bold, italics: true),
Y = height / 4,
Margin = new MarginPadding { Right = margin }
},
date = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Y = height / 4,
Margin = new MarginPadding { Right = margin }
},
new Container
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.Both,
Height = 0.5f,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding(10),
Children = new Drawable[]
{
bottomBackground = new Box { RelativeSizeAxes = Axes.Both },
middleLine = new Box
new AutoSizingGrid
{
RelativeSizeAxes = Axes.X,
Height = 1,
},
rank = new DrawableRank(ScoreRank.F)
{
Origin = Anchor.BottomLeft,
Size = new Vector2(avatar_size, 40),
FillMode = FillMode.Fit,
Y = height / 4,
Margin = new MarginPadding { Left = margin }
},
new FillFlowContainer<InfoColumn>
{
Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Position = new Vector2(height / 2, height / 4),
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15, 0),
Children = new[]
Content = new[]
{
totalScore = new InfoColumn("Score"),
accuracy = new InfoColumn("Accuracy"),
statistics = new InfoColumn("300/100/50"),
new Drawable[]
{
userSection = new TopScoreUserSection
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
},
null,
statisticsSection = new TopScoreStatisticsSection
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
}
},
},
},
modsContainer = new ScoreModsContainer
{
AutoSizeAxes = Axes.Y,
Width = 80,
Position = new Vector2(height / 2, height / 4),
ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.Absolute, 20) },
RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) },
}
}
},
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = bottomBackground.Colour = colours.Gray4;
middleLine.Colour = colours.Gray2;
date.Colour = colours.Gray9;
BorderColour = rankText.Colour = colours.Yellow;
backgroundIdleColour = colours.Gray3;
backgroundHoveredColour = colours.Gray4;
background.Colour = backgroundIdleColour;
}
/// <summary>
/// Sets the score to be displayed.
/// </summary>
public ScoreInfo Score
{
set
{
userSection.Score = value;
statisticsSection.Score = value;
}
}
protected override bool OnHover(HoverEvent e)
{
background.FadeIn(fade_duration, Easing.OutQuint);
background.FadeColour(backgroundHoveredColour, fade_duration, Easing.OutQuint);
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
background.FadeOut(fade_duration, Easing.OutQuint);
background.FadeColour(backgroundIdleColour, fade_duration, Easing.OutQuint);
base.OnHoverLost(e);
}
private class InfoColumn : FillFlowContainer
private class AutoSizingGrid : GridContainer
{
private readonly OsuSpriteText headerText;
private readonly OsuSpriteText valueText;
public string Value
public AutoSizingGrid()
{
set
{
if (valueText.Text == value)
return;
valueText.Text = value;
}
get => valueText.Text;
}
public InfoColumn(string header)
{
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Vertical;
Spacing = new Vector2(0, 3);
Children = new Drawable[]
{
headerText = new OsuSpriteText
{
Text = header,
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold)
},
valueText = new OsuSpriteText { Font = OsuFont.GetFont(size: 25, weight: FontWeight.Regular, italics: true) }
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
headerText.Colour = colours.Gray9;
AutoSizeAxes = Axes.Y;
}
}
}
@@ -0,0 +1,192 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class ScoreTable : TableContainer
{
private const float horizontal_inset = 20;
private const float row_height = 25;
private const int text_size = 14;
private readonly FillFlowContainer backgroundFlow;
private Color4 highAccuracyColour;
public ScoreTable()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Padding = new MarginPadding { Horizontal = horizontal_inset };
RowSize = new Dimension(GridSizeMode.Absolute, row_height);
AddInternal(backgroundFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Depth = 1f,
Padding = new MarginPadding { Horizontal = -horizontal_inset },
Margin = new MarginPadding { Top = row_height }
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
highAccuracyColour = colours.GreenLight;
}
public IReadOnlyList<ScoreInfo> Scores
{
set
{
Content = null;
backgroundFlow.Clear();
if (value == null || !value.Any())
return;
for (int i = 0; i < value.Count; i++)
backgroundFlow.Add(new ScoreTableRowBackground(i));
Columns = createHeaders(value[0]);
Content = value.Select((s, i) => createContent(i, s)).ToArray().ToRectangular();
}
}
private TableColumn[] createHeaders(ScoreInfo score)
{
var columns = new List<TableColumn>
{
new TableColumn("rank", Anchor.CentreRight, new Dimension(GridSizeMode.AutoSize)),
new TableColumn("", Anchor.Centre, new Dimension(GridSizeMode.Absolute, 70)), // grade
new TableColumn("score", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)),
new TableColumn("accuracy", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)),
new TableColumn("player", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 150)),
new TableColumn("max combo", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 70, maxSize: 90))
};
foreach (var statistic in score.Statistics)
columns.Add(new TableColumn(statistic.Key.GetDescription(), Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 50, maxSize: 70)));
columns.AddRange(new[]
{
new TableColumn("pp", Anchor.CentreLeft, new Dimension(GridSizeMode.Distributed, minSize: 40, maxSize: 70)),
new TableColumn("mods", Anchor.CentreLeft, new Dimension(GridSizeMode.AutoSize)),
});
return columns.ToArray();
}
private Drawable[] createContent(int index, ScoreInfo score)
{
var content = new List<Drawable>
{
new OsuSpriteText
{
Text = $"#{index + 1}",
Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold)
},
new DrawableRank(score.Rank)
{
Size = new Vector2(30, 20)
},
new OsuSpriteText
{
Margin = new MarginPadding { Right = horizontal_inset },
Text = $@"{score.TotalScore:N0}",
Font = OsuFont.GetFont(size: text_size, weight: index == 0 ? FontWeight.Bold : FontWeight.Medium)
},
new OsuSpriteText
{
Margin = new MarginPadding { Right = horizontal_inset },
Text = $@"{score.Accuracy:P2}",
Font = OsuFont.GetFont(size: text_size),
Colour = score.Accuracy == 1 ? highAccuracyColour : Color4.White
},
};
var username = new LinkFlowContainer(t => t.Font = OsuFont.GetFont(size: text_size)) { AutoSizeAxes = Axes.Both };
username.AddUserLink(score.User);
content.AddRange(new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Right = horizontal_inset },
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
new DrawableFlag(score.User.Country) { Size = new Vector2(20, 13) },
username
}
},
new OsuSpriteText
{
Text = $@"{score.MaxCombo:N0}x",
Font = OsuFont.GetFont(size: text_size)
}
});
foreach (var kvp in score.Statistics)
{
content.Add(new OsuSpriteText
{
Text = $"{kvp.Value}",
Font = OsuFont.GetFont(size: text_size),
Colour = kvp.Value == 0 ? Color4.Gray : Color4.White
});
}
content.AddRange(new Drawable[]
{
new OsuSpriteText
{
Text = $@"{score.PP:N0}",
Font = OsuFont.GetFont(size: text_size)
},
new FillFlowContainer
{
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
ChildrenEnumerable = score.Mods.Select(m => new ModIcon(m)
{
AutoSizeAxes = Axes.Both,
Scale = new Vector2(0.3f)
})
},
});
return content.ToArray();
}
protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty);
private class HeaderText : OsuSpriteText
{
public HeaderText(string text)
{
Text = text.ToUpper();
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black);
}
}
}
}
@@ -0,0 +1,64 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class ScoreTableRowBackground : CompositeDrawable
{
private const int fade_duration = 100;
private readonly Box hoveredBackground;
private readonly Box background;
public ScoreTableRowBackground(int index)
{
RelativeSizeAxes = Axes.X;
Height = 25;
CornerRadius = 3;
Masking = true;
InternalChildren = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
hoveredBackground = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
},
};
if (index % 2 != 0)
background.Alpha = 0;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
hoveredBackground.Colour = colours.Gray4;
background.Colour = colours.Gray3;
}
protected override bool OnHover(HoverEvent e)
{
hoveredBackground.FadeIn(fade_duration, Easing.OutQuint);
return base.OnHover(e);
}
protected override void OnHoverLost(HoverLostEvent e)
{
hoveredBackground.FadeOut(fade_duration, Easing.OutQuint);
base.OnHoverLost(e);
}
}
}
@@ -1,38 +1,91 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osuTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osuTK;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Scoring;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class ScoresContainer : Container
public class ScoresContainer : CompositeDrawable
{
private const int spacing = 15;
private const int fade_duration = 200;
private readonly FillFlowContainer flow;
private readonly Box background;
private readonly ScoreTable scoreTable;
private readonly DrawableTopScore topScore;
private readonly LoadingAnimation loadingAnimation;
[Resolved]
private IAPIProvider api { get; set; }
public ScoresContainer()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChildren = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.95f,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, spacing),
Margin = new MarginPadding { Vertical = spacing },
Children = new Drawable[]
{
topScore = new DrawableTopScore(),
scoreTable = new ScoreTable
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
}
}
},
loadingAnimation = new LoadingAnimation
{
Alpha = 0,
Margin = new MarginPadding(20)
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
background.Colour = colours.Gray2;
updateDisplay();
}
private bool loading
{
set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration);
}
private IEnumerable<ScoreInfo> scores;
private BeatmapInfo beatmap;
private GetScoresRequest getScoresRequest;
private IReadOnlyList<ScoreInfo> scores;
public IEnumerable<ScoreInfo> Scores
public IReadOnlyList<ScoreInfo> Scores
{
get => scores;
set
@@ -44,8 +97,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
}
}
private GetScoresRequest getScoresRequest;
private IAPIProvider api;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
@@ -71,68 +123,15 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
loading = false;
var scoreCount = scores?.Count() ?? 0;
scoreTable.Scores = scores?.Count > 1 ? scores : new List<ScoreInfo>();
if (scoreCount == 0)
if (scores?.Any() == true)
{
topScore.Hide();
flow.Clear();
return;
topScore.Score = scores.FirstOrDefault();
topScore.Show();
}
topScore.Score = scores.FirstOrDefault();
topScore.Show();
flow.Clear();
if (scoreCount < 2)
return;
for (int i = 1; i < scoreCount; i++)
flow.Add(new DrawableScore(i, scores.ElementAt(i)));
}
public ScoresContainer()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.95f,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, spacing),
Margin = new MarginPadding { Vertical = spacing },
Children = new Drawable[]
{
topScore = new DrawableTopScore(),
flow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 1),
},
}
},
loadingAnimation = new LoadingAnimation
{
Alpha = 0,
Margin = new MarginPadding(20)
},
};
}
[BackgroundDependencyLoader]
private void load(IAPIProvider api)
{
this.api = api;
updateDisplay();
else
topScore.Hide();
}
protected override void Dispose(bool isDisposing)
@@ -0,0 +1,204 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osuTK;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class TopScoreStatisticsSection : CompositeDrawable
{
private const float margin = 10;
private readonly FontUsage smallFont = OsuFont.GetFont(size: 20);
private readonly FontUsage largeFont = OsuFont.GetFont(size: 25);
private readonly TextColumn totalScoreColumn;
private readonly TextColumn accuracyColumn;
private readonly TextColumn maxComboColumn;
private readonly TextColumn ppColumn;
private readonly FillFlowContainer<InfoColumn> statisticsColumns;
private readonly ModsInfoColumn modsColumn;
public TopScoreStatisticsSection()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(margin, 0),
Children = new Drawable[]
{
statisticsColumns = new FillFlowContainer<InfoColumn>
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(margin, 0),
},
ppColumn = new TextColumn("pp", smallFont),
modsColumn = new ModsInfoColumn(),
}
},
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(margin, 0),
Children = new Drawable[]
{
totalScoreColumn = new TextColumn("total score", largeFont),
accuracyColumn = new TextColumn("accuracy", largeFont),
maxComboColumn = new TextColumn("max combo", largeFont)
}
},
}
};
}
/// <summary>
/// Sets the score to be displayed.
/// </summary>
public ScoreInfo Score
{
set
{
totalScoreColumn.Text = $@"{value.TotalScore:N0}";
accuracyColumn.Text = $@"{value.Accuracy:P2}";
maxComboColumn.Text = $@"{value.MaxCombo:N0}x";
ppColumn.Text = $@"{value.PP:N0}";
statisticsColumns.ChildrenEnumerable = value.Statistics.Select(kvp => createStatisticsColumn(kvp.Key, kvp.Value));
modsColumn.Mods = value.Mods;
}
}
private TextColumn createStatisticsColumn(HitResult hitResult, int count) => new TextColumn(hitResult.GetDescription(), smallFont)
{
Text = count.ToString()
};
private class InfoColumn : CompositeDrawable
{
private readonly Box separator;
public InfoColumn(string title, Drawable content)
{
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 2),
Children = new[]
{
new OsuSpriteText
{
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black),
Text = title.ToUpper()
},
separator = new Box
{
RelativeSizeAxes = Axes.X,
Height = 2
},
content
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
separator.Colour = colours.Gray5;
}
}
private class TextColumn : InfoColumn
{
private readonly SpriteText text;
public TextColumn(string title, FontUsage font)
: this(title, new OsuSpriteText { Font = font })
{
}
private TextColumn(string title, SpriteText text)
: base(title, text)
{
this.text = text;
}
public LocalisedString Text
{
set => text.Text = value;
}
}
private class ModsInfoColumn : InfoColumn
{
private readonly FillFlowContainer modsContainer;
public ModsInfoColumn()
: this(new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal
})
{
}
private ModsInfoColumn(FillFlowContainer modsContainer)
: base("mods", modsContainer)
{
this.modsContainer = modsContainer;
}
public IEnumerable<Mod> Mods
{
set
{
modsContainer.Clear();
foreach (Mod mod in value)
{
modsContainer.Add(new ModIcon(mod)
{
AutoSizeAxes = Axes.Both,
Scale = new Vector2(0.3f),
});
}
}
}
}
}
}
@@ -0,0 +1,127 @@
// 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 Humanizer;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Scoring;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
public class TopScoreUserSection : CompositeDrawable
{
private readonly SpriteText rankText;
private readonly DrawableRank rank;
private readonly UpdateableAvatar avatar;
private readonly LinkFlowContainer usernameText;
private readonly SpriteText date;
private readonly DrawableFlag flag;
public TopScoreUserSection()
{
AutoSizeAxes = Axes.Both;
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
rankText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "#1",
Font = OsuFont.GetFont(size: 30, weight: FontWeight.Bold, italics: true)
},
rank = new DrawableRank(ScoreRank.F)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(40),
FillMode = FillMode.Fit,
},
avatar = new UpdateableAvatar
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(80),
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.25f),
Offset = new Vector2(0, 2),
Radius = 1,
},
},
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 3),
Children = new Drawable[]
{
usernameText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold, italics: true))
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
},
date = new SpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold)
},
flag = new DrawableFlag
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(20, 13),
},
}
}
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
rankText.Colour = colours.Yellow;
}
/// <summary>
/// Sets the score to be displayed.
/// </summary>
public ScoreInfo Score
{
set
{
avatar.User = value.User;
flag.Country = value.User.Country;
date.Text = $@"achieved {value.Date.Humanize()}";
usernameText.Clear();
usernameText.AddUserLink(value.User);
rank.UpdateRank(value.Rank);
}
}
}
}

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