Merge branch 'master' into skillsrework

This commit is contained in:
Dean Herbert
2021-09-17 16:58:50 +09:00
177 changed files with 2188 additions and 922 deletions

View File

@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(GameHost host, OsuGameBase gameBase) private void load(GameHost host, OsuGameBase gameBase)
{ {
OsuGame game = new OsuGame();
game.SetHost(host);
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new Box
@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4.Black, Colour = Color4.Black,
}, },
game
}; };
AddGame(new OsuGame());
} }
} }
} }

View File

@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.Pippidon.Tests
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(GameHost host, OsuGameBase gameBase) private void load(GameHost host, OsuGameBase gameBase)
{ {
OsuGame game = new OsuGame();
game.SetHost(host);
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new Box
@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.Pippidon.Tests
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4.Black, Colour = Color4.Black,
}, },
game
}; };
AddGame(new OsuGame());
} }
} }
} }

View File

@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(GameHost host, OsuGameBase gameBase) private void load(GameHost host, OsuGameBase gameBase)
{ {
OsuGame game = new OsuGame();
game.SetHost(host);
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new Box
@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4.Black, Colour = Color4.Black,
}, },
game
}; };
AddGame(new OsuGame());
} }
} }
} }

View File

@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.Pippidon.Tests
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(GameHost host, OsuGameBase gameBase) private void load(GameHost host, OsuGameBase gameBase)
{ {
OsuGame game = new OsuGame();
game.SetHost(host);
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new Box
@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.Pippidon.Tests
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4.Black, Colour = Color4.Black,
}, },
game
}; };
AddGame(new OsuGame());
} }
} }
} }

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osuTK; using osuTK;
@ -61,9 +62,9 @@ namespace osu.Game.Rulesets.Pippidon.UI
} }
} }
public bool OnPressed(PippidonAction action) public bool OnPressed(KeyBindingPressEvent<PippidonAction> e)
{ {
switch (action) switch (e.Action)
{ {
case PippidonAction.MoveUp: case PippidonAction.MoveUp:
changeLane(-1); changeLane(-1);
@ -78,7 +79,7 @@ namespace osu.Game.Rulesets.Pippidon.UI
} }
} }
public void OnReleased(PippidonAction action) public void OnReleased(KeyBindingReleaseEvent<PippidonAction> e)
{ {
} }

View File

@ -51,11 +51,11 @@
<Reference Include="Java.Interop" /> <Reference Include="Java.Interop" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.907.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2021.913.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.907.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2021.916.1" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Transitive Dependencies"> <ItemGroup Label="Transitive Dependencies">
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. --> <!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
<PackageReference Include="Realm" Version="10.3.0" /> <PackageReference Include="Realm" Version="10.5.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -44,9 +44,9 @@ namespace osu.Game.Rulesets.Catch.Mods
} }
// disable keyboard controls // disable keyboard controls
public bool OnPressed(CatchAction action) => true; public bool OnPressed(KeyBindingPressEvent<CatchAction> e) => true;
public void OnReleased(CatchAction action) public void OnReleased(KeyBindingReleaseEvent<CatchAction> e)
{ {
} }

View File

@ -5,6 +5,7 @@ using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Replays;
@ -144,9 +145,9 @@ namespace osu.Game.Rulesets.Catch.UI
Catcher.VisualDirection = Direction.Left; Catcher.VisualDirection = Direction.Left;
} }
public bool OnPressed(CatchAction action) public bool OnPressed(KeyBindingPressEvent<CatchAction> e)
{ {
switch (action) switch (e.Action)
{ {
case CatchAction.MoveLeft: case CatchAction.MoveLeft:
currentDirection--; currentDirection--;
@ -164,9 +165,9 @@ namespace osu.Game.Rulesets.Catch.UI
return false; return false;
} }
public void OnReleased(CatchAction action) public void OnReleased(KeyBindingReleaseEvent<CatchAction> e)
{ {
switch (action) switch (e.Action)
{ {
case CatchAction.MoveLeft: case CatchAction.MoveLeft:
currentDirection++; currentDirection++;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{ {
c.Add(CreateHitObject().With(h => c.Add(CreateHitObject().With(h =>
{ {
h.HitObject.StartTime = START_TIME; h.HitObject.StartTime = Time.Current + 5000;
h.AccentColour.Value = Color4.Orange; h.AccentColour.Value = Color4.Orange;
})); }));
}) })
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{ {
c.Add(CreateHitObject().With(h => c.Add(CreateHitObject().With(h =>
{ {
h.HitObject.StartTime = START_TIME; h.HitObject.StartTime = Time.Current + 5000;
h.AccentColour.Value = Color4.Orange; h.AccentColour.Value = Color4.Orange;
})); }));
}) })

View File

@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
/// </summary> /// </summary>
public abstract class ManiaSkinnableTestScene : SkinnableTestScene public abstract class ManiaSkinnableTestScene : SkinnableTestScene
{ {
protected const double START_TIME = 1000000000;
[Cached(Type = typeof(IScrollingInfo))] [Cached(Type = typeof(IScrollingInfo))]
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo(); private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
@ -55,27 +53,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>(); public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction; IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
IBindable<double> IScrollingInfo.TimeRange { get; } = new Bindable<double>(1000); IBindable<double> IScrollingInfo.TimeRange { get; } = new Bindable<double>(5000);
IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ZeroScrollAlgorithm(); IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ConstantScrollAlgorithm();
}
private class ZeroScrollAlgorithm : IScrollAlgorithm
{
public double GetDisplayStartTime(double originTime, float offset, double timeRange, float scrollLength)
=> double.MinValue;
public float GetLength(double startTime, double endTime, double timeRange, float scrollLength)
=> scrollLength;
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength)
=> (float)((time - START_TIME) / timeRange) * scrollLength;
public double TimeAt(float position, double currentTime, double timeRange, float scrollLength)
=> 0;
public void Reset()
{
}
} }
} }
} }

View File

@ -3,6 +3,7 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Mania.Tests
AddStep("Hold key", () => AddStep("Hold key", () =>
{ {
clock.CurrentTime = 0; clock.CurrentTime = 0;
note.OnPressed(ManiaAction.Key1); note.OnPressed(new KeyBindingPressEvent<ManiaAction>(GetContainingInputManager().CurrentState, ManiaAction.Key1));
}); });
AddStep("progress time", () => clock.CurrentTime = 500); AddStep("progress time", () => clock.CurrentTime = 500);
AddAssert("head is visible", () => note.Head.Alpha == 1); AddAssert("head is visible", () => note.Head.Alpha == 1);

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -13,6 +14,10 @@ using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Configuration;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI;
namespace osu.Game.Rulesets.Mania.Tests namespace osu.Game.Rulesets.Mania.Tests
{ {
@ -22,14 +27,65 @@ namespace osu.Game.Rulesets.Mania.Tests
[Resolved] [Resolved]
private RulesetConfigCache configCache { get; set; } private RulesetConfigCache configCache { get; set; }
private readonly Bindable<bool> configTimingBasedNoteColouring = new Bindable<bool>(); private Bindable<bool> configTimingBasedNoteColouring;
protected override void LoadComplete() private ManualClock clock;
private DrawableManiaRuleset drawableRuleset;
[SetUpSteps]
public void SetUpSteps()
{
AddStep("setup hierarchy", () => Child = new Container
{
Clock = new FramedClock(clock = new ManualClock()),
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new[]
{
drawableRuleset = (DrawableManiaRuleset)Ruleset.Value.CreateInstance().CreateDrawableRulesetWith(createTestBeatmap())
}
});
AddStep("retrieve config bindable", () =>
{
var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
configTimingBasedNoteColouring = config.GetBindable<bool>(ManiaRulesetSetting.TimingBasedNoteColouring);
});
}
[Test]
public void TestSimple()
{
AddStep("enable", () => configTimingBasedNoteColouring.Value = true);
AddStep("disable", () => configTimingBasedNoteColouring.Value = false);
}
[Test]
public void TestToggleOffScreen()
{
AddStep("enable", () => configTimingBasedNoteColouring.Value = true);
seekTo(10000);
AddStep("disable", () => configTimingBasedNoteColouring.Value = false);
seekTo(0);
AddAssert("all notes not coloured", () => this.ChildrenOfType<DrawableNote>().All(note => note.Colour == Colour4.White));
seekTo(10000);
AddStep("enable again", () => configTimingBasedNoteColouring.Value = true);
seekTo(0);
AddAssert("some notes coloured", () => this.ChildrenOfType<DrawableNote>().Any(note => note.Colour != Colour4.White));
}
private void seekTo(double time)
{
AddStep($"seek to {time}", () => clock.CurrentTime = time);
AddUntilStep("wait for seek", () => Precision.AlmostEquals(drawableRuleset.FrameStableClock.CurrentTime, time, 1));
}
private ManiaBeatmap createTestBeatmap()
{ {
const double beat_length = 500; const double beat_length = 500;
var ruleset = new ManiaRuleset();
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 }) var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 })
{ {
HitObjects = HitObjects =
@ -45,7 +101,7 @@ namespace osu.Game.Rulesets.Mania.Tests
new Note { StartTime = beat_length } new Note { StartTime = beat_length }
}, },
ControlPointInfo = new ControlPointInfo(), ControlPointInfo = new ControlPointInfo(),
BeatmapInfo = { Ruleset = ruleset.RulesetInfo }, BeatmapInfo = { Ruleset = Ruleset.Value },
}; };
foreach (var note in beatmap.HitObjects) foreach (var note in beatmap.HitObjects)
@ -57,24 +113,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{ {
BeatLength = beat_length BeatLength = beat_length
}); });
return beatmap;
Child = new Container
{
Clock = new FramedClock(new ManualClock()),
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new[]
{
ruleset.CreateDrawableRulesetWith(beatmap)
}
};
var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
config.BindWith(ManiaRulesetSetting.TimingBasedNoteColouring, configTimingBasedNoteColouring);
AddStep("Enable", () => configTimingBasedNoteColouring.Value = true);
AddStep("Disable", () => configTimingBasedNoteColouring.Value = false);
} }
} }
} }

View File

@ -7,6 +7,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Mania.Skinning.Default; using osu.Game.Rulesets.Mania.Skinning.Default;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
@ -253,12 +254,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
HoldBrokenTime = Time.Current; HoldBrokenTime = Time.Current;
} }
public bool OnPressed(ManiaAction action) public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
{ {
if (AllJudged) if (AllJudged)
return false; return false;
if (action != Action.Value) if (e.Action != Action.Value)
return false; return false;
// do not run any of this logic when rewinding, as it inverts order of presses/releases. // do not run any of this logic when rewinding, as it inverts order of presses/releases.
@ -288,12 +289,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
isHitting.Value = true; isHitting.Value = true;
} }
public void OnReleased(ManiaAction action) public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{ {
if (AllJudged) if (AllJudged)
return; return;
if (action != Action.Value) if (e.Action != Action.Value)
return; return;
// do not run any of this logic when rewinding, as it inverts order of presses/releases. // do not run any of this logic when rewinding, as it inverts order of presses/releases.

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.Objects.Drawables namespace osu.Game.Rulesets.Mania.Objects.Drawables
@ -43,9 +44,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
// it will be hidden along with its parenting hold note when required. // it will be hidden along with its parenting hold note when required.
} }
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note public override bool OnPressed(KeyBindingPressEvent<ManiaAction> e) => false; // Handled by the hold note
public override void OnReleased(ManiaAction action) public override void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{ {
} }
} }

View File

@ -3,6 +3,7 @@
using System.Diagnostics; using System.Diagnostics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects.Drawables namespace osu.Game.Rulesets.Mania.Objects.Drawables
@ -68,9 +69,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}); });
} }
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note public override bool OnPressed(KeyBindingPressEvent<ManiaAction> e) => false; // Handled by the hold note
public override void OnReleased(ManiaAction action) public override void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{ {
} }
} }

View File

