mirror of
https://github.com/ppy/osu.git
synced 2024-11-14 16:37:26 +08:00
Merge branch 'master' into path-type-menuitem
This commit is contained in:
commit
cda6757f52
@ -54,6 +54,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1205.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1210.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -12,14 +12,14 @@ namespace osu.Game.Rulesets.Catch.MathUtils
|
|||||||
{
|
{
|
||||||
private const double int_to_real = 1.0 / (int.MaxValue + 1.0);
|
private const double int_to_real = 1.0 / (int.MaxValue + 1.0);
|
||||||
private const uint int_mask = 0x7FFFFFFF;
|
private const uint int_mask = 0x7FFFFFFF;
|
||||||
private const uint y = 842502087;
|
private const uint y_initial = 842502087;
|
||||||
private const uint z = 3579807591;
|
private const uint z_initial = 3579807591;
|
||||||
private const uint w = 273326509;
|
private const uint w_initial = 273326509;
|
||||||
private uint _x, _y = y, _z = z, _w = w;
|
private uint x, y = y_initial, z = z_initial, w = w_initial;
|
||||||
|
|
||||||
public FastRandom(int seed)
|
public FastRandom(int seed)
|
||||||
{
|
{
|
||||||
_x = (uint)seed;
|
x = (uint)seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FastRandom()
|
public FastRandom()
|
||||||
@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Catch.MathUtils
|
|||||||
/// <returns>The random value.</returns>
|
/// <returns>The random value.</returns>
|
||||||
public uint NextUInt()
|
public uint NextUInt()
|
||||||
{
|
{
|
||||||
uint t = _x ^ (_x << 11);
|
uint t = x ^ (x << 11);
|
||||||
_x = _y;
|
x = y;
|
||||||
_y = _z;
|
y = z;
|
||||||
_z = _w;
|
z = w;
|
||||||
return _w = _w ^ (_w >> 19) ^ t ^ (t >> 8);
|
return w = w ^ (w >> 19) ^ t ^ (t >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChild = new SkinnableSprite("Gameplay/catch/fruit-catcher-idle")
|
InternalChild = new SkinnableSprite("Gameplay/catch/fruit-catcher-idle", confineMode: ConfineMode.ScaleDownToFit)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
|
@ -128,8 +128,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
slider.Path.ControlPoints.Remove(c);
|
slider.Path.ControlPoints.Remove(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are 0 remaining control points, treat the slider as being deleted
|
// If there are 0 or 1 remaining control points, the slider is in a degenerate (single point) form and should be deleted
|
||||||
if (slider.Path.ControlPoints.Count == 0)
|
if (slider.Path.ControlPoints.Count <= 1)
|
||||||
{
|
{
|
||||||
placementHandler?.Delete(slider);
|
placementHandler?.Delete(slider);
|
||||||
return true;
|
return true;
|
||||||
@ -163,7 +163,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
|
|
||||||
return new MenuItem[]
|
return new MenuItem[]
|
||||||
{
|
{
|
||||||
new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints)}", MenuItemType.Destructive, () => deleteSelected()),
|
new OsuMenuItem($"Delete {"control point".ToQuantity(selectedPoints, selectedPoints > 1 ? ShowQuantityAs.Numeric : ShowQuantityAs.None)}", MenuItemType.Destructive, () => deleteSelected()),
|
||||||
new OsuMenuItem("Type")
|
new OsuMenuItem("Type")
|
||||||
{
|
{
|
||||||
Items = new[]
|
Items = new[]
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
@ -191,15 +190,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
Initial,
|
Initial,
|
||||||
Body,
|
Body,
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Segment
|
|
||||||
{
|
|
||||||
public readonly List<Vector2> ControlPoints = new List<Vector2>();
|
|
||||||
|
|
||||||
public Segment(Vector2 offset)
|
|
||||||
{
|
|
||||||
ControlPoints.Add(offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
int insertionIndex = 0;
|
int insertionIndex = 0;
|
||||||
float minDistance = float.MaxValue;
|
float minDistance = float.MaxValue;
|
||||||
|
|
||||||
for (int i = 0; i < HitObject.Path.ControlPoints.Count - 2; i++)
|
for (int i = 0; i < HitObject.Path.ControlPoints.Count - 1; i++)
|
||||||
{
|
{
|
||||||
float dist = new Line(HitObject.Path.ControlPoints[i].Position.Value, HitObject.Path.ControlPoints[i + 1].Position.Value).DistanceToPoint(position);
|
float dist = new Line(HitObject.Path.ControlPoints[i].Position.Value, HitObject.Path.ControlPoints[i + 1].Position.Value).DistanceToPoint(position);
|
||||||
|
|
||||||
|
@ -3,15 +3,14 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning
|
namespace osu.Game.Rulesets.Osu.Skinning
|
||||||
{
|
{
|
||||||
public class LegacyCursor : CompositeDrawable
|
public class LegacyCursor : OsuCursorSprite
|
||||||
{
|
{
|
||||||
private NonPlayfieldSprite cursor;
|
|
||||||
private bool spin;
|
private bool spin;
|
||||||
|
|
||||||
public LegacyCursor()
|
public LegacyCursor()
|
||||||
@ -27,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
spin = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorRotate)?.Value ?? true;
|
spin = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.CursorRotate)?.Value ?? true;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
new NonPlayfieldSprite
|
new NonPlayfieldSprite
|
||||||
{
|
{
|
||||||
@ -35,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
cursor = new NonPlayfieldSprite
|
ExpandTarget = new NonPlayfieldSprite
|
||||||
{
|
{
|
||||||
Texture = skin.GetTexture("cursor"),
|
Texture = skin.GetTexture("cursor"),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -47,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
if (spin)
|
if (spin)
|
||||||
cursor.Spin(10000, RotationDirection.Clockwise);
|
ExpandTarget.Spin(10000, RotationDirection.Clockwise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,9 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
private bool cursorExpand;
|
private bool cursorExpand;
|
||||||
|
|
||||||
private Container expandTarget;
|
private SkinnableDrawable cursorSprite;
|
||||||
|
|
||||||
|
private Drawable expandTarget => (cursorSprite.Drawable as OsuCursorSprite)?.ExpandTarget ?? cursorSprite;
|
||||||
|
|
||||||
public OsuCursor()
|
public OsuCursor()
|
||||||
{
|
{
|
||||||
@ -37,12 +39,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
InternalChild = expandTarget = new Container
|
InternalChild = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Child = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling)
|
Child = cursorSprite = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling)
|
||||||
{
|
{
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -62,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad);
|
public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad);
|
||||||
|
|
||||||
private class DefaultCursor : CompositeDrawable
|
private class DefaultCursor : OsuCursorSprite
|
||||||
{
|
{
|
||||||
public DefaultCursor()
|
public DefaultCursor()
|
||||||
{
|
{
|
||||||
@ -71,10 +73,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
new CircularContainer
|
ExpandTarget = new CircularContainer
|
||||||
{
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
BorderThickness = size / 6,
|
BorderThickness = size / 6,
|
||||||
|
17
osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs
Normal file
17
osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||||
|
{
|
||||||
|
public abstract class OsuCursorSprite : CompositeDrawable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The an optional piece of the cursor to expand when in a clicked state.
|
||||||
|
/// If null, the whole cursor will be affected by expansion.
|
||||||
|
/// </summary>
|
||||||
|
public Drawable ExpandTarget { get; protected set; }
|
||||||
|
}
|
||||||
|
}
|
@ -57,8 +57,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
beforeLoadAction?.Invoke();
|
beforeLoadAction?.Invoke();
|
||||||
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||||
|
|
||||||
foreach (var mod in Mods.Value.OfType<IApplicableToClock>())
|
foreach (var mod in Mods.Value.OfType<IApplicableToTrack>())
|
||||||
mod.ApplyToClock(Beatmap.Value.Track);
|
mod.ApplyToTrack(Beatmap.Value.Track);
|
||||||
|
|
||||||
InputManager.Child = container = new TestPlayerLoaderContainer(
|
InputManager.Child = container = new TestPlayerLoaderContainer(
|
||||||
loader = new TestPlayerLoader(() =>
|
loader = new TestPlayerLoader(() =>
|
||||||
|
65
osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs
Normal file
65
osu.Game.Tests/Visual/Settings/TestSceneSettingsSource.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Settings
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneSettingsSource : OsuTestScene
|
||||||
|
{
|
||||||
|
public TestSceneSettingsSource()
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(20),
|
||||||
|
Width = 0.5f,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Padding = new MarginPadding(50),
|
||||||
|
ChildrenEnumerable = new TestTargetClass().CreateSettingsControls()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestTargetClass
|
||||||
|
{
|
||||||
|
[SettingSource("Sample bool", "Clicking this changes a setting")]
|
||||||
|
public BindableBool TickBindable { get; } = new BindableBool();
|
||||||
|
|
||||||
|
[SettingSource("Sample float", "Change something for a mod")]
|
||||||
|
public BindableFloat SliderBindable { get; } = new BindableFloat
|
||||||
|
{
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = 10,
|
||||||
|
Default = 5,
|
||||||
|
Value = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Sample enum", "Change something for a mod")]
|
||||||
|
public Bindable<TestEnum> EnumBindable { get; } = new Bindable<TestEnum>
|
||||||
|
{
|
||||||
|
Default = TestEnum.Value1,
|
||||||
|
Value = TestEnum.Value2
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Sample string", "Change something for a mod")]
|
||||||
|
public Bindable<string> StringBindable { get; } = new Bindable<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TestEnum
|
||||||
|
{
|
||||||
|
Value1,
|
||||||
|
Value2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -95,6 +95,42 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
AddAssert("filter count is 1", () => songSelect.FilterCount == 1);
|
AddAssert("filter count is 1", () => songSelect.FilterCount == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoFilterOnSimpleResume()
|
||||||
|
{
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
|
||||||
|
createSongSelect();
|
||||||
|
|
||||||
|
AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child")));
|
||||||
|
AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
|
||||||
|
|
||||||
|
AddStep("return", () => songSelect.MakeCurrent());
|
||||||
|
AddUntilStep("wait for current", () => songSelect.IsCurrentScreen());
|
||||||
|
AddAssert("filter count is 1", () => songSelect.FilterCount == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFilterOnResumeAfterChange()
|
||||||
|
{
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
addRulesetImportStep(0);
|
||||||
|
|
||||||
|
AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, false));
|
||||||
|
|
||||||
|
createSongSelect();
|
||||||
|
|
||||||
|
AddStep("push child screen", () => Stack.Push(new TestSceneOsuScreenStack.TestScreen("test child")));
|
||||||
|
AddUntilStep("wait for not current", () => !songSelect.IsCurrentScreen());
|
||||||
|
|
||||||
|
AddStep("change convert setting", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true));
|
||||||
|
|
||||||
|
AddStep("return", () => songSelect.MakeCurrent());
|
||||||
|
AddUntilStep("wait for current", () => songSelect.IsCurrentScreen());
|
||||||
|
AddAssert("filter count is 2", () => songSelect.FilterCount == 2);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAudioResuming()
|
public void TestAudioResuming()
|
||||||
{
|
{
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
AddAssert("Parallax is off", () => stack.ParallaxAmount == 0);
|
AddAssert("Parallax is off", () => stack.ParallaxAmount == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestScreen : ScreenWithBeatmapBackground
|
public class TestScreen : ScreenWithBeatmapBackground
|
||||||
{
|
{
|
||||||
private readonly string screenText;
|
private readonly string screenText;
|
||||||
|
|
||||||
|
@ -83,22 +83,16 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScrollState _scrollState;
|
private ScrollState scrollState;
|
||||||
|
|
||||||
private ScrollState scrollState
|
private void setScrollState(ScrollState newstate)
|
||||||
{
|
{
|
||||||
get => _scrollState;
|
if (scrollState == newstate)
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_scrollState == value)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_scrollState = value;
|
|
||||||
|
|
||||||
delayedStateChangeDelegate?.Cancel();
|
delayedStateChangeDelegate?.Cancel();
|
||||||
|
|
||||||
switch (value)
|
switch (scrollState = newstate)
|
||||||
{
|
{
|
||||||
case ScrollState.Scrolling:
|
case ScrollState.Scrolling:
|
||||||
resetSelected();
|
resetSelected();
|
||||||
@ -113,7 +107,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
|||||||
speedTo(0f, 2000);
|
speedTo(0f, 2000);
|
||||||
tracker.FadeIn(200);
|
tracker.FadeIn(200);
|
||||||
|
|
||||||
delayedStateChangeDelegate = Scheduler.AddDelayed(() => scrollState = ScrollState.Stopped, 2300);
|
delayedStateChangeDelegate = Scheduler.AddDelayed(() => setScrollState(ScrollState.Stopped), 2300);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ScrollState.Stopped:
|
case ScrollState.Stopped:
|
||||||
@ -153,7 +147,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
|||||||
st.Selected = true;
|
st.Selected = true;
|
||||||
OnSelected?.Invoke(st.Team);
|
OnSelected?.Invoke(st.Team);
|
||||||
|
|
||||||
delayedStateChangeDelegate = Scheduler.AddDelayed(() => scrollState = ScrollState.Idle, 10000);
|
delayedStateChangeDelegate = Scheduler.AddDelayed(() => setScrollState(ScrollState.Idle), 10000);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ScrollState.Idle:
|
case ScrollState.Idle:
|
||||||
@ -166,7 +160,6 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void AddTeam(TournamentTeam team)
|
public void AddTeam(TournamentTeam team)
|
||||||
{
|
{
|
||||||
@ -176,7 +169,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
|||||||
availableTeams.Add(team);
|
availableTeams.Add(team);
|
||||||
|
|
||||||
RemoveAll(c => c is ScrollingTeam);
|
RemoveAll(c => c is ScrollingTeam);
|
||||||
scrollState = ScrollState.Idle;
|
setScrollState(ScrollState.Idle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddTeams(IEnumerable<TournamentTeam> teams)
|
public void AddTeams(IEnumerable<TournamentTeam> teams)
|
||||||
@ -192,7 +185,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
|||||||
{
|
{
|
||||||
availableTeams.Clear();
|
availableTeams.Clear();
|
||||||
RemoveAll(c => c is ScrollingTeam);
|
RemoveAll(c => c is ScrollingTeam);
|
||||||
scrollState = ScrollState.Idle;
|
setScrollState(ScrollState.Idle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveTeam(TournamentTeam team)
|
public void RemoveTeam(TournamentTeam team)
|
||||||
@ -217,7 +210,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
|||||||
if (availableTeams.Count == 0)
|
if (availableTeams.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
scrollState = ScrollState.Scrolling;
|
setScrollState(ScrollState.Scrolling);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopScrolling()
|
public void StopScrolling()
|
||||||
@ -232,13 +225,13 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollState = ScrollState.Stopping;
|
setScrollState(ScrollState.Stopping);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
scrollState = ScrollState.Idle;
|
setScrollState(ScrollState.Idle);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateAfterChildren()
|
protected override void UpdateAfterChildren()
|
||||||
@ -305,7 +298,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
|||||||
private void speedTo(float value, double duration = 0, Easing easing = Easing.None) =>
|
private void speedTo(float value, double duration = 0, Easing easing = Easing.None) =>
|
||||||
this.TransformTo(nameof(speed), value, duration, easing);
|
this.TransformTo(nameof(speed), value, duration, easing);
|
||||||
|
|
||||||
private enum ScrollState
|
protected enum ScrollState
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
Idle,
|
Idle,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -9,8 +10,8 @@ using osu.Game.Rulesets;
|
|||||||
|
|
||||||
namespace osu.Game.Configuration
|
namespace osu.Game.Configuration
|
||||||
{
|
{
|
||||||
public abstract class DatabasedConfigManager<T> : ConfigManager<T>
|
public abstract class DatabasedConfigManager<TLookup> : ConfigManager<TLookup>
|
||||||
where T : struct
|
where TLookup : struct, Enum
|
||||||
{
|
{
|
||||||
private readonly SettingsStore settings;
|
private readonly SettingsStore settings;
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
private readonly List<DatabasedSetting> dirtySettings = new List<DatabasedSetting>();
|
private readonly List<DatabasedSetting> dirtySettings = new List<DatabasedSetting>();
|
||||||
|
|
||||||
protected override void AddBindable<TBindable>(T lookup, Bindable<TBindable> bindable)
|
protected override void AddBindable<TBindable>(TLookup lookup, Bindable<TBindable> bindable)
|
||||||
{
|
{
|
||||||
base.AddBindable(lookup, bindable);
|
base.AddBindable(lookup, bindable);
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
|
|
||||||
namespace osu.Game.Configuration
|
namespace osu.Game.Configuration
|
||||||
{
|
{
|
||||||
public class InMemoryConfigManager<T> : ConfigManager<T>
|
public class InMemoryConfigManager<TLookup> : ConfigManager<TLookup>
|
||||||
where T : struct
|
where TLookup : struct, Enum
|
||||||
{
|
{
|
||||||
public InMemoryConfigManager()
|
public InMemoryConfigManager()
|
||||||
{
|
{
|
||||||
|
110
osu.Game/Configuration/SettingSourceAttribute.cs
Normal file
110
osu.Game/Configuration/SettingSourceAttribute.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
|
||||||
|
namespace osu.Game.Configuration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An attribute to mark a bindable as being exposed to the user via settings controls.
|
||||||
|
/// Can be used in conjunction with <see cref="SettingSourceExtensions.CreateSettingsControls"/> to automatically create UI controls.
|
||||||
|
/// </summary>
|
||||||
|
[MeansImplicitUse]
|
||||||
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
|
public class SettingSourceAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string Label { get; }
|
||||||
|
|
||||||
|
public string Description { get; }
|
||||||
|
|
||||||
|
public SettingSourceAttribute(string label, string description = null)
|
||||||
|
{
|
||||||
|
Label = label ?? string.Empty;
|
||||||
|
Description = description ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SettingSourceExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<Drawable> CreateSettingsControls(this object obj)
|
||||||
|
{
|
||||||
|
foreach (var property in obj.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance))
|
||||||
|
{
|
||||||
|
var attr = property.GetCustomAttribute<SettingSourceAttribute>(true);
|
||||||
|
|
||||||
|
if (attr == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var prop = property.GetValue(obj);
|
||||||
|
|
||||||
|
switch (prop)
|
||||||
|
{
|
||||||
|
case BindableNumber<float> bNumber:
|
||||||
|
yield return new SettingsSlider<float>
|
||||||
|
{
|
||||||
|
LabelText = attr.Label,
|
||||||
|
Bindable = bNumber
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BindableNumber<double> bNumber:
|
||||||
|
yield return new SettingsSlider<double>
|
||||||
|
{
|
||||||
|
LabelText = attr.Label,
|
||||||
|
Bindable = bNumber
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BindableNumber<int> bNumber:
|
||||||
|
yield return new SettingsSlider<int>
|
||||||
|
{
|
||||||
|
LabelText = attr.Label,
|
||||||
|
Bindable = bNumber
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Bindable<bool> bBool:
|
||||||
|
yield return new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = attr.Label,
|
||||||
|
Bindable = bBool
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Bindable<string> bString:
|
||||||
|
yield return new SettingsTextBox
|
||||||
|
{
|
||||||
|
LabelText = attr.Label,
|
||||||
|
Bindable = bString
|
||||||
|
};
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IBindable bindable:
|
||||||
|
var dropdownType = typeof(SettingsEnumDropdown<>).MakeGenericType(bindable.GetType().GetGenericArguments()[0]);
|
||||||
|
var dropdown = (Drawable)Activator.CreateInstance(dropdownType);
|
||||||
|
|
||||||
|
dropdown.GetType().GetProperty(nameof(IHasCurrentValue<object>.Current))?.SetValue(dropdown, obj);
|
||||||
|
|
||||||
|
yield return dropdown;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"{nameof(SettingSourceAttribute)} was attached to an unsupported type ({prop})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,7 @@ namespace osu.Game.Graphics
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||||
public static TransformSequence<T> FadeAccent<T>(this T accentedDrawable, Color4 newColour, double duration = 0, Easing easing = Easing.None)
|
public static TransformSequence<T> FadeAccent<T>(this T accentedDrawable, Color4 newColour, double duration = 0, Easing easing = Easing.None)
|
||||||
where T : IHasAccentColour
|
where T : class, IHasAccentColour
|
||||||
=> accentedDrawable.TransformTo(nameof(accentedDrawable.AccentColour), newColour, duration, easing);
|
=> accentedDrawable.TransformTo(nameof(accentedDrawable.AccentColour), newColour, duration, easing);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -6,12 +6,10 @@ using System;
|
|||||||
namespace osu.Game.Graphics.UserInterface
|
namespace osu.Game.Graphics.UserInterface
|
||||||
{
|
{
|
||||||
public class OsuEnumDropdown<T> : OsuDropdown<T>
|
public class OsuEnumDropdown<T> : OsuDropdown<T>
|
||||||
|
where T : struct, Enum
|
||||||
{
|
{
|
||||||
public OsuEnumDropdown()
|
public OsuEnumDropdown()
|
||||||
{
|
{
|
||||||
if (!typeof(T).IsEnum)
|
|
||||||
throw new InvalidOperationException("OsuEnumDropdown only supports enums as the generic type argument");
|
|
||||||
|
|
||||||
Items = (T[])Enum.GetValues(typeof(T));
|
Items = (T[])Enum.GetValues(typeof(T));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Online
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly Bindable<DownloadState> State = new Bindable<DownloadState>();
|
protected readonly Bindable<DownloadState> State = new Bindable<DownloadState>();
|
||||||
|
|
||||||
protected readonly Bindable<double> Progress = new Bindable<double>();
|
protected readonly BindableNumber<double> Progress = new BindableNumber<double> { MinValue = 0, MaxValue = 1 };
|
||||||
|
|
||||||
protected DownloadTrackingComposite(TModel model = null)
|
protected DownloadTrackingComposite(TModel model = null)
|
||||||
{
|
{
|
||||||
|
@ -45,23 +45,25 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
[JsonProperty("beatmap")]
|
[JsonProperty("beatmap")]
|
||||||
private APIBeatmap apiBeatmap { get; set; }
|
private APIBeatmap apiBeatmap { get; set; }
|
||||||
|
|
||||||
|
private APIMod[] allowedModsBacking;
|
||||||
|
|
||||||
[JsonProperty("allowed_mods")]
|
[JsonProperty("allowed_mods")]
|
||||||
private APIMod[] allowedMods
|
private APIMod[] allowedMods
|
||||||
{
|
{
|
||||||
get => AllowedMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
|
get => AllowedMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
|
||||||
set => _allowedMods = value;
|
set => allowedModsBacking = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private APIMod[] requiredModsBacking;
|
||||||
|
|
||||||
[JsonProperty("required_mods")]
|
[JsonProperty("required_mods")]
|
||||||
private APIMod[] requiredMods
|
private APIMod[] requiredMods
|
||||||
{
|
{
|
||||||
get => RequiredMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
|
get => RequiredMods.Select(m => new APIMod { Acronym = m.Acronym }).ToArray();
|
||||||
set => _requiredMods = value;
|
set => requiredModsBacking = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BeatmapInfo beatmap;
|
private BeatmapInfo beatmap;
|
||||||
private APIMod[] _allowedMods;
|
|
||||||
private APIMod[] _requiredMods;
|
|
||||||
|
|
||||||
public void MapObjects(BeatmapManager beatmaps, RulesetStore rulesets)
|
public void MapObjects(BeatmapManager beatmaps, RulesetStore rulesets)
|
||||||
{
|
{
|
||||||
@ -70,20 +72,20 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
Beatmap = apiBeatmap == null ? beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == BeatmapID) : apiBeatmap.ToBeatmap(rulesets);
|
Beatmap = apiBeatmap == null ? beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == BeatmapID) : apiBeatmap.ToBeatmap(rulesets);
|
||||||
Ruleset = rulesets.GetRuleset(RulesetID);
|
Ruleset = rulesets.GetRuleset(RulesetID);
|
||||||
|
|
||||||
if (_allowedMods != null)
|
if (allowedModsBacking != null)
|
||||||
{
|
{
|
||||||
AllowedMods.Clear();
|
AllowedMods.Clear();
|
||||||
AllowedMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => _allowedMods.Any(m => m.Acronym == mod.Acronym)));
|
AllowedMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => allowedModsBacking.Any(m => m.Acronym == mod.Acronym)));
|
||||||
|
|
||||||
_allowedMods = null;
|
allowedModsBacking = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_requiredMods != null)
|
if (requiredModsBacking != null)
|
||||||
{
|
{
|
||||||
RequiredMods.Clear();
|
RequiredMods.Clear();
|
||||||
RequiredMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => _requiredMods.Any(m => m.Acronym == mod.Acronym)));
|
RequiredMods.AddRange(Ruleset.CreateInstance().GetAllMods().Where(mod => requiredModsBacking.Any(m => m.Acronym == mod.Acronym)));
|
||||||
|
|
||||||
_requiredMods = null;
|
requiredModsBacking = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,8 +261,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
if (allowRateAdjustments)
|
if (allowRateAdjustments)
|
||||||
{
|
{
|
||||||
foreach (var mod in mods.Value.OfType<IApplicableToClock>())
|
foreach (var mod in mods.Value.OfType<IApplicableToTrack>())
|
||||||
mod.ApplyToClock(track);
|
mod.ApplyToTrack(track);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -11,6 +12,7 @@ using osu.Game.Graphics.Containers;
|
|||||||
namespace osu.Game.Overlays.SearchableList
|
namespace osu.Game.Overlays.SearchableList
|
||||||
{
|
{
|
||||||
public class DisplayStyleControl<T> : Container
|
public class DisplayStyleControl<T> : Container
|
||||||
|
where T : struct, Enum
|
||||||
{
|
{
|
||||||
public readonly SlimEnumDropdown<T> Dropdown;
|
public readonly SlimEnumDropdown<T> Dropdown;
|
||||||
public readonly Bindable<PanelDisplayStyle> DisplayStyle = new Bindable<PanelDisplayStyle>();
|
public readonly Bindable<PanelDisplayStyle> DisplayStyle = new Bindable<PanelDisplayStyle>();
|
||||||
|
@ -13,7 +13,9 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.SearchableList
|
namespace osu.Game.Overlays.SearchableList
|
||||||
{
|
{
|
||||||
public abstract class SearchableListFilterControl<T, U> : Container
|
public abstract class SearchableListFilterControl<TTab, TCategory> : Container
|
||||||
|
where TTab : struct, Enum
|
||||||
|
where TCategory : struct, Enum
|
||||||
{
|
{
|
||||||
private const float padding = 10;
|
private const float padding = 10;
|
||||||
|
|
||||||
@ -21,12 +23,12 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
private readonly Box tabStrip;
|
private readonly Box tabStrip;
|
||||||
|
|
||||||
public readonly SearchTextBox Search;
|
public readonly SearchTextBox Search;
|
||||||
public readonly PageTabControl<T> Tabs;
|
public readonly PageTabControl<TTab> Tabs;
|
||||||
public readonly DisplayStyleControl<U> DisplayStyleControl;
|
public readonly DisplayStyleControl<TCategory> DisplayStyleControl;
|
||||||
|
|
||||||
protected abstract Color4 BackgroundColour { get; }
|
protected abstract Color4 BackgroundColour { get; }
|
||||||
protected abstract T DefaultTab { get; }
|
protected abstract TTab DefaultTab { get; }
|
||||||
protected abstract U DefaultCategory { get; }
|
protected abstract TCategory DefaultCategory { get; }
|
||||||
protected virtual Drawable CreateSupplementaryControls() => null;
|
protected virtual Drawable CreateSupplementaryControls() => null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -36,9 +38,6 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
|
|
||||||
protected SearchableListFilterControl()
|
protected SearchableListFilterControl()
|
||||||
{
|
{
|
||||||
if (!typeof(T).IsEnum)
|
|
||||||
throw new InvalidOperationException("SearchableListFilterControl's sort tabs only support enums as the generic type argument");
|
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
var controls = CreateSupplementaryControls();
|
var controls = CreateSupplementaryControls();
|
||||||
@ -90,7 +89,7 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding { Right = 225 },
|
Padding = new MarginPadding { Right = 225 },
|
||||||
Child = Tabs = new PageTabControl<T>
|
Child = Tabs = new PageTabControl<TTab>
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
},
|
},
|
||||||
@ -105,7 +104,7 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DisplayStyleControl = new DisplayStyleControl<U>
|
DisplayStyleControl = new DisplayStyleControl<TCategory>
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
|
@ -14,6 +14,7 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
namespace osu.Game.Overlays.SearchableList
|
namespace osu.Game.Overlays.SearchableList
|
||||||
{
|
{
|
||||||
public abstract class SearchableListHeader<T> : Container
|
public abstract class SearchableListHeader<T> : Container
|
||||||
|
where T : struct, Enum
|
||||||
{
|
{
|
||||||
public readonly HeaderTabControl<T> Tabs;
|
public readonly HeaderTabControl<T> Tabs;
|
||||||
|
|
||||||
@ -24,9 +25,6 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
|
|
||||||
protected SearchableListHeader()
|
protected SearchableListHeader()
|
||||||
{
|
{
|
||||||
if (!typeof(T).IsEnum)
|
|
||||||
throw new InvalidOperationException("BrowseHeader only supports enums as the generic type argument");
|
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = 90;
|
Height = 90;
|
||||||
|
|
||||||
|
@ -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 osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -16,19 +17,22 @@ namespace osu.Game.Overlays.SearchableList
|
|||||||
public const float WIDTH_PADDING = 80;
|
public const float WIDTH_PADDING = 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class SearchableListOverlay<T, U, S> : SearchableListOverlay
|
public abstract class SearchableListOverlay<THeader, TTab, TCategory> : SearchableListOverlay
|
||||||
|
where THeader : struct, Enum
|
||||||
|
where TTab : struct, Enum
|
||||||
|
where TCategory : struct, Enum
|
||||||
{
|
{
|
||||||
private readonly Container scrollContainer;
|
private readonly Container scrollContainer;
|
||||||
|
|
||||||
protected readonly SearchableListHeader<T> Header;
|
protected readonly SearchableListHeader<THeader> Header;
|
||||||
protected readonly SearchableListFilterControl<U, S> Filter;
|
protected readonly SearchableListFilterControl<TTab, TCategory> Filter;
|
||||||
protected readonly FillFlowContainer ScrollFlow;
|
protected readonly FillFlowContainer ScrollFlow;
|
||||||
|
|
||||||
protected abstract Color4 BackgroundColour { get; }
|
protected abstract Color4 BackgroundColour { get; }
|
||||||
protected abstract Color4 TrianglesColourLight { get; }
|
protected abstract Color4 TrianglesColourLight { get; }
|
||||||
protected abstract Color4 TrianglesColourDark { get; }
|
protected abstract Color4 TrianglesColourDark { get; }
|
||||||
protected abstract SearchableListHeader<T> CreateHeader();
|
protected abstract SearchableListHeader<THeader> CreateHeader();
|
||||||
protected abstract SearchableListFilterControl<U, S> CreateFilterControl();
|
protected abstract SearchableListFilterControl<TTab, TCategory> CreateFilterControl();
|
||||||
|
|
||||||
protected SearchableListOverlay()
|
protected SearchableListOverlay()
|
||||||
{
|
{
|
||||||
|
@ -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 osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -11,6 +12,7 @@ using osuTK;
|
|||||||
namespace osu.Game.Overlays.SearchableList
|
namespace osu.Game.Overlays.SearchableList
|
||||||
{
|
{
|
||||||
public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
|
public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
|
||||||
|
where T : struct, Enum
|
||||||
{
|
{
|
||||||
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader();
|
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader();
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
public class SettingsEnumDropdown<T> : SettingsDropdown<T>
|
public class SettingsEnumDropdown<T> : SettingsDropdown<T>
|
||||||
|
where T : struct, Enum
|
||||||
{
|
{
|
||||||
protected override OsuDropdown<T> CreateDropdown() => new DropdownControl();
|
protected override OsuDropdown<T> CreateDropdown() => new DropdownControl();
|
||||||
|
|
||||||
|
@ -53,27 +53,10 @@ namespace osu.Game.Overlays.Settings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hold a reference to the provided bindable so we don't have to in every settings section.
|
|
||||||
private Bindable<T> bindable;
|
|
||||||
|
|
||||||
public virtual Bindable<T> Bindable
|
public virtual Bindable<T> Bindable
|
||||||
{
|
{
|
||||||
get => bindable;
|
get => controlWithCurrent.Current;
|
||||||
|
set => controlWithCurrent.Current = value;
|
||||||
set
|
|
||||||
{
|
|
||||||
if (bindable != null)
|
|
||||||
controlWithCurrent?.Current.UnbindFrom(bindable);
|
|
||||||
|
|
||||||
bindable = value;
|
|
||||||
controlWithCurrent?.Current.BindTo(bindable);
|
|
||||||
|
|
||||||
if (ShowsDefaultIndicator)
|
|
||||||
{
|
|
||||||
restoreDefaultButton.Bindable = bindable.GetBoundCopy();
|
|
||||||
restoreDefaultButton.Bindable.TriggerChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual IEnumerable<string> FilterTerms => Keywords == null ? new[] { LabelText } : new List<string>(Keywords) { LabelText }.ToArray();
|
public virtual IEnumerable<string> FilterTerms => Keywords == null ? new[] { LabelText } : new List<string>(Keywords) { LabelText }.ToArray();
|
||||||
@ -110,7 +93,12 @@ namespace osu.Game.Overlays.Settings
|
|||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
if (controlWithCurrent != null)
|
if (controlWithCurrent != null)
|
||||||
|
{
|
||||||
controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; };
|
controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; };
|
||||||
|
|
||||||
|
if (ShowsDefaultIndicator)
|
||||||
|
restoreDefaultButton.Bindable = controlWithCurrent.Current;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RestoreDefaultValueButton : Container, IHasTooltip
|
private class RestoreDefaultValueButton : Container, IHasTooltip
|
||||||
@ -125,6 +113,7 @@ namespace osu.Game.Overlays.Settings
|
|||||||
bindable = value;
|
bindable = value;
|
||||||
bindable.ValueChanged += _ => UpdateState();
|
bindable.ValueChanged += _ => UpdateState();
|
||||||
bindable.DisabledChanged += _ => UpdateState();
|
bindable.DisabledChanged += _ => UpdateState();
|
||||||
|
UpdateState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Configuration
|
namespace osu.Game.Rulesets.Configuration
|
||||||
{
|
{
|
||||||
public abstract class RulesetConfigManager<T> : DatabasedConfigManager<T>, IRulesetConfigManager
|
public abstract class RulesetConfigManager<TLookup> : DatabasedConfigManager<TLookup>, IRulesetConfigManager
|
||||||
where T : struct
|
where TLookup : struct, Enum
|
||||||
{
|
{
|
||||||
protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
|
protected RulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null)
|
||||||
: base(settings, ruleset, variant)
|
: base(settings, ruleset, variant)
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
@ -41,10 +41,10 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
|
|
||||||
IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
|
IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
|
||||||
|
|
||||||
var clock = new StopwatchClock();
|
var track = new TrackVirtual(10000);
|
||||||
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
|
mods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(track));
|
||||||
|
|
||||||
return calculate(playableBeatmap, mods, clock.Rate);
|
return calculate(playableBeatmap, mods, track.Rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -35,9 +35,9 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
|
|
||||||
protected virtual void ApplyMods(Mod[] mods)
|
protected virtual void ApplyMods(Mod[] mods)
|
||||||
{
|
{
|
||||||
var clock = new StopwatchClock();
|
var track = new TrackVirtual(10000);
|
||||||
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
|
mods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(track));
|
||||||
TimeRate = clock.Rate;
|
TimeRate = track.Rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
|
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
// 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.Timing;
|
using osu.Framework.Audio.Track;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An interface for mods that make adjustments to the track.
|
/// An interface for mods that make adjustments to the track.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IApplicableToClock : IApplicableMod
|
public interface IApplicableToTrack : IApplicableMod
|
||||||
{
|
{
|
||||||
void ApplyToClock(IAdjustableClock clock);
|
void ApplyToTrack(Track track);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +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.Audio;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Timing;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
@ -14,12 +13,9 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override IconUsage Icon => FontAwesome.Solid.Question;
|
public override IconUsage Icon => FontAwesome.Solid.Question;
|
||||||
public override string Description => "Whoaaaaa...";
|
public override string Description => "Whoaaaaa...";
|
||||||
|
|
||||||
public override void ApplyToClock(IAdjustableClock clock)
|
public override void ApplyToTrack(Track track)
|
||||||
{
|
{
|
||||||
if (clock is IHasPitchAdjust pitchAdjust)
|
track.Frequency.Value *= RateAdjust;
|
||||||
pitchAdjust.PitchAdjust *= RateAdjust;
|
|
||||||
else
|
|
||||||
base.ApplyToClock(clock);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ using osu.Game.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
public abstract class ModDoubleTime : ModTimeAdjust, IApplicableToClock
|
public abstract class ModDoubleTime : ModTimeAdjust
|
||||||
{
|
{
|
||||||
public override string Name => "Double Time";
|
public override string Name => "Double Time";
|
||||||
public override string Acronym => "DT";
|
public override string Acronym => "DT";
|
||||||
|
@ -8,7 +8,7 @@ using osu.Game.Graphics;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
public abstract class ModHalfTime : ModTimeAdjust, IApplicableToClock
|
public abstract class ModHalfTime : ModTimeAdjust
|
||||||
{
|
{
|
||||||
public override string Name => "Half Time";
|
public override string Name => "Half Time";
|
||||||
public override string Acronym => "HT";
|
public override string Acronym => "HT";
|
||||||
|
@ -1,9 +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.Audio;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
@ -15,12 +14,9 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override IconUsage Icon => OsuIcon.ModNightcore;
|
public override IconUsage Icon => OsuIcon.ModNightcore;
|
||||||
public override string Description => "Uguuuuuuuu...";
|
public override string Description => "Uguuuuuuuu...";
|
||||||
|
|
||||||
public override void ApplyToClock(IAdjustableClock clock)
|
public override void ApplyToTrack(Track track)
|
||||||
{
|
{
|
||||||
if (clock is IHasPitchAdjust pitchAdjust)
|
track.Frequency.Value *= RateAdjust;
|
||||||
pitchAdjust.PitchAdjust *= RateAdjust;
|
|
||||||
else
|
|
||||||
base.ApplyToClock(clock);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,23 +2,19 @@
|
|||||||
// 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;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Timing;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
public abstract class ModTimeAdjust : Mod
|
public abstract class ModTimeAdjust : Mod, IApplicableToTrack
|
||||||
{
|
{
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) };
|
||||||
|
|
||||||
protected abstract double RateAdjust { get; }
|
protected abstract double RateAdjust { get; }
|
||||||
|
|
||||||
public virtual void ApplyToClock(IAdjustableClock clock)
|
public virtual void ApplyToTrack(Track track)
|
||||||
{
|
{
|
||||||
if (clock is IHasTempoAdjust tempo)
|
track.Tempo.Value *= RateAdjust;
|
||||||
tempo.TempoAdjust *= RateAdjust;
|
|
||||||
else
|
|
||||||
clock.Rate *= RateAdjust;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,14 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Timing;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToClock, IApplicableToBeatmap
|
public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToTrack, IApplicableToBeatmap
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The point in the beatmap at which the final ramping rate should be reached.
|
/// The point in the beatmap at which the final ramping rate should be reached.
|
||||||
@ -24,11 +23,11 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
|
|
||||||
private double finalRateTime;
|
private double finalRateTime;
|
||||||
private double beginRampTime;
|
private double beginRampTime;
|
||||||
private IAdjustableClock clock;
|
private Track track;
|
||||||
|
|
||||||
public virtual void ApplyToClock(IAdjustableClock clock)
|
public virtual void ApplyToTrack(Track track)
|
||||||
{
|
{
|
||||||
this.clock = clock;
|
this.track = track;
|
||||||
|
|
||||||
lastAdjust = 1;
|
lastAdjust = 1;
|
||||||
|
|
||||||
@ -46,7 +45,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
|
|
||||||
public virtual void Update(Playfield playfield)
|
public virtual void Update(Playfield playfield)
|
||||||
{
|
{
|
||||||
applyAdjustment((clock.CurrentTime - beginRampTime) / finalRateTime);
|
applyAdjustment((track.CurrentTime - beginRampTime) / finalRateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double lastAdjust = 1;
|
private double lastAdjust = 1;
|
||||||
@ -59,23 +58,8 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
{
|
{
|
||||||
double adjust = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment));
|
double adjust = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment));
|
||||||
|
|
||||||
switch (clock)
|
track.Tempo.Value /= lastAdjust;
|
||||||
{
|
track.Tempo.Value *= adjust;
|
||||||
case IHasPitchAdjust pitch:
|
|
||||||
pitch.PitchAdjust /= lastAdjust;
|
|
||||||
pitch.PitchAdjust *= adjust;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IHasTempoAdjust tempo:
|
|
||||||
tempo.TempoAdjust /= lastAdjust;
|
|
||||||
tempo.TempoAdjust *= adjust;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
clock.Rate /= lastAdjust;
|
|
||||||
clock.Rate *= adjust;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastAdjust = adjust;
|
lastAdjust = adjust;
|
||||||
}
|
}
|
||||||
|
@ -257,14 +257,13 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
{
|
{
|
||||||
if (type == PathType.PerfectCurve)
|
if (type == PathType.PerfectCurve)
|
||||||
{
|
{
|
||||||
if (vertices.Length == 3)
|
if (vertices.Length != 3)
|
||||||
|
type = PathType.Bezier;
|
||||||
|
else if (isLinear(vertices))
|
||||||
{
|
{
|
||||||
// osu-stable special-cased colinear perfect curves to a linear path
|
// osu-stable special-cased colinear perfect curves to a linear path
|
||||||
if (isLinear(vertices))
|
|
||||||
type = PathType.Linear;
|
type = PathType.Linear;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
type = PathType.Bezier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var points = new List<PathControlPoint>(vertices.Length)
|
var points = new List<PathControlPoint>(vertices.Length)
|
||||||
|
@ -30,8 +30,9 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// Creates a new <see cref="PathControlPoint"/>.
|
/// Creates a new <see cref="PathControlPoint"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PathControlPoint()
|
public PathControlPoint()
|
||||||
: this(Vector2.Zero, null)
|
|
||||||
{
|
{
|
||||||
|
Position.ValueChanged += _ => Changed?.Invoke();
|
||||||
|
Type.ValueChanged += _ => Changed?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -40,12 +41,10 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// <param name="position">The initial position.</param>
|
/// <param name="position">The initial position.</param>
|
||||||
/// <param name="type">The initial type.</param>
|
/// <param name="type">The initial type.</param>
|
||||||
public PathControlPoint(Vector2 position, PathType? type = null)
|
public PathControlPoint(Vector2 position, PathType? type = null)
|
||||||
|
: this()
|
||||||
{
|
{
|
||||||
Position.Value = position;
|
Position.Value = position;
|
||||||
Type.Value = type;
|
Type.Value = type;
|
||||||
|
|
||||||
Position.ValueChanged += _ => Changed?.Invoke();
|
|
||||||
Type.ValueChanged += _ => Changed?.Invoke();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(PathControlPoint other) => Position.Value == other?.Position.Value && Type.Value == other.Type.Value;
|
public bool Equals(PathControlPoint other) => Position.Value == other?.Position.Value && Type.Value == other.Type.Value;
|
||||||
|
@ -511,15 +511,19 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
public IEnumerable<string> GetAvailableResources() => throw new NotImplementedException();
|
public IEnumerable<string> GetAvailableResources() => throw new NotImplementedException();
|
||||||
|
|
||||||
public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException();
|
public void AddAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable) => throw new NotImplementedException();
|
||||||
|
|
||||||
public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException();
|
public void RemoveAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable) => throw new NotImplementedException();
|
||||||
|
|
||||||
public BindableDouble Volume => throw new NotImplementedException();
|
public BindableNumber<double> Volume => throw new NotImplementedException();
|
||||||
|
|
||||||
public BindableDouble Balance => throw new NotImplementedException();
|
public BindableNumber<double> Balance => throw new NotImplementedException();
|
||||||
|
|
||||||
public BindableDouble Frequency => throw new NotImplementedException();
|
public BindableNumber<double> Frequency => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public BindableNumber<double> Tempo => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public IBindable<double> GetAggregate(AdjustableProperty type) => throw new NotImplementedException();
|
||||||
|
|
||||||
public IBindable<double> AggregateVolume => throw new NotImplementedException();
|
public IBindable<double> AggregateVolume => throw new NotImplementedException();
|
||||||
|
|
||||||
@ -527,6 +531,8 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
public IBindable<double> AggregateFrequency => throw new NotImplementedException();
|
public IBindable<double> AggregateFrequency => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public IBindable<double> AggregateTempo => throw new NotImplementedException();
|
||||||
|
|
||||||
public int PlaybackConcurrency
|
public int PlaybackConcurrency
|
||||||
{
|
{
|
||||||
get => throw new NotImplementedException();
|
get => throw new NotImplementedException();
|
||||||
|
@ -28,9 +28,9 @@ namespace osu.Game.Screens.Play
|
|||||||
private readonly IReadOnlyList<Mod> mods;
|
private readonly IReadOnlyList<Mod> mods;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The original source (usually a <see cref="WorkingBeatmap"/>'s track).
|
/// The <see cref="WorkingBeatmap"/>'s track.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private IAdjustableClock sourceClock;
|
private Track track;
|
||||||
|
|
||||||
public readonly BindableBool IsPaused = new BindableBool();
|
public readonly BindableBool IsPaused = new BindableBool();
|
||||||
|
|
||||||
@ -72,8 +72,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
sourceClock = (IAdjustableClock)beatmap.Track ?? new StopwatchClock();
|
track = beatmap.Track;
|
||||||
(sourceClock as IAdjustableAudioComponent)?.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
||||||
|
|
||||||
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||||
|
|
||||||
@ -127,11 +127,11 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
sourceClock.Reset();
|
track.Reset();
|
||||||
|
|
||||||
Schedule(() =>
|
Schedule(() =>
|
||||||
{
|
{
|
||||||
adjustableClock.ChangeSource(sourceClock);
|
adjustableClock.ChangeSource(track);
|
||||||
updateRate();
|
updateRate();
|
||||||
|
|
||||||
if (!IsPaused.Value)
|
if (!IsPaused.Value)
|
||||||
@ -197,13 +197,13 @@ namespace osu.Game.Screens.Play
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void StopUsingBeatmapClock()
|
public void StopUsingBeatmapClock()
|
||||||
{
|
{
|
||||||
if (sourceClock != beatmap.Track)
|
if (track != beatmap.Track)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
removeSourceClockAdjustments();
|
removeSourceClockAdjustments();
|
||||||
|
|
||||||
sourceClock = new TrackVirtual(beatmap.Track.Length);
|
track = new TrackVirtual(beatmap.Track.Length);
|
||||||
adjustableClock.ChangeSource(sourceClock);
|
adjustableClock.ChangeSource(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -218,18 +218,15 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private void updateRate()
|
private void updateRate()
|
||||||
{
|
{
|
||||||
if (sourceClock == null) return;
|
if (track == null) return;
|
||||||
|
|
||||||
speedAdjustmentsApplied = true;
|
speedAdjustmentsApplied = true;
|
||||||
sourceClock.ResetSpeedAdjustments();
|
track.ResetSpeedAdjustments();
|
||||||
|
|
||||||
if (sourceClock is IHasTempoAdjust tempo)
|
track.Tempo.Value = UserPlaybackRate.Value;
|
||||||
tempo.TempoAdjust = UserPlaybackRate.Value;
|
|
||||||
else
|
|
||||||
sourceClock.Rate = UserPlaybackRate.Value;
|
|
||||||
|
|
||||||
foreach (var mod in mods.OfType<IApplicableToClock>())
|
foreach (var mod in mods.OfType<IApplicableToTrack>())
|
||||||
mod.ApplyToClock(sourceClock);
|
mod.ApplyToTrack(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
@ -237,18 +234,18 @@ namespace osu.Game.Screens.Play
|
|||||||
base.Dispose(isDisposing);
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
removeSourceClockAdjustments();
|
removeSourceClockAdjustments();
|
||||||
sourceClock = null;
|
track = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeSourceClockAdjustments()
|
private void removeSourceClockAdjustments()
|
||||||
{
|
{
|
||||||
if (speedAdjustmentsApplied)
|
if (speedAdjustmentsApplied)
|
||||||
{
|
{
|
||||||
sourceClock.ResetSpeedAdjustments();
|
track.ResetSpeedAdjustments();
|
||||||
speedAdjustmentsApplied = false;
|
speedAdjustmentsApplied = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
(sourceClock as IAdjustableAudioComponent)?.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
track.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,26 +188,22 @@ namespace osu.Game.Screens.Play
|
|||||||
InternalButtons.Add(button);
|
InternalButtons.Add(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _selectionIndex = -1;
|
private int selectionIndex = -1;
|
||||||
|
|
||||||
private int selectionIndex
|
private void setSelected(int value)
|
||||||
{
|
{
|
||||||
get => _selectionIndex;
|
if (selectionIndex == value)
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_selectionIndex == value)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Deselect the previously-selected button
|
// Deselect the previously-selected button
|
||||||
if (_selectionIndex != -1)
|
if (selectionIndex != -1)
|
||||||
InternalButtons[_selectionIndex].Selected.Value = false;
|
InternalButtons[selectionIndex].Selected.Value = false;
|
||||||
|
|
||||||
_selectionIndex = value;
|
selectionIndex = value;
|
||||||
|
|
||||||
// Select the newly-selected button
|
// Select the newly-selected button
|
||||||
if (_selectionIndex != -1)
|
if (selectionIndex != -1)
|
||||||
InternalButtons[_selectionIndex].Selected.Value = true;
|
InternalButtons[selectionIndex].Selected.Value = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
@ -218,16 +214,16 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
case Key.Up:
|
case Key.Up:
|
||||||
if (selectionIndex == -1 || selectionIndex == 0)
|
if (selectionIndex == -1 || selectionIndex == 0)
|
||||||
selectionIndex = InternalButtons.Count - 1;
|
setSelected(InternalButtons.Count - 1);
|
||||||
else
|
else
|
||||||
selectionIndex--;
|
setSelected(selectionIndex - 1);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case Key.Down:
|
case Key.Down:
|
||||||
if (selectionIndex == -1 || selectionIndex == InternalButtons.Count - 1)
|
if (selectionIndex == -1 || selectionIndex == InternalButtons.Count - 1)
|
||||||
selectionIndex = 0;
|
setSelected(0);
|
||||||
else
|
else
|
||||||
selectionIndex++;
|
setSelected(selectionIndex + 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -266,9 +262,9 @@ namespace osu.Game.Screens.Play
|
|||||||
private void buttonSelectionChanged(DialogButton button, bool isSelected)
|
private void buttonSelectionChanged(DialogButton button, bool isSelected)
|
||||||
{
|
{
|
||||||
if (!isSelected)
|
if (!isSelected)
|
||||||
selectionIndex = -1;
|
setSelected(-1);
|
||||||
else
|
else
|
||||||
selectionIndex = InternalButtons.IndexOf(button);
|
setSelected(InternalButtons.IndexOf(button));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateRetryCount()
|
private void updateRetryCount()
|
||||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
|
|
||||||
public struct OptionalRange<T> : IEquatable<OptionalRange<T>>
|
public struct OptionalRange<T> : IEquatable<OptionalRange<T>>
|
||||||
where T : struct, IComparable
|
where T : struct
|
||||||
{
|
{
|
||||||
public bool HasFilter => Max != null || Min != null;
|
public bool HasFilter => Max != null || Min != null;
|
||||||
|
|
||||||
|
@ -170,7 +170,7 @@ namespace osu.Game.Screens.Select
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void updateCriteriaRange<T>(ref FilterCriteria.OptionalRange<T> range, string op, T value)
|
private static void updateCriteriaRange<T>(ref FilterCriteria.OptionalRange<T> range, string op, T value)
|
||||||
where T : struct, IComparable
|
where T : struct
|
||||||
{
|
{
|
||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
|
@ -262,8 +262,10 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
protected virtual void ApplyFilterToCarousel(FilterCriteria criteria)
|
protected virtual void ApplyFilterToCarousel(FilterCriteria criteria)
|
||||||
{
|
{
|
||||||
if (this.IsCurrentScreen())
|
// if not the current screen, we want to get carousel in a good presentation state before displaying (resume or enter).
|
||||||
Carousel.Filter(criteria);
|
bool shouldDebounce = this.IsCurrentScreen();
|
||||||
|
|
||||||
|
Schedule(() => Carousel.Filter(criteria, shouldDebounce));
|
||||||
}
|
}
|
||||||
|
|
||||||
private DependencyContainer dependencies;
|
private DependencyContainer dependencies;
|
||||||
@ -437,8 +439,6 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
base.OnEntering(last);
|
base.OnEntering(last);
|
||||||
|
|
||||||
Carousel.Filter(FilterControl.CreateCriteria(), false);
|
|
||||||
|
|
||||||
this.FadeInFromZero(250);
|
this.FadeInFromZero(250);
|
||||||
FilterControl.Activate();
|
FilterControl.Activate();
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
|
|
||||||
namespace osu.Game.Skinning
|
namespace osu.Game.Skinning
|
||||||
{
|
{
|
||||||
public class SkinConfigManager<T> : ConfigManager<T> where T : struct
|
public class SkinConfigManager<TLookup> : ConfigManager<TLookup> where TLookup : struct, Enum
|
||||||
{
|
{
|
||||||
protected override void PerformLoad()
|
protected override void PerformLoad()
|
||||||
{
|
{
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||||
public static TransformSequence<T> TransformFlipH<T>(this T flippable, bool newValue, double delay = 0)
|
public static TransformSequence<T> TransformFlipH<T>(this T flippable, bool newValue, double delay = 0)
|
||||||
where T : IFlippable
|
where T : class, IFlippable
|
||||||
=> flippable.TransformTo(flippable.PopulateTransform(new TransformFlipH(), newValue, delay));
|
=> flippable.TransformTo(flippable.PopulateTransform(new TransformFlipH(), newValue, delay));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -49,7 +49,7 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
/// <returns>A <see cref="TransformSequence{T}"/> to which further transforms can be added.</returns>
|
||||||
public static TransformSequence<T> TransformFlipV<T>(this T flippable, bool newValue, double delay = 0)
|
public static TransformSequence<T> TransformFlipV<T>(this T flippable, bool newValue, double delay = 0)
|
||||||
where T : IFlippable
|
where T : class, IFlippable
|
||||||
=> flippable.TransformTo(flippable.PopulateTransform(new TransformFlipV(), newValue, delay));
|
=> flippable.TransformTo(flippable.PopulateTransform(new TransformFlipV(), newValue, delay));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
// note that this will override any mod rate application
|
// note that this will override any mod rate application
|
||||||
Beatmap.Value.Track.TempoAdjust = Clock.Rate;
|
Beatmap.Value.Track.Tempo.Value = Clock.Rate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class ScreenTestScene : ManualInputManagerTestScene
|
public abstract class ScreenTestScene : ManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
private readonly OsuScreenStack stack;
|
protected readonly OsuScreenStack Stack;
|
||||||
|
|
||||||
private readonly Container content;
|
private readonly Container content;
|
||||||
|
|
||||||
@ -22,16 +22,16 @@ namespace osu.Game.Tests.Visual
|
|||||||
{
|
{
|
||||||
base.Content.AddRange(new Drawable[]
|
base.Content.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both },
|
Stack = new OsuScreenStack { RelativeSizeAxes = Axes.Both },
|
||||||
content = new Container { RelativeSizeAxes = Axes.Both }
|
content = new Container { RelativeSizeAxes = Axes.Both }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void LoadScreen(OsuScreen screen)
|
protected void LoadScreen(OsuScreen screen)
|
||||||
{
|
{
|
||||||
if (stack.CurrentScreen != null)
|
if (Stack.CurrentScreen != null)
|
||||||
stack.Exit();
|
Stack.Exit();
|
||||||
stack.Push(screen);
|
Stack.Push(screen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2019.1205.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2019.1210.1" />
|
||||||
<PackageReference Include="Sentry" Version="1.2.0" />
|
<PackageReference Include="Sentry" Version="1.2.0" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1205.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1210.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
@ -82,7 +82,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2019.1205.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2019.1210.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
<PackageReference Include="SharpCompress" Version="0.24.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user