mirror of
https://github.com/ppy/osu.git
synced 2025-01-15 07:22:55 +08:00
Merge branch 'master' into perf-calculator-remove-working-beatmap
This commit is contained in:
commit
f1a3b6d0ba
@ -52,6 +52,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.1001.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.1004.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -9,7 +9,7 @@ using osu.Framework.Android;
|
|||||||
|
|
||||||
namespace osu.Android
|
namespace osu.Android
|
||||||
{
|
{
|
||||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullSensor, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)]
|
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)]
|
||||||
public class OsuGameActivity : AndroidGameActivity
|
public class OsuGameActivity : AndroidGameActivity
|
||||||
{
|
{
|
||||||
protected override Framework.Game CreateGame() => new OsuGameAndroid();
|
protected override Framework.Game CreateGame() => new OsuGameAndroid();
|
||||||
|
@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
if (!result.Type.AffectsCombo() || !result.HasResult)
|
if (!result.Type.AffectsCombo() || !result.HasResult)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (result.Type == HitResult.Miss)
|
if (!result.IsHit)
|
||||||
{
|
{
|
||||||
updateCombo(0, null);
|
updateCombo(0, null);
|
||||||
return;
|
return;
|
||||||
|
@ -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 NUnit.Framework;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -13,7 +14,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
public class TestSceneHoldNote : ManiaHitObjectTestScene
|
public class TestSceneHoldNote : ManiaHitObjectTestScene
|
||||||
{
|
{
|
||||||
public TestSceneHoldNote()
|
[Test]
|
||||||
|
public void TestHoldNote()
|
||||||
{
|
{
|
||||||
AddToggleStep("toggle hitting", v =>
|
AddToggleStep("toggle hitting", v =>
|
||||||
{
|
{
|
||||||
|
@ -28,8 +28,15 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneNotes : OsuTestScene
|
public class TestSceneNotes : OsuTestScene
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[Test]
|
||||||
private void load()
|
public void TestVariousNotes()
|
||||||
|
{
|
||||||
|
DrawableNote note1 = null;
|
||||||
|
DrawableNote note2 = null;
|
||||||
|
DrawableHoldNote holdNote1 = null;
|
||||||
|
DrawableHoldNote holdNote2 = null;
|
||||||
|
|
||||||
|
AddStep("create notes", () =>
|
||||||
{
|
{
|
||||||
Child = new FillFlowContainer
|
Child = new FillFlowContainer
|
||||||
{
|
{
|
||||||
@ -41,12 +48,13 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
Spacing = new Vector2(20),
|
Spacing = new Vector2(20),
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
createNoteDisplay(ScrollingDirection.Down, 1, out var note1),
|
createNoteDisplay(ScrollingDirection.Down, 1, out note1),
|
||||||
createNoteDisplay(ScrollingDirection.Up, 2, out var note2),
|
createNoteDisplay(ScrollingDirection.Up, 2, out note2),
|
||||||
createHoldNoteDisplay(ScrollingDirection.Down, 1, out var holdNote1),
|
createHoldNoteDisplay(ScrollingDirection.Down, 1, out holdNote1),
|
||||||
createHoldNoteDisplay(ScrollingDirection.Up, 2, out var holdNote2),
|
createHoldNoteDisplay(ScrollingDirection.Up, 2, out holdNote2),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
|
||||||
AddAssert("note 1 facing downwards", () => verifyAnchors(note1, Anchor.y2));
|
AddAssert("note 1 facing downwards", () => verifyAnchors(note1, Anchor.y2));
|
||||||
AddAssert("note 2 facing upwards", () => verifyAnchors(note2, Anchor.y0));
|
AddAssert("note 2 facing upwards", () => verifyAnchors(note2, Anchor.y0));
|
||||||
|
@ -2,10 +2,40 @@
|
|||||||
// 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.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Judgements
|
namespace osu.Game.Rulesets.Mania.Judgements
|
||||||
{
|
{
|
||||||
public class ManiaJudgement : Judgement
|
public class ManiaJudgement : Judgement
|
||||||
{
|
{
|
||||||
|
protected override double HealthIncreaseFor(HitResult result)
|
||||||
|
{
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case HitResult.LargeTickHit:
|
||||||
|
return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
|
||||||
|
|
||||||
|
case HitResult.LargeTickMiss:
|
||||||
|
return -DEFAULT_MAX_HEALTH_INCREASE * 0.1;
|
||||||
|
|
||||||
|
case HitResult.Meh:
|
||||||
|
return -DEFAULT_MAX_HEALTH_INCREASE * 0.5;
|
||||||
|
|
||||||
|
case HitResult.Ok:
|
||||||
|
return -DEFAULT_MAX_HEALTH_INCREASE * 0.3;
|
||||||
|
|
||||||
|
case HitResult.Good:
|
||||||
|
return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
|
||||||
|
|
||||||
|
case HitResult.Great:
|
||||||
|
return DEFAULT_MAX_HEALTH_INCREASE * 0.8;
|
||||||
|
|
||||||
|
case HitResult.Perfect:
|
||||||
|
return DEFAULT_MAX_HEALTH_INCREASE;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return base.HealthIncreaseFor(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,12 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
new SettingsEnumDropdown<ManiaScrollingDirection>
|
new SettingsEnumDropdown<ManiaScrollingDirection>
|
||||||
{
|
{
|
||||||
LabelText = "Scrolling direction",
|
LabelText = "Scrolling direction",
|
||||||
Bindable = config.GetBindable<ManiaScrollingDirection>(ManiaRulesetSetting.ScrollDirection)
|
Current = config.GetBindable<ManiaScrollingDirection>(ManiaRulesetSetting.ScrollDirection)
|
||||||
},
|
},
|
||||||
new SettingsSlider<double, TimeSlider>
|
new SettingsSlider<double, TimeSlider>
|
||||||
{
|
{
|
||||||
LabelText = "Scroll speed",
|
LabelText = "Scroll speed",
|
||||||
Bindable = config.GetBindable<double>(ManiaRulesetSetting.ScrollTime),
|
Current = config.GetBindable<double>(ManiaRulesetSetting.ScrollTime),
|
||||||
KeyboardStep = 5
|
KeyboardStep = 5
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -243,7 +243,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
endHold();
|
endHold();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Tail.Result.Type == HitResult.Miss)
|
if (Tail.Judged && !Tail.IsHit)
|
||||||
HasBroken = true;
|
HasBroken = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
if (!userTriggered)
|
if (!userTriggered)
|
||||||
{
|
{
|
||||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||||
ApplyResult(r => r.Type = HitResult.Miss);
|
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -136,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Causes this <see cref="DrawableManiaHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
|
/// Causes this <see cref="DrawableManiaHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss);
|
public void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class DrawableManiaHitObject<TObject> : DrawableManiaHitObject
|
public abstract class DrawableManiaHitObject<TObject> : DrawableManiaHitObject
|
||||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
if (!userTriggered)
|
if (!userTriggered)
|
||||||
{
|
{
|
||||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||||
ApplyResult(r => r.Type = HitResult.Miss);
|
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
@ -20,7 +20,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
private int depthIndex;
|
private int depthIndex;
|
||||||
|
|
||||||
public TestSceneHitCircle()
|
[Test]
|
||||||
|
public void TestVariousHitCircles()
|
||||||
{
|
{
|
||||||
AddStep("Miss Big Single", () => SetContents(() => testSingle(2)));
|
AddStep("Miss Big Single", () => SetContents(() => testSingle(2)));
|
||||||
AddStep("Miss Medium Single", () => SetContents(() => testSingle(5)));
|
AddStep("Miss Medium Single", () => SetContents(() => testSingle(5)));
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
HitObjects = { new HitCircle { Position = new Vector2(256, 192) } }
|
HitObjects = { new HitCircle { Position = new Vector2(256, 192) } }
|
||||||
},
|
},
|
||||||
PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset < -hitWindows.WindowFor(HitResult.Meh) && Player.Results[0].Type == HitResult.Miss
|
PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset < -hitWindows.WindowFor(HitResult.Meh) && !Player.Results[0].IsHit
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
Autoplay = false,
|
Autoplay = false,
|
||||||
Beatmap = beatmap,
|
Beatmap = beatmap,
|
||||||
PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset >= hitWindows.WindowFor(HitResult.Meh) && Player.Results[0].Type == HitResult.Miss
|
PassCondition = () => Player.Results.Count > 0 && Player.Results[0].TimeOffset >= hitWindows.WindowFor(HitResult.Meh) && !Player.Results[0].IsHit
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
private int depthIndex;
|
private int depthIndex;
|
||||||
|
|
||||||
public TestSceneSlider()
|
[Test]
|
||||||
|
public void TestVariousSliders()
|
||||||
{
|
{
|
||||||
AddStep("Big Single", () => SetContents(() => testSimpleBig()));
|
AddStep("Big Single", () => SetContents(() => testSimpleBig()));
|
||||||
AddStep("Medium Single", () => SetContents(() => testSimpleMedium()));
|
AddStep("Medium Single", () => SetContents(() => testSimpleMedium()));
|
||||||
|
@ -314,11 +314,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private bool assertMaxJudge() => judgementResults.Any() && judgementResults.All(t => t.Type == t.Judgement.MaxResult);
|
private bool assertMaxJudge() => judgementResults.Any() && judgementResults.All(t => t.Type == t.Judgement.MaxResult);
|
||||||
|
|
||||||
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.IgnoreHit && judgementResults.First().Type == HitResult.Miss;
|
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.SmallTickHit && !judgementResults.First().IsHit;
|
||||||
|
|
||||||
private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.IgnoreHit;
|
private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.SmallTickHit;
|
||||||
|
|
||||||
private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.IgnoreMiss;
|
private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.SmallTickMiss;
|
||||||
|
|
||||||
private ScoreAccessibleReplayPlayer currentPlayer;
|
private ScoreAccessibleReplayPlayer currentPlayer;
|
||||||
|
|
||||||
|
@ -1,7 +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 System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -10,40 +16,219 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
public class OsuSelectionHandler : SelectionHandler
|
public class OsuSelectionHandler : SelectionHandler
|
||||||
{
|
{
|
||||||
public override bool HandleMovement(MoveSelectionEvent moveEvent)
|
protected override void OnSelectionChanged()
|
||||||
{
|
{
|
||||||
Vector2 minPosition = new Vector2(float.MaxValue, float.MaxValue);
|
base.OnSelectionChanged();
|
||||||
Vector2 maxPosition = new Vector2(float.MinValue, float.MinValue);
|
|
||||||
|
|
||||||
// Go through all hitobjects to make sure they would remain in the bounds of the editor after movement, before any movement is attempted
|
bool canOperate = SelectedHitObjects.Count() > 1 || SelectedHitObjects.Any(s => s is Slider);
|
||||||
foreach (var h in SelectedHitObjects.OfType<OsuHitObject>())
|
|
||||||
{
|
SelectionBox.CanRotate = canOperate;
|
||||||
if (h is Spinner)
|
SelectionBox.CanScaleX = canOperate;
|
||||||
{
|
SelectionBox.CanScaleY = canOperate;
|
||||||
// Spinners don't support position adjustments
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stacking is not considered
|
protected override void OnOperationEnded()
|
||||||
minPosition = Vector2.ComponentMin(minPosition, Vector2.ComponentMin(h.EndPosition + moveEvent.InstantDelta, h.Position + moveEvent.InstantDelta));
|
{
|
||||||
maxPosition = Vector2.ComponentMax(maxPosition, Vector2.ComponentMax(h.EndPosition + moveEvent.InstantDelta, h.Position + moveEvent.InstantDelta));
|
base.OnOperationEnded();
|
||||||
|
referenceOrigin = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minPosition.X < 0 || minPosition.Y < 0 || maxPosition.X > DrawWidth || maxPosition.Y > DrawHeight)
|
public override bool HandleMovement(MoveSelectionEvent moveEvent) =>
|
||||||
return false;
|
moveSelection(moveEvent.InstantDelta);
|
||||||
|
|
||||||
foreach (var h in SelectedHitObjects.OfType<OsuHitObject>())
|
/// <summary>
|
||||||
|
/// During a transform, the initial origin is stored so it can be used throughout the operation.
|
||||||
|
/// </summary>
|
||||||
|
private Vector2? referenceOrigin;
|
||||||
|
|
||||||
|
public override bool HandleFlip(Direction direction)
|
||||||
{
|
{
|
||||||
if (h is Spinner)
|
var hitObjects = selectedMovableObjects;
|
||||||
|
|
||||||
|
var selectedObjectsQuad = getSurroundingQuad(hitObjects);
|
||||||
|
var centre = selectedObjectsQuad.Centre;
|
||||||
|
|
||||||
|
foreach (var h in hitObjects)
|
||||||
{
|
{
|
||||||
// Spinners don't support position adjustments
|
var pos = h.Position;
|
||||||
continue;
|
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case Direction.Horizontal:
|
||||||
|
pos.X = centre.X - (pos.X - centre.X);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Direction.Vertical:
|
||||||
|
pos.Y = centre.Y - (pos.Y - centre.Y);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Position += moveEvent.InstantDelta;
|
h.Position = pos;
|
||||||
|
|
||||||
|
if (h is Slider slider)
|
||||||
|
{
|
||||||
|
foreach (var point in slider.Path.ControlPoints)
|
||||||
|
{
|
||||||
|
point.Position.Value = new Vector2(
|
||||||
|
(direction == Direction.Horizontal ? -1 : 1) * point.Position.Value.X,
|
||||||
|
(direction == Direction.Vertical ? -1 : 1) * point.Position.Value.Y
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool HandleScale(Vector2 scale, Anchor reference)
|
||||||
|
{
|
||||||
|
adjustScaleFromAnchor(ref scale, reference);
|
||||||
|
|
||||||
|
var hitObjects = selectedMovableObjects;
|
||||||
|
|
||||||
|
// for the time being, allow resizing of slider paths only if the slider is
|
||||||
|
// the only hit object selected. with a group selection, it's likely the user
|
||||||
|
// is not looking to change the duration of the slider but expand the whole pattern.
|
||||||
|
if (hitObjects.Length == 1 && hitObjects.First() is Slider slider)
|
||||||
|
{
|
||||||
|
Quad quad = getSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position.Value));
|
||||||
|
Vector2 pathRelativeDeltaScale = new Vector2(1 + scale.X / quad.Width, 1 + scale.Y / quad.Height);
|
||||||
|
|
||||||
|
foreach (var point in slider.Path.ControlPoints)
|
||||||
|
point.Position.Value *= pathRelativeDeltaScale;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// move the selection before scaling if dragging from top or left anchors.
|
||||||
|
if ((reference & Anchor.x0) > 0 && !moveSelection(new Vector2(-scale.X, 0))) return false;
|
||||||
|
if ((reference & Anchor.y0) > 0 && !moveSelection(new Vector2(0, -scale.Y))) return false;
|
||||||
|
|
||||||
|
Quad quad = getSurroundingQuad(hitObjects);
|
||||||
|
|
||||||
|
foreach (var h in hitObjects)
|
||||||
|
{
|
||||||
|
h.Position = new Vector2(
|
||||||
|
quad.TopLeft.X + (h.X - quad.TopLeft.X) / quad.Width * (quad.Width + scale.X),
|
||||||
|
quad.TopLeft.Y + (h.Y - quad.TopLeft.Y) / quad.Height * (quad.Height + scale.Y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void adjustScaleFromAnchor(ref Vector2 scale, Anchor reference)
|
||||||
|
{
|
||||||
|
// cancel out scale in axes we don't care about (based on which drag handle was used).
|
||||||
|
if ((reference & Anchor.x1) > 0) scale.X = 0;
|
||||||
|
if ((reference & Anchor.y1) > 0) scale.Y = 0;
|
||||||
|
|
||||||
|
// reverse the scale direction if dragging from top or left.
|
||||||
|
if ((reference & Anchor.x0) > 0) scale.X = -scale.X;
|
||||||
|
if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HandleRotation(float delta)
|
||||||
|
{
|
||||||
|
var hitObjects = selectedMovableObjects;
|
||||||
|
|
||||||
|
Quad quad = getSurroundingQuad(hitObjects);
|
||||||
|
|
||||||
|
referenceOrigin ??= quad.Centre;
|
||||||
|
|
||||||
|
foreach (var h in hitObjects)
|
||||||
|
{
|
||||||
|
h.Position = rotatePointAroundOrigin(h.Position, referenceOrigin.Value, delta);
|
||||||
|
|
||||||
|
if (h is IHasPath path)
|
||||||
|
{
|
||||||
|
foreach (var point in path.Path.ControlPoints)
|
||||||
|
point.Position.Value = rotatePointAroundOrigin(point.Position.Value, Vector2.Zero, delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this isn't always the case but let's be lenient for now.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool moveSelection(Vector2 delta)
|
||||||
|
{
|
||||||
|
var hitObjects = selectedMovableObjects;
|
||||||
|
|
||||||
|
Quad quad = getSurroundingQuad(hitObjects);
|
||||||
|
|
||||||
|
if (quad.TopLeft.X + delta.X < 0 ||
|
||||||
|
quad.TopLeft.Y + delta.Y < 0 ||
|
||||||
|
quad.BottomRight.X + delta.X > DrawWidth ||
|
||||||
|
quad.BottomRight.Y + delta.Y > DrawHeight)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (var h in hitObjects)
|
||||||
|
h.Position += delta;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a gamefield-space quad surrounding the provided hit objects.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hitObjects">The hit objects to calculate a quad for.</param>
|
||||||
|
private Quad getSurroundingQuad(OsuHitObject[] hitObjects) =>
|
||||||
|
getSurroundingQuad(hitObjects.SelectMany(h => new[] { h.Position, h.EndPosition }));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a gamefield-space quad surrounding the provided points.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="points">The points to calculate a quad for.</param>
|
||||||
|
private Quad getSurroundingQuad(IEnumerable<Vector2> points)
|
||||||
|
{
|
||||||
|
if (!SelectedHitObjects.Any())
|
||||||
|
return new Quad();
|
||||||
|
|
||||||
|
Vector2 minPosition = new Vector2(float.MaxValue, float.MaxValue);
|
||||||
|
Vector2 maxPosition = new Vector2(float.MinValue, float.MinValue);
|
||||||
|
|
||||||
|
// Go through all hitobjects to make sure they would remain in the bounds of the editor after movement, before any movement is attempted
|
||||||
|
foreach (var p in points)
|
||||||
|
{
|
||||||
|
minPosition = Vector2.ComponentMin(minPosition, p);
|
||||||
|
maxPosition = Vector2.ComponentMax(maxPosition, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 size = maxPosition - minPosition;
|
||||||
|
|
||||||
|
return new Quad(minPosition.X, minPosition.Y, size.X, size.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All osu! hitobjects which can be moved/rotated/scaled.
|
||||||
|
/// </summary>
|
||||||
|
private OsuHitObject[] selectedMovableObjects => SelectedHitObjects
|
||||||
|
.OfType<OsuHitObject>()
|
||||||
|
.Where(h => !(h is Spinner))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotate a point around an arbitrary origin.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The point.</param>
|
||||||
|
/// <param name="origin">The centre origin to rotate around.</param>
|
||||||
|
/// <param name="angle">The angle to rotate (in degrees).</param>
|
||||||
|
private static Vector2 rotatePointAroundOrigin(Vector2 point, Vector2 origin, float angle)
|
||||||
|
{
|
||||||
|
angle = -angle;
|
||||||
|
|
||||||
|
point.X -= origin.X;
|
||||||
|
point.Y -= origin.Y;
|
||||||
|
|
||||||
|
Vector2 ret;
|
||||||
|
ret.X = point.X * MathF.Cos(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Sin(MathUtils.DegreesToRadians(angle));
|
||||||
|
ret.Y = point.X * -MathF.Sin(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Cos(MathUtils.DegreesToRadians(angle));
|
||||||
|
|
||||||
|
ret.X += origin.X;
|
||||||
|
ret.Y += origin.Y;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,9 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
base.ApplyToDrawableHitObjects(drawables);
|
base.ApplyToDrawableHitObjects(drawables);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double lastSliderHeadFadeOutStartTime;
|
||||||
|
private double lastSliderHeadFadeOutDuration;
|
||||||
|
|
||||||
protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
|
protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
|
||||||
{
|
{
|
||||||
if (!(drawable is DrawableOsuHitObject d))
|
if (!(drawable is DrawableOsuHitObject d))
|
||||||
@ -54,7 +57,35 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
switch (drawable)
|
switch (drawable)
|
||||||
{
|
{
|
||||||
|
case DrawableSliderTail sliderTail:
|
||||||
|
// use stored values from head circle to achieve same fade sequence.
|
||||||
|
fadeOutDuration = lastSliderHeadFadeOutDuration;
|
||||||
|
fadeOutStartTime = lastSliderHeadFadeOutStartTime;
|
||||||
|
|
||||||
|
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
|
||||||
|
sliderTail.FadeOut(fadeOutDuration);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DrawableSliderRepeat sliderRepeat:
|
||||||
|
// use stored values from head circle to achieve same fade sequence.
|
||||||
|
fadeOutDuration = lastSliderHeadFadeOutDuration;
|
||||||
|
fadeOutStartTime = lastSliderHeadFadeOutStartTime;
|
||||||
|
|
||||||
|
using (drawable.BeginAbsoluteSequence(fadeOutStartTime, true))
|
||||||
|
// only apply to circle piece – reverse arrow is not affected by hidden.
|
||||||
|
sliderRepeat.CirclePiece.FadeOut(fadeOutDuration);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case DrawableHitCircle circle:
|
case DrawableHitCircle circle:
|
||||||
|
|
||||||
|
if (circle is DrawableSliderHead)
|
||||||
|
{
|
||||||
|
lastSliderHeadFadeOutDuration = fadeOutDuration;
|
||||||
|
lastSliderHeadFadeOutStartTime = fadeOutStartTime;
|
||||||
|
}
|
||||||
|
|
||||||
// we don't want to see the approach circle
|
// we don't want to see the approach circle
|
||||||
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
|
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
|
||||||
circle.ApproachCircle.Hide();
|
circle.ApproachCircle.Hide();
|
||||||
|
@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
if (!userTriggered)
|
if (!userTriggered)
|
||||||
{
|
{
|
||||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||||
ApplyResult(r => r.Type = HitResult.Miss);
|
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
var circleResult = (OsuHitCircleJudgementResult)r;
|
var circleResult = (OsuHitCircleJudgementResult)r;
|
||||||
|
|
||||||
// Todo: This should also consider misses, but they're a little more interesting to handle, since we don't necessarily know the position at the time of a miss.
|
// Todo: This should also consider misses, but they're a little more interesting to handle, since we don't necessarily know the position at the time of a miss.
|
||||||
if (result != HitResult.Miss)
|
if (result.IsHit())
|
||||||
{
|
{
|
||||||
var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position);
|
var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position);
|
||||||
circleResult.CursorPositionAtHit = HitObject.StackedPosition + (localMousePosition - DrawSize / 2);
|
circleResult.CursorPositionAtHit = HitObject.StackedPosition + (localMousePosition - DrawSize / 2);
|
||||||
|
@ -8,7 +8,6 @@ using osu.Game.Rulesets.Judgements;
|
|||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -68,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Causes this <see cref="DrawableOsuHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
|
/// Causes this <see cref="DrawableOsuHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss);
|
public void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||||
|
|
||||||
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement);
|
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(HitObject, judgement);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ using osu.Game.Configuration;
|
|||||||
using osuTK;
|
using osuTK;
|
||||||
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.Scoring;
|
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -67,7 +66,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
if (JudgedObject != null)
|
if (JudgedObject != null)
|
||||||
{
|
{
|
||||||
lightingColour = JudgedObject.AccentColour.GetBoundCopy();
|
lightingColour = JudgedObject.AccentColour.GetBoundCopy();
|
||||||
lightingColour.BindValueChanged(colour => Lighting.Colour = Result.Type == HitResult.Miss ? Color4.Transparent : colour.NewValue, true);
|
lightingColour.BindValueChanged(colour => Lighting.Colour = Result.IsHit ? colour.NewValue : Color4.Transparent, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,6 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Skinning;
|
using osu.Game.Rulesets.Osu.Skinning;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -52,6 +51,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling),
|
Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling),
|
||||||
|
tailContainer = new Container<DrawableSliderTail> { RelativeSizeAxes = Axes.Both },
|
||||||
tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
|
||||||
repeatContainer = new Container<DrawableSliderRepeat> { RelativeSizeAxes = Axes.Both },
|
repeatContainer = new Container<DrawableSliderRepeat> { RelativeSizeAxes = Axes.Both },
|
||||||
Ball = new SliderBall(s, this)
|
Ball = new SliderBall(s, this)
|
||||||
@ -63,7 +63,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
Alpha = 0
|
Alpha = 0
|
||||||
},
|
},
|
||||||
headContainer = new Container<DrawableSliderHead> { RelativeSizeAxes = Axes.Both },
|
headContainer = new Container<DrawableSliderHead> { RelativeSizeAxes = Axes.Both },
|
||||||
tailContainer = new Container<DrawableSliderTail> { RelativeSizeAxes = Axes.Both },
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +249,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
// rather than doing it this way, we should probably attach the sample to the tail circle.
|
// rather than doing it this way, we should probably attach the sample to the tail circle.
|
||||||
// this can only be done after we stop using LegacyLastTick.
|
// this can only be done after we stop using LegacyLastTick.
|
||||||
if (TailCircle.Result.Type != HitResult.Miss)
|
if (TailCircle.IsHit)
|
||||||
base.PlaySamples();
|
base.PlaySamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,9 +6,11 @@ using System.Collections.Generic;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
@ -22,6 +24,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
private readonly Drawable scaleContainer;
|
private readonly Drawable scaleContainer;
|
||||||
|
|
||||||
|
public readonly Drawable CirclePiece;
|
||||||
|
|
||||||
public override bool DisplayResult => false;
|
public override bool DisplayResult => false;
|
||||||
|
|
||||||
public DrawableSliderRepeat(SliderRepeat sliderRepeat, DrawableSlider drawableSlider)
|
public DrawableSliderRepeat(SliderRepeat sliderRepeat, DrawableSlider drawableSlider)
|
||||||
@ -34,7 +38,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
InternalChild = scaleContainer = new ReverseArrowPiece();
|
InternalChild = scaleContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
// no default for this; only visible in legacy skins.
|
||||||
|
CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty()),
|
||||||
|
arrow = new ReverseArrowPiece(),
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly IBindable<float> scaleBindable = new BindableFloat();
|
private readonly IBindable<float> scaleBindable = new BindableFloat();
|
||||||
@ -85,6 +100,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
private bool hasRotation;
|
private bool hasRotation;
|
||||||
|
|
||||||
|
private readonly ReverseArrowPiece arrow;
|
||||||
|
|
||||||
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
|
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
|
||||||
{
|
{
|
||||||
// When the repeat is hit, the arrow should fade out on spot rather than following the slider
|
// When the repeat is hit, the arrow should fade out on spot rather than following the slider
|
||||||
@ -114,18 +131,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
}
|
}
|
||||||
|
|
||||||
float aimRotation = MathUtils.RadiansToDegrees(MathF.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
|
float aimRotation = MathUtils.RadiansToDegrees(MathF.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
|
||||||
while (Math.Abs(aimRotation - Rotation) > 180)
|
while (Math.Abs(aimRotation - arrow.Rotation) > 180)
|
||||||
aimRotation += aimRotation < Rotation ? 360 : -360;
|
aimRotation += aimRotation < arrow.Rotation ? 360 : -360;
|
||||||
|
|
||||||
if (!hasRotation)
|
if (!hasRotation)
|
||||||
{
|
{
|
||||||
Rotation = aimRotation;
|
arrow.Rotation = aimRotation;
|
||||||
hasRotation = true;
|
hasRotation = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If we're already snaking, interpolate to smooth out sharp curves (linear sliders, mainly).
|
// If we're already snaking, interpolate to smooth out sharp curves (linear sliders, mainly).
|
||||||
Rotation = Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, 100), Rotation, aimRotation, 0, 50, Easing.OutQuint);
|
arrow.Rotation = Interpolation.ValueAt(Math.Clamp(Clock.ElapsedFrameTime, 0, 100), arrow.Rotation, aimRotation, 0, 50, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +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.Diagnostics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking
|
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking, ITrackSnaking
|
||||||
{
|
{
|
||||||
private readonly Slider slider;
|
private readonly SliderTailCircle tailCircle;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
|
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
|
||||||
@ -18,28 +23,73 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
public bool Tracking { get; set; }
|
public bool Tracking { get; set; }
|
||||||
|
|
||||||
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
private readonly IBindable<float> scaleBindable = new BindableFloat();
|
||||||
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
|
||||||
|
|
||||||
public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
|
private readonly SkinnableDrawable circlePiece;
|
||||||
: base(hitCircle)
|
|
||||||
|
private readonly Container scaleContainer;
|
||||||
|
|
||||||
|
public DrawableSliderTail(Slider slider, SliderTailCircle tailCircle)
|
||||||
|
: base(tailCircle)
|
||||||
{
|
{
|
||||||
this.slider = slider;
|
this.tailCircle = tailCircle;
|
||||||
|
|
||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||||
FillMode = FillMode.Fit;
|
|
||||||
|
|
||||||
AlwaysPresent = true;
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
scaleContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
// no default for this; only visible in legacy skins.
|
||||||
|
circlePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderTailHitCircle), _ => Empty())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
positionBindable.BindTo(hitCircle.PositionBindable);
|
[BackgroundDependencyLoader]
|
||||||
pathVersion.BindTo(slider.Path.Version);
|
private void load()
|
||||||
|
{
|
||||||
|
scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
|
||||||
|
scaleBindable.BindTo(HitObject.ScaleBindable);
|
||||||
|
}
|
||||||
|
|
||||||
positionBindable.BindValueChanged(_ => updatePosition());
|
protected override void UpdateInitialTransforms()
|
||||||
pathVersion.BindValueChanged(_ => updatePosition(), true);
|
{
|
||||||
|
base.UpdateInitialTransforms();
|
||||||
|
|
||||||
// TODO: This has no drawable content. Support for skins should be added.
|
circlePiece.FadeInFromZero(HitObject.TimeFadeIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateStateTransforms(ArmedState state)
|
||||||
|
{
|
||||||
|
base.UpdateStateTransforms(state);
|
||||||
|
|
||||||
|
Debug.Assert(HitObject.HitWindows != null);
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case ArmedState.Idle:
|
||||||
|
this.Delay(HitObject.TimePreempt).FadeOut(500);
|
||||||
|
|
||||||
|
Expire(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArmedState.Miss:
|
||||||
|
this.FadeOut(100);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ArmedState.Hit:
|
||||||
|
// todo: temporary / arbitrary
|
||||||
|
this.Delay(800).FadeOut();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
@ -48,6 +98,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePosition() => Position = HitObject.Position - slider.Position;
|
public void UpdateSnakingPosition(Vector2 start, Vector2 end) =>
|
||||||
|
Position = tailCircle.RepeatIndex % 2 == 0 ? end : start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Trigger a miss result for remaining ticks to avoid infinite gameplay.
|
// Trigger a miss result for remaining ticks to avoid infinite gameplay.
|
||||||
foreach (var tick in ticks.Where(t => !t.IsHit))
|
foreach (var tick in ticks.Where(t => !t.Result.HasResult))
|
||||||
tick.TriggerResult(false);
|
tick.TriggerResult(false);
|
||||||
|
|
||||||
ApplyResult(r =>
|
ApplyResult(r =>
|
||||||
@ -224,7 +224,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
else if (Progress > .75)
|
else if (Progress > .75)
|
||||||
r.Type = HitResult.Meh;
|
r.Type = HitResult.Meh;
|
||||||
else if (Time.Current >= Spinner.EndTime)
|
else if (Time.Current >= Spinner.EndTime)
|
||||||
r.Type = HitResult.Miss;
|
r.Type = r.Judgement.MinResult;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +268,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
while (wholeSpins != spins)
|
while (wholeSpins != spins)
|
||||||
{
|
{
|
||||||
var tick = ticks.FirstOrDefault(t => !t.IsHit);
|
var tick = ticks.FirstOrDefault(t => !t.Result.HasResult);
|
||||||
|
|
||||||
// tick may be null if we've hit the spin limit.
|
// tick may be null if we've hit the spin limit.
|
||||||
if (tick != null)
|
if (tick != null)
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
Masking = true;
|
Masking = true;
|
||||||
BorderThickness = 10;
|
BorderThickness = 9; // roughly matches slider borders and makes stacked circles distinctly visible from each other.
|
||||||
BorderColour = Color4.White;
|
BorderColour = Color4.White;
|
||||||
|
|
||||||
Child = new Box
|
Child = new Box
|
||||||
|
@ -176,6 +176,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
// if this is to change, we should revisit this.
|
// if this is to change, we should revisit this.
|
||||||
AddNested(TailCircle = new SliderTailCircle(this)
|
AddNested(TailCircle = new SliderTailCircle(this)
|
||||||
{
|
{
|
||||||
|
RepeatIndex = e.SpanIndex,
|
||||||
StartTime = e.Time,
|
StartTime = e.Time,
|
||||||
Position = EndPosition,
|
Position = EndPosition,
|
||||||
StackHeight = StackHeight
|
StackHeight = StackHeight
|
||||||
@ -183,10 +184,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SliderEventType.Repeat:
|
case SliderEventType.Repeat:
|
||||||
AddNested(new SliderRepeat
|
AddNested(new SliderRepeat(this)
|
||||||
{
|
{
|
||||||
RepeatIndex = e.SpanIndex,
|
RepeatIndex = e.SpanIndex,
|
||||||
SpanDuration = SpanDuration,
|
|
||||||
StartTime = StartTime + (e.SpanIndex + 1) * SpanDuration,
|
StartTime = StartTime + (e.SpanIndex + 1) * SpanDuration,
|
||||||
Position = Position + Path.PositionAt(e.PathProgress),
|
Position = Position + Path.PositionAt(e.PathProgress),
|
||||||
StackHeight = StackHeight,
|
StackHeight = StackHeight,
|
||||||
|
@ -1,9 +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.
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects
|
|
||||||
{
|
|
||||||
public class SliderCircle : HitCircle
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
50
osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs
Normal file
50
osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Objects
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A hit circle which is at the end of a slider path (either repeat or final tail).
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SliderEndCircle : HitCircle
|
||||||
|
{
|
||||||
|
private readonly Slider slider;
|
||||||
|
|
||||||
|
protected SliderEndCircle(Slider slider)
|
||||||
|
{
|
||||||
|
this.slider = slider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RepeatIndex { get; set; }
|
||||||
|
|
||||||
|
public double SpanDuration => slider.SpanDuration;
|
||||||
|
|
||||||
|
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||||
|
{
|
||||||
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
|
if (RepeatIndex > 0)
|
||||||
|
{
|
||||||
|
// Repeat points after the first span should appear behind the still-visible one.
|
||||||
|
TimeFadeIn = 0;
|
||||||
|
|
||||||
|
// The next end circle should appear exactly after the previous circle (on the same end) is hit.
|
||||||
|
TimePreempt = SpanDuration * 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// taken from osu-stable
|
||||||
|
const float first_end_circle_preempt_adjust = 2 / 3f;
|
||||||
|
|
||||||
|
// The first end circle should fade in with the slider.
|
||||||
|
TimePreempt = (StartTime - slider.StartTime) + slider.TimePreempt * first_end_circle_preempt_adjust;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +1,19 @@
|
|||||||
// 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.Beatmaps;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects
|
namespace osu.Game.Rulesets.Osu.Objects
|
||||||
{
|
{
|
||||||
public class SliderRepeat : OsuHitObject
|
public class SliderRepeat : SliderEndCircle
|
||||||
{
|
{
|
||||||
public int RepeatIndex { get; set; }
|
public SliderRepeat(Slider slider)
|
||||||
public double SpanDuration { get; set; }
|
: base(slider)
|
||||||
|
|
||||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
|
||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
|
||||||
|
|
||||||
// Out preempt should be one span early to give the user ample warning.
|
|
||||||
TimePreempt += SpanDuration;
|
|
||||||
|
|
||||||
// We want to show the first RepeatPoint as the TimePreempt dictates but on short (and possibly fast) sliders
|
|
||||||
// we may need to cut down this time on following RepeatPoints to only show up to two RepeatPoints at any given time.
|
|
||||||
if (RepeatIndex > 0)
|
|
||||||
TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new SliderRepeatJudgement();
|
public override Judgement CreateJudgement() => new SliderRepeatJudgement();
|
||||||
|
|
||||||
public class SliderRepeatJudgement : OsuJudgement
|
public class SliderRepeatJudgement : OsuJudgement
|
||||||
|
@ -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 osu.Framework.Bindables;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
@ -13,23 +12,18 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// Note that this should not be used for timing correctness.
|
/// Note that this should not be used for timing correctness.
|
||||||
/// See <see cref="SliderEventType.LegacyLastTick"/> usage in <see cref="Slider"/> for more information.
|
/// See <see cref="SliderEventType.LegacyLastTick"/> usage in <see cref="Slider"/> for more information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SliderTailCircle : SliderCircle
|
public class SliderTailCircle : SliderEndCircle
|
||||||
{
|
{
|
||||||
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
|
||||||
|
|
||||||
public SliderTailCircle(Slider slider)
|
public SliderTailCircle(Slider slider)
|
||||||
|
: base(slider)
|
||||||
{
|
{
|
||||||
pathVersion.BindTo(slider.Path.Version);
|
|
||||||
pathVersion.BindValueChanged(_ => Position = slider.EndPosition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new SliderTailJudgement();
|
public override Judgement CreateJudgement() => new SliderTailJudgement();
|
||||||
|
|
||||||
public class SliderTailJudgement : OsuJudgement
|
public class SliderTailJudgement : OsuJudgement
|
||||||
{
|
{
|
||||||
public override HitResult MaxResult => HitResult.IgnoreHit;
|
public override HitResult MaxResult => HitResult.SmallTickHit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
ReverseArrow,
|
ReverseArrow,
|
||||||
HitCircleText,
|
HitCircleText,
|
||||||
SliderHeadHitCircle,
|
SliderHeadHitCircle,
|
||||||
|
SliderTailHitCircle,
|
||||||
SliderFollowCircle,
|
SliderFollowCircle,
|
||||||
SliderBall,
|
SliderBall,
|
||||||
SliderBody,
|
SliderBody,
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -15,6 +18,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
private bool disjointTrail;
|
private bool disjointTrail;
|
||||||
private double lastTrailTime;
|
private double lastTrailTime;
|
||||||
|
private IBindable<float> cursorSize;
|
||||||
|
|
||||||
public LegacyCursorTrail()
|
public LegacyCursorTrail()
|
||||||
{
|
{
|
||||||
@ -22,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin)
|
private void load(ISkinSource skin, OsuConfigManager config)
|
||||||
{
|
{
|
||||||
Texture = skin.GetTexture("cursortrail");
|
Texture = skin.GetTexture("cursortrail");
|
||||||
disjointTrail = skin.GetTexture("cursormiddle") == null;
|
disjointTrail = skin.GetTexture("cursormiddle") == null;
|
||||||
@ -32,12 +36,16 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
// stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation.
|
// stable "magic ratio". see OsuPlayfieldAdjustmentContainer for full explanation.
|
||||||
Texture.ScaleAdjust *= 1.6f;
|
Texture.ScaleAdjust *= 1.6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cursorSize = config.GetBindable<float>(OsuSetting.GameplayCursorSize).GetBoundCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override double FadeDuration => disjointTrail ? 150 : 500;
|
protected override double FadeDuration => disjointTrail ? 150 : 500;
|
||||||
|
|
||||||
protected override bool InterpolateMovements => !disjointTrail;
|
protected override bool InterpolateMovements => !disjointTrail;
|
||||||
|
|
||||||
|
protected override float IntervalMultiplier => 1 / Math.Max(cursorSize.Value, 1);
|
||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
{
|
{
|
||||||
if (!disjointTrail)
|
if (!disjointTrail)
|
||||||
|
@ -21,10 +21,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
public class LegacyMainCirclePiece : CompositeDrawable
|
public class LegacyMainCirclePiece : CompositeDrawable
|
||||||
{
|
{
|
||||||
private readonly string priorityLookup;
|
private readonly string priorityLookup;
|
||||||
|
private readonly bool hasNumber;
|
||||||
|
|
||||||
public LegacyMainCirclePiece(string priorityLookup = null)
|
public LegacyMainCirclePiece(string priorityLookup = null, bool hasNumber = true)
|
||||||
{
|
{
|
||||||
this.priorityLookup = priorityLookup;
|
this.priorityLookup = priorityLookup;
|
||||||
|
this.hasNumber = hasNumber;
|
||||||
|
|
||||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||||
}
|
}
|
||||||
@ -47,6 +49,23 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject;
|
OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject;
|
||||||
|
|
||||||
|
bool allowFallback = false;
|
||||||
|
|
||||||
|
// attempt lookup using priority specification
|
||||||
|
Texture baseTexture = getTextureWithFallback(string.Empty);
|
||||||
|
|
||||||
|
// if the base texture was not found without a fallback, switch on fallback mode and re-perform the lookup.
|
||||||
|
if (baseTexture == null)
|
||||||
|
{
|
||||||
|
allowFallback = true;
|
||||||
|
baseTexture = getTextureWithFallback(string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
// at this point, any further texture fetches should be correctly using the priority source if the base texture was retrieved using it.
|
||||||
|
// the flow above handles the case where a sliderendcircle.png is retrieved from the skin, but sliderendcircleoverlay.png doesn't exist.
|
||||||
|
// expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png (potentially from the default/fall-through skin).
|
||||||
|
Texture overlayTexture = getTextureWithFallback("overlay");
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
circleSprites = new Container<Sprite>
|
circleSprites = new Container<Sprite>
|
||||||
@ -58,19 +77,23 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
hitCircleSprite = new Sprite
|
hitCircleSprite = new Sprite
|
||||||
{
|
{
|
||||||
Texture = getTextureWithFallback(string.Empty),
|
Texture = baseTexture,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
},
|
||||||
hitCircleOverlay = new Sprite
|
hitCircleOverlay = new Sprite
|
||||||
{
|
{
|
||||||
Texture = getTextureWithFallback("overlay"),
|
Texture = overlayTexture,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText
|
};
|
||||||
|
|
||||||
|
if (hasNumber)
|
||||||
|
{
|
||||||
|
AddInternal(hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText
|
||||||
{
|
{
|
||||||
Font = OsuFont.Numeric.With(size: 40),
|
Font = OsuFont.Numeric.With(size: 40),
|
||||||
UseFullGlyphHeight = false,
|
UseFullGlyphHeight = false,
|
||||||
@ -78,8 +101,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
},
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
bool overlayAboveNumber = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true;
|
bool overlayAboveNumber = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true;
|
||||||
|
|
||||||
@ -95,8 +118,13 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
Texture tex = null;
|
Texture tex = null;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(priorityLookup))
|
if (!string.IsNullOrEmpty(priorityLookup))
|
||||||
|
{
|
||||||
tex = skin.GetTexture($"{priorityLookup}{name}");
|
tex = skin.GetTexture($"{priorityLookup}{name}");
|
||||||
|
|
||||||
|
if (!allowFallback)
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
return tex ?? skin.GetTexture($"hitcircle{name}");
|
return tex ?? skin.GetTexture($"hitcircle{name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,6 +135,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
state.BindValueChanged(updateState, true);
|
state.BindValueChanged(updateState, true);
|
||||||
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
accentColour.BindValueChanged(colour => hitCircleSprite.Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true);
|
||||||
|
if (hasNumber)
|
||||||
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,6 +149,8 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
|
||||||
circleSprites.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
circleSprites.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||||
|
|
||||||
|
if (hasNumber)
|
||||||
|
{
|
||||||
var legacyVersion = skin.GetConfig<LegacySetting, decimal>(LegacySetting.Version)?.Value;
|
var legacyVersion = skin.GetConfig<LegacySetting, decimal>(LegacySetting.Version)?.Value;
|
||||||
|
|
||||||
if (legacyVersion >= 2.0m)
|
if (legacyVersion >= 2.0m)
|
||||||
@ -131,6 +162,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
hitCircleText.FadeOut(legacy_fade_duration, Easing.Out);
|
hitCircleText.FadeOut(legacy_fade_duration, Easing.Out);
|
||||||
hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.SliderTailHitCircle:
|
||||||
|
if (hasHitCircle.Value)
|
||||||
|
return new LegacyMainCirclePiece("sliderendcircle", false);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
case OsuSkinComponents.SliderHeadHitCircle:
|
case OsuSkinComponents.SliderHeadHitCircle:
|
||||||
if (hasHitCircle.Value)
|
if (hasHitCircle.Value)
|
||||||
return new LegacyMainCirclePiece("sliderstartcircle");
|
return new LegacyMainCirclePiece("sliderstartcircle");
|
||||||
|
@ -119,6 +119,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual bool InterpolateMovements => true;
|
protected virtual bool InterpolateMovements => true;
|
||||||
|
|
||||||
|
protected virtual float IntervalMultiplier => 1.0f;
|
||||||
|
|
||||||
private Vector2? lastPosition;
|
private Vector2? lastPosition;
|
||||||
private readonly InputResampler resampler = new InputResampler();
|
private readonly InputResampler resampler = new InputResampler();
|
||||||
|
|
||||||
@ -147,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
float distance = diff.Length;
|
float distance = diff.Length;
|
||||||
Vector2 direction = diff / distance;
|
Vector2 direction = diff / distance;
|
||||||
|
|
||||||
float interval = partSize.X / 2.5f;
|
float interval = partSize.X / 2.5f * IntervalMultiplier;
|
||||||
|
|
||||||
for (float d = interval; d < distance; d += interval)
|
for (float d = interval; d < distance; d += interval)
|
||||||
{
|
{
|
||||||
|
@ -27,17 +27,17 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Snaking in sliders",
|
LabelText = "Snaking in sliders",
|
||||||
Bindable = config.GetBindable<bool>(OsuRulesetSetting.SnakingInSliders)
|
Current = config.GetBindable<bool>(OsuRulesetSetting.SnakingInSliders)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Snaking out sliders",
|
LabelText = "Snaking out sliders",
|
||||||
Bindable = config.GetBindable<bool>(OsuRulesetSetting.SnakingOutSliders)
|
Current = config.GetBindable<bool>(OsuRulesetSetting.SnakingOutSliders)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Cursor trail",
|
LabelText = "Cursor trail",
|
||||||
Bindable = config.GetBindable<bool>(OsuRulesetSetting.ShowCursorTrail)
|
Current = config.GetBindable<bool>(OsuRulesetSetting.ShowCursorTrail)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -30,8 +29,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
|
|
||||||
private readonly Random rng = new Random(1337);
|
private readonly Random rng = new Random(1337);
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[Test]
|
||||||
private void load()
|
public void TestVariousHits()
|
||||||
{
|
{
|
||||||
AddStep("Hit", () => addHitJudgement(false));
|
AddStep("Hit", () => addHitJudgement(false));
|
||||||
AddStep("Strong hit", () => addStrongHitJudgement(false));
|
AddStep("Strong hit", () => addStrongHitJudgement(false));
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Taiko.Judgements
|
|||||||
{
|
{
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
case HitResult.Great:
|
case HitResult.SmallTickHit:
|
||||||
return 0.15;
|
return 0.15;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
if (!(obj is DrawableDrumRollTick))
|
if (!(obj is DrawableDrumRollTick))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (result.Type > HitResult.Miss)
|
if (result.IsHit)
|
||||||
rollingHits++;
|
rollingHits++;
|
||||||
else
|
else
|
||||||
rollingHits--;
|
rollingHits--;
|
||||||
@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Ok);
|
ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Ok);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ApplyResult(r => r.Type = HitResult.Miss);
|
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateStateTransforms(ArmedState state)
|
protected override void UpdateStateTransforms(ArmedState state)
|
||||||
|
@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
if (!userTriggered)
|
if (!userTriggered)
|
||||||
{
|
{
|
||||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||||
ApplyResult(r => r.Type = HitResult.Miss);
|
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (!validActionPressed)
|
if (!validActionPressed)
|
||||||
ApplyResult(r => r.Type = HitResult.Miss);
|
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||||
else
|
else
|
||||||
ApplyResult(r => r.Type = result);
|
ApplyResult(r => r.Type = result);
|
||||||
}
|
}
|
||||||
|
@ -211,9 +211,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
tick.TriggerResult(false);
|
tick.TriggerResult(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var hitResult = numHits > HitObject.RequiredHits / 2 ? HitResult.Ok : HitResult.Miss;
|
ApplyResult(r => r.Type = numHits > HitObject.RequiredHits / 2 ? HitResult.Ok : r.Judgement.MinResult);
|
||||||
|
|
||||||
ApplyResult(r => r.Type = hitResult);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
|||||||
private double hpMultiplier;
|
private double hpMultiplier;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// HP multiplier for a <see cref="HitResult.Miss"/>.
|
/// HP multiplier for a <see cref="HitResult"/> that does not satisfy <see cref="HitResultExtensions.IsHit"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private double hpMissMultiplier;
|
private double hpMissMultiplier;
|
||||||
|
|
||||||
@ -45,6 +45,6 @@ namespace osu.Game.Rulesets.Taiko.Scoring
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override double GetHealthIncreaseFor(JudgementResult result)
|
protected override double GetHealthIncreaseFor(JudgementResult result)
|
||||||
=> base.GetHealthIncreaseFor(result) * (result.Type == HitResult.Miss ? hpMissMultiplier : hpMultiplier);
|
=> base.GetHealthIncreaseFor(result) * (result.IsHit ? hpMultiplier : hpMissMultiplier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
if (r?.Type.AffectsCombo() == false)
|
if (r?.Type.AffectsCombo() == false)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
passing = r == null || r.Type > HitResult.Miss;
|
passing = r == null || r.IsHit;
|
||||||
|
|
||||||
foreach (var sprite in InternalChildren.OfType<ScrollerSprite>())
|
foreach (var sprite in InternalChildren.OfType<ScrollerSprite>())
|
||||||
sprite.Passing = passing;
|
sprite.Passing = passing;
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
Alpha = 0.15f;
|
Alpha = 0.15f;
|
||||||
Masking = true;
|
Masking = true;
|
||||||
|
|
||||||
if (result == HitResult.Miss)
|
if (!result.IsHit())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
|
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
|
||||||
|
@ -163,7 +163,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
target = centreHit;
|
target = centreHit;
|
||||||
back = centre;
|
back = centre;
|
||||||
|
|
||||||
if (gameplayClock?.IsSeeking != true)
|
|
||||||
drumSample.Centre?.Play();
|
drumSample.Centre?.Play();
|
||||||
}
|
}
|
||||||
else if (action == RimAction)
|
else if (action == RimAction)
|
||||||
@ -171,7 +170,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
target = rimHit;
|
target = rimHit;
|
||||||
back = rim;
|
back = rim;
|
||||||
|
|
||||||
if (gameplayClock?.IsSeeking != true)
|
|
||||||
drumSample.Rim?.Play();
|
drumSample.Rim?.Play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,14 @@ namespace osu.Game.Tests.Editing
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class EditorChangeHandlerTest
|
public class EditorChangeHandlerTest
|
||||||
{
|
{
|
||||||
|
private int stateChangedFired;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
stateChangedFired = 0;
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSaveRestoreState()
|
public void TestSaveRestoreState()
|
||||||
{
|
{
|
||||||
@ -23,6 +31,8 @@ namespace osu.Game.Tests.Editing
|
|||||||
addArbitraryChange(beatmap);
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
@ -30,6 +40,8 @@ namespace osu.Game.Tests.Editing
|
|||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.True);
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
|
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -45,6 +57,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
string hash = handler.CurrentStateHash;
|
string hash = handler.CurrentStateHash;
|
||||||
|
|
||||||
@ -52,6 +65,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
|
||||||
Assert.That(hash, Is.EqualTo(handler.CurrentStateHash));
|
Assert.That(hash, Is.EqualTo(handler.CurrentStateHash));
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(1));
|
||||||
|
|
||||||
handler.RestoreState(-1);
|
handler.RestoreState(-1);
|
||||||
|
|
||||||
@ -60,6 +74,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
// we should only be able to restore once even though we saved twice.
|
// we should only be able to restore once even though we saved twice.
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.True);
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -71,6 +86,8 @@ namespace osu.Game.Tests.Editing
|
|||||||
|
|
||||||
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++)
|
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++)
|
||||||
{
|
{
|
||||||
|
Assert.That(stateChangedFired, Is.EqualTo(i));
|
||||||
|
|
||||||
addArbitraryChange(beatmap);
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
}
|
}
|
||||||
@ -114,7 +131,10 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var beatmap = new EditorBeatmap(new Beatmap());
|
var beatmap = new EditorBeatmap(new Beatmap());
|
||||||
|
|
||||||
return (new EditorChangeHandler(beatmap), beatmap);
|
var changeHandler = new EditorChangeHandler(beatmap);
|
||||||
|
|
||||||
|
changeHandler.OnStateChange += () => stateChangedFired++;
|
||||||
|
return (changeHandler, beatmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addArbitraryChange(EditorBeatmap beatmap)
|
private void addArbitraryChange(EditorBeatmap beatmap)
|
||||||
|
39
osu.Game.Tests/NonVisual/GameplayClockTest.cs
Normal file
39
osu.Game.Tests/NonVisual/GameplayClockTest.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.NonVisual
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class GameplayClockTest
|
||||||
|
{
|
||||||
|
[TestCase(0)]
|
||||||
|
[TestCase(1)]
|
||||||
|
public void TestTrueGameplayRateWithZeroAdjustment(double underlyingClockRate)
|
||||||
|
{
|
||||||
|
var framedClock = new FramedClock(new ManualClock { Rate = underlyingClockRate });
|
||||||
|
var gameplayClock = new TestGameplayClock(framedClock);
|
||||||
|
|
||||||
|
gameplayClock.MutableNonGameplayAdjustments.Add(new BindableDouble());
|
||||||
|
|
||||||
|
Assert.That(gameplayClock.TrueGameplayRate, Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestGameplayClock : GameplayClock
|
||||||
|
{
|
||||||
|
public List<Bindable<double>> MutableNonGameplayAdjustments { get; } = new List<Bindable<double>>();
|
||||||
|
|
||||||
|
public override IEnumerable<Bindable<double>> NonGameplayAdjustments => MutableNonGameplayAdjustments;
|
||||||
|
|
||||||
|
public TestGameplayClock(IFrameBasedClock underlyingClock)
|
||||||
|
: base(underlyingClock)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs
Normal file
69
osu.Game.Tests/Visual/Editing/TestSceneComposeSelectBox.cs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Editing
|
||||||
|
{
|
||||||
|
public class TestSceneComposeSelectBox : OsuTestScene
|
||||||
|
{
|
||||||
|
private Container selectionArea;
|
||||||
|
|
||||||
|
public TestSceneComposeSelectBox()
|
||||||
|
{
|
||||||
|
SelectionBox selectionBox = null;
|
||||||
|
|
||||||
|
AddStep("create box", () =>
|
||||||
|
Child = selectionArea = new Container
|
||||||
|
{
|
||||||
|
Size = new Vector2(400),
|
||||||
|
Position = -new Vector2(150),
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
selectionBox = new SelectionBox
|
||||||
|
{
|
||||||
|
CanRotate = true,
|
||||||
|
CanScaleX = true,
|
||||||
|
CanScaleY = true,
|
||||||
|
|
||||||
|
OnRotation = handleRotation,
|
||||||
|
OnScale = handleScale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AddToggleStep("toggle rotation", state => selectionBox.CanRotate = state);
|
||||||
|
AddToggleStep("toggle x", state => selectionBox.CanScaleX = state);
|
||||||
|
AddToggleStep("toggle y", state => selectionBox.CanScaleY = state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleScale(Vector2 amount, Anchor reference)
|
||||||
|
{
|
||||||
|
if ((reference & Anchor.y1) == 0)
|
||||||
|
{
|
||||||
|
int directionY = (reference & Anchor.y0) > 0 ? -1 : 1;
|
||||||
|
if (directionY < 0)
|
||||||
|
selectionArea.Y += amount.Y;
|
||||||
|
selectionArea.Height += directionY * amount.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((reference & Anchor.x1) == 0)
|
||||||
|
{
|
||||||
|
int directionX = (reference & Anchor.x0) > 0 ? -1 : 1;
|
||||||
|
if (directionX < 0)
|
||||||
|
selectionArea.X += amount.X;
|
||||||
|
selectionArea.Width += directionX * amount.X;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRotation(float angle)
|
||||||
|
{
|
||||||
|
// kinda silly and wrong, but just showing that the drag handles work.
|
||||||
|
selectionArea.Rotation += angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,8 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@ -22,6 +24,9 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
protected override bool EditorComponentsReady => Editor.ChildrenOfType<SetupScreen>().SingleOrDefault()?.IsLoaded == true;
|
protected override bool EditorComponentsReady => Editor.ChildrenOfType<SetupScreen>().SingleOrDefault()?.IsLoaded == true;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private BeatmapManager beatmapManager { get; set; }
|
||||||
|
|
||||||
public override void SetUpSteps()
|
public override void SetUpSteps()
|
||||||
{
|
{
|
||||||
AddStep("set dummy", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null));
|
AddStep("set dummy", () => Beatmap.Value = new DummyWorkingBeatmap(Audio, null));
|
||||||
@ -38,6 +43,15 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
{
|
{
|
||||||
AddStep("save beatmap", () => Editor.Save());
|
AddStep("save beatmap", () => Editor.Save());
|
||||||
AddAssert("new beatmap persisted", () => EditorBeatmap.BeatmapInfo.ID > 0);
|
AddAssert("new beatmap persisted", () => EditorBeatmap.BeatmapInfo.ID > 0);
|
||||||
|
AddAssert("new beatmap in database", () => beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID)?.DeletePending == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestExitWithoutSave()
|
||||||
|
{
|
||||||
|
AddStep("exit without save", () => Editor.Exit());
|
||||||
|
AddUntilStep("wait for exit", () => !Editor.IsCurrentScreen());
|
||||||
|
AddAssert("new beatmap not persisted", () => beatmapManager.QueryBeatmapSet(s => s.ID == EditorBeatmap.BeatmapInfo.BeatmapSet.ID)?.DeletePending == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -55,7 +69,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
using (var zip = ZipArchive.Open(temp))
|
using (var zip = ZipArchive.Open(temp))
|
||||||
zip.WriteToDirectory(extractedFolder);
|
zip.WriteToDirectory(extractedFolder);
|
||||||
|
|
||||||
bool success = setup.ChangeAudioTrack(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3"));
|
bool success = setup.ChildrenOfType<ResourcesSection>().First().ChangeAudioTrack(Path.Combine(extractedFolder, "03. Renatus - Soleily 192kbps.mp3"));
|
||||||
|
|
||||||
File.Delete(temp);
|
File.Delete(temp);
|
||||||
Directory.Delete(extractedFolder, true);
|
Directory.Delete(extractedFolder, true);
|
||||||
|
@ -19,12 +19,14 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
public void TestSlidingSampleStopsOnSeek()
|
public void TestSlidingSampleStopsOnSeek()
|
||||||
{
|
{
|
||||||
DrawableSlider slider = null;
|
DrawableSlider slider = null;
|
||||||
DrawableSample[] samples = null;
|
DrawableSample[] loopingSamples = null;
|
||||||
|
DrawableSample[] onceOffSamples = null;
|
||||||
|
|
||||||
AddStep("get first slider", () =>
|
AddStep("get first slider", () =>
|
||||||
{
|
{
|
||||||
slider = Editor.ChildrenOfType<DrawableSlider>().OrderBy(s => s.HitObject.StartTime).First();
|
slider = Editor.ChildrenOfType<DrawableSlider>().OrderBy(s => s.HitObject.StartTime).First();
|
||||||
samples = slider.ChildrenOfType<DrawableSample>().ToArray();
|
onceOffSamples = slider.ChildrenOfType<DrawableSample>().Where(s => !s.Looping).ToArray();
|
||||||
|
loopingSamples = slider.ChildrenOfType<DrawableSample>().Where(s => s.Looping).ToArray();
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("start playback", () => EditorClock.Start());
|
AddStep("start playback", () => EditorClock.Start());
|
||||||
@ -34,14 +36,15 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
if (!slider.Tracking.Value)
|
if (!slider.Tracking.Value)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!samples.Any(s => s.Playing))
|
if (!loopingSamples.Any(s => s.Playing))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
EditorClock.Seek(20000);
|
EditorClock.Seek(20000);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("slider samples are not playing", () => samples.Length == 5 && samples.All(s => s.Played && !s.Playing));
|
AddAssert("non-looping samples are playing", () => onceOffSamples.Length == 4 && loopingSamples.All(s => s.Played || s.Playing));
|
||||||
|
AddAssert("looping samples are not playing", () => loopingSamples.Length == 1 && loopingSamples.All(s => s.Played && !s.Playing));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics.Audio;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public class TestSceneGameplaySamplePlayback : PlayerTestScene
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestAllSamplesStopDuringSeek()
|
||||||
|
{
|
||||||
|
DrawableSlider slider = null;
|
||||||
|
DrawableSample[] samples = null;
|
||||||
|
ISamplePlaybackDisabler gameplayClock = null;
|
||||||
|
|
||||||
|
AddStep("get variables", () =>
|
||||||
|
{
|
||||||
|
gameplayClock = Player.ChildrenOfType<FrameStabilityContainer>().First().GameplayClock;
|
||||||
|
slider = Player.ChildrenOfType<DrawableSlider>().OrderBy(s => s.HitObject.StartTime).First();
|
||||||
|
samples = slider.ChildrenOfType<DrawableSample>().ToArray();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for slider sliding then seek", () =>
|
||||||
|
{
|
||||||
|
if (!slider.Tracking.Value)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!samples.Any(s => s.Playing))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player.ChildrenOfType<GameplayClockContainer>().First().Seek(40000);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("sample playback disabled", () => gameplayClock.SamplePlaybackDisabled.Value);
|
||||||
|
|
||||||
|
// because we are in frame stable context, it's quite likely that not all samples are "played" at this point.
|
||||||
|
// the important thing is that at least one started, and that sample has since stopped.
|
||||||
|
AddAssert("all looping samples stopped immediately", () => allStopped(allLoopingSounds));
|
||||||
|
AddUntilStep("all samples stopped eventually", () => allStopped(allSounds));
|
||||||
|
|
||||||
|
AddAssert("sample playback still disabled", () => gameplayClock.SamplePlaybackDisabled.Value);
|
||||||
|
|
||||||
|
AddUntilStep("seek finished, sample playback enabled", () => !gameplayClock.SamplePlaybackDisabled.Value);
|
||||||
|
AddUntilStep("any sample is playing", () => Player.ChildrenOfType<PausableSkinnableSound>().Any(s => s.IsPlaying));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<PausableSkinnableSound> allSounds => Player.ChildrenOfType<PausableSkinnableSound>();
|
||||||
|
private IEnumerable<PausableSkinnableSound> allLoopingSounds => allSounds.Where(sound => sound.Looping);
|
||||||
|
|
||||||
|
private bool allStopped(IEnumerable<PausableSkinnableSound> sounds) => sounds.All(sound => !sound.IsPlaying);
|
||||||
|
|
||||||
|
protected override bool Autoplay => true;
|
||||||
|
|
||||||
|
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
// 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.Graphics.UserInterfaceV2;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
public class TestSceneLabelledSliderBar : OsuTestScene
|
||||||
|
{
|
||||||
|
[TestCase(false)]
|
||||||
|
[TestCase(true)]
|
||||||
|
public void TestSliderBar(bool hasDescription) => createSliderBar(hasDescription);
|
||||||
|
|
||||||
|
private void createSliderBar(bool hasDescription = false)
|
||||||
|
{
|
||||||
|
AddStep("create component", () =>
|
||||||
|
{
|
||||||
|
LabelledSliderBar<double> component;
|
||||||
|
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Width = 500,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Child = component = new LabelledSliderBar<double>
|
||||||
|
{
|
||||||
|
Current = new BindableDouble(5)
|
||||||
|
{
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = 10,
|
||||||
|
Precision = 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
component.Label = "a sample component";
|
||||||
|
component.Description = hasDescription ? "this text describes the component" : string.Empty;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,34 +10,34 @@ namespace osu.Game.Tournament.Components
|
|||||||
{
|
{
|
||||||
public class DateTextBox : SettingsTextBox
|
public class DateTextBox : SettingsTextBox
|
||||||
{
|
{
|
||||||
public new Bindable<DateTimeOffset> Bindable
|
public new Bindable<DateTimeOffset> Current
|
||||||
{
|
{
|
||||||
get => bindable;
|
get => current;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
bindable = value.GetBoundCopy();
|
current = value.GetBoundCopy();
|
||||||
bindable.BindValueChanged(dto =>
|
current.BindValueChanged(dto =>
|
||||||
base.Bindable.Value = dto.NewValue.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"), true);
|
base.Current.Value = dto.NewValue.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hold a reference to the provided bindable so we don't have to in every settings section.
|
// hold a reference to the provided bindable so we don't have to in every settings section.
|
||||||
private Bindable<DateTimeOffset> bindable = new Bindable<DateTimeOffset>();
|
private Bindable<DateTimeOffset> current = new Bindable<DateTimeOffset>();
|
||||||
|
|
||||||
public DateTextBox()
|
public DateTextBox()
|
||||||
{
|
{
|
||||||
base.Bindable = new Bindable<string>();
|
base.Current = new Bindable<string>();
|
||||||
|
|
||||||
((OsuTextBox)Control).OnCommit += (sender, newText) =>
|
((OsuTextBox)Control).OnCommit += (sender, newText) =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bindable.Value = DateTimeOffset.Parse(sender.Text);
|
current.Value = DateTimeOffset.Parse(sender.Text);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// reset textbox content to its last valid state on a parse failure.
|
// reset textbox content to its last valid state on a parse failure.
|
||||||
bindable.TriggerChange();
|
current.TriggerChange();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -63,25 +63,25 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
{
|
{
|
||||||
LabelText = "Name",
|
LabelText = "Name",
|
||||||
Width = 0.33f,
|
Width = 0.33f,
|
||||||
Bindable = Model.Name
|
Current = Model.Name
|
||||||
},
|
},
|
||||||
new SettingsTextBox
|
new SettingsTextBox
|
||||||
{
|
{
|
||||||
LabelText = "Description",
|
LabelText = "Description",
|
||||||
Width = 0.33f,
|
Width = 0.33f,
|
||||||
Bindable = Model.Description
|
Current = Model.Description
|
||||||
},
|
},
|
||||||
new DateTextBox
|
new DateTextBox
|
||||||
{
|
{
|
||||||
LabelText = "Start Time",
|
LabelText = "Start Time",
|
||||||
Width = 0.33f,
|
Width = 0.33f,
|
||||||
Bindable = Model.StartDate
|
Current = Model.StartDate
|
||||||
},
|
},
|
||||||
new SettingsSlider<int>
|
new SettingsSlider<int>
|
||||||
{
|
{
|
||||||
LabelText = "Best of",
|
LabelText = "Best of",
|
||||||
Width = 0.33f,
|
Width = 0.33f,
|
||||||
Bindable = Model.BestOf
|
Current = Model.BestOf
|
||||||
},
|
},
|
||||||
new SettingsButton
|
new SettingsButton
|
||||||
{
|
{
|
||||||
@ -186,14 +186,14 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
LabelText = "Beatmap ID",
|
LabelText = "Beatmap ID",
|
||||||
RelativeSizeAxes = Axes.None,
|
RelativeSizeAxes = Axes.None,
|
||||||
Width = 200,
|
Width = 200,
|
||||||
Bindable = beatmapId,
|
Current = beatmapId,
|
||||||
},
|
},
|
||||||
new SettingsTextBox
|
new SettingsTextBox
|
||||||
{
|
{
|
||||||
LabelText = "Mods",
|
LabelText = "Mods",
|
||||||
RelativeSizeAxes = Axes.None,
|
RelativeSizeAxes = Axes.None,
|
||||||
Width = 200,
|
Width = 200,
|
||||||
Bindable = mods,
|
Current = mods,
|
||||||
},
|
},
|
||||||
drawableContainer = new Container
|
drawableContainer = new Container
|
||||||
{
|
{
|
||||||
|
@ -74,13 +74,13 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
{
|
{
|
||||||
LabelText = "Mod",
|
LabelText = "Mod",
|
||||||
Width = 0.33f,
|
Width = 0.33f,
|
||||||
Bindable = Model.Mod
|
Current = Model.Mod
|
||||||
},
|
},
|
||||||
new SettingsSlider<int>
|
new SettingsSlider<int>
|
||||||
{
|
{
|
||||||
LabelText = "Seed",
|
LabelText = "Seed",
|
||||||
Width = 0.33f,
|
Width = 0.33f,
|
||||||
Bindable = Model.Seed
|
Current = Model.Seed
|
||||||
},
|
},
|
||||||
new SettingsButton
|
new SettingsButton
|
||||||
{
|
{
|
||||||
@ -187,21 +187,21 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
LabelText = "Beatmap ID",
|
LabelText = "Beatmap ID",
|
||||||
RelativeSizeAxes = Axes.None,
|
RelativeSizeAxes = Axes.None,
|
||||||
Width = 200,
|
Width = 200,
|
||||||
Bindable = beatmapId,
|
Current = beatmapId,
|
||||||
},
|
},
|
||||||
new SettingsSlider<int>
|
new SettingsSlider<int>
|
||||||
{
|
{
|
||||||
LabelText = "Seed",
|
LabelText = "Seed",
|
||||||
RelativeSizeAxes = Axes.None,
|
RelativeSizeAxes = Axes.None,
|
||||||
Width = 200,
|
Width = 200,
|
||||||
Bindable = beatmap.Seed
|
Current = beatmap.Seed
|
||||||
},
|
},
|
||||||
new SettingsTextBox
|
new SettingsTextBox
|
||||||
{
|
{
|
||||||
LabelText = "Score",
|
LabelText = "Score",
|
||||||
RelativeSizeAxes = Axes.None,
|
RelativeSizeAxes = Axes.None,
|
||||||
Width = 200,
|
Width = 200,
|
||||||
Bindable = score,
|
Current = score,
|
||||||
},
|
},
|
||||||
drawableContainer = new Container
|
drawableContainer = new Container
|
||||||
{
|
{
|
||||||
|
@ -102,31 +102,31 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
{
|
{
|
||||||
LabelText = "Name",
|
LabelText = "Name",
|
||||||
Width = 0.2f,
|
Width = 0.2f,
|
||||||
Bindable = Model.FullName
|
Current = Model.FullName
|
||||||
},
|
},
|
||||||
new SettingsTextBox
|
new SettingsTextBox
|
||||||
{
|
{
|
||||||
LabelText = "Acronym",
|
LabelText = "Acronym",
|
||||||
Width = 0.2f,
|
Width = 0.2f,
|
||||||
Bindable = Model.Acronym
|
Current = Model.Acronym
|
||||||
},
|
},
|
||||||
new SettingsTextBox
|
new SettingsTextBox
|
||||||
{
|
{
|
||||||
LabelText = "Flag",
|
LabelText = "Flag",
|
||||||
Width = 0.2f,
|
Width = 0.2f,
|
||||||
Bindable = Model.FlagName
|
Current = Model.FlagName
|
||||||
},
|
},
|
||||||
new SettingsTextBox
|
new SettingsTextBox
|
||||||
{
|
{
|
||||||
LabelText = "Seed",
|
LabelText = "Seed",
|
||||||
Width = 0.2f,
|
Width = 0.2f,
|
||||||
Bindable = Model.Seed
|
Current = Model.Seed
|
||||||
},
|
},
|
||||||
new SettingsSlider<int>
|
new SettingsSlider<int>
|
||||||
{
|
{
|
||||||
LabelText = "Last Year Placement",
|
LabelText = "Last Year Placement",
|
||||||
Width = 0.33f,
|
Width = 0.33f,
|
||||||
Bindable = Model.LastYearPlacing
|
Current = Model.LastYearPlacing
|
||||||
},
|
},
|
||||||
new SettingsButton
|
new SettingsButton
|
||||||
{
|
{
|
||||||
@ -247,7 +247,7 @@ namespace osu.Game.Tournament.Screens.Editors
|
|||||||
LabelText = "User ID",
|
LabelText = "User ID",
|
||||||
RelativeSizeAxes = Axes.None,
|
RelativeSizeAxes = Axes.None,
|
||||||
Width = 200,
|
Width = 200,
|
||||||
Bindable = userId,
|
Current = userId,
|
||||||
},
|
},
|
||||||
drawableContainer = new Container
|
drawableContainer = new Container
|
||||||
{
|
{
|
||||||
|
@ -113,13 +113,13 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
|||||||
new SettingsSlider<int>
|
new SettingsSlider<int>
|
||||||
{
|
{
|
||||||
LabelText = "Chroma width",
|
LabelText = "Chroma width",
|
||||||
Bindable = LadderInfo.ChromaKeyWidth,
|
Current = LadderInfo.ChromaKeyWidth,
|
||||||
KeyboardStep = 1,
|
KeyboardStep = 1,
|
||||||
},
|
},
|
||||||
new SettingsSlider<int>
|
new SettingsSlider<int>
|
||||||
{
|
{
|
||||||
LabelText = "Players per team",
|
LabelText = "Players per team",
|
||||||
Bindable = LadderInfo.PlayersPerTeam,
|
Current = LadderInfo.PlayersPerTeam,
|
||||||
KeyboardStep = 1,
|
KeyboardStep = 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,15 +51,15 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
|||||||
|
|
||||||
editorInfo.Selected.ValueChanged += selection =>
|
editorInfo.Selected.ValueChanged += selection =>
|
||||||
{
|
{
|
||||||
roundDropdown.Bindable = selection.NewValue?.Round;
|
roundDropdown.Current = selection.NewValue?.Round;
|
||||||
losersCheckbox.Current = selection.NewValue?.Losers;
|
losersCheckbox.Current = selection.NewValue?.Losers;
|
||||||
dateTimeBox.Bindable = selection.NewValue?.Date;
|
dateTimeBox.Current = selection.NewValue?.Date;
|
||||||
|
|
||||||
team1Dropdown.Bindable = selection.NewValue?.Team1;
|
team1Dropdown.Current = selection.NewValue?.Team1;
|
||||||
team2Dropdown.Bindable = selection.NewValue?.Team2;
|
team2Dropdown.Current = selection.NewValue?.Team2;
|
||||||
};
|
};
|
||||||
|
|
||||||
roundDropdown.Bindable.ValueChanged += round =>
|
roundDropdown.Current.ValueChanged += round =>
|
||||||
{
|
{
|
||||||
if (editorInfo.Selected.Value?.Date.Value < round.NewValue?.StartDate.Value)
|
if (editorInfo.Selected.Value?.Date.Value < round.NewValue?.StartDate.Value)
|
||||||
{
|
{
|
||||||
@ -88,7 +88,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
|||||||
{
|
{
|
||||||
public SettingsRoundDropdown(BindableList<TournamentRound> rounds)
|
public SettingsRoundDropdown(BindableList<TournamentRound> rounds)
|
||||||
{
|
{
|
||||||
Bindable = new Bindable<TournamentRound>();
|
Current = new Bindable<TournamentRound>();
|
||||||
|
|
||||||
foreach (var r in rounds.Prepend(new TournamentRound()))
|
foreach (var r in rounds.Prepend(new TournamentRound()))
|
||||||
add(r);
|
add(r);
|
||||||
|
@ -61,7 +61,7 @@ namespace osu.Game.Tournament.Screens.TeamIntro
|
|||||||
new SettingsTeamDropdown(LadderInfo.Teams)
|
new SettingsTeamDropdown(LadderInfo.Teams)
|
||||||
{
|
{
|
||||||
LabelText = "Show specific team",
|
LabelText = "Show specific team",
|
||||||
Bindable = currentTeam,
|
Current = currentTeam,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,25 @@ namespace osu.Game.Beatmaps
|
|||||||
return computeDifficulty(key, beatmapInfo, rulesetInfo);
|
return computeDifficulty(key, beatmapInfo, rulesetInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="DifficultyRating"/> that describes a star rating.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// For more information, see: https://osu.ppy.sh/help/wiki/Difficulties
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="starRating">The star rating.</param>
|
||||||
|
/// <returns>The <see cref="DifficultyRating"/> that best describes <paramref name="starRating"/>.</returns>
|
||||||
|
public static DifficultyRating GetDifficultyRating(double starRating)
|
||||||
|
{
|
||||||
|
if (starRating < 2.0) return DifficultyRating.Easy;
|
||||||
|
if (starRating < 2.7) return DifficultyRating.Normal;
|
||||||
|
if (starRating < 4.0) return DifficultyRating.Hard;
|
||||||
|
if (starRating < 5.3) return DifficultyRating.Insane;
|
||||||
|
if (starRating < 6.5) return DifficultyRating.Expert;
|
||||||
|
|
||||||
|
return DifficultyRating.ExpertPlus;
|
||||||
|
}
|
||||||
|
|
||||||
private CancellationTokenSource trackedUpdateCancellationSource;
|
private CancellationTokenSource trackedUpdateCancellationSource;
|
||||||
private readonly List<CancellationTokenSource> linkedCancellationSources = new List<CancellationTokenSource>();
|
private readonly List<CancellationTokenSource> linkedCancellationSources = new List<CancellationTokenSource>();
|
||||||
|
|
||||||
@ -307,5 +326,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
// Todo: Add more members (BeatmapInfo.DifficultyRating? Attributes? Etc...)
|
// Todo: Add more members (BeatmapInfo.DifficultyRating? Attributes? Etc...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DifficultyRating DifficultyRating => BeatmapDifficultyManager.GetDifficultyRating(Stars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,21 +135,7 @@ namespace osu.Game.Beatmaps
|
|||||||
public List<ScoreInfo> Scores { get; set; }
|
public List<ScoreInfo> Scores { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public DifficultyRating DifficultyRating
|
public DifficultyRating DifficultyRating => BeatmapDifficultyManager.GetDifficultyRating(StarDifficulty);
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var rating = StarDifficulty;
|
|
||||||
|
|
||||||
if (rating < 2.0) return DifficultyRating.Easy;
|
|
||||||
if (rating < 2.7) return DifficultyRating.Normal;
|
|
||||||
if (rating < 4.0) return DifficultyRating.Hard;
|
|
||||||
if (rating < 5.3) return DifficultyRating.Insane;
|
|
||||||
if (rating < 6.5) return DifficultyRating.Expert;
|
|
||||||
|
|
||||||
return DifficultyRating.ExpertPlus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string[] SearchableTerms => new[]
|
public string[] SearchableTerms => new[]
|
||||||
{
|
{
|
||||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly WorkingBeatmap DefaultBeatmap;
|
public readonly WorkingBeatmap DefaultBeatmap;
|
||||||
|
|
||||||
public override string[] HandledExtensions => new[] { ".osz" };
|
public override IEnumerable<string> HandledExtensions => new[] { ".osz" };
|
||||||
|
|
||||||
protected override string[] HashableFileTypes => new[] { ".osu" };
|
protected override string[] HashableFileTypes => new[] { ".osu" };
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// 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.Game.Graphics;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.ControlPoints
|
namespace osu.Game.Beatmaps.ControlPoints
|
||||||
{
|
{
|
||||||
@ -18,6 +20,8 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
|
|
||||||
public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time);
|
public int CompareTo(ControlPoint other) => Time.CompareTo(other.Time);
|
||||||
|
|
||||||
|
public virtual Color4 GetRepresentingColour(OsuColour colours) => colours.Yellow;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether this <see cref="ControlPoint"/> results in a meaningful change when placed alongside another.
|
/// Determines whether this <see cref="ControlPoint"/> results in a meaningful change when placed alongside another.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// 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.Game.Graphics;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.ControlPoints
|
namespace osu.Game.Beatmaps.ControlPoints
|
||||||
{
|
{
|
||||||
@ -23,6 +25,8 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
MaxValue = 10
|
MaxValue = 10
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public override Color4 GetRepresentingColour(OsuColour colours) => colours.GreenDark;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The speed multiplier at this control point.
|
/// The speed multiplier at this control point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// 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.Game.Graphics;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.ControlPoints
|
namespace osu.Game.Beatmaps.ControlPoints
|
||||||
{
|
{
|
||||||
@ -18,6 +20,8 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly BindableBool OmitFirstBarLineBindable = new BindableBool();
|
public readonly BindableBool OmitFirstBarLineBindable = new BindableBool();
|
||||||
|
|
||||||
|
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Purple;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the first bar line of this control point is ignored.
|
/// Whether the first bar line of this control point is ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.ControlPoints
|
namespace osu.Game.Beatmaps.ControlPoints
|
||||||
{
|
{
|
||||||
@ -16,6 +18,8 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
SampleVolumeBindable = { Disabled = true }
|
SampleVolumeBindable = { Disabled = true }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Pink;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default sample bank at this control point.
|
/// The default sample bank at this control point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps.ControlPoints
|
namespace osu.Game.Beatmaps.ControlPoints
|
||||||
{
|
{
|
||||||
@ -18,6 +20,8 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const double default_beat_length = 60000.0 / 60.0;
|
private const double default_beat_length = 60000.0 / 60.0;
|
||||||
|
|
||||||
|
public override Color4 GetRepresentingColour(OsuColour colours) => colours.YellowDark;
|
||||||
|
|
||||||
public static readonly TimingControlPoint DEFAULT = new TimingControlPoint
|
public static readonly TimingControlPoint DEFAULT = new TimingControlPoint
|
||||||
{
|
{
|
||||||
BeatLengthBindable =
|
BeatLengthBindable =
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
// 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 System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -14,6 +18,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -21,9 +26,6 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
{
|
{
|
||||||
public class DifficultyIcon : CompositeDrawable, IHasCustomTooltip
|
public class DifficultyIcon : CompositeDrawable, IHasCustomTooltip
|
||||||
{
|
{
|
||||||
private readonly BeatmapInfo beatmap;
|
|
||||||
private readonly RulesetInfo ruleset;
|
|
||||||
|
|
||||||
private readonly Container iconContainer;
|
private readonly Container iconContainer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -35,23 +37,49 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
set => iconContainer.Size = value;
|
set => iconContainer.Size = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DifficultyIcon(BeatmapInfo beatmap, RulesetInfo ruleset = null, bool shouldShowTooltip = true)
|
[NotNull]
|
||||||
|
private readonly BeatmapInfo beatmap;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private readonly RulesetInfo ruleset;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private readonly IReadOnlyList<Mod> mods;
|
||||||
|
|
||||||
|
private readonly bool shouldShowTooltip;
|
||||||
|
private readonly IBindable<StarDifficulty> difficultyBindable = new Bindable<StarDifficulty>();
|
||||||
|
|
||||||
|
private Drawable background;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="DifficultyIcon"/> with a given <see cref="RulesetInfo"/> and <see cref="Mod"/> combination.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmap">The beatmap to show the difficulty of.</param>
|
||||||
|
/// <param name="ruleset">The ruleset to show the difficulty with.</param>
|
||||||
|
/// <param name="mods">The mods to show the difficulty with.</param>
|
||||||
|
/// <param name="shouldShowTooltip">Whether to display a tooltip when hovered.</param>
|
||||||
|
public DifficultyIcon([NotNull] BeatmapInfo beatmap, [CanBeNull] RulesetInfo ruleset, [CanBeNull] IReadOnlyList<Mod> mods, bool shouldShowTooltip = true)
|
||||||
|
: this(beatmap, shouldShowTooltip)
|
||||||
|
{
|
||||||
|
this.ruleset = ruleset ?? beatmap.Ruleset;
|
||||||
|
this.mods = mods ?? Array.Empty<Mod>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="DifficultyIcon"/> that follows the currently-selected ruleset and mods.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="beatmap">The beatmap to show the difficulty of.</param>
|
||||||
|
/// <param name="shouldShowTooltip">Whether to display a tooltip when hovered.</param>
|
||||||
|
public DifficultyIcon([NotNull] BeatmapInfo beatmap, bool shouldShowTooltip = true)
|
||||||
{
|
{
|
||||||
this.beatmap = beatmap ?? throw new ArgumentNullException(nameof(beatmap));
|
this.beatmap = beatmap ?? throw new ArgumentNullException(nameof(beatmap));
|
||||||
|
this.shouldShowTooltip = shouldShowTooltip;
|
||||||
this.ruleset = ruleset ?? beatmap.Ruleset;
|
|
||||||
if (shouldShowTooltip)
|
|
||||||
TooltipContent = beatmap;
|
|
||||||
|
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
InternalChild = iconContainer = new Container { Size = new Vector2(20f) };
|
InternalChild = iconContainer = new Container { Size = new Vector2(20f) };
|
||||||
}
|
}
|
||||||
|
|
||||||
public ITooltip GetCustomTooltip() => new DifficultyIconTooltip();
|
|
||||||
|
|
||||||
public object TooltipContent { get; }
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
@ -70,10 +98,10 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
Type = EdgeEffectType.Shadow,
|
Type = EdgeEffectType.Shadow,
|
||||||
Radius = 5,
|
Radius = 5,
|
||||||
},
|
},
|
||||||
Child = new Box
|
Child = background = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = colours.ForDifficultyRating(beatmap.DifficultyRating),
|
Colour = colours.ForDifficultyRating(beatmap.DifficultyRating) // Default value that will be re-populated once difficulty calculation completes
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ConstrainedIconContainer
|
new ConstrainedIconContainer
|
||||||
@ -82,16 +110,73 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
// the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment)
|
// the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment)
|
||||||
Icon = ruleset?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }
|
Icon = (ruleset ?? beatmap.Ruleset)?.CreateInstance()?.CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.Regular.QuestionCircle }
|
||||||
}
|
},
|
||||||
|
new DelayedLoadUnloadWrapper(() => new DifficultyRetriever(beatmap, ruleset, mods) { StarDifficulty = { BindTarget = difficultyBindable } }, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
difficultyBindable.BindValueChanged(difficulty => background.Colour = colours.ForDifficultyRating(difficulty.NewValue.DifficultyRating));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITooltip GetCustomTooltip() => new DifficultyIconTooltip();
|
||||||
|
|
||||||
|
public object TooltipContent => shouldShowTooltip ? new DifficultyIconTooltipContent(beatmap, difficultyBindable) : null;
|
||||||
|
|
||||||
|
private class DifficultyRetriever : Component
|
||||||
|
{
|
||||||
|
public readonly Bindable<StarDifficulty> StarDifficulty = new Bindable<StarDifficulty>();
|
||||||
|
|
||||||
|
private readonly BeatmapInfo beatmap;
|
||||||
|
private readonly RulesetInfo ruleset;
|
||||||
|
private readonly IReadOnlyList<Mod> mods;
|
||||||
|
|
||||||
|
private CancellationTokenSource difficultyCancellation;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private BeatmapDifficultyManager difficultyManager { get; set; }
|
||||||
|
|
||||||
|
public DifficultyRetriever(BeatmapInfo beatmap, RulesetInfo ruleset, IReadOnlyList<Mod> mods)
|
||||||
|
{
|
||||||
|
this.beatmap = beatmap;
|
||||||
|
this.ruleset = ruleset;
|
||||||
|
this.mods = mods;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IBindable<StarDifficulty> localStarDifficulty;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
difficultyCancellation = new CancellationTokenSource();
|
||||||
|
localStarDifficulty = ruleset != null
|
||||||
|
? difficultyManager.GetBindableDifficulty(beatmap, ruleset, mods, difficultyCancellation.Token)
|
||||||
|
: difficultyManager.GetBindableDifficulty(beatmap, difficultyCancellation.Token);
|
||||||
|
localStarDifficulty.BindValueChanged(difficulty => StarDifficulty.Value = difficulty.NewValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
difficultyCancellation?.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DifficultyIconTooltipContent
|
||||||
|
{
|
||||||
|
public readonly BeatmapInfo Beatmap;
|
||||||
|
public readonly IBindable<StarDifficulty> Difficulty;
|
||||||
|
|
||||||
|
public DifficultyIconTooltipContent(BeatmapInfo beatmap, IBindable<StarDifficulty> difficulty)
|
||||||
|
{
|
||||||
|
Beatmap = beatmap;
|
||||||
|
Difficulty = difficulty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DifficultyIconTooltip : VisibilityContainer, ITooltip
|
private class DifficultyIconTooltip : VisibilityContainer, ITooltip
|
||||||
{
|
{
|
||||||
private readonly OsuSpriteText difficultyName, starRating;
|
private readonly OsuSpriteText difficultyName, starRating;
|
||||||
private readonly Box background;
|
private readonly Box background;
|
||||||
|
|
||||||
private readonly FillFlowContainer difficultyFlow;
|
private readonly FillFlowContainer difficultyFlow;
|
||||||
|
|
||||||
public DifficultyIconTooltip()
|
public DifficultyIconTooltip()
|
||||||
@ -159,14 +244,22 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
background.Colour = colours.Gray3;
|
background.Colour = colours.Gray3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly IBindable<StarDifficulty> starDifficulty = new Bindable<StarDifficulty>();
|
||||||
|
|
||||||
public bool SetContent(object content)
|
public bool SetContent(object content)
|
||||||
{
|
{
|
||||||
if (!(content is BeatmapInfo beatmap))
|
if (!(content is DifficultyIconTooltipContent iconContent))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
difficultyName.Text = beatmap.Version;
|
difficultyName.Text = iconContent.Beatmap.Version;
|
||||||
starRating.Text = $"{beatmap.StarDifficulty:0.##}";
|
|
||||||
difficultyFlow.Colour = colours.ForDifficultyRating(beatmap.DifficultyRating, true);
|
starDifficulty.UnbindAll();
|
||||||
|
starDifficulty.BindTo(iconContent.Difficulty);
|
||||||
|
starDifficulty.BindValueChanged(difficulty =>
|
||||||
|
{
|
||||||
|
starRating.Text = $"{difficulty.NewValue.Stars:0.##}";
|
||||||
|
difficultyFlow.Colour = colours.ForDifficultyRating(difficulty.NewValue.DifficultyRating, true);
|
||||||
|
}, true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
public class GroupedDifficultyIcon : DifficultyIcon
|
public class GroupedDifficultyIcon : DifficultyIcon
|
||||||
{
|
{
|
||||||
public GroupedDifficultyIcon(List<BeatmapInfo> beatmaps, RulesetInfo ruleset, Color4 counterColour)
|
public GroupedDifficultyIcon(List<BeatmapInfo> beatmaps, RulesetInfo ruleset, Color4 counterColour)
|
||||||
: base(beatmaps.OrderBy(b => b.StarDifficulty).Last(), ruleset, false)
|
: base(beatmaps.OrderBy(b => b.StarDifficulty).Last(), ruleset, null, false)
|
||||||
{
|
{
|
||||||
AddInternal(new OsuSpriteText
|
AddInternal(new OsuSpriteText
|
||||||
{
|
{
|
||||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Configuration
|
|||||||
yield return new SettingsSlider<float>
|
yield return new SettingsSlider<float>
|
||||||
{
|
{
|
||||||
LabelText = attr.Label,
|
LabelText = attr.Label,
|
||||||
Bindable = bNumber,
|
Current = bNumber,
|
||||||
KeyboardStep = 0.1f,
|
KeyboardStep = 0.1f,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ namespace osu.Game.Configuration
|
|||||||
yield return new SettingsSlider<double>
|
yield return new SettingsSlider<double>
|
||||||
{
|
{
|
||||||
LabelText = attr.Label,
|
LabelText = attr.Label,
|
||||||
Bindable = bNumber,
|
Current = bNumber,
|
||||||
KeyboardStep = 0.1f,
|
KeyboardStep = 0.1f,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ namespace osu.Game.Configuration
|
|||||||
yield return new SettingsSlider<int>
|
yield return new SettingsSlider<int>
|
||||||
{
|
{
|
||||||
LabelText = attr.Label,
|
LabelText = attr.Label,
|
||||||
Bindable = bNumber
|
Current = bNumber
|
||||||
};
|
};
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -86,7 +86,7 @@ namespace osu.Game.Configuration
|
|||||||
yield return new SettingsCheckbox
|
yield return new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = attr.Label,
|
LabelText = attr.Label,
|
||||||
Bindable = bBool
|
Current = bBool
|
||||||
};
|
};
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -95,7 +95,7 @@ namespace osu.Game.Configuration
|
|||||||
yield return new SettingsTextBox
|
yield return new SettingsTextBox
|
||||||
{
|
{
|
||||||
LabelText = attr.Label,
|
LabelText = attr.Label,
|
||||||
Bindable = bString
|
Current = bString
|
||||||
};
|
};
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -105,7 +105,7 @@ namespace osu.Game.Configuration
|
|||||||
var dropdown = (Drawable)Activator.CreateInstance(dropdownType);
|
var dropdown = (Drawable)Activator.CreateInstance(dropdownType);
|
||||||
|
|
||||||
dropdownType.GetProperty(nameof(SettingsDropdown<object>.LabelText))?.SetValue(dropdown, attr.Label);
|
dropdownType.GetProperty(nameof(SettingsDropdown<object>.LabelText))?.SetValue(dropdown, attr.Label);
|
||||||
dropdownType.GetProperty(nameof(SettingsDropdown<object>.Bindable))?.SetValue(dropdown, bindable);
|
dropdownType.GetProperty(nameof(SettingsDropdown<object>.Current))?.SetValue(dropdown, bindable);
|
||||||
|
|
||||||
yield return dropdown;
|
yield return dropdown;
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
private readonly Bindable<WeakReference<TModel>> itemRemoved = new Bindable<WeakReference<TModel>>();
|
private readonly Bindable<WeakReference<TModel>> itemRemoved = new Bindable<WeakReference<TModel>>();
|
||||||
|
|
||||||
public virtual string[] HandledExtensions => new[] { ".zip" };
|
public virtual IEnumerable<string> HandledExtensions => new[] { ".zip" };
|
||||||
|
|
||||||
public virtual bool SupportsImportFromStable => RuntimeInfo.IsDesktop;
|
public virtual bool SupportsImportFromStable => RuntimeInfo.IsDesktop;
|
||||||
|
|
||||||
|
@ -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.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace osu.Game.Database
|
namespace osu.Game.Database
|
||||||
@ -19,6 +20,6 @@ namespace osu.Game.Database
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// An array of accepted file extensions (in the standard format of ".abc").
|
/// An array of accepted file extensions (in the standard format of ".abc").
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string[] HandledExtensions { get; }
|
IEnumerable<string> HandledExtensions { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
24
osu.Game/Graphics/UserInterfaceV2/LabelledSliderBar.cs
Normal file
24
osu.Game/Graphics/UserInterfaceV2/LabelledSliderBar.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
|
{
|
||||||
|
public class LabelledSliderBar<TNumber> : LabelledComponent<SettingsSlider<TNumber>, TNumber>
|
||||||
|
where TNumber : struct, IEquatable<TNumber>, IComparable<TNumber>, IConvertible
|
||||||
|
{
|
||||||
|
public LabelledSliderBar()
|
||||||
|
: base(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override SettingsSlider<TNumber> CreateComponent() => new SettingsSlider<TNumber>
|
||||||
|
{
|
||||||
|
TransferValueOnCommit = true,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -232,9 +232,9 @@ namespace osu.Game
|
|||||||
dependencies.Cache(new SessionStatics());
|
dependencies.Cache(new SessionStatics());
|
||||||
dependencies.Cache(new OsuColour());
|
dependencies.Cache(new OsuColour());
|
||||||
|
|
||||||
fileImporters.Add(BeatmapManager);
|
RegisterImportHandler(BeatmapManager);
|
||||||
fileImporters.Add(ScoreManager);
|
RegisterImportHandler(ScoreManager);
|
||||||
fileImporters.Add(SkinManager);
|
RegisterImportHandler(SkinManager);
|
||||||
|
|
||||||
// tracks play so loud our samples can't keep up.
|
// tracks play so loud our samples can't keep up.
|
||||||
// this adds a global reduction of track volume for the time being.
|
// this adds a global reduction of track volume for the time being.
|
||||||
@ -343,6 +343,18 @@ namespace osu.Game
|
|||||||
|
|
||||||
private readonly List<ICanAcceptFiles> fileImporters = new List<ICanAcceptFiles>();
|
private readonly List<ICanAcceptFiles> fileImporters = new List<ICanAcceptFiles>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a global handler for file imports. Most recently registered will have precedence.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handler">The handler to register.</param>
|
||||||
|
public void RegisterImportHandler(ICanAcceptFiles handler) => fileImporters.Insert(0, handler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregister a global handler for file imports.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handler">The previously registered handler.</param>
|
||||||
|
public void UnregisterImportHandler(ICanAcceptFiles handler) => fileImporters.Remove(handler);
|
||||||
|
|
||||||
public async Task Import(params string[] paths)
|
public async Task Import(params string[] paths)
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(paths.First())?.ToLowerInvariant();
|
var extension = Path.GetExtension(paths.First())?.ToLowerInvariant();
|
||||||
@ -354,7 +366,7 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray();
|
public IEnumerable<string> HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions);
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
|||||||
|
|
||||||
updateItems();
|
updateItems();
|
||||||
|
|
||||||
dropdown.Bindable = audio.AudioDevice;
|
dropdown.Current = audio.AudioDevice;
|
||||||
|
|
||||||
audio.OnNewDevice += onDeviceChanged;
|
audio.OnNewDevice += onDeviceChanged;
|
||||||
audio.OnLostDevice += onDeviceChanged;
|
audio.OnLostDevice += onDeviceChanged;
|
||||||
|
@ -21,23 +21,23 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Interface voices",
|
LabelText = "Interface voices",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.MenuVoice)
|
Current = config.GetBindable<bool>(OsuSetting.MenuVoice)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "osu! music theme",
|
LabelText = "osu! music theme",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.MenuMusic)
|
Current = config.GetBindable<bool>(OsuSetting.MenuMusic)
|
||||||
},
|
},
|
||||||
new SettingsDropdown<IntroSequence>
|
new SettingsDropdown<IntroSequence>
|
||||||
{
|
{
|
||||||
LabelText = "Intro sequence",
|
LabelText = "Intro sequence",
|
||||||
Bindable = config.GetBindable<IntroSequence>(OsuSetting.IntroSequence),
|
Current = config.GetBindable<IntroSequence>(OsuSetting.IntroSequence),
|
||||||
Items = Enum.GetValues(typeof(IntroSequence)).Cast<IntroSequence>()
|
Items = Enum.GetValues(typeof(IntroSequence)).Cast<IntroSequence>()
|
||||||
},
|
},
|
||||||
new SettingsDropdown<BackgroundSource>
|
new SettingsDropdown<BackgroundSource>
|
||||||
{
|
{
|
||||||
LabelText = "Background source",
|
LabelText = "Background source",
|
||||||
Bindable = config.GetBindable<BackgroundSource>(OsuSetting.MenuBackgroundSource),
|
Current = config.GetBindable<BackgroundSource>(OsuSetting.MenuBackgroundSource),
|
||||||
Items = Enum.GetValues(typeof(BackgroundSource)).Cast<BackgroundSource>()
|
Items = Enum.GetValues(typeof(BackgroundSource)).Cast<BackgroundSource>()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
|||||||
new SettingsSlider<double, OffsetSlider>
|
new SettingsSlider<double, OffsetSlider>
|
||||||
{
|
{
|
||||||
LabelText = "Audio offset",
|
LabelText = "Audio offset",
|
||||||
Bindable = config.GetBindable<double>(OsuSetting.AudioOffset),
|
Current = config.GetBindable<double>(OsuSetting.AudioOffset),
|
||||||
KeyboardStep = 1f
|
KeyboardStep = 1f
|
||||||
},
|
},
|
||||||
new SettingsButton
|
new SettingsButton
|
||||||
|
@ -20,28 +20,28 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
|||||||
new SettingsSlider<double>
|
new SettingsSlider<double>
|
||||||
{
|
{
|
||||||
LabelText = "Master",
|
LabelText = "Master",
|
||||||
Bindable = audio.Volume,
|
Current = audio.Volume,
|
||||||
KeyboardStep = 0.01f,
|
KeyboardStep = 0.01f,
|
||||||
DisplayAsPercentage = true
|
DisplayAsPercentage = true
|
||||||
},
|
},
|
||||||
new SettingsSlider<double>
|
new SettingsSlider<double>
|
||||||
{
|
{
|
||||||
LabelText = "Master (window inactive)",
|
LabelText = "Master (window inactive)",
|
||||||
Bindable = config.GetBindable<double>(OsuSetting.VolumeInactive),
|
Current = config.GetBindable<double>(OsuSetting.VolumeInactive),
|
||||||
KeyboardStep = 0.01f,
|
KeyboardStep = 0.01f,
|
||||||
DisplayAsPercentage = true
|
DisplayAsPercentage = true
|
||||||
},
|
},
|
||||||
new SettingsSlider<double>
|
new SettingsSlider<double>
|
||||||
{
|
{
|
||||||
LabelText = "Effect",
|
LabelText = "Effect",
|
||||||
Bindable = audio.VolumeSample,
|
Current = audio.VolumeSample,
|
||||||
KeyboardStep = 0.01f,
|
KeyboardStep = 0.01f,
|
||||||
DisplayAsPercentage = true
|
DisplayAsPercentage = true
|
||||||
},
|
},
|
||||||
new SettingsSlider<double>
|
new SettingsSlider<double>
|
||||||
{
|
{
|
||||||
LabelText = "Music",
|
LabelText = "Music",
|
||||||
Bindable = audio.VolumeTrack,
|
Current = audio.VolumeTrack,
|
||||||
KeyboardStep = 0.01f,
|
KeyboardStep = 0.01f,
|
||||||
DisplayAsPercentage = true
|
DisplayAsPercentage = true
|
||||||
},
|
},
|
||||||
|
@ -19,12 +19,12 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Show log overlay",
|
LabelText = "Show log overlay",
|
||||||
Bindable = frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay)
|
Current = frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Bypass front-to-back render pass",
|
LabelText = "Bypass front-to-back render pass",
|
||||||
Bindable = config.GetBindable<bool>(DebugSetting.BypassFrontToBackPass)
|
Current = config.GetBindable<bool>(DebugSetting.BypassFrontToBackPass)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -21,62 +21,62 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
new SettingsSlider<double>
|
new SettingsSlider<double>
|
||||||
{
|
{
|
||||||
LabelText = "Background dim",
|
LabelText = "Background dim",
|
||||||
Bindable = config.GetBindable<double>(OsuSetting.DimLevel),
|
Current = config.GetBindable<double>(OsuSetting.DimLevel),
|
||||||
KeyboardStep = 0.01f,
|
KeyboardStep = 0.01f,
|
||||||
DisplayAsPercentage = true
|
DisplayAsPercentage = true
|
||||||
},
|
},
|
||||||
new SettingsSlider<double>
|
new SettingsSlider<double>
|
||||||
{
|
{
|
||||||
LabelText = "Background blur",
|
LabelText = "Background blur",
|
||||||
Bindable = config.GetBindable<double>(OsuSetting.BlurLevel),
|
Current = config.GetBindable<double>(OsuSetting.BlurLevel),
|
||||||
KeyboardStep = 0.01f,
|
KeyboardStep = 0.01f,
|
||||||
DisplayAsPercentage = true
|
DisplayAsPercentage = true
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Lighten playfield during breaks",
|
LabelText = "Lighten playfield during breaks",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.LightenDuringBreaks)
|
Current = config.GetBindable<bool>(OsuSetting.LightenDuringBreaks)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Show score overlay",
|
LabelText = "Show score overlay",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.ShowInterface)
|
Current = config.GetBindable<bool>(OsuSetting.ShowInterface)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Show difficulty graph on progress bar",
|
LabelText = "Show difficulty graph on progress bar",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.ShowProgressGraph)
|
Current = config.GetBindable<bool>(OsuSetting.ShowProgressGraph)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Show health display even when you can't fail",
|
LabelText = "Show health display even when you can't fail",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.ShowHealthDisplayWhenCantFail),
|
Current = config.GetBindable<bool>(OsuSetting.ShowHealthDisplayWhenCantFail),
|
||||||
Keywords = new[] { "hp", "bar" }
|
Keywords = new[] { "hp", "bar" }
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Fade playfield to red when health is low",
|
LabelText = "Fade playfield to red when health is low",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.FadePlayfieldWhenHealthLow),
|
Current = config.GetBindable<bool>(OsuSetting.FadePlayfieldWhenHealthLow),
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Always show key overlay",
|
LabelText = "Always show key overlay",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.KeyOverlay)
|
Current = config.GetBindable<bool>(OsuSetting.KeyOverlay)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Positional hitsounds",
|
LabelText = "Positional hitsounds",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.PositionalHitSounds)
|
Current = config.GetBindable<bool>(OsuSetting.PositionalHitSounds)
|
||||||
},
|
},
|
||||||
new SettingsEnumDropdown<ScoreMeterType>
|
new SettingsEnumDropdown<ScoreMeterType>
|
||||||
{
|
{
|
||||||
LabelText = "Score meter type",
|
LabelText = "Score meter type",
|
||||||
Bindable = config.GetBindable<ScoreMeterType>(OsuSetting.ScoreMeter)
|
Current = config.GetBindable<ScoreMeterType>(OsuSetting.ScoreMeter)
|
||||||
},
|
},
|
||||||
new SettingsEnumDropdown<ScoringMode>
|
new SettingsEnumDropdown<ScoringMode>
|
||||||
{
|
{
|
||||||
LabelText = "Score display mode",
|
LabelText = "Score display mode",
|
||||||
Bindable = config.GetBindable<ScoringMode>(OsuSetting.ScoreDisplayMode)
|
Current = config.GetBindable<ScoringMode>(OsuSetting.ScoreDisplayMode)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
Add(new SettingsCheckbox
|
Add(new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Disable Windows key during gameplay",
|
LabelText = "Disable Windows key during gameplay",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.GameplayDisableWinKey)
|
Current = config.GetBindable<bool>(OsuSetting.GameplayDisableWinKey)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Increase visibility of first object when visual impairment mods are enabled",
|
LabelText = "Increase visibility of first object when visual impairment mods are enabled",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.IncreaseFirstObjectVisibility),
|
Current = config.GetBindable<bool>(OsuSetting.IncreaseFirstObjectVisibility),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -31,31 +31,31 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Right mouse drag to absolute scroll",
|
LabelText = "Right mouse drag to absolute scroll",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.SongSelectRightMouseScroll),
|
Current = config.GetBindable<bool>(OsuSetting.SongSelectRightMouseScroll),
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Show converted beatmaps",
|
LabelText = "Show converted beatmaps",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.ShowConvertedBeatmaps),
|
Current = config.GetBindable<bool>(OsuSetting.ShowConvertedBeatmaps),
|
||||||
},
|
},
|
||||||
new SettingsSlider<double, StarsSlider>
|
new SettingsSlider<double, StarsSlider>
|
||||||
{
|
{
|
||||||
LabelText = "Display beatmaps from",
|
LabelText = "Display beatmaps from",
|
||||||
Bindable = config.GetBindable<double>(OsuSetting.DisplayStarsMinimum),
|
Current = config.GetBindable<double>(OsuSetting.DisplayStarsMinimum),
|
||||||
KeyboardStep = 0.1f,
|
KeyboardStep = 0.1f,
|
||||||
Keywords = new[] { "minimum", "maximum", "star", "difficulty" }
|
Keywords = new[] { "minimum", "maximum", "star", "difficulty" }
|
||||||
},
|
},
|
||||||
new SettingsSlider<double, MaximumStarsSlider>
|
new SettingsSlider<double, MaximumStarsSlider>
|
||||||
{
|
{
|
||||||
LabelText = "up to",
|
LabelText = "up to",
|
||||||
Bindable = config.GetBindable<double>(OsuSetting.DisplayStarsMaximum),
|
Current = config.GetBindable<double>(OsuSetting.DisplayStarsMaximum),
|
||||||
KeyboardStep = 0.1f,
|
KeyboardStep = 0.1f,
|
||||||
Keywords = new[] { "minimum", "maximum", "star", "difficulty" }
|
Keywords = new[] { "minimum", "maximum", "star", "difficulty" }
|
||||||
},
|
},
|
||||||
new SettingsEnumDropdown<RandomSelectAlgorithm>
|
new SettingsEnumDropdown<RandomSelectAlgorithm>
|
||||||
{
|
{
|
||||||
LabelText = "Random selection algorithm",
|
LabelText = "Random selection algorithm",
|
||||||
Bindable = config.GetBindable<RandomSelectAlgorithm>(OsuSetting.RandomSelectAlgorithm),
|
Current = config.GetBindable<RandomSelectAlgorithm>(OsuSetting.RandomSelectAlgorithm),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Prefer metadata in original language",
|
LabelText = "Prefer metadata in original language",
|
||||||
Bindable = frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowUnicode)
|
Current = frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowUnicode)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -240,12 +240,12 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Remember username",
|
LabelText = "Remember username",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.SaveUsername),
|
Current = config.GetBindable<bool>(OsuSetting.SaveUsername),
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Stay signed in",
|
LabelText = "Stay signed in",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.SavePassword),
|
Current = config.GetBindable<bool>(OsuSetting.SavePassword),
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Settings.Sections.General
|
|||||||
Add(new SettingsEnumDropdown<ReleaseStream>
|
Add(new SettingsEnumDropdown<ReleaseStream>
|
||||||
{
|
{
|
||||||
LabelText = "Release stream",
|
LabelText = "Release stream",
|
||||||
Bindable = config.GetBindable<ReleaseStream>(OsuSetting.ReleaseStream),
|
Current = config.GetBindable<ReleaseStream>(OsuSetting.ReleaseStream),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (updateManager?.CanCheckForUpdate == true)
|
if (updateManager?.CanCheckForUpdate == true)
|
||||||
|
@ -19,22 +19,22 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Storyboard / Video",
|
LabelText = "Storyboard / Video",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.ShowStoryboard)
|
Current = config.GetBindable<bool>(OsuSetting.ShowStoryboard)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Hit Lighting",
|
LabelText = "Hit Lighting",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.HitLighting)
|
Current = config.GetBindable<bool>(OsuSetting.HitLighting)
|
||||||
},
|
},
|
||||||
new SettingsEnumDropdown<ScreenshotFormat>
|
new SettingsEnumDropdown<ScreenshotFormat>
|
||||||
{
|
{
|
||||||
LabelText = "Screenshot format",
|
LabelText = "Screenshot format",
|
||||||
Bindable = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat)
|
Current = config.GetBindable<ScreenshotFormat>(OsuSetting.ScreenshotFormat)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Show menu cursor in screenshots",
|
LabelText = "Show menu cursor in screenshots",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.ScreenshotCaptureMenuCursor)
|
Current = config.GetBindable<bool>(OsuSetting.ScreenshotCaptureMenuCursor)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
windowModeDropdown = new SettingsDropdown<WindowMode>
|
windowModeDropdown = new SettingsDropdown<WindowMode>
|
||||||
{
|
{
|
||||||
LabelText = "Screen mode",
|
LabelText = "Screen mode",
|
||||||
Bindable = config.GetBindable<WindowMode>(FrameworkSetting.WindowMode),
|
Current = config.GetBindable<WindowMode>(FrameworkSetting.WindowMode),
|
||||||
ItemSource = windowModes,
|
ItemSource = windowModes,
|
||||||
},
|
},
|
||||||
resolutionSettingsContainer = new Container
|
resolutionSettingsContainer = new Container
|
||||||
@ -74,14 +74,14 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
{
|
{
|
||||||
LabelText = "UI Scaling",
|
LabelText = "UI Scaling",
|
||||||
TransferValueOnCommit = true,
|
TransferValueOnCommit = true,
|
||||||
Bindable = osuConfig.GetBindable<float>(OsuSetting.UIScale),
|
Current = osuConfig.GetBindable<float>(OsuSetting.UIScale),
|
||||||
KeyboardStep = 0.01f,
|
KeyboardStep = 0.01f,
|
||||||
Keywords = new[] { "scale", "letterbox" },
|
Keywords = new[] { "scale", "letterbox" },
|
||||||
},
|
},
|
||||||
new SettingsEnumDropdown<ScalingMode>
|
new SettingsEnumDropdown<ScalingMode>
|
||||||
{
|
{
|
||||||
LabelText = "Screen Scaling",
|
LabelText = "Screen Scaling",
|
||||||
Bindable = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling),
|
Current = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling),
|
||||||
Keywords = new[] { "scale", "letterbox" },
|
Keywords = new[] { "scale", "letterbox" },
|
||||||
},
|
},
|
||||||
scalingSettings = new FillFlowContainer<SettingsSlider<float>>
|
scalingSettings = new FillFlowContainer<SettingsSlider<float>>
|
||||||
@ -97,28 +97,28 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
new SettingsSlider<float>
|
new SettingsSlider<float>
|
||||||
{
|
{
|
||||||
LabelText = "Horizontal position",
|
LabelText = "Horizontal position",
|
||||||
Bindable = scalingPositionX,
|
Current = scalingPositionX,
|
||||||
KeyboardStep = 0.01f,
|
KeyboardStep = 0.01f,
|
||||||
DisplayAsPercentage = true
|
DisplayAsPercentage = true
|
||||||
},
|
},
|
||||||
new SettingsSlider<float>
|
new SettingsSlider<float>
|
||||||
{
|
{
|
||||||
LabelText = "Vertical position",
|
LabelText = "Vertical position",
|
||||||
Bindable = scalingPositionY,
|
Current = scalingPositionY,
|
||||||
KeyboardStep = 0.01f,
|
KeyboardStep = 0.01f,
|
||||||
DisplayAsPercentage = true
|
DisplayAsPercentage = true
|
||||||
},
|
},
|
||||||
new SettingsSlider<float>
|
new SettingsSlider<float>
|
||||||
{
|
{
|
||||||
LabelText = "Horizontal scale",
|
LabelText = "Horizontal scale",
|
||||||
Bindable = scalingSizeX,
|
Current = scalingSizeX,
|
||||||
KeyboardStep = 0.01f,
|
KeyboardStep = 0.01f,
|
||||||
DisplayAsPercentage = true
|
DisplayAsPercentage = true
|
||||||
},
|
},
|
||||||
new SettingsSlider<float>
|
new SettingsSlider<float>
|
||||||
{
|
{
|
||||||
LabelText = "Vertical scale",
|
LabelText = "Vertical scale",
|
||||||
Bindable = scalingSizeY,
|
Current = scalingSizeY,
|
||||||
KeyboardStep = 0.01f,
|
KeyboardStep = 0.01f,
|
||||||
DisplayAsPercentage = true
|
DisplayAsPercentage = true
|
||||||
},
|
},
|
||||||
@ -126,7 +126,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
scalingSettings.ForEach(s => bindPreviewEvent(s.Bindable));
|
scalingSettings.ForEach(s => bindPreviewEvent(s.Current));
|
||||||
|
|
||||||
var resolutions = getResolutions();
|
var resolutions = getResolutions();
|
||||||
|
|
||||||
@ -137,10 +137,10 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
LabelText = "Resolution",
|
LabelText = "Resolution",
|
||||||
ShowsDefaultIndicator = false,
|
ShowsDefaultIndicator = false,
|
||||||
Items = resolutions,
|
Items = resolutions,
|
||||||
Bindable = sizeFullscreen
|
Current = sizeFullscreen
|
||||||
};
|
};
|
||||||
|
|
||||||
windowModeDropdown.Bindable.BindValueChanged(mode =>
|
windowModeDropdown.Current.BindValueChanged(mode =>
|
||||||
{
|
{
|
||||||
if (mode.NewValue == WindowMode.Fullscreen)
|
if (mode.NewValue == WindowMode.Fullscreen)
|
||||||
{
|
{
|
||||||
|
@ -23,17 +23,17 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
new SettingsEnumDropdown<FrameSync>
|
new SettingsEnumDropdown<FrameSync>
|
||||||
{
|
{
|
||||||
LabelText = "Frame limiter",
|
LabelText = "Frame limiter",
|
||||||
Bindable = config.GetBindable<FrameSync>(FrameworkSetting.FrameSync)
|
Current = config.GetBindable<FrameSync>(FrameworkSetting.FrameSync)
|
||||||
},
|
},
|
||||||
new SettingsEnumDropdown<ExecutionMode>
|
new SettingsEnumDropdown<ExecutionMode>
|
||||||
{
|
{
|
||||||
LabelText = "Threading mode",
|
LabelText = "Threading mode",
|
||||||
Bindable = config.GetBindable<ExecutionMode>(FrameworkSetting.ExecutionMode)
|
Current = config.GetBindable<ExecutionMode>(FrameworkSetting.ExecutionMode)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Show FPS",
|
LabelText = "Show FPS",
|
||||||
Bindable = osuConfig.GetBindable<bool>(OsuSetting.ShowFpsDisplay)
|
Current = osuConfig.GetBindable<bool>(OsuSetting.ShowFpsDisplay)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -20,17 +20,17 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Rotate cursor when dragging",
|
LabelText = "Rotate cursor when dragging",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.CursorRotation)
|
Current = config.GetBindable<bool>(OsuSetting.CursorRotation)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Parallax",
|
LabelText = "Parallax",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.MenuParallax)
|
Current = config.GetBindable<bool>(OsuSetting.MenuParallax)
|
||||||
},
|
},
|
||||||
new SettingsSlider<float, TimeSlider>
|
new SettingsSlider<float, TimeSlider>
|
||||||
{
|
{
|
||||||
LabelText = "Hold-to-confirm activation time",
|
LabelText = "Hold-to-confirm activation time",
|
||||||
Bindable = config.GetBindable<float>(OsuSetting.UIHoldActivationDelay),
|
Current = config.GetBindable<float>(OsuSetting.UIHoldActivationDelay),
|
||||||
KeyboardStep = 50
|
KeyboardStep = 50
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -35,32 +35,32 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Raw input",
|
LabelText = "Raw input",
|
||||||
Bindable = rawInputToggle
|
Current = rawInputToggle
|
||||||
},
|
},
|
||||||
new SensitivitySetting
|
new SensitivitySetting
|
||||||
{
|
{
|
||||||
LabelText = "Cursor sensitivity",
|
LabelText = "Cursor sensitivity",
|
||||||
Bindable = sensitivityBindable
|
Current = sensitivityBindable
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Map absolute input to window",
|
LabelText = "Map absolute input to window",
|
||||||
Bindable = config.GetBindable<bool>(FrameworkSetting.MapAbsoluteInputToWindow)
|
Current = config.GetBindable<bool>(FrameworkSetting.MapAbsoluteInputToWindow)
|
||||||
},
|
},
|
||||||
new SettingsEnumDropdown<ConfineMouseMode>
|
new SettingsEnumDropdown<ConfineMouseMode>
|
||||||
{
|
{
|
||||||
LabelText = "Confine mouse cursor to window",
|
LabelText = "Confine mouse cursor to window",
|
||||||
Bindable = config.GetBindable<ConfineMouseMode>(FrameworkSetting.ConfineMouseMode),
|
Current = config.GetBindable<ConfineMouseMode>(FrameworkSetting.ConfineMouseMode),
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Disable mouse wheel during gameplay",
|
LabelText = "Disable mouse wheel during gameplay",
|
||||||
Bindable = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableWheel)
|
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableWheel)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Disable mouse buttons during gameplay",
|
LabelText = "Disable mouse buttons during gameplay",
|
||||||
Bindable = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons)
|
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,13 +19,13 @@ namespace osu.Game.Overlays.Settings.Sections.Online
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Warn about opening external links",
|
LabelText = "Warn about opening external links",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.ExternalLinkWarning)
|
Current = config.GetBindable<bool>(OsuSetting.ExternalLinkWarning)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Prefer downloads without video",
|
LabelText = "Prefer downloads without video",
|
||||||
Keywords = new[] { "no-video" },
|
Keywords = new[] { "no-video" },
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.PreferNoVideo)
|
Current = config.GetBindable<bool>(OsuSetting.PreferNoVideo)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -47,29 +47,29 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
new SettingsSlider<float, SizeSlider>
|
new SettingsSlider<float, SizeSlider>
|
||||||
{
|
{
|
||||||
LabelText = "Menu cursor size",
|
LabelText = "Menu cursor size",
|
||||||
Bindable = config.GetBindable<float>(OsuSetting.MenuCursorSize),
|
Current = config.GetBindable<float>(OsuSetting.MenuCursorSize),
|
||||||
KeyboardStep = 0.01f
|
KeyboardStep = 0.01f
|
||||||
},
|
},
|
||||||
new SettingsSlider<float, SizeSlider>
|
new SettingsSlider<float, SizeSlider>
|
||||||
{
|
{
|
||||||
LabelText = "Gameplay cursor size",
|
LabelText = "Gameplay cursor size",
|
||||||
Bindable = config.GetBindable<float>(OsuSetting.GameplayCursorSize),
|
Current = config.GetBindable<float>(OsuSetting.GameplayCursorSize),
|
||||||
KeyboardStep = 0.01f
|
KeyboardStep = 0.01f
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Adjust gameplay cursor size based on current beatmap",
|
LabelText = "Adjust gameplay cursor size based on current beatmap",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.AutoCursorSize)
|
Current = config.GetBindable<bool>(OsuSetting.AutoCursorSize)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Beatmap skins",
|
LabelText = "Beatmap skins",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.BeatmapSkins)
|
Current = config.GetBindable<bool>(OsuSetting.BeatmapSkins)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Beatmap hitsounds",
|
LabelText = "Beatmap hitsounds",
|
||||||
Bindable = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds)
|
Current = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
|||||||
|
|
||||||
config.BindWith(OsuSetting.Skin, configBindable);
|
config.BindWith(OsuSetting.Skin, configBindable);
|
||||||
|
|
||||||
skinDropdown.Bindable = dropdownBindable;
|
skinDropdown.Current = dropdownBindable;
|
||||||
skinDropdown.Items = skins.GetAllUsableSkins().ToArray();
|
skinDropdown.Items = skins.GetAllUsableSkins().ToArray();
|
||||||
|
|
||||||
// Todo: This should not be necessary when OsuConfigManager is databased
|
// Todo: This should not be necessary when OsuConfigManager is databased
|
||||||
|
@ -21,7 +21,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Overlays.Settings
|
namespace osu.Game.Overlays.Settings
|
||||||
{
|
{
|
||||||
public abstract class SettingsItem<T> : Container, IFilterable, ISettingsItem
|
public abstract class SettingsItem<T> : Container, IFilterable, ISettingsItem, IHasCurrentValue<T>
|
||||||
{
|
{
|
||||||
protected abstract Drawable CreateControl();
|
protected abstract Drawable CreateControl();
|
||||||
|
|
||||||
@ -54,7 +54,14 @@ namespace osu.Game.Overlays.Settings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Bindable<T> Bindable
|
[Obsolete("Use Current instead")] // Can be removed 20210406
|
||||||
|
public Bindable<T> Bindable
|
||||||
|
{
|
||||||
|
get => Current;
|
||||||
|
set => Current = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual Bindable<T> Current
|
||||||
{
|
{
|
||||||
get => controlWithCurrent.Current;
|
get => controlWithCurrent.Current;
|
||||||
set => controlWithCurrent.Current = value;
|
set => controlWithCurrent.Current = value;
|
||||||
|
@ -40,17 +40,28 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
Playfield.DisplayJudgements.Value = false;
|
Playfield.DisplayJudgements.Value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private IEditorChangeHandler changeHandler { get; set; }
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
beatmap.HitObjectAdded += addHitObject;
|
beatmap.HitObjectAdded += addHitObject;
|
||||||
beatmap.HitObjectUpdated += updateReplay;
|
|
||||||
beatmap.HitObjectRemoved += removeHitObject;
|
beatmap.HitObjectRemoved += removeHitObject;
|
||||||
|
|
||||||
|
if (changeHandler != null)
|
||||||
|
{
|
||||||
|
// for now only regenerate replay on a finalised state change, not HitObjectUpdated.
|
||||||
|
changeHandler.OnStateChange += updateReplay;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
beatmap.HitObjectUpdated += _ => updateReplay();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateReplay(HitObject obj = null) =>
|
private void updateReplay() => drawableRuleset.RegenerateAutoplay();
|
||||||
drawableRuleset.RegenerateAutoplay();
|
|
||||||
|
|
||||||
private void addHitObject(HitObject hitObject)
|
private void addHitObject(HitObject hitObject)
|
||||||
{
|
{
|
||||||
@ -58,8 +69,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
drawableRuleset.Playfield.Add(drawableObject);
|
drawableRuleset.Playfield.Add(drawableObject);
|
||||||
drawableRuleset.Playfield.PostProcess();
|
drawableRuleset.Playfield.PostProcess();
|
||||||
|
|
||||||
updateReplay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeHitObject(HitObject hitObject)
|
private void removeHitObject(HitObject hitObject)
|
||||||
@ -68,8 +77,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
drawableRuleset.Playfield.Remove(drawableObject);
|
drawableRuleset.Playfield.Remove(drawableObject);
|
||||||
drawableRuleset.Playfield.PostProcess();
|
drawableRuleset.Playfield.PostProcess();
|
||||||
|
|
||||||
drawableRuleset.RegenerateAutoplay();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool PropagatePositionalInputSubTree => false;
|
public override bool PropagatePositionalInputSubTree => false;
|
||||||
|
@ -109,40 +109,40 @@ namespace osu.Game.Rulesets.Judgements
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case HitResult.SmallTickHit:
|
case HitResult.SmallTickHit:
|
||||||
return DEFAULT_MAX_HEALTH_INCREASE * 0.05;
|
return DEFAULT_MAX_HEALTH_INCREASE * 0.5;
|
||||||
|
|
||||||
case HitResult.SmallTickMiss:
|
case HitResult.SmallTickMiss:
|
||||||
return -DEFAULT_MAX_HEALTH_INCREASE * 0.05;
|
return -DEFAULT_MAX_HEALTH_INCREASE * 0.5;
|
||||||
|
|
||||||
case HitResult.LargeTickHit:
|
case HitResult.LargeTickHit:
|
||||||
return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
|
return DEFAULT_MAX_HEALTH_INCREASE;
|
||||||
|
|
||||||
case HitResult.LargeTickMiss:
|
case HitResult.LargeTickMiss:
|
||||||
return -DEFAULT_MAX_HEALTH_INCREASE * 0.1;
|
return -DEFAULT_MAX_HEALTH_INCREASE;
|
||||||
|
|
||||||
case HitResult.Miss:
|
case HitResult.Miss:
|
||||||
return -DEFAULT_MAX_HEALTH_INCREASE;
|
return -DEFAULT_MAX_HEALTH_INCREASE;
|
||||||
|
|
||||||
case HitResult.Meh:
|
case HitResult.Meh:
|
||||||
return -DEFAULT_MAX_HEALTH_INCREASE * 0.5;
|
return -DEFAULT_MAX_HEALTH_INCREASE * 0.05;
|
||||||
|
|
||||||
case HitResult.Ok:
|
case HitResult.Ok:
|
||||||
return -DEFAULT_MAX_HEALTH_INCREASE * 0.3;
|
return DEFAULT_MAX_HEALTH_INCREASE * 0.5;
|
||||||
|
|
||||||
case HitResult.Good:
|
case HitResult.Good:
|
||||||
return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
|
return DEFAULT_MAX_HEALTH_INCREASE * 0.75;
|
||||||
|
|
||||||
case HitResult.Great:
|
case HitResult.Great:
|
||||||
return DEFAULT_MAX_HEALTH_INCREASE * 0.8;
|
|
||||||
|
|
||||||
case HitResult.Perfect:
|
|
||||||
return DEFAULT_MAX_HEALTH_INCREASE;
|
return DEFAULT_MAX_HEALTH_INCREASE;
|
||||||
|
|
||||||
|
case HitResult.Perfect:
|
||||||
|
return DEFAULT_MAX_HEALTH_INCREASE * 1.05;
|
||||||
|
|
||||||
case HitResult.SmallBonus:
|
case HitResult.SmallBonus:
|
||||||
return DEFAULT_MAX_HEALTH_INCREASE * 0.1;
|
return DEFAULT_MAX_HEALTH_INCREASE * 0.5;
|
||||||
|
|
||||||
case HitResult.LargeBonus:
|
case HitResult.LargeBonus:
|
||||||
return DEFAULT_MAX_HEALTH_INCREASE * 0.2;
|
return DEFAULT_MAX_HEALTH_INCREASE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,9 +385,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stops playback of all samples. Automatically called when <see cref="DrawableHitObject{TObject}"/>'s lifetime has been exceeded.
|
/// Stops playback of all relevant samples. Generally only looping samples should be stopped by this, and the rest let to play out.
|
||||||
|
/// Automatically called when <see cref="DrawableHitObject{TObject}"/>'s lifetime has been exceeded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void StopAllSamples() => Samples?.Stop();
|
public virtual void StopAllSamples()
|
||||||
|
{
|
||||||
|
if (Samples?.Looping == true)
|
||||||
|
Samples.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
@ -457,6 +462,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
foreach (var nested in NestedHitObjects)
|
foreach (var nested in NestedHitObjects)
|
||||||
nested.OnKilled();
|
nested.OnKilled();
|
||||||
|
|
||||||
|
// failsafe to ensure looping samples don't get stuck in a playing state.
|
||||||
|
// this could occur in a non-frame-stable context where DrawableHitObjects get killed before a SkinnableSound has the chance to be stopped.
|
||||||
StopAllSamples();
|
StopAllSamples();
|
||||||
|
|
||||||
UpdateResult(false);
|
UpdateResult(false);
|
||||||
@ -506,19 +513,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
|
|
||||||
Result.TimeOffset = Math.Min(HitObject.HitWindows.WindowFor(HitResult.Miss), Time.Current - endTime);
|
Result.TimeOffset = Math.Min(HitObject.HitWindows.WindowFor(HitResult.Miss), Time.Current - endTime);
|
||||||
|
|
||||||
switch (Result.Type)
|
if (Result.HasResult)
|
||||||
{
|
updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss);
|
||||||
case HitResult.None:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HitResult.Miss:
|
|
||||||
updateState(ArmedState.Miss);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
updateState(ArmedState.Hit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnNewResult?.Invoke(this, Result);
|
OnNewResult?.Invoke(this, Result);
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hit windows for this <see cref="HitObject"/>.
|
/// The hit windows for this <see cref="HitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
public HitWindows HitWindows { get; set; }
|
public HitWindows HitWindows { get; set; }
|
||||||
|
|
||||||
private readonly List<HitObject> nestedHitObjects = new List<HitObject>();
|
private readonly List<HitObject> nestedHitObjects = new List<HitObject>();
|
||||||
|
@ -35,6 +35,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
public GameplayClock GameplayClock => stabilityGameplayClock;
|
public GameplayClock GameplayClock => stabilityGameplayClock;
|
||||||
|
|
||||||
[Cached(typeof(GameplayClock))]
|
[Cached(typeof(GameplayClock))]
|
||||||
|
[Cached(typeof(ISamplePlaybackDisabler))]
|
||||||
private readonly StabilityGameplayClock stabilityGameplayClock;
|
private readonly StabilityGameplayClock stabilityGameplayClock;
|
||||||
|
|
||||||
public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
|
public FrameStabilityContainer(double gameplayStartTime = double.MinValue)
|
||||||
@ -58,13 +59,16 @@ namespace osu.Game.Rulesets.UI
|
|||||||
private int direction;
|
private int direction;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(GameplayClock clock)
|
private void load(GameplayClock clock, ISamplePlaybackDisabler sampleDisabler)
|
||||||
{
|
{
|
||||||
if (clock != null)
|
if (clock != null)
|
||||||
{
|
{
|
||||||
parentGameplayClock = stabilityGameplayClock.ParentGameplayClock = clock;
|
parentGameplayClock = stabilityGameplayClock.ParentGameplayClock = clock;
|
||||||
GameplayClock.IsPaused.BindTo(clock.IsPaused);
|
GameplayClock.IsPaused.BindTo(clock.IsPaused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is a bit temporary. should really be done inside of GameplayClock (but requires large structural changes).
|
||||||
|
stabilityGameplayClock.ParentSampleDisabler = sampleDisabler;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -206,12 +210,16 @@ namespace osu.Game.Rulesets.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setClock()
|
private void setClock()
|
||||||
|
{
|
||||||
|
if (parentGameplayClock == null)
|
||||||
{
|
{
|
||||||
// in case a parent gameplay clock isn't available, just use the parent clock.
|
// in case a parent gameplay clock isn't available, just use the parent clock.
|
||||||
parentGameplayClock ??= Clock;
|
parentGameplayClock ??= Clock;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Clock = GameplayClock;
|
Clock = GameplayClock;
|
||||||
ProcessCustomClock = false;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReplayInputHandler ReplayInputHandler { get; set; }
|
public ReplayInputHandler ReplayInputHandler { get; set; }
|
||||||
@ -220,6 +228,8 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
public GameplayClock ParentGameplayClock;
|
public GameplayClock ParentGameplayClock;
|
||||||
|
|
||||||
|
public ISamplePlaybackDisabler ParentSampleDisabler;
|
||||||
|
|
||||||
public override IEnumerable<Bindable<double>> NonGameplayAdjustments => ParentGameplayClock?.NonGameplayAdjustments ?? Enumerable.Empty<Bindable<double>>();
|
public override IEnumerable<Bindable<double>> NonGameplayAdjustments => ParentGameplayClock?.NonGameplayAdjustments ?? Enumerable.Empty<Bindable<double>>();
|
||||||
|
|
||||||
public StabilityGameplayClock(FramedClock underlyingClock)
|
public StabilityGameplayClock(FramedClock underlyingClock)
|
||||||
@ -227,7 +237,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsSeeking => ParentGameplayClock != null && Math.Abs(CurrentTime - ParentGameplayClock.CurrentTime) > 200;
|
protected override bool ShouldDisableSamplePlayback =>
|
||||||
|
// handle the case where playback is catching up to real-time.
|
||||||
|
base.ShouldDisableSamplePlayback
|
||||||
|
|| ParentSampleDisabler?.SamplePlaybackDisabled.Value == true
|
||||||
|
|| (ParentGameplayClock != null && Math.Abs(CurrentTime - ParentGameplayClock.CurrentTime) > 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user