@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Configuration;
@ -66,6 +67,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
StartTimeBindable.BindValueChanged(_ => updateSnapColour(), true); StartTimeBindable.BindValueChanged(_ => updateSnapColour(), true);
} }
protected override void OnApply()
{
base.OnApply();
updateSnapColour();
}
protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e) protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
{ {
base.OnDirectionChanged(e); base.OnDirectionChanged(e);
@ -91,9 +98,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
ApplyResult(r => r.Type = result); ApplyResult(r => r.Type = result);
} }
public virtual bool OnPressed(ManiaAction action) public virtual bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
{ {
if (action != Action.Value) if (e.Action != Action.Value)
return false; return false;
if (CheckHittable?.Invoke(this, Time.Current) == false) if (CheckHittable?.Invoke(this, Time.Current) == false)
@ -102,7 +109,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return UpdateResult(true); return UpdateResult(true);
} }
public virtual void OnReleased(ManiaAction action) public virtual void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{ {
} }

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK; using osuTK;
@ -76,9 +77,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
} }
} }
public bool OnPressed(ManiaAction action) public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
{ {
if (action == Column.Action.Value) if (e.Action == Column.Action.Value)
{ {
light.FadeIn(); light.FadeIn();
light.ScaleTo(Vector2.One); light.ScaleTo(Vector2.One);
@ -87,12 +88,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
return false; return false;
} }
public void OnReleased(ManiaAction action) public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{ {
// Todo: Should be 400 * 100 / CurrentBPM // Todo: Should be 400 * 100 / CurrentBPM
const double animation_length = 250; const double animation_length = 250;
if (action == Column.Action.Value) if (e.Action == Column.Action.Value)
{ {
light.FadeTo(0, animation_length); light.FadeTo(0, animation_length);
light.ScaleTo(new Vector2(1, 0), animation_length); light.ScaleTo(new Vector2(1, 0), animation_length);

View File

@ -1,18 +1,18 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mania.Skinning.Legacy namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
public class LegacyHoldNoteHeadPiece : LegacyNotePiece public class LegacyHoldNoteHeadPiece : LegacyNotePiece
{ {
protected override Texture GetTexture(ISkinSource skin) protected override Drawable GetAnimation(ISkinSource skin)
{ {
// TODO: Should fallback to the head from default legacy skin instead of note. // TODO: Should fallback to the head from default legacy skin instead of note.
return GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage) return GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage); ?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
} }
} }
} }

View File

@ -2,7 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -18,12 +18,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
: new ValueChangedEvent<ScrollingDirection>(ScrollingDirection.Up, ScrollingDirection.Up)); : new ValueChangedEvent<ScrollingDirection>(ScrollingDirection.Up, ScrollingDirection.Up));
} }
protected override Texture GetTexture(ISkinSource skin) protected override Drawable GetAnimation(ISkinSource skin)
{ {
// TODO: Should fallback to the head from default legacy skin instead of note. // TODO: Should fallback to the head from default legacy skin instead of note.
return GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteTailImage) return GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteTailImage)
?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage) ?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage); ?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
} }
} }
} }

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -86,9 +87,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
} }
} }
public bool OnPressed(ManiaAction action) public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
{ {
if (action == column.Action.Value) if (e.Action == column.Action.Value)
{ {
upSprite.FadeTo(0); upSprite.FadeTo(0);
downSprite.FadeTo(1); downSprite.FadeTo(1);
@ -97,9 +98,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
return false; return false;
} }
public void OnReleased(ManiaAction action) public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{ {
if (action == column.Action.Value) if (e.Action == column.Action.Value)
{ {
upSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(1); upSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(1);
downSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(0); downSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(0);

View File

@ -1,9 +1,11 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.OpenGL.Textures;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
@ -19,7 +21,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>(); private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
private Container directionContainer; private Container directionContainer;
private Sprite noteSprite;
[CanBeNull]
private Drawable noteAnimation;
private float? minimumColumnWidth; private float? minimumColumnWidth;
@ -39,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
Origin = Anchor.BottomCentre, Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Child = noteSprite = new Sprite { Texture = GetTexture(skin) } Child = noteAnimation = GetAnimation(skin) ?? Empty()
}; };
direction.BindTo(scrollingInfo.Direction); direction.BindTo(scrollingInfo.Direction);
@ -50,12 +54,18 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
{ {
base.Update(); base.Update();
if (noteSprite.Texture != null) Texture texture = null;
if (noteAnimation is Sprite sprite)
texture = sprite.Texture;
else if (noteAnimation is TextureAnimation textureAnimation && textureAnimation.FrameCount > 0)
texture = textureAnimation.CurrentFrame;
if (texture != null)
{ {
// The height is scaled to the minimum column width, if provided. // The height is scaled to the minimum column width, if provided.
float minimumWidth = minimumColumnWidth ?? DrawWidth; float minimumWidth = minimumColumnWidth ?? DrawWidth;
noteAnimation.Scale = Vector2.Divide(new Vector2(DrawWidth, minimumWidth), texture.DisplayWidth);
noteSprite.Scale = Vector2.Divide(new Vector2(DrawWidth, minimumWidth), noteSprite.Texture.DisplayWidth);
} }
} }
@ -73,9 +83,11 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
} }
} }
protected virtual Texture GetTexture(ISkinSource skin) => GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage); [CanBeNull]
protected virtual Drawable GetAnimation(ISkinSource skin) => GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
protected Texture GetTextureFromLookup(ISkin skin, LegacyManiaSkinConfigurationLookups lookup) [CanBeNull]
protected Drawable GetAnimationFromLookup(ISkin skin, LegacyManiaSkinConfigurationLookups lookup)
{ {
string suffix = string.Empty; string suffix = string.Empty;
@ -93,7 +105,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
string noteImage = GetColumnSkinConfig<string>(skin, lookup)?.Value string noteImage = GetColumnSkinConfig<string>(skin, lookup)?.Value
?? $"mania-note{FallbackColumnIndex}{suffix}"; ?? $"mania-note{FallbackColumnIndex}{suffix}";
return skin.GetTexture(noteImage, WrapMode.ClampToEdge, WrapMode.ClampToEdge); return skin.GetAnimation(noteImage, WrapMode.ClampToEdge, WrapMode.ClampToEdge, true, true);
} }
} }
} }

View File

@ -10,6 +10,7 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Pooling;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.Mania.UI.Components;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
@ -122,16 +123,16 @@ namespace osu.Game.Rulesets.Mania.UI
HitObjectArea.Explosions.Add(hitExplosionPool.Get(e => e.Apply(result))); HitObjectArea.Explosions.Add(hitExplosionPool.Get(e => e.Apply(result)));
} }
public bool OnPressed(ManiaAction action) public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
{ {
if (action != Action.Value) if (e.Action != Action.Value)
return false; return false;
sampleTriggerSource.Play(); sampleTriggerSource.Play();
return true; return true;
} }
public void OnReleased(ManiaAction action) public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{ {
} }

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osuTK.Graphics; using osuTK.Graphics;
@ -91,16 +92,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components
direction.Value == ScrollingDirection.Up ? dimPoint : brightPoint); direction.Value == ScrollingDirection.Up ? dimPoint : brightPoint);
} }
public bool OnPressed(ManiaAction action) public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
{ {
if (action == this.action.Value) if (e.Action == action.Value)
backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint); backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
return false; return false;
} }
public void OnReleased(ManiaAction action) public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{ {
if (action == this.action.Value) if (e.Action == action.Value)
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint); backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
} }
} }

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osuTK.Graphics; using osuTK.Graphics;
@ -74,16 +75,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components
} }
} }
public bool OnPressed(ManiaAction action) public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
{ {
if (action == column.Action.Value) if (e.Action == column.Action.Value)
backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint); backgroundOverlay.FadeTo(1, 50, Easing.OutQuint).Then().FadeTo(0.5f, 250, Easing.OutQuint);
return false; return false;
} }
public void OnReleased(ManiaAction action) public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{ {
if (action == column.Action.Value) if (e.Action == column.Action.Value)
backgroundOverlay.FadeTo(0, 250, Easing.OutQuint); backgroundOverlay.FadeTo(0, 250, Easing.OutQuint);
} }
} }

View File

@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -101,16 +102,16 @@ namespace osu.Game.Rulesets.Mania.UI.Components
} }
} }
public bool OnPressed(ManiaAction action) public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
{ {
if (action == column.Action.Value) if (e.Action == column.Action.Value)
keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint).Then().ScaleTo(1.3f, 250, Easing.OutQuint); keyIcon.ScaleTo(1.4f, 50, Easing.OutQuint).Then().ScaleTo(1.3f, 250, Easing.OutQuint);
return false; return false;
} }
public void OnReleased(ManiaAction action) public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
{ {
if (action == column.Action.Value) if (e.Action == column.Action.Value)
keyIcon.ScaleTo(1f, 125, Easing.OutQuint); keyIcon.ScaleTo(1f, 125, Easing.OutQuint);
} }
} }

View File

@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
[TestCase(6.6915334809485199d, "diffcalc-test")] [TestCase(6.5942751255134722d, "diffcalc-test")]
[TestCase(1.0366129190339499d, "zero-length-sliders")] [TestCase(1.0421872184658874d, "zero-length-sliders")]
public void Test(double expected, string name) public void Test(double expected, string name)
=> base.Test(expected, name); => base.Test(expected, name);
[TestCase(8.3966904501824473d, "diffcalc-test")] [TestCase(8.2925466073736125d, "diffcalc-test")]
[TestCase(1.2732783892964523d, "zero-length-sliders")] [TestCase(1.2735460729578572d, "zero-length-sliders")]
public void TestClockRateAdjusted(double expected, string name) public void TestClockRateAdjusted(double expected, string name)
=> Test(expected, name, new OsuModDoubleTime()); => Test(expected, name, new OsuModDoubleTime());

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics.OpenGL.Textures;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Framework.Testing.Input; using osu.Framework.Testing.Input;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Audio; using osu.Game.Audio;
@ -143,9 +144,9 @@ namespace osu.Game.Rulesets.Osu.Tests
pressed = value; pressed = value;
if (value) if (value)
OnPressed(OsuAction.LeftButton); OnPressed(new KeyBindingPressEvent<OsuAction>(GetContainingInputManager().CurrentState, OsuAction.LeftButton));
else else
OnReleased(OsuAction.LeftButton); OnReleased(new KeyBindingReleaseEvent<OsuAction>(GetContainingInputManager().CurrentState, OsuAction.LeftButton));
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
@ -97,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private void scheduleHit() => AddStep("schedule action", () => private void scheduleHit() => AddStep("schedule action", () =>
{ {
var delay = hitCircle.StartTime - hitCircle.HitWindows.WindowFor(HitResult.Great) - Time.Current; var delay = hitCircle.StartTime - hitCircle.HitWindows.WindowFor(HitResult.Great) - Time.Current;
Scheduler.AddDelayed(() => hitAreaReceptor.OnPressed(OsuAction.LeftButton), delay); Scheduler.AddDelayed(() => hitAreaReceptor.OnPressed(new KeyBindingPressEvent<OsuAction>(GetContainingInputManager().CurrentState, OsuAction.LeftButton)), delay);
}); });
} }
} }

View File

@ -20,6 +20,7 @@ using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Storyboards; using osu.Game.Storyboards;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
@ -86,9 +87,9 @@ namespace osu.Game.Rulesets.Osu.Tests
if (firstObject == null) if (firstObject == null)
return false; return false;
var skinnable = firstObject.ApproachCircle.Child as SkinnableDrawable; var skinnable = firstObject.ApproachCircle;
if (skin == null && skinnable?.Drawable is Sprite) if (skin == null && skinnable?.Drawable is DefaultApproachCircle)
// check for default skin provider // check for default skin provider
return true; return true;

View File

@ -34,7 +34,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000;
double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000;
double basePerformance = Math.Pow(Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1), 1 / 1.1);
double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0;
HitWindows hitWindows = new OsuHitWindows(); HitWindows hitWindows = new OsuHitWindows();
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);

View File

@ -127,9 +127,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
return false; return false;
} }
public bool OnPressed(PlatformAction action) public bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
{ {
switch (action) switch (e.Action)
{ {
case PlatformAction.Delete: case PlatformAction.Delete:
return DeleteSelected(); return DeleteSelected();
@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
return false; return false;
} }
public void OnReleased(PlatformAction action) public void OnReleased(KeyBindingReleaseEvent<PlatformAction> e)
{ {
} }

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Judgements;
@ -25,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public OsuAction? HitAction => HitArea.HitAction; public OsuAction? HitAction => HitArea.HitAction;
protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle; protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle;
public ApproachCircle ApproachCircle { get; private set; } public SkinnableDrawable ApproachCircle { get; private set; }
public HitReceptor HitArea { get; private set; } public HitReceptor HitArea { get; private set; }
public SkinnableDrawable CirclePiece { get; private set; } public SkinnableDrawable CirclePiece { get; private set; }
@ -74,8 +75,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
}, },
ApproachCircle = new ApproachCircle ApproachCircle = new ProxyableSkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.ApproachCircle), _ => new DefaultApproachCircle())
{ {
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0, Alpha = 0,
Scale = new Vector2(4), Scale = new Vector2(4),
} }
@ -88,7 +92,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue)); ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue));
AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue);
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -228,15 +231,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
CornerExponent = 2; CornerExponent = 2;
} }
public bool OnPressed(OsuAction action) public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
{ {
switch (action) switch (e.Action)
{ {
case OsuAction.LeftButton: case OsuAction.LeftButton:
case OsuAction.RightButton: case OsuAction.RightButton:
if (IsHovered && (Hit?.Invoke() ?? false)) if (IsHovered && (Hit?.Invoke() ?? false))
{ {
HitAction = action; HitAction = e.Action;
return true; return true;
} }
@ -246,7 +249,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return false; return false;
} }
public void OnReleased(OsuAction action) public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
{
}
}
private class ProxyableSkinnableDrawable : SkinnableDrawable
{
public override bool RemoveWhenNotAlive => false;
public ProxyableSkinnableDrawable(ISkinComponent component, Func<ISkinComponent, Drawable> defaultImplementation = null, ConfineMode confineMode = ConfineMode.NoScaling)
: base(component, defaultImplementation, confineMode)
{ {
} }
} }

View File

@ -18,5 +18,6 @@ namespace osu.Game.Rulesets.Osu
SliderBall, SliderBall,
SliderBody, SliderBody,
SpinnerBody, SpinnerBody,
ApproachCircle,
} }
} }

View File

@ -1,50 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class ApproachCircle : Container
{
public override bool RemoveWhenNotAlive => false;
public ApproachCircle()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
Child = new SkinnableApproachCircle();
}
private class SkinnableApproachCircle : SkinnableSprite
{
public SkinnableApproachCircle()
: base("Gameplay/osu/approachcircle")
{
}
protected override Drawable CreateDefault(ISkinComponent component)
{
var drawable = base.CreateDefault(component);
// account for the sprite being used for the default approach circle being taken from stable,
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.
drawable.Scale = new Vector2(128 / 118f);
return drawable;
}
}
}
}

View File

@ -0,0 +1,49 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Skinning.Default
{
public class DefaultApproachCircle : SkinnableSprite
{
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
[Resolved]
private DrawableHitObject drawableObject { get; set; }
public DefaultApproachCircle()
: base("Gameplay/osu/approachcircle")
{
}
[BackgroundDependencyLoader]
private void load()
{
accentColour.BindTo(drawableObject.AccentColour);
}
protected override void LoadComplete()
{
base.LoadComplete();
accentColour.BindValueChanged(colour => Colour = colour.NewValue, true);
}
protected override Drawable CreateDefault(ISkinComponent component)
{
var drawable = base.CreateDefault(component);
// Although this is a non-legacy component, osu-resources currently stores approach circle as a legacy-like texture.
// See LegacyApproachCircle for documentation as to why this is required.
drawable.Scale = new Vector2(128 / 118f);
return drawable;
}
}
}

View File

@ -0,0 +1,49 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
public class LegacyApproachCircle : SkinnableSprite
{
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
[Resolved]
private DrawableHitObject drawableObject { get; set; }
public LegacyApproachCircle()
: base("Gameplay/osu/approachcircle")
{
}
[BackgroundDependencyLoader]
private void load()
{
accentColour.BindTo(drawableObject.AccentColour);
}
protected override void LoadComplete()
{
base.LoadComplete();
accentColour.BindValueChanged(colour => Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
}
protected override Drawable CreateDefault(ISkinComponent component)
{
var drawable = base.CreateDefault(component);
// account for the sprite being used for the default approach circle being taken from stable,
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.
drawable.Scale = new Vector2(128 / 118f);
return drawable;
}
}
}

View File

@ -108,6 +108,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
return new LegacyOldStyleSpinner(); return new LegacyOldStyleSpinner();
return null; return null;
case OsuSkinComponents.ApproachCircle:
return new LegacyApproachCircle();
} }
} }

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Configuration;
@ -115,9 +116,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
(ActiveCursor as OsuCursor)?.Contract(); (ActiveCursor as OsuCursor)?.Contract();
} }
public bool OnPressed(OsuAction action) public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
{ {
switch (action) switch (e.Action)
{ {
case OsuAction.LeftButton: case OsuAction.LeftButton:
case OsuAction.RightButton: case OsuAction.RightButton:
@ -129,9 +130,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
return false; return false;
} }
public void OnReleased(OsuAction action) public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
{ {
switch (action) switch (e.Action)
{ {
case OsuAction.LeftButton: case OsuAction.LeftButton:
case OsuAction.RightButton: case OsuAction.RightButton:

View File

@ -89,9 +89,9 @@ namespace osu.Game.Rulesets.Osu.UI
base.OnHoverLost(e); base.OnHoverLost(e);
} }
public bool OnPressed(OsuAction action) public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
{ {
switch (action) switch (e.Action)
{ {
case OsuAction.LeftButton: case OsuAction.LeftButton:
case OsuAction.RightButton: case OsuAction.RightButton:
@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Osu.UI
return false; return false;
} }
public void OnReleased(OsuAction action) public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
{ {
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Input.Events;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -37,6 +38,6 @@ namespace osu.Game.Rulesets.Taiko.Tests
Result.Type = Type; Result.Type = Type;
} }
public override bool OnPressed(TaikoAction action) => false; public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
} }
} }

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Linq; using System.Linq;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects.Drawables;
@ -30,6 +31,6 @@ namespace osu.Game.Rulesets.Taiko.Tests
nestedStrongHit.Result.Type = hitBoth ? Type : HitResult.Miss; nestedStrongHit.Result.Type = hitBoth ? Type : HitResult.Miss;
} }
public override bool OnPressed(TaikoAction action) => false; public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
} }
} }

View File

@ -11,6 +11,7 @@ using osu.Game.Rulesets.Objects.Drawables;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -112,7 +113,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollBody), protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollBody),
_ => new ElongatedCirclePiece()); _ => new ElongatedCirclePiece());
public override bool OnPressed(TaikoAction action) => false; public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
private void onNewResult(DrawableHitObject obj, JudgementResult result) private void onNewResult(DrawableHitObject obj, JudgementResult result)
{ {
@ -196,7 +197,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
public override bool OnPressed(TaikoAction action) => false; public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
} }
} }
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Skinning.Default; using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -61,9 +62,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
} }
} }
public override bool OnPressed(TaikoAction action) public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{ {
JudgementType = action == TaikoAction.LeftRim || action == TaikoAction.RightRim ? HitType.Rim : HitType.Centre; JudgementType = e.Action == TaikoAction.LeftRim || e.Action == TaikoAction.RightRim ? HitType.Rim : HitType.Centre;
return UpdateResult(true); return UpdateResult(true);
} }
@ -91,7 +92,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
} }
public override bool OnPressed(TaikoAction action) => false; public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
} }
} }
} }

View File

@ -8,6 +8,7 @@ using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -145,19 +146,19 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
ApplyResult(r => r.Type = result); ApplyResult(r => r.Type = result);
} }
public override bool OnPressed(TaikoAction action) public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{ {
if (pressHandledThisFrame) if (pressHandledThisFrame)
return true; return true;
if (Judged) if (Judged)
return false; return false;
validActionPressed = HitActions.Contains(action); validActionPressed = HitActions.Contains(e.Action);
// Only count this as handled if the new judgement is a hit // Only count this as handled if the new judgement is a hit
var result = UpdateResult(true); var result = UpdateResult(true);
if (IsHit) if (IsHit)
HitAction = action; HitAction = e.Action;
// Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded // Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded
// E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note // E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note
@ -165,11 +166,11 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return result; return result;
} }
public override void OnReleased(TaikoAction action) public override void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
{ {
if (action == HitAction) if (e.Action == HitAction)
HitAction = null; HitAction = null;
base.OnReleased(action); base.OnReleased(e);
} }
protected override void Update() protected override void Update()
@ -265,7 +266,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
ApplyResult(r => r.Type = r.Judgement.MaxResult); ApplyResult(r => r.Type = r.Judgement.MaxResult);
} }
public override bool OnPressed(TaikoAction action) public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{ {
// Don't process actions until the main hitobject is hit // Don't process actions until the main hitobject is hit
if (!ParentHitObject.IsHit) if (!ParentHitObject.IsHit)
@ -276,7 +277,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return false; return false;
// Don't handle invalid hit action presses // Don't handle invalid hit action presses
if (!ParentHitObject.HitActions.Contains(action)) if (!ParentHitObject.HitActions.Contains(e.Action))
return false; return false;
return UpdateResult(true); return UpdateResult(true);

View File

@ -12,6 +12,7 @@ using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Skinning.Default; using osu.Game.Rulesets.Taiko.Skinning.Default;
@ -266,13 +267,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private bool? lastWasCentre; private bool? lastWasCentre;
public override bool OnPressed(TaikoAction action) public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{ {
// Don't handle keys before the swell starts // Don't handle keys before the swell starts
if (Time.Current < HitObject.StartTime) if (Time.Current < HitObject.StartTime)
return false; return false;
var isCentre = action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre; var isCentre = e.Action == TaikoAction.LeftCentre || e.Action == TaikoAction.RightCentre;
// Ensure alternating centre and rim hits // Ensure alternating centre and rim hits
if (lastWasCentre == isCentre) if (lastWasCentre == isCentre)

View File

@ -3,6 +3,7 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Taiko.Skinning.Default; using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -34,7 +35,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{ {
} }
public override bool OnPressed(TaikoAction action) => false; public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollTick), protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.DrumRollTick),
_ => new TickPiece()); _ => new TickPiece());

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -76,9 +77,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
/// </summary> /// </summary>
public Drawable CreateProxiedContent() => proxiedContent.CreateProxy(); public Drawable CreateProxiedContent() => proxiedContent.CreateProxy();
public abstract bool OnPressed(TaikoAction action); public abstract bool OnPressed(KeyBindingPressEvent<TaikoAction> e);
public virtual void OnReleased(TaikoAction action) public virtual void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
{ {
} }

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -141,16 +142,16 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
Centre.Texture = skin.GetTexture(@"taiko-drum-inner"); Centre.Texture = skin.GetTexture(@"taiko-drum-inner");
} }
public bool OnPressed(TaikoAction action) public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{ {
Drawable target = null; Drawable target = null;
if (action == CentreAction) if (e.Action == CentreAction)
{ {
target = Centre; target = Centre;
sampleTriggerSource.Play(HitType.Centre); sampleTriggerSource.Play(HitType.Centre);
} }
else if (action == RimAction) else if (e.Action == RimAction)
{ {
target = Rim; target = Rim;
sampleTriggerSource.Play(HitType.Rim); sampleTriggerSource.Play(HitType.Rim);
@ -173,7 +174,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
return false; return false;
} }
public void OnReleased(TaikoAction action) public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
{ {
} }
} }

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
@ -151,19 +152,19 @@ namespace osu.Game.Rulesets.Taiko.UI
[Resolved(canBeNull: true)] [Resolved(canBeNull: true)]
private GameplayClock gameplayClock { get; set; } private GameplayClock gameplayClock { get; set; }
public bool OnPressed(TaikoAction action) public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
{ {
Drawable target = null; Drawable target = null;
Drawable back = null; Drawable back = null;
if (action == CentreAction) if (e.Action == CentreAction)
{ {
target = centreHit; target = centreHit;
back = centre; back = centre;
sampleTriggerSource.Play(HitType.Centre); sampleTriggerSource.Play(HitType.Centre);
} }
else if (action == RimAction) else if (e.Action == RimAction)
{ {
target = rimHit; target = rimHit;
back = rim; back = rim;
@ -195,7 +196,7 @@ namespace osu.Game.Rulesets.Taiko.UI
return false; return false;
} }
public void OnReleased(TaikoAction action) public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
{ {
} }
} }

View File

@ -64,6 +64,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsFalse(beatmapInfo.LetterboxInBreaks); Assert.IsFalse(beatmapInfo.LetterboxInBreaks);
Assert.IsFalse(beatmapInfo.SpecialStyle); Assert.IsFalse(beatmapInfo.SpecialStyle);
Assert.IsFalse(beatmapInfo.WidescreenStoryboard); Assert.IsFalse(beatmapInfo.WidescreenStoryboard);
Assert.IsFalse(beatmapInfo.SamplesMatchPlaybackRate);
Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown); Assert.AreEqual(CountdownType.None, beatmapInfo.Countdown);
Assert.AreEqual(0, beatmapInfo.CountdownOffset); Assert.AreEqual(0, beatmapInfo.CountdownOffset);
} }

View File

@ -7,28 +7,19 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Dialog; using osu.Game.Overlays.Dialog;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.Menus;
using osu.Game.Screens.Menu;
using osu.Game.Tests.Beatmaps.IO; using osu.Game.Tests.Beatmaps.IO;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Editing namespace osu.Game.Tests.Visual.Editing
{ {
public class TestSceneDifficultySwitching : ScreenTestScene public class TestSceneDifficultySwitching : EditorTestScene
{ {
private BeatmapSetInfo importedBeatmapSet; protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
private Editor editor;
// required for screen transitions to work properly protected override bool IsolateSavingFromDatabase => false;
// (see comment in EditorLoader.LogoArriving).
[Cached]
private OsuLogo logo = new OsuLogo
{
Alpha = 0
};
[Resolved] [Resolved]
private OsuGameBase game { get; set; } private OsuGameBase game { get; set; }
@ -36,20 +27,18 @@ namespace osu.Game.Tests.Visual.Editing
[Resolved] [Resolved]
private BeatmapManager beatmaps { get; set; } private BeatmapManager beatmaps { get; set; }
[BackgroundDependencyLoader] private BeatmapSetInfo importedBeatmapSet;
private void load() => Add(logo);
[SetUpSteps] public override void SetUpSteps()
public void SetUp()
{ {
AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result); AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Result);
base.SetUpSteps();
}
AddStep("set current beatmap", () => Beatmap.Value = beatmaps.GetWorkingBeatmap(importedBeatmapSet.Beatmaps.First())); protected override void LoadEditor()
AddStep("push loader", () => Stack.Push(new EditorLoader())); {
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedBeatmapSet.Beatmaps.First());
AddUntilStep("wait for editor push", () => Stack.CurrentScreen is Editor); base.LoadEditor();
AddStep("store editor", () => editor = (Editor)Stack.CurrentScreen);
AddUntilStep("wait for editor to load", () => editor.IsLoaded);
} }
[Test] [Test]
@ -66,17 +55,66 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("stack empty", () => Stack.CurrentScreen == null); AddAssert("stack empty", () => Stack.CurrentScreen == null);
} }
[Test]
public void TestClockPositionPreservedBetweenSwitches()
{
BeatmapInfo targetDifficulty = null;
AddStep("seek editor to 00:05:00", () => EditorClock.Seek(5000));
AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo)));
switchToDifficulty(() => targetDifficulty);
confirmEditingBeatmap(() => targetDifficulty);
AddAssert("editor clock at 00:05:00", () => EditorClock.CurrentTime == 5000);
AddStep("exit editor", () => Stack.Exit());
// ensure editor loader didn't resume.
AddAssert("stack empty", () => Stack.CurrentScreen == null);
}
[Test]
public void TestClipboardPreservedAfterSwitch([Values] bool sameRuleset)
{
BeatmapInfo targetDifficulty = null;
AddStep("select first object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.First()));
AddStep("copy object", () => Editor.Copy());
AddStep("set target difficulty", () =>
{
targetDifficulty = sameRuleset
? importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo) && beatmap.RulesetID == Beatmap.Value.BeatmapInfo.RulesetID)
: importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo) && beatmap.RulesetID != Beatmap.Value.BeatmapInfo.RulesetID);
});
switchToDifficulty(() => targetDifficulty);
confirmEditingBeatmap(() => targetDifficulty);
AddAssert("no objects selected", () => !EditorBeatmap.SelectedHitObjects.Any());
AddStep("paste object", () => Editor.Paste());
if (sameRuleset)
AddAssert("object was pasted", () => EditorBeatmap.SelectedHitObjects.Any());
else
AddAssert("object was not pasted", () => !EditorBeatmap.SelectedHitObjects.Any());
AddStep("exit editor", () => Stack.Exit());
if (sameRuleset)
{
AddUntilStep("prompt for save dialog shown", () => DialogOverlay.CurrentDialog is PromptForSaveDialog);
AddStep("discard changes", () => ((PromptForSaveDialog)DialogOverlay.CurrentDialog).PerformOkAction());
}
// ensure editor loader didn't resume.
AddAssert("stack empty", () => Stack.CurrentScreen == null);
}
[Test] [Test]
public void TestPreventSwitchDueToUnsavedChanges() public void TestPreventSwitchDueToUnsavedChanges()
{ {
BeatmapInfo targetDifficulty = null; BeatmapInfo targetDifficulty = null;
PromptForSaveDialog saveDialog = null; PromptForSaveDialog saveDialog = null;
AddStep("remove first hitobject", () => AddStep("remove first hitobject", () => EditorBeatmap.RemoveAt(0));
{
var editorBeatmap = editor.ChildrenOfType<EditorBeatmap>().Single();
editorBeatmap.RemoveAt(0);
});
AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo))); AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo)));
switchToDifficulty(() => targetDifficulty); switchToDifficulty(() => targetDifficulty);
@ -105,11 +143,7 @@ namespace osu.Game.Tests.Visual.Editing
BeatmapInfo targetDifficulty = null; BeatmapInfo targetDifficulty = null;
PromptForSaveDialog saveDialog = null; PromptForSaveDialog saveDialog = null;
AddStep("remove first hitobject", () => AddStep("remove first hitobject", () => EditorBeatmap.RemoveAt(0));
{
var editorBeatmap = editor.ChildrenOfType<EditorBeatmap>().Single();
editorBeatmap.RemoveAt(0);
});
AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo))); AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo)));
switchToDifficulty(() => targetDifficulty); switchToDifficulty(() => targetDifficulty);
@ -132,39 +166,12 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("stack empty", () => Stack.CurrentScreen == null); AddAssert("stack empty", () => Stack.CurrentScreen == null);
} }
private void switchToDifficulty(Func<BeatmapInfo> difficulty) private void switchToDifficulty(Func<BeatmapInfo> difficulty) => AddStep("switch to difficulty", () => Editor.SwitchToDifficulty(difficulty.Invoke()));
{
AddUntilStep("wait for menubar to load", () => editor.ChildrenOfType<EditorMenuBar>().Any());
AddStep("open file menu", () =>
{
var menuBar = editor.ChildrenOfType<EditorMenuBar>().Single();
var fileMenu = menuBar.ChildrenOfType<DrawableOsuMenuItem>().First();
InputManager.MoveMouseTo(fileMenu);
InputManager.Click(MouseButton.Left);
});
AddStep("open difficulty menu", () =>
{
var difficultySelector =
editor.ChildrenOfType<DrawableOsuMenuItem>().Single(item => item.Item.Text.Value.ToString().Contains("Change difficulty"));
InputManager.MoveMouseTo(difficultySelector);
});
AddWaitStep("wait for open", 3);
AddStep("switch to target difficulty", () =>
{
var difficultyMenuItem =
editor.ChildrenOfType<DrawableOsuMenuItem>()
.Last(item => item.Item is DifficultyMenuItem difficultyItem && difficultyItem.Beatmap.Equals(difficulty.Invoke()));
InputManager.MoveMouseTo(difficultyMenuItem);
InputManager.Click(MouseButton.Left);
});
}
private void confirmEditingBeatmap(Func<BeatmapInfo> targetDifficulty) private void confirmEditingBeatmap(Func<BeatmapInfo> targetDifficulty)
{ {
AddUntilStep("current beatmap is correct", () => Beatmap.Value.BeatmapInfo.Equals(targetDifficulty.Invoke())); AddUntilStep("current beatmap is correct", () => Beatmap.Value.BeatmapInfo.Equals(targetDifficulty.Invoke()));
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor); AddUntilStep("current screen is editor", () => Stack.CurrentScreen == Editor && Editor?.IsLoaded == true);
} }
} }
} }

View File

@ -11,6 +11,7 @@ using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Setup; using osu.Game.Screens.Edit.Setup;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
using SharpCompress.Archives; using SharpCompress.Archives;
@ -55,6 +56,9 @@ namespace osu.Game.Tests.Visual.Editing
[Test] [Test]
public void TestExitWithoutSave() public void TestExitWithoutSave()
{ {
EditorBeatmap editorBeatmap = null;
AddStep("store editor beatmap", () => editorBeatmap = EditorBeatmap);
AddStep("exit without save", () => AddStep("exit without save", () =>
{ {
Editor.Exit(); Editor.Exit();
@ -62,7 +66,7 @@ namespace osu.Game.Tests.Visual.Editing
}); });
AddUntilStep("wait for exit", () => !Editor.IsCurrentScreen()); AddUntilStep("wait for exit", () => !Editor.IsCurrentScreen());
AddAssert("new beatmap not persisted", () => beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID)?.DeletePending == true); AddAssert("new beatmap not persisted", () => beatmapManager.QueryBeatmapSet(s => s.ID == editorBeatmap.BeatmapInfo.BeatmapSet.ID)?.DeletePending == true);
} }
[Test] [Test]

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
@ -80,13 +81,13 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
public bool ReceivedAction; public bool ReceivedAction;
public bool OnPressed(TestAction action) public bool OnPressed(KeyBindingPressEvent<TestAction> e)
{ {
ReceivedAction = action == TestAction.Down; ReceivedAction = e.Action == TestAction.Down;
return true; return true;
} }
public void OnReleased(TestAction action) public void OnReleased(KeyBindingReleaseEvent<TestAction> e)
{ {
} }
} }

View File

@ -54,7 +54,11 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique) recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{ {
Recorder = recorder = new TestReplayRecorder(new Score { Replay = replay }) Recorder = recorder = new TestReplayRecorder(new Score
{
Replay = replay,
ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo }
})
{ {
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos), ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
}, },
@ -222,13 +226,13 @@ namespace osu.Game.Tests.Visual.Gameplay
return base.OnMouseMove(e); return base.OnMouseMove(e);
} }
public bool OnPressed(TestAction action) public bool OnPressed(KeyBindingPressEvent<TestAction> e)
{ {
box.Colour = Color4.White; box.Colour = Color4.White;
return true; return true;
} }
public void OnReleased(TestAction action) public void OnReleased(KeyBindingReleaseEvent<TestAction> e)
{ {
box.Colour = Color4.Black; box.Colour = Color4.Black;
} }

View File

@ -45,7 +45,11 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique) recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{ {
Recorder = new TestReplayRecorder(new Score { Replay = replay }) Recorder = new TestReplayRecorder(new Score
{
Replay = replay,
ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo }
})
{ {
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos) ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos)
}, },
@ -155,13 +159,13 @@ namespace osu.Game.Tests.Visual.Gameplay
return base.OnMouseMove(e); return base.OnMouseMove(e);
} }
public bool OnPressed(TestAction action) public bool OnPressed(KeyBindingPressEvent<TestAction> e)
{ {
box.Colour = Color4.White; box.Colour = Color4.White;
return true; return true;
} }
public void OnReleased(TestAction action) public void OnReleased(KeyBindingReleaseEvent<TestAction> e)
{ {
box.Colour = Color4.Black; box.Colour = Color4.Black;
} }

View File

@ -279,13 +279,13 @@ namespace osu.Game.Tests.Visual.Gameplay
return base.OnMouseMove(e); return base.OnMouseMove(e);
} }
public bool OnPressed(TestAction action) public bool OnPressed(KeyBindingPressEvent<TestAction> e)
{ {
box.Colour = Color4.White; box.Colour = Color4.White;
return true; return true;
} }
public void OnReleased(TestAction action) public void OnReleased(KeyBindingReleaseEvent<TestAction> e)
{ {
box.Colour = Color4.Black; box.Colour = Color4.Black;
} }
@ -354,7 +354,7 @@ namespace osu.Game.Tests.Visual.Gameplay
internal class TestReplayRecorder : ReplayRecorder<TestAction> internal class TestReplayRecorder : ReplayRecorder<TestAction>
{ {
public TestReplayRecorder() public TestReplayRecorder()
: base(new Score()) : base(new Score { ScoreInfo = { Beatmap = new BeatmapInfo() } })
{ {
} }

View File

@ -3,6 +3,7 @@
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing; using osu.Framework.Testing;
@ -82,7 +83,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
} }
[Test] [Test]
public void TestJoinRoomWithPassword() public void TestJoinRoomWithIncorrectPassword()
{
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
AddStep("select room", () => InputManager.Key(Key.Down));
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "wrong");
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
AddAssert("room not joined", () => loungeScreen.IsCurrentScreen());
AddUntilStep("password prompt still visible", () => passwordEntryPopover.State.Value == Visibility.Visible);
}
[Test]
public void TestJoinRoomWithCorrectPassword()
{ {
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null; DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;

View File

@ -3,6 +3,7 @@
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Online.Rooms; using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Multiplayer.Match; using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
@ -15,11 +16,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
{ {
SelectedRoom.Value = new Room(); SelectedRoom.Value = new Room();
Child = new MultiplayerMatchFooter Child = new Container
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Height = 50 RelativeSizeAxes = Axes.X,
Height = 50,
Child = new MultiplayerMatchFooter()
}; };
}); });
} }

View File

@ -75,7 +75,6 @@ namespace osu.Game.Tests.Visual.Navigation
typeof(FileStore), typeof(FileStore),
typeof(ScoreManager), typeof(ScoreManager),
typeof(BeatmapManager), typeof(BeatmapManager),
typeof(SettingsStore),
typeof(RulesetConfigCache), typeof(RulesetConfigCache),
typeof(OsuColour), typeof(OsuColour),
typeof(IBindable<WorkingBeatmap>), typeof(IBindable<WorkingBeatmap>),
@ -97,9 +96,6 @@ namespace osu.Game.Tests.Visual.Navigation
{ {
AddStep("create game", () => AddStep("create game", () =>
{ {
game = new OsuGame();
game.SetHost(host);
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new Box
@ -107,8 +103,9 @@ namespace osu.Game.Tests.Visual.Navigation
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4.Black, Colour = Color4.Black,
}, },
game
}; };
AddGame(game = new OsuGame());
}); });
AddUntilStep("wait for load", () => game.IsLoaded); AddUntilStep("wait for load", () => game.IsLoaded);

View File

@ -77,7 +77,13 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("press enter", () => InputManager.Key(Key.Enter)); AddStep("press enter", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); AddUntilStep("wait for player", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
});
AddAssert("retry count is 0", () => player.RestartCount == 0); AddAssert("retry count is 0", () => player.RestartCount == 0);
AddStep("attempt to retry", () => player.ChildrenOfType<HotkeyRetryOverlay>().First().Action()); AddStep("attempt to retry", () => player.ChildrenOfType<HotkeyRetryOverlay>().First().Action());
@ -104,7 +110,14 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("set mods", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModDoubleTime { SpeedChange = { Value = 2 } } }); AddStep("set mods", () => Game.SelectedMods.Value = new Mod[] { new OsuModNoFail(), new OsuModDoubleTime { SpeedChange = { Value = 2 } } });
AddStep("press enter", () => InputManager.Key(Key.Enter)); AddStep("press enter", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
AddUntilStep("wait for player", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
});
AddUntilStep("wait for track playing", () => beatmap().Track.IsRunning); AddUntilStep("wait for track playing", () => beatmap().Track.IsRunning);
AddStep("seek to near end", () => player.ChildrenOfType<GameplayClockContainer>().First().Seek(beatmap().Beatmap.HitObjects[^1].StartTime - 1000)); AddStep("seek to near end", () => player.ChildrenOfType<GameplayClockContainer>().First().Seek(beatmap().Beatmap.HitObjects[^1].StartTime - 1000));
AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded); AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded);
@ -131,7 +144,13 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("press enter", () => InputManager.Key(Key.Enter)); AddStep("press enter", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); AddUntilStep("wait for player", () =>
{
// dismiss any notifications that may appear (ie. muted notification).
clickMouseInCentre();
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
});
AddUntilStep("wait for fail", () => player.HasFailed); AddUntilStep("wait for fail", () => player.HasFailed);
AddUntilStep("wait for track stop", () => !Game.MusicController.IsPlaying); AddUntilStep("wait for track stop", () => !Game.MusicController.IsPlaying);
@ -399,7 +418,15 @@ namespace osu.Game.Tests.Visual.Navigation
AddStep("Hold escape", () => InputManager.PressKey(Key.Escape)); AddStep("Hold escape", () => InputManager.PressKey(Key.Escape));
AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroTriangles); AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroTriangles);
AddStep("Release escape", () => InputManager.ReleaseKey(Key.Escape));
AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null); AddUntilStep("Wait for game exit", () => Game.ScreenStack.CurrentScreen == null);
AddStep("test dispose doesn't crash", () => Game.Dispose());
}
private void clickMouseInCentre()
{
InputManager.MoveMouseTo(Game.ScreenSpaceDrawQuad.Centre);
InputManager.Click(MouseButton.Left);
} }
private void pushEscape() => private void pushEscape() =>

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
@ -85,6 +86,22 @@ namespace osu.Game.Tests.Visual.Online
case JoinChannelRequest joinChannel: case JoinChannelRequest joinChannel:
joinChannel.TriggerSuccess(); joinChannel.TriggerSuccess();
return true; return true;
case GetUserRequest getUser:
if (getUser.Lookup.Equals("some body", StringComparison.OrdinalIgnoreCase))
{
getUser.TriggerSuccess(new User
{
Username = "some body",
Id = 1,
});
}
else
{
getUser.TriggerFailure(new Exception());
}
return true;
} }
return false; return false;
@ -322,6 +339,27 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("Current channel is channel 1", () => currentChannel == channel1); AddAssert("Current channel is channel 1", () => currentChannel == channel1);
} }
[Test]
public void TestChatCommand()
{
AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
AddStep("Open chat with user", () => channelManager.PostCommand("chat some body"));
AddAssert("PM channel is selected", () =>
channelManager.CurrentChannel.Value.Type == ChannelType.PM && channelManager.CurrentChannel.Value.Users.Single().Username == "some body");
AddStep("Open chat with non-existent user", () => channelManager.PostCommand("chat nobody"));
AddAssert("Last message is error", () => channelManager.CurrentChannel.Value.Messages.Last() is ErrorMessage);
// Make sure no unnecessary requests are made when the PM channel is already open.
AddStep("Select channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
AddStep("Unregister request handling", () => ((DummyAPIAccess)API).HandleRequest = null);
AddStep("Open chat with user", () => channelManager.PostCommand("chat some body"));
AddAssert("PM channel is selected", () =>
channelManager.CurrentChannel.Value.Type == ChannelType.PM && channelManager.CurrentChannel.Value.Users.Single().Username == "some body");
}
private void pressChannelHotkey(int number) private void pressChannelHotkey(int number)
{ {
var channelKey = Key.Number0 + number; var channelKey = Key.Number0 + number;

View File

@ -234,7 +234,7 @@ namespace osu.Game.Tests.Visual.Settings
{ {
AddAssert($"Check {name} is bound to {keyName}", () => AddAssert($"Check {name} is bound to {keyName}", () =>
{ {
var firstRow = panel.ChildrenOfType<KeyBindingRow>().First(r => r.ChildrenOfType<OsuSpriteText>().Any(s => s.Text == name)); var firstRow = panel.ChildrenOfType<KeyBindingRow>().First(r => r.ChildrenOfType<OsuSpriteText>().Any(s => s.Text.ToString() == name));
var firstButton = firstRow.ChildrenOfType<KeyBindingRow.KeyButton>().First(); var firstButton = firstRow.ChildrenOfType<KeyBindingRow.KeyButton>().First();
return firstButton.Text.Text == keyName; return firstButton.Text.Text == keyName;
@ -247,7 +247,7 @@ namespace osu.Game.Tests.Visual.Settings
AddStep($"Scroll to {name}", () => AddStep($"Scroll to {name}", () =>
{ {
var firstRow = panel.ChildrenOfType<KeyBindingRow>().First(r => r.ChildrenOfType<OsuSpriteText>().Any(s => s.Text == name)); var firstRow = panel.ChildrenOfType<KeyBindingRow>().First(r => r.ChildrenOfType<OsuSpriteText>().Any(s => s.Text.ToString() == name));
firstButton = firstRow.ChildrenOfType<KeyBindingRow.KeyButton>().First(); firstButton = firstRow.ChildrenOfType<KeyBindingRow.KeyButton>().First();
panel.ChildrenOfType<SettingsPanel.SettingsSectionsContainer>().First().ScrollTo(firstButton); panel.ChildrenOfType<SettingsPanel.SettingsSectionsContainer>().First().ScrollTo(firstButton);

View File

@ -5,8 +5,8 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Screens; using osu.Game.Screens;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osuTK.Graphics; using osuTK.Graphics;
@ -18,10 +18,18 @@ namespace osu.Game.Tests.Visual
{ {
private TestOsuScreenStack stack; private TestOsuScreenStack stack;
[SetUpSteps] [Cached]
public void SetUpSteps() private MusicController musicController = new MusicController();
[BackgroundDependencyLoader]
private void load()
{ {
AddStep("Create new screen stack", () => { Child = stack = new TestOsuScreenStack { RelativeSizeAxes = Axes.Both }; }); stack = new TestOsuScreenStack { RelativeSizeAxes = Axes.Both };
Add(musicController);
Add(stack);
LoadComponent(stack);
} }
[Test] [Test]
@ -42,6 +50,44 @@ namespace osu.Game.Tests.Visual
AddAssert("Parallax is off", () => stack.ParallaxAmount == 0); AddAssert("Parallax is off", () => stack.ParallaxAmount == 0);
} }
[Test]
public void AllowTrackAdjustmentsTest()
{
AddStep("push allowing screen", () => stack.Push(loadNewScreen<AllowScreen>()));
AddAssert("allows adjustments 1", () => musicController.AllowTrackAdjustments);
AddStep("push inheriting screen", () => stack.Push(loadNewScreen<InheritScreen>()));
AddAssert("allows adjustments 2", () => musicController.AllowTrackAdjustments);
AddStep("push disallowing screen", () => stack.Push(loadNewScreen<DisallowScreen>()));
AddAssert("disallows adjustments 3", () => !musicController.AllowTrackAdjustments);
AddStep("push inheriting screen", () => stack.Push(loadNewScreen<InheritScreen>()));
AddAssert("disallows adjustments 4", () => !musicController.AllowTrackAdjustments);
AddStep("push inheriting screen", () => stack.Push(loadNewScreen<InheritScreen>()));
AddAssert("disallows adjustments 5", () => !musicController.AllowTrackAdjustments);
AddStep("push allowing screen", () => stack.Push(loadNewScreen<AllowScreen>()));
AddAssert("allows adjustments 6", () => musicController.AllowTrackAdjustments);
// Now start exiting from screens
AddStep("exit screen", () => stack.Exit());
AddAssert("disallows adjustments 7", () => !musicController.AllowTrackAdjustments);
AddStep("exit screen", () => stack.Exit());
AddAssert("disallows adjustments 8", () => !musicController.AllowTrackAdjustments);
AddStep("exit screen", () => stack.Exit());
AddAssert("disallows adjustments 9", () => !musicController.AllowTrackAdjustments);
AddStep("exit screen", () => stack.Exit());
AddAssert("allows adjustments 10", () => musicController.AllowTrackAdjustments);
AddStep("exit screen", () => stack.Exit());
AddAssert("allows adjustments 11", () => musicController.AllowTrackAdjustments);
}
public class TestScreen : ScreenWithBeatmapBackground public class TestScreen : ScreenWithBeatmapBackground
{ {
private readonly string screenText; private readonly string screenText;
@ -78,5 +124,26 @@ namespace osu.Game.Tests.Visual
{ {
public new float ParallaxAmount => base.ParallaxAmount; public new float ParallaxAmount => base.ParallaxAmount;
} }
private class AllowScreen : OsuScreen
{
public override bool? AllowTrackAdjustments => true;
}
public class DisallowScreen : OsuScreen
{
public override bool? AllowTrackAdjustments => false;
}
private class InheritScreen : OsuScreen
{
}
private OsuScreen loadNewScreen<T>() where T : OsuScreen, new()
{
OsuScreen screen = new T();
LoadComponent(screen);
return screen;
}
} }
} }

View File

@ -93,6 +93,12 @@ namespace osu.Game.Beatmaps
public bool WidescreenStoryboard { get; set; } public bool WidescreenStoryboard { get; set; }
public bool EpilepsyWarning { get; set; } public bool EpilepsyWarning { get; set; }
/// <summary>
/// Whether or not sound samples should change rate when playing with speed-changing mods.
/// TODO: only read/write supported for now, requires implementation in gameplay.
/// </summary>
public bool SamplesMatchPlaybackRate { get; set; }
public CountdownType Countdown { get; set; } = CountdownType.Normal; public CountdownType Countdown { get; set; } = CountdownType.Normal;
/// <summary> /// <summary>

View File

@ -129,6 +129,7 @@ namespace osu.Game.Beatmaps
Ruleset = ruleset, Ruleset = ruleset,
Metadata = metadata, Metadata = metadata,
WidescreenStoryboard = true, WidescreenStoryboard = true,
SamplesMatchPlaybackRate = true,
} }
} }
}; };

View File

@ -180,6 +180,10 @@ namespace osu.Game.Beatmaps.Formats
beatmap.BeatmapInfo.EpilepsyWarning = Parsing.ParseInt(pair.Value) == 1; beatmap.BeatmapInfo.EpilepsyWarning = Parsing.ParseInt(pair.Value) == 1;
break; break;
case @"SamplesMatchPlaybackRate":
beatmap.BeatmapInfo.SamplesMatchPlaybackRate = Parsing.ParseInt(pair.Value) == 1;
break;
case @"Countdown": case @"Countdown":
beatmap.BeatmapInfo.Countdown = (CountdownType)Enum.Parse(typeof(CountdownType), pair.Value); beatmap.BeatmapInfo.Countdown = (CountdownType)Enum.Parse(typeof(CountdownType), pair.Value);
break; break;

View File

@ -105,8 +105,8 @@ namespace osu.Game.Beatmaps.Formats
if (beatmap.BeatmapInfo.RulesetID == 3) if (beatmap.BeatmapInfo.RulesetID == 3)
writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.BeatmapInfo.SpecialStyle ? '1' : '0')}")); writer.WriteLine(FormattableString.Invariant($"SpecialStyle: {(beatmap.BeatmapInfo.SpecialStyle ? '1' : '0')}"));
writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.BeatmapInfo.WidescreenStoryboard ? '1' : '0')}")); writer.WriteLine(FormattableString.Invariant($"WidescreenStoryboard: {(beatmap.BeatmapInfo.WidescreenStoryboard ? '1' : '0')}"));
// if (b.SamplesMatchPlaybackRate) if (beatmap.BeatmapInfo.SamplesMatchPlaybackRate)
// writer.WriteLine(@"SamplesMatchPlaybackRate: 1"); writer.WriteLine(@"SamplesMatchPlaybackRate: 1");
} }
private void handleEditor(TextWriter writer) private void handleEditor(TextWriter writer)

View File

@ -1,103 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Configuration;
using osu.Game.Rulesets;
namespace osu.Game.Configuration
{
public abstract class DatabasedConfigManager<TLookup> : ConfigManager<TLookup>
where TLookup : struct, Enum
{
private readonly SettingsStore settings;
private readonly int? variant;
private List<DatabasedSetting> databasedSettings;
private readonly RulesetInfo ruleset;
private bool legacySettingsExist;
protected DatabasedConfigManager(SettingsStore settings, RulesetInfo ruleset = null, int? variant = null)
{
this.settings = settings;
this.ruleset = ruleset;
this.variant = variant;
Load();
InitialiseDefaults();
}
protected override void PerformLoad()
{
databasedSettings = settings.Query(ruleset?.ID, variant);
legacySettingsExist = databasedSettings.Any(s => int.TryParse(s.Key, out _));
}
protected override bool PerformSave()
{
lock (dirtySettings)
{
foreach (var setting in dirtySettings)
settings.Update(setting);
dirtySettings.Clear();
}
return true;
}
private readonly List<DatabasedSetting> dirtySettings = new List<DatabasedSetting>();
protected override void AddBindable<TBindable>(TLookup lookup, Bindable<TBindable> bindable)
{
base.AddBindable(lookup, bindable);
if (legacySettingsExist)
{
var legacySetting = databasedSettings.Find(s => s.Key == ((int)(object)lookup).ToString());
if (legacySetting != null)
{
bindable.Parse(legacySetting.Value);
settings.Delete(legacySetting);
}
}
var setting = databasedSettings.Find(s => s.Key == lookup.ToString());
if (setting != null)
{
bindable.Parse(setting.Value);
}
else
{
settings.Update(setting = new DatabasedSetting
{
Key = lookup.ToString(),
Value = bindable.Value,
RulesetID = ruleset?.ID,
Variant = variant,
});
databasedSettings.Add(setting);
}
bindable.ValueChanged += b =>
{
setting.Value = b.NewValue;
lock (dirtySettings)
{
if (!dirtySettings.Contains(setting))
dirtySettings.Add(setting);
}
};
}
}
}

View File

@ -7,7 +7,7 @@ using osu.Game.Database;
namespace osu.Game.Configuration namespace osu.Game.Configuration
{ {
[Table("Settings")] [Table("Settings")]
public class DatabasedSetting : IHasPrimaryKey public class DatabasedSetting : IHasPrimaryKey // can be removed 20220315.
{ {
public int ID { get; set; } public int ID { get; set; }

View File

@ -0,0 +1,32 @@
// 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.Game.Database;
using Realms;
#nullable enable
namespace osu.Game.Configuration
{
[MapTo(@"RulesetSetting")]
public class RealmRulesetSetting : RealmObject, IHasGuidPrimaryKey
{
[PrimaryKey]
public Guid ID { get; set; } = Guid.NewGuid();
[Indexed]
public int RulesetID { get; set; }
[Indexed]
public int Variant { get; set; }
[Required]
public string Key { get; set; } = string.Empty;
[Required]
public string Value { get; set; } = string.Empty;
public override string ToString() => $"{Key} => {Value}";
}
}

View File

@ -1,46 +1,20 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Database; using osu.Game.Database;
namespace osu.Game.Configuration namespace osu.Game.Configuration
{ {
public class SettingsStore : DatabaseBackedStore public class SettingsStore
{ {
public event Action SettingChanged; // this class mostly exists as a wrapper to avoid breaking the ruleset API (see usage in RulesetConfigManager).
// it may cease to exist going forward, depending on how the structure of the config data layer changes.
public SettingsStore(DatabaseContextFactory contextFactory) public readonly RealmContextFactory Realm;
: base(contextFactory)
public SettingsStore(RealmContextFactory realmFactory)
{ {
} Realm = realmFactory;
/// <summary>
/// Retrieve <see cref="DatabasedSetting"/>s for a specified ruleset/variant content.
/// </summary>
/// <param name="rulesetId">The ruleset's internal ID.</param>
/// <param name="variant">An optional variant.</param>
public List<DatabasedSetting> Query(int? rulesetId = null, int? variant = null) =>
ContextFactory.Get().DatabasedSetting.Where(b => b.RulesetID == rulesetId && b.Variant == variant).ToList();
public void Update(DatabasedSetting setting)
{
using (ContextFactory.GetForWrite())
{
var newValue = setting.Value;
Refresh(ref setting);
setting.Value = newValue;
}
SettingChanged?.Invoke();
}
public void Delete(DatabasedSetting setting)
{
using (var usage = ContextFactory.GetForWrite())
usage.Context.Remove(setting);
} }
} }
} }

View File

@ -163,8 +163,11 @@ namespace osu.Game.Database
public void FlushConnections() public void FlushConnections()
{ {
foreach (var context in threadContexts.Values) if (threadContexts != null)
context.Dispose(); {
foreach (var context in threadContexts.Values)
context.Dispose();
}
recycleThreadContexts(); recycleThreadContexts();
} }

View File

@ -12,7 +12,6 @@ using osu.Game.Configuration;
using osu.Game.IO; using osu.Game.IO;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Scoring; using osu.Game.Scoring;
using DatabasedKeyBinding = osu.Game.Input.Bindings.DatabasedKeyBinding;
using LogLevel = Microsoft.Extensions.Logging.LogLevel; using LogLevel = Microsoft.Extensions.Logging.LogLevel;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -24,14 +23,13 @@ namespace osu.Game.Database
public DbSet<BeatmapDifficulty> BeatmapDifficulty { get; set; } public DbSet<BeatmapDifficulty> BeatmapDifficulty { get; set; }
public DbSet<BeatmapMetadata> BeatmapMetadata { get; set; } public DbSet<BeatmapMetadata> BeatmapMetadata { get; set; }
public DbSet<BeatmapSetInfo> BeatmapSetInfo { get; set; } public DbSet<BeatmapSetInfo> BeatmapSetInfo { get; set; }
public DbSet<DatabasedSetting> DatabasedSetting { get; set; }
public DbSet<FileInfo> FileInfo { get; set; } public DbSet<FileInfo> FileInfo { get; set; }
public DbSet<RulesetInfo> RulesetInfo { get; set; } public DbSet<RulesetInfo> RulesetInfo { get; set; }
public DbSet<SkinInfo> SkinInfo { get; set; } public DbSet<SkinInfo> SkinInfo { get; set; }
public DbSet<ScoreInfo> ScoreInfo { get; set; } public DbSet<ScoreInfo> ScoreInfo { get; set; }
// migrated to realm // migrated to realm
public DbSet<DatabasedKeyBinding> DatabasedKeyBinding { get; set; } public DbSet<DatabasedSetting> DatabasedSetting { get; set; }
private readonly string connectionString; private readonly string connectionString;
@ -138,11 +136,6 @@ namespace osu.Game.Database
modelBuilder.Entity<SkinInfo>().HasIndex(b => b.Hash).IsUnique(); modelBuilder.Entity<SkinInfo>().HasIndex(b => b.Hash).IsUnique();
modelBuilder.Entity<SkinInfo>().HasIndex(b => b.DeletePending); modelBuilder.Entity<SkinInfo>().HasIndex(b => b.DeletePending);
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => new { b.RulesetID, b.Variant });
modelBuilder.Entity<DatabasedKeyBinding>().HasIndex(b => b.IntAction);
modelBuilder.Entity<DatabasedKeyBinding>().Ignore(b => b.KeyCombination);
modelBuilder.Entity<DatabasedKeyBinding>().Ignore(b => b.Action);
modelBuilder.Entity<DatabasedSetting>().HasIndex(b => new { b.RulesetID, b.Variant }); modelBuilder.Entity<DatabasedSetting>().HasIndex(b => new { b.RulesetID, b.Variant });
modelBuilder.Entity<FileInfo>().HasIndex(b => b.Hash).IsUnique(); modelBuilder.Entity<FileInfo>().HasIndex(b => b.Hash).IsUnique();

View File

@ -38,6 +38,33 @@ namespace osu.Game.Extensions
return repeatDelegate; return repeatDelegate;
} }
/// <summary>
/// Shakes this drawable.
/// </summary>
/// <param name="target">The target to shake.</param>
/// <param name="shakeDuration">The length of a single shake.</param>
/// <param name="shakeMagnitude">Pixels of displacement per shake.</param>
/// <param name="maximumLength">The maximum length the shake should last.</param>
public static void Shake(this Drawable target, double shakeDuration = 80, float shakeMagnitude = 8, double? maximumLength = null)
{
// if we don't have enough time, don't bother shaking.
if (maximumLength < shakeDuration * 2)
return;
var sequence = target.MoveToX(shakeMagnitude, shakeDuration / 2, Easing.OutSine).Then()
.MoveToX(-shakeMagnitude, shakeDuration, Easing.InOutSine).Then();
// if we don't have enough time for the second shake, skip it.
if (!maximumLength.HasValue || maximumLength >= shakeDuration * 4)
{
sequence = sequence
.MoveToX(shakeMagnitude, shakeDuration, Easing.InOutSine).Then()
.MoveToX(-shakeMagnitude, shakeDuration, Easing.InOutSine).Then();
}
sequence.MoveToX(0, shakeDuration / 2, Easing.InSine);
}
/// <summary> /// <summary>
/// Accepts a delta vector in screen-space coordinates and converts it to one which can be applied to this drawable's position. /// Accepts a delta vector in screen-space coordinates and converts it to one which can be applied to this drawable's position.
/// </summary> /// </summary>

View File

@ -13,6 +13,7 @@ using osu.Framework.Graphics.Primitives;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics.Batches; using osu.Framework.Graphics.Batches;
using osu.Framework.Graphics.OpenGL.Buffers;
using osu.Framework.Graphics.OpenGL.Vertices; using osu.Framework.Graphics.OpenGL.Vertices;
using osu.Framework.Lists; using osu.Framework.Lists;
@ -181,7 +182,10 @@ namespace osu.Game.Graphics.Backgrounds
private void addTriangles(bool randomY) private void addTriangles(bool randomY)
{ {
AimCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio); // limited by the maximum size of QuadVertexBuffer for safety.
const int max_triangles = QuadVertexBuffer<TexturedVertex2D>.MAX_QUADS;
AimCount = (int)Math.Min(max_triangles, (DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio));
for (int i = 0; i < AimCount - parts.Count; i++) for (int i = 0; i < AimCount - parts.Count; i++)
parts.Add(createTriangle(randomY)); parts.Add(createTriangle(randomY));

View File

@ -88,9 +88,9 @@ namespace osu.Game.Graphics.Containers
base.OnMouseUp(e); base.OnMouseUp(e);
} }
public virtual bool OnPressed(GlobalAction action) public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{ {
switch (action) switch (e.Action)
{ {
case GlobalAction.Back: case GlobalAction.Back:
Hide(); Hide();
@ -103,7 +103,7 @@ namespace osu.Game.Graphics.Containers
return false; return false;
} }
public void OnReleased(GlobalAction action) public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{ {
} }

View File

@ -1,8 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Extensions;
namespace osu.Game.Graphics.Containers namespace osu.Game.Graphics.Containers
{ {
@ -16,40 +16,10 @@ namespace osu.Game.Graphics.Containers
/// </summary> /// </summary>
public float ShakeDuration = 80; public float ShakeDuration = 80;
/// <summary>
/// Total number of shakes. May be shortened if possible.
/// </summary>
public float TotalShakes = 4;
/// <summary>
/// Pixels of displacement per shake.
/// </summary>
public float ShakeMagnitude = 8;
/// <summary> /// <summary>
/// Shake the contents of this container. /// Shake the contents of this container.
/// </summary> /// </summary>
/// <param name="maximumLength">The maximum length the shake should last.</param> /// <param name="maximumLength">The maximum length the shake should last.</param>
public void Shake(double? maximumLength = null) public void Shake(double? maximumLength = null) => this.Shake(ShakeDuration, maximumLength: maximumLength);
{
const float shake_amount = 8;
// if we don't have enough time, don't bother shaking.
if (maximumLength < ShakeDuration * 2)
return;
var sequence = this.MoveToX(shake_amount, ShakeDuration / 2, Easing.OutSine).Then()
.MoveToX(-shake_amount, ShakeDuration, Easing.InOutSine).Then();
// if we don't have enough time for the second shake, skip it.
if (!maximumLength.HasValue || maximumLength >= ShakeDuration * 4)
{
sequence = sequence
.MoveToX(shake_amount, ShakeDuration, Easing.InOutSine).Then()
.MoveToX(-shake_amount, ShakeDuration, Easing.InOutSine).Then();
}
sequence.MoveToX(0, ShakeDuration / 2, Easing.InSine);
}
} }
} }

View File

@ -12,6 +12,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Configuration; using osu.Game.Configuration;
@ -57,9 +58,9 @@ namespace osu.Game.Graphics
shutter = audio.Samples.Get("UI/shutter"); shutter = audio.Samples.Get("UI/shutter");
} }
public bool OnPressed(GlobalAction action) public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{ {
switch (action) switch (e.Action)
{ {
case GlobalAction.TakeScreenshot: case GlobalAction.TakeScreenshot:
shutter.Play(); shutter.Play();
@ -70,7 +71,7 @@ namespace osu.Game.Graphics
return false; return false;
} }
public void OnReleased(GlobalAction action) public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{ {
} }

View File

@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface namespace osu.Game.Graphics.UserInterface
@ -61,9 +62,9 @@ namespace osu.Game.Graphics.UserInterface
{ {
public Action OnBackPressed; public Action OnBackPressed;
public bool OnPressed(GlobalAction action) public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{ {
switch (action) switch (e.Action)
{ {
case GlobalAction.Back: case GlobalAction.Back:
OnBackPressed?.Invoke(); OnBackPressed?.Invoke();
@ -73,7 +74,7 @@ namespace osu.Game.Graphics.UserInterface
return false; return false;
} }
public void OnReleased(GlobalAction action) public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{ {
} }
} }

View File

@ -70,11 +70,11 @@ namespace osu.Game.Graphics.UserInterface
return base.OnKeyDown(e); return base.OnKeyDown(e);
} }
public virtual bool OnPressed(GlobalAction action) public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{ {
if (!HasFocus) return false; if (!HasFocus) return false;
if (action == GlobalAction.Back) if (e.Action == GlobalAction.Back)
{ {
if (Text.Length > 0) if (Text.Length > 0)
{ {
@ -86,7 +86,7 @@ namespace osu.Game.Graphics.UserInterface
return false; return false;
} }
public void OnReleased(GlobalAction action) public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{ {
} }

View File

@ -46,7 +46,11 @@ namespace osu.Game.Graphics.UserInterface
}, },
}; };
Current.ValueChanged += filled => fill.FadeTo(filled.NewValue ? 1 : 0, 200, Easing.OutQuint); Current.ValueChanged += filled =>
{
fill.FadeTo(filled.NewValue ? 1 : 0, 200, Easing.OutQuint);
this.TransformTo(nameof(BorderThickness), filled.NewValue ? 8.5f : border_width, 200, Easing.OutQuint);
};
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -30,9 +30,9 @@ namespace osu.Game.Graphics.UserInterface
PlaceholderText = "type to search"; PlaceholderText = "type to search";
} }
public override bool OnPressed(PlatformAction action) public override bool OnPressed(KeyBindingPressEvent<PlatformAction> e)
{ {
switch (action) switch (e.Action)
{ {
case PlatformAction.MoveBackwardLine: case PlatformAction.MoveBackwardLine:
case PlatformAction.MoveForwardLine: case PlatformAction.MoveForwardLine:
@ -43,7 +43,7 @@ namespace osu.Game.Graphics.UserInterface
return false; return false;
} }
return base.OnPressed(action); return base.OnPressed(e);
} }
protected override bool OnKeyDown(KeyDownEvent e) protected override bool OnKeyDown(KeyDownEvent e)

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Overlays; using osu.Game.Overlays;
using osuTK; using osuTK;
@ -55,12 +56,12 @@ namespace osu.Game.Graphics.UserInterfaceV2
this.FadeOut(fade_duration, Easing.OutQuint); this.FadeOut(fade_duration, Easing.OutQuint);
} }
public bool OnPressed(GlobalAction action) public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{ {
if (State.Value == Visibility.Hidden) if (State.Value == Visibility.Hidden)
return false; return false;
if (action == GlobalAction.Back) if (e.Action == GlobalAction.Back)
{ {
Hide(); Hide();
return true; return true;
@ -69,7 +70,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
return false; return false;
} }
public void OnReleased(GlobalAction action) public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{ {
} }
} }

View File

@ -1,39 +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.ComponentModel.DataAnnotations.Schema;
using osu.Framework.Input.Bindings;
using osu.Game.Database;
namespace osu.Game.Input.Bindings
{
[Table("KeyBinding")]
public class DatabasedKeyBinding : IKeyBinding, IHasPrimaryKey
{
public int ID { get; set; }
public int? RulesetID { get; set; }
public int? Variant { get; set; }
[Column("Keys")]
public string KeysString { get; set; }
[Column("Action")]
public int IntAction { get; set; }
[NotMapped]
public KeyCombination KeyCombination
{
get => KeysString;
set => KeysString = value.ToString();
}
[NotMapped]
public object Action
{
get => IntAction;
set => IntAction = (int)value;
}
}
}

View File

@ -2,11 +2,12 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Localisation;
using osu.Game.Localisation;
namespace osu.Game.Input.Bindings namespace osu.Game.Input.Bindings
{ {
@ -137,152 +138,152 @@ namespace osu.Game.Input.Bindings
public enum GlobalAction public enum GlobalAction
{ {
[Description("Toggle chat overlay")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleChat))]
ToggleChat, ToggleChat,
[Description("Toggle social overlay")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleSocial))]
ToggleSocial, ToggleSocial,
[Description("Reset input settings")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ResetInputSettings))]
ResetInputSettings, ResetInputSettings,
[Description("Toggle toolbar")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleToolbar))]
ToggleToolbar, ToggleToolbar,
[Description("Toggle settings")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleSettings))]
ToggleSettings, ToggleSettings,
[Description("Toggle beatmap listing")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleBeatmapListing))]
ToggleBeatmapListing, ToggleBeatmapListing,
[Description("Increase volume")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseVolume))]
IncreaseVolume, IncreaseVolume,
[Description("Decrease volume")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseVolume))]
DecreaseVolume, DecreaseVolume,
[Description("Toggle mute")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleMute))]
ToggleMute, ToggleMute,
// In-Game Keybindings // In-Game Keybindings
[Description("Skip cutscene")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SkipCutscene))]
SkipCutscene, SkipCutscene,
[Description("Quick retry (hold)")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.QuickRetry))]
QuickRetry, QuickRetry,
[Description("Take screenshot")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.TakeScreenshot))]
TakeScreenshot, TakeScreenshot,
[Description("Toggle gameplay mouse buttons")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleGameplayMouseButtons))]
ToggleGameplayMouseButtons, ToggleGameplayMouseButtons,
[Description("Back")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.Back))]
Back, Back,
[Description("Increase scroll speed")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseScrollSpeed))]
IncreaseScrollSpeed, IncreaseScrollSpeed,
[Description("Decrease scroll speed")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseScrollSpeed))]
DecreaseScrollSpeed, DecreaseScrollSpeed,
[Description("Select")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.Select))]
Select, Select,
[Description("Quick exit (hold)")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.QuickExit))]
QuickExit, QuickExit,
// Game-wide beatmap music controller keybindings // Game-wide beatmap music controller keybindings
[Description("Next track")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicNext))]
MusicNext, MusicNext,
[Description("Previous track")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicPrev))]
MusicPrev, MusicPrev,
[Description("Play / pause")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.MusicPlay))]
MusicPlay, MusicPlay,
[Description("Toggle now playing overlay")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleNowPlaying))]
ToggleNowPlaying, ToggleNowPlaying,
[Description("Previous selection")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectPrevious))]
SelectPrevious, SelectPrevious,
[Description("Next selection")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectNext))]
SelectNext, SelectNext,
[Description("Home")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.Home))]
Home, Home,
[Description("Toggle notifications")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleNotifications))]
ToggleNotifications, ToggleNotifications,
[Description("Pause gameplay")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.PauseGameplay))]
PauseGameplay, PauseGameplay,
// Editor // Editor
[Description("Setup mode")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorSetupMode))]
EditorSetupMode, EditorSetupMode,
[Description("Compose mode")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorComposeMode))]
EditorComposeMode, EditorComposeMode,
[Description("Design mode")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorDesignMode))]
EditorDesignMode, EditorDesignMode,
[Description("Timing mode")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTimingMode))]
EditorTimingMode, EditorTimingMode,
[Description("Hold for HUD")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.HoldForHUD))]
HoldForHUD, HoldForHUD,
[Description("Random skin")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.RandomSkin))]
RandomSkin, RandomSkin,
[Description("Pause / resume replay")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.TogglePauseReplay))]
TogglePauseReplay, TogglePauseReplay,
[Description("Toggle in-game interface")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameInterface))]
ToggleInGameInterface, ToggleInGameInterface,
// Song select keybindings // Song select keybindings
[Description("Toggle Mod Select")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleModSelection))]
ToggleModSelection, ToggleModSelection,
[Description("Random")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectNextRandom))]
SelectNextRandom, SelectNextRandom,
[Description("Rewind")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SelectPreviousRandom))]
SelectPreviousRandom, SelectPreviousRandom,
[Description("Beatmap Options")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleBeatmapOptions))]
ToggleBeatmapOptions, ToggleBeatmapOptions,
[Description("Verify mode")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorVerifyMode))]
EditorVerifyMode, EditorVerifyMode,
[Description("Nudge selection left")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorNudgeLeft))]
EditorNudgeLeft, EditorNudgeLeft,
[Description("Nudge selection right")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorNudgeRight))]
EditorNudgeRight, EditorNudgeRight,
[Description("Toggle skin editor")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleSkinEditor))]
ToggleSkinEditor, ToggleSkinEditor,
[Description("Previous volume meter")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.PreviousVolumeMeter))]
PreviousVolumeMeter, PreviousVolumeMeter,
[Description("Next volume meter")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.NextVolumeMeter))]
NextVolumeMeter, NextVolumeMeter,
[Description("Seek replay forward")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SeekReplayForward))]
SeekReplayForward, SeekReplayForward,
[Description("Seek replay backward")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.SeekReplayBackward))]
SeekReplayBackward, SeekReplayBackward,
[Description("Toggle chat focus")] [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleChatFocus))]
ToggleChatFocus ToggleChatFocus
} }
} }

View File

@ -55,13 +55,13 @@ namespace osu.Game.Input
isIdle.Value = TimeSpentIdle > timeToIdle && AllowIdle; isIdle.Value = TimeSpentIdle > timeToIdle && AllowIdle;
} }
public bool OnPressed(PlatformAction action) => updateLastInteractionTime(); public bool OnPressed(KeyBindingPressEvent<PlatformAction> e) => updateLastInteractionTime();
public void OnReleased(PlatformAction action) => updateLastInteractionTime(); public void OnReleased(KeyBindingReleaseEvent<PlatformAction> e) => updateLastInteractionTime();
public bool OnPressed(GlobalAction action) => updateLastInteractionTime(); public bool OnPressed(KeyBindingPressEvent<GlobalAction> e) => updateLastInteractionTime();
public void OnReleased(GlobalAction action) => updateLastInteractionTime(); public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e) => updateLastInteractionTime();
protected override bool Handle(UIEvent e) protected override bool Handle(UIEvent e)
{ {

View File

@ -0,0 +1,254 @@
// 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.Localisation;
namespace osu.Game.Localisation
{
public static class GlobalActionKeyBindingStrings
{
private const string prefix = @"osu.Game.Resources.Localisation.GlobalActionKeyBinding";
/// <summary>
/// "Toggle chat overlay"
/// </summary>
public static LocalisableString ToggleChat => new TranslatableString(getKey(@"toggle_chat"), @"Toggle chat overlay");
/// <summary>
/// "Toggle social overlay"
/// </summary>
public static LocalisableString ToggleSocial => new TranslatableString(getKey(@"toggle_social"), @"Toggle social overlay");
/// <summary>
/// "Reset input settings"
/// </summary>
public static LocalisableString ResetInputSettings => new TranslatableString(getKey(@"reset_input_settings"), @"Reset input settings");
/// <summary>
/// "Toggle toolbar"
/// </summary>
public static LocalisableString ToggleToolbar => new TranslatableString(getKey(@"toggle_toolbar"), @"Toggle toolbar");
/// <summary>
/// "Toggle settings"
/// </summary>
public static LocalisableString ToggleSettings => new TranslatableString(getKey(@"toggle_settings"), @"Toggle settings");
/// <summary>
/// "Toggle beatmap listing"
/// </summary>
public static LocalisableString ToggleBeatmapListing => new TranslatableString(getKey(@"toggle_beatmap_listing"), @"Toggle beatmap listing");
/// <summary>
/// "Increase volume"
/// </summary>
public static LocalisableString IncreaseVolume => new TranslatableString(getKey(@"increase_volume"), @"Increase volume");
/// <summary>
/// "Decrease volume"
/// </summary>
public static LocalisableString DecreaseVolume => new TranslatableString(getKey(@"decrease_volume"), @"Decrease volume");
/// <summary>
/// "Toggle mute"
/// </summary>
public static LocalisableString ToggleMute => new TranslatableString(getKey(@"toggle_mute"), @"Toggle mute");
/// <summary>
/// "Skip cutscene"
/// </summary>
public static LocalisableString SkipCutscene => new TranslatableString(getKey(@"skip_cutscene"), @"Skip cutscene");
/// <summary>
/// "Quick retry (hold)"
/// </summary>
public static LocalisableString QuickRetry => new TranslatableString(getKey(@"quick_retry"), @"Quick retry (hold)");
/// <summary>
/// "Take screenshot"
/// </summary>
public static LocalisableString TakeScreenshot => new TranslatableString(getKey(@"take_screenshot"), @"Take screenshot");
/// <summary>
/// "Toggle gameplay mouse buttons"
/// </summary>
public static LocalisableString ToggleGameplayMouseButtons => new TranslatableString(getKey(@"toggle_gameplay_mouse_buttons"), @"Toggle gameplay mouse buttons");
/// <summary>
/// "Back"
/// </summary>
public static LocalisableString Back => new TranslatableString(getKey(@"back"), @"Back");
/// <summary>
/// "Increase scroll speed"
/// </summary>
public static LocalisableString IncreaseScrollSpeed => new TranslatableString(getKey(@"increase_scroll_speed"), @"Increase scroll speed");
/// <summary>
/// "Decrease scroll speed"
/// </summary>
public static LocalisableString DecreaseScrollSpeed => new TranslatableString(getKey(@"decrease_scroll_speed"), @"Decrease scroll speed");
/// <summary>
/// "Select"
/// </summary>
public static LocalisableString Select => new TranslatableString(getKey(@"select"), @"Select");
/// <summary>
/// "Quick exit (hold)"
/// </summary>
public static LocalisableString QuickExit => new TranslatableString(getKey(@"quick_exit"), @"Quick exit (hold)");
/// <summary>
/// "Next track"
/// </summary>
public static LocalisableString MusicNext => new TranslatableString(getKey(@"music_next"), @"Next track");
/// <summary>
/// "Previous track"
/// </summary>
public static LocalisableString MusicPrev => new TranslatableString(getKey(@"music_prev"), @"Previous track");
/// <summary>
/// "Play / pause"
/// </summary>
public static LocalisableString MusicPlay => new TranslatableString(getKey(@"music_play"), @"Play / pause");
/// <summary>
/// "Toggle now playing overlay"
/// </summary>
public static LocalisableString ToggleNowPlaying => new TranslatableString(getKey(@"toggle_now_playing"), @"Toggle now playing overlay");
/// <summary>
/// "Previous selection"
/// </summary>
public static LocalisableString SelectPrevious => new TranslatableString(getKey(@"select_previous"), @"Previous selection");
/// <summary>
/// "Next selection"
/// </summary>
public static LocalisableString SelectNext => new TranslatableString(getKey(@"select_next"), @"Next selection");
/// <summary>
/// "Home"
/// </summary>
public static LocalisableString Home => new TranslatableString(getKey(@"home"), @"Home");
/// <summary>
/// "Toggle notifications"
/// </summary>
public static LocalisableString ToggleNotifications => new TranslatableString(getKey(@"toggle_notifications"), @"Toggle notifications");
/// <summary>
/// "Pause gameplay"
/// </summary>
public static LocalisableString PauseGameplay => new TranslatableString(getKey(@"pause_gameplay"), @"Pause gameplay");
/// <summary>
/// "Setup mode"
/// </summary>
public static LocalisableString EditorSetupMode => new TranslatableString(getKey(@"editor_setup_mode"), @"Setup mode");
/// <summary>
/// "Compose mode"
/// </summary>
public static LocalisableString EditorComposeMode => new TranslatableString(getKey(@"editor_compose_mode"), @"Compose mode");
/// <summary>
/// "Design mode"
/// </summary>
public static LocalisableString EditorDesignMode => new TranslatableString(getKey(@"editor_design_mode"), @"Design mode");
/// <summary>
/// "Timing mode"
/// </summary>
public static LocalisableString EditorTimingMode => new TranslatableString(getKey(@"editor_timing_mode"), @"Timing mode");
/// <summary>
/// "Hold for HUD"
/// </summary>
public static LocalisableString HoldForHUD => new TranslatableString(getKey(@"hold_for_hud"), @"Hold for HUD");
/// <summary>
/// "Random skin"
/// </summary>
public static LocalisableString RandomSkin => new TranslatableString(getKey(@"random_skin"), @"Random skin");
/// <summary>
/// "Pause / resume replay"
/// </summary>
public static LocalisableString TogglePauseReplay => new TranslatableString(getKey(@"toggle_pause_replay"), @"Pause / resume replay");
/// <summary>
/// "Toggle in-game interface"
/// </summary>
public static LocalisableString ToggleInGameInterface => new TranslatableString(getKey(@"toggle_in_game_interface"), @"Toggle in-game interface");
/// <summary>
/// "Toggle Mod Select"
/// </summary>
public static LocalisableString ToggleModSelection => new TranslatableString(getKey(@"toggle_mod_selection"), @"Toggle Mod Select");
/// <summary>
/// "Random"
/// </summary>
public static LocalisableString SelectNextRandom => new TranslatableString(getKey(@"select_next_random"), @"Random");
/// <summary>
/// "Rewind"
/// </summary>
public static LocalisableString SelectPreviousRandom => new TranslatableString(getKey(@"select_previous_random"), @"Rewind");
/// <summary>
/// "Beatmap Options"
/// </summary>
public static LocalisableString ToggleBeatmapOptions => new TranslatableString(getKey(@"toggle_beatmap_options"), @"Beatmap Options");
/// <summary>
/// "Verify mode"
/// </summary>
public static LocalisableString EditorVerifyMode => new TranslatableString(getKey(@"editor_verify_mode"), @"Verify mode");
/// <summary>
/// "Nudge selection left"
/// </summary>
public static LocalisableString EditorNudgeLeft => new TranslatableString(getKey(@"editor_nudge_left"), @"Nudge selection left");
/// <summary>
/// "Nudge selection right"
/// </summary>
public static LocalisableString EditorNudgeRight => new TranslatableString(getKey(@"editor_nudge_right"), @"Nudge selection right");
/// <summary>
/// "Toggle skin editor"
/// </summary>
public static LocalisableString ToggleSkinEditor => new TranslatableString(getKey(@"toggle_skin_editor"), @"Toggle skin editor");
/// <summary>
/// "Previous volume meter"
/// </summary>
public static LocalisableString PreviousVolumeMeter => new TranslatableString(getKey(@"previous_volume_meter"), @"Previous volume meter");
/// <summary>
/// "Next volume meter"
/// </summary>
public static LocalisableString NextVolumeMeter => new TranslatableString(getKey(@"next_volume_meter"), @"Next volume meter");
/// <summary>
/// "Seek replay forward"
/// </summary>
public static LocalisableString SeekReplayForward => new TranslatableString(getKey(@"seek_replay_forward"), @"Seek replay forward");
/// <summary>
/// "Seek replay backward"
/// </summary>
public static LocalisableString SeekReplayBackward => new TranslatableString(getKey(@"seek_replay_backward"), @"Seek replay backward");
/// <summary>
/// "Toggle chat focus"
/// </summary>
public static LocalisableString ToggleChatFocus => new TranslatableString(getKey(@"toggle_chat_focus"), @"Toggle chat focus");
private static string getKey(string key) => $"{prefix}:{key}";
}
}

View File

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

View File

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

View File

@ -81,6 +81,8 @@ namespace osu.Game.Migrations
b.Property<int>("RulesetID"); b.Property<int>("RulesetID");
b.Property<bool>("SamplesMatchPlaybackRate");
b.Property<bool>("SpecialStyle"); b.Property<bool>("SpecialStyle");
b.Property<float>("StackLeniency"); b.Property<float>("StackLeniency");

View File

@ -8,7 +8,7 @@ namespace osu.Game.Online.API.Requests
{ {
public class GetUserRequest : APIRequest<User> public class GetUserRequest : APIRequest<User>
{ {
private readonly string lookup; public readonly string Lookup;
public readonly RulesetInfo Ruleset; public readonly RulesetInfo Ruleset;
private readonly LookupType lookupType; private readonly LookupType lookupType;
@ -26,7 +26,7 @@ namespace osu.Game.Online.API.Requests
/// <param name="ruleset">The ruleset to get the user's info for.</param> /// <param name="ruleset">The ruleset to get the user's info for.</param>
public GetUserRequest(long? userId = null, RulesetInfo ruleset = null) public GetUserRequest(long? userId = null, RulesetInfo ruleset = null)
{ {
lookup = userId.ToString(); Lookup = userId.ToString();
lookupType = LookupType.Id; lookupType = LookupType.Id;
Ruleset = ruleset; Ruleset = ruleset;
} }
@ -38,12 +38,12 @@ namespace osu.Game.Online.API.Requests
/// <param name="ruleset">The ruleset to get the user's info for.</param> /// <param name="ruleset">The ruleset to get the user's info for.</param>
public GetUserRequest(string username = null, RulesetInfo ruleset = null) public GetUserRequest(string username = null, RulesetInfo ruleset = null)
{ {
lookup = username; Lookup = username;
lookupType = LookupType.Username; lookupType = LookupType.Username;
Ruleset = ruleset; Ruleset = ruleset;
} }
protected override string Target => lookup != null ? $@"users/{lookup}/{Ruleset?.ShortName}?k={lookupType.ToString().ToLower()}" : $@"me/{Ruleset?.ShortName}"; protected override string Target => Lookup != null ? $@"users/{Lookup}/{Ruleset?.ShortName}?key={lookupType.ToString().ToLower()}" : $@"me/{Ruleset?.ShortName}";
private enum LookupType private enum LookupType
{ {

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