mirror of
https://github.com/ppy/osu.git
synced 2025-02-05 04:13:03 +08:00
Merge branch 'master' into formats-nrt
This commit is contained in:
commit
c27f06c0a7
@ -10,7 +10,7 @@
|
|||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.815.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.817.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="sh.ppy.osulazer" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="sh.ppy.osulazer" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" />
|
||||||
|
<!-- for editor usage -->
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||||
</manifest>
|
</manifest>
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- using a different name because package name cannot contain 'catch' -->
|
<!-- using a different name because package name cannot contain 'catch' -->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Catch_Tests.Android" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Catch_Tests.Android" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" />
|
||||||
</manifest>
|
</manifest>
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Mania.Tests.Android" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Mania.Tests.Android" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!mania Test" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!mania Test" />
|
||||||
</manifest>
|
</manifest>
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
|||||||
{
|
{
|
||||||
private const double individual_decay_base = 0.125;
|
private const double individual_decay_base = 0.125;
|
||||||
private const double overall_decay_base = 0.30;
|
private const double overall_decay_base = 0.30;
|
||||||
private const double release_threshold = 24;
|
private const double release_threshold = 30;
|
||||||
|
|
||||||
protected override double SkillMultiplier => 1;
|
protected override double SkillMultiplier => 1;
|
||||||
protected override double StrainDecayBase => 1;
|
protected override double StrainDecayBase => 1;
|
||||||
@ -50,10 +50,13 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
|||||||
for (int i = 0; i < endTimes.Length; ++i)
|
for (int i = 0; i < endTimes.Length; ++i)
|
||||||
{
|
{
|
||||||
// The current note is overlapped if a previous note or end is overlapping the current note body
|
// The current note is overlapped if a previous note or end is overlapping the current note body
|
||||||
isOverlapping |= Precision.DefinitelyBigger(endTimes[i], startTime, 1) && Precision.DefinitelyBigger(endTime, endTimes[i], 1);
|
isOverlapping |= Precision.DefinitelyBigger(endTimes[i], startTime, 1) &&
|
||||||
|
Precision.DefinitelyBigger(endTime, endTimes[i], 1) &&
|
||||||
|
Precision.DefinitelyBigger(startTime, startTimes[i], 1);
|
||||||
|
|
||||||
// We give a slight bonus to everything if something is held meanwhile
|
// We give a slight bonus to everything if something is held meanwhile
|
||||||
if (Precision.DefinitelyBigger(endTimes[i], endTime, 1))
|
if (Precision.DefinitelyBigger(endTimes[i], endTime, 1) &&
|
||||||
|
Precision.DefinitelyBigger(startTime, startTimes[i], 1))
|
||||||
holdFactor = 1.25;
|
holdFactor = 1.25;
|
||||||
|
|
||||||
closestEndTime = Math.Min(closestEndTime, Math.Abs(endTime - endTimes[i]));
|
closestEndTime = Math.Min(closestEndTime, Math.Abs(endTime - endTimes[i]));
|
||||||
@ -70,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
|||||||
// 0.0 +--------+-+---------------> Release Difference / ms
|
// 0.0 +--------+-+---------------> Release Difference / ms
|
||||||
// release_threshold
|
// release_threshold
|
||||||
if (isOverlapping)
|
if (isOverlapping)
|
||||||
holdAddition = 1 / (1 + Math.Exp(0.5 * (release_threshold - closestEndTime)));
|
holdAddition = 1 / (1 + Math.Exp(0.27 * (release_threshold - closestEndTime)));
|
||||||
|
|
||||||
// Decay and increase individualStrains in own column
|
// Decay and increase individualStrains in own column
|
||||||
individualStrains[column] = applyDecay(individualStrains[column], startTime - startTimes[column], individual_decay_base);
|
individualStrains[column] = applyDecay(individualStrains[column], startTime - startTimes[column], individual_decay_base);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Osu.Tests.Android" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Osu.Tests.Android" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!standard Test" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!standard Test" />
|
||||||
</manifest>
|
</manifest>
|
@ -9,6 +9,7 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
@ -70,12 +71,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
base.Content.Children = new Drawable[]
|
base.Content.Children = new Drawable[]
|
||||||
{
|
{
|
||||||
editorClock = new EditorClock(editorBeatmap),
|
editorClock = new EditorClock(editorBeatmap),
|
||||||
snapProvider,
|
new PopoverContainer { Child = snapProvider },
|
||||||
Content
|
Content
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
|
protected override Container<Drawable> Content { get; } = new PopoverContainer { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup() => Schedule(() =>
|
public void Setup() => Schedule(() =>
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Edit;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||||
|
{
|
||||||
|
public partial class TestScenePreciseRotation : TestSceneOsuEditor
|
||||||
|
{
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHotkeyHandling()
|
||||||
|
{
|
||||||
|
AddStep("select single circle", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.OfType<HitCircle>().First()));
|
||||||
|
AddStep("press rotate hotkey", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.Key(Key.R);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
AddUntilStep("no popover present", () => this.ChildrenOfType<PreciseRotationPopover>().Count(), () => Is.Zero);
|
||||||
|
|
||||||
|
AddStep("select first three objects", () =>
|
||||||
|
{
|
||||||
|
EditorBeatmap.SelectedHitObjects.Clear();
|
||||||
|
EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects.Take(3));
|
||||||
|
});
|
||||||
|
AddStep("press rotate hotkey", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.Key(Key.R);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
AddUntilStep("popover present", () => this.ChildrenOfType<PreciseRotationPopover>().Count(), () => Is.EqualTo(1));
|
||||||
|
AddStep("press rotate hotkey", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.Key(Key.R);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
AddUntilStep("no popover present", () => this.ChildrenOfType<PreciseRotationPopover>().Count(), () => Is.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRotateCorrectness()
|
||||||
|
{
|
||||||
|
AddStep("replace objects", () =>
|
||||||
|
{
|
||||||
|
EditorBeatmap.Clear();
|
||||||
|
EditorBeatmap.AddRange(new HitObject[]
|
||||||
|
{
|
||||||
|
new HitCircle { Position = new Vector2(100) },
|
||||||
|
new HitCircle { Position = new Vector2(200) },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
AddStep("select both circles", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
|
||||||
|
AddStep("press rotate hotkey", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.Key(Key.R);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
AddUntilStep("popover present", getPopover, () => Is.Not.Null);
|
||||||
|
|
||||||
|
AddStep("rotate by 180deg", () => getPopover().ChildrenOfType<TextBox>().Single().Current.Value = "180");
|
||||||
|
AddAssert("first object rotated 180deg around playfield centre",
|
||||||
|
() => EditorBeatmap.HitObjects.OfType<HitCircle>().ElementAt(0).Position,
|
||||||
|
() => Is.EqualTo(OsuPlayfield.BASE_SIZE - new Vector2(100)));
|
||||||
|
AddAssert("second object rotated 180deg around playfield centre",
|
||||||
|
() => EditorBeatmap.HitObjects.OfType<HitCircle>().ElementAt(1).Position,
|
||||||
|
() => Is.EqualTo(OsuPlayfield.BASE_SIZE - new Vector2(200)));
|
||||||
|
|
||||||
|
AddStep("change rotation origin", () => getPopover().ChildrenOfType<EditorRadioButton>().ElementAt(1).TriggerClick());
|
||||||
|
AddAssert("first object rotated 90deg around selection centre",
|
||||||
|
() => EditorBeatmap.HitObjects.OfType<HitCircle>().ElementAt(0).Position, () => Is.EqualTo(new Vector2(200, 200)));
|
||||||
|
AddAssert("second object rotated 90deg around selection centre",
|
||||||
|
() => EditorBeatmap.HitObjects.OfType<HitCircle>().ElementAt(1).Position, () => Is.EqualTo(new Vector2(100, 100)));
|
||||||
|
|
||||||
|
PreciseRotationPopover? getPopover() => this.ChildrenOfType<PreciseRotationPopover>().SingleOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -85,6 +85,11 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
// we may be entering the screen with a selection already active
|
// we may be entering the screen with a selection already active
|
||||||
updateDistanceSnapGrid();
|
updateDistanceSnapGrid();
|
||||||
|
|
||||||
|
RightToolbox.Add(new TransformToolboxGroup
|
||||||
|
{
|
||||||
|
RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override ComposeBlueprintContainer CreateBlueprintContainer()
|
protected override ComposeBlueprintContainer CreateBlueprintContainer()
|
||||||
|
107
osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs
Normal file
107
osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||||
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Edit
|
||||||
|
{
|
||||||
|
public partial class PreciseRotationPopover : OsuPopover
|
||||||
|
{
|
||||||
|
private readonly SelectionRotationHandler rotationHandler;
|
||||||
|
|
||||||
|
private readonly Bindable<PreciseRotationInfo> rotationInfo = new Bindable<PreciseRotationInfo>(new PreciseRotationInfo(0, RotationOrigin.PlayfieldCentre));
|
||||||
|
|
||||||
|
private SliderWithTextBoxInput<float> angleInput = null!;
|
||||||
|
private EditorRadioButtonCollection rotationOrigin = null!;
|
||||||
|
|
||||||
|
public PreciseRotationPopover(SelectionRotationHandler rotationHandler)
|
||||||
|
{
|
||||||
|
this.rotationHandler = rotationHandler;
|
||||||
|
|
||||||
|
AllowableAnchors = new[] { Anchor.CentreLeft, Anchor.CentreRight };
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Child = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Width = 220,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Spacing = new Vector2(20),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
angleInput = new SliderWithTextBoxInput<float>("Angle (degrees):")
|
||||||
|
{
|
||||||
|
Current = new BindableNumber<float>
|
||||||
|
{
|
||||||
|
MinValue = -360,
|
||||||
|
MaxValue = 360,
|
||||||
|
Precision = 1
|
||||||
|
},
|
||||||
|
Instantaneous = true
|
||||||
|
},
|
||||||
|
rotationOrigin = new EditorRadioButtonCollection
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Items = new[]
|
||||||
|
{
|
||||||
|
new RadioButton("Playfield centre",
|
||||||
|
() => rotationInfo.Value = rotationInfo.Value with { Origin = RotationOrigin.PlayfieldCentre },
|
||||||
|
() => new SpriteIcon { Icon = FontAwesome.Regular.Square }),
|
||||||
|
new RadioButton("Selection centre",
|
||||||
|
() => rotationInfo.Value = rotationInfo.Value with { Origin = RotationOrigin.SelectionCentre },
|
||||||
|
() => new SpriteIcon { Icon = FontAwesome.Solid.VectorSquare })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
ScheduleAfterChildren(() => angleInput.TakeFocus());
|
||||||
|
angleInput.Current.BindValueChanged(angle => rotationInfo.Value = rotationInfo.Value with { Degrees = angle.NewValue });
|
||||||
|
rotationOrigin.Items.First().Select();
|
||||||
|
|
||||||
|
rotationInfo.BindValueChanged(rotation =>
|
||||||
|
{
|
||||||
|
rotationHandler.Update(rotation.NewValue.Degrees, rotation.NewValue.Origin == RotationOrigin.PlayfieldCentre ? OsuPlayfield.BASE_SIZE / 2 : null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopIn()
|
||||||
|
{
|
||||||
|
base.PopIn();
|
||||||
|
rotationHandler.Begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopOut()
|
||||||
|
{
|
||||||
|
base.PopOut();
|
||||||
|
|
||||||
|
if (IsLoaded)
|
||||||
|
rotationHandler.Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum RotationOrigin
|
||||||
|
{
|
||||||
|
PlayfieldCentre,
|
||||||
|
SelectionCentre
|
||||||
|
}
|
||||||
|
|
||||||
|
public record PreciseRotationInfo(float Degrees, RotationOrigin Origin);
|
||||||
|
}
|
80
osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs
Normal file
80
osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Input.Bindings;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Input.Bindings;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
using osu.Game.Screens.Edit.Components;
|
||||||
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Edit
|
||||||
|
{
|
||||||
|
public partial class TransformToolboxGroup : EditorToolboxGroup, IKeyBindingHandler<GlobalAction>
|
||||||
|
{
|
||||||
|
private readonly Bindable<bool> canRotate = new BindableBool();
|
||||||
|
|
||||||
|
private EditorToolButton rotateButton = null!;
|
||||||
|
|
||||||
|
public SelectionRotationHandler RotationHandler { get; init; } = null!;
|
||||||
|
|
||||||
|
public TransformToolboxGroup()
|
||||||
|
: base("transform")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Child = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
rotateButton = new EditorToolButton("Rotate",
|
||||||
|
() => new SpriteIcon { Icon = FontAwesome.Solid.Undo },
|
||||||
|
() => new PreciseRotationPopover(RotationHandler)),
|
||||||
|
// TODO: scale
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
// bindings to `Enabled` on the buttons are decoupled on purpose
|
||||||
|
// due to the weird `OsuButton` behaviour of resetting `Enabled` to `false` when `Action` is set.
|
||||||
|
canRotate.BindTo(RotationHandler.CanRotate);
|
||||||
|
canRotate.BindValueChanged(_ => rotateButton.Enabled.Value = canRotate.Value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||||
|
{
|
||||||
|
if (e.Repeat) return false;
|
||||||
|
|
||||||
|
switch (e.Action)
|
||||||
|
{
|
||||||
|
case GlobalAction.EditorToggleRotateControl:
|
||||||
|
{
|
||||||
|
rotateButton.TriggerClick();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -257,7 +257,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
texture.Bind();
|
texture.Bind();
|
||||||
|
|
||||||
for (int i = 0; i < points.Count; i++)
|
for (int i = 0; i < points.Count; i++)
|
||||||
drawPointQuad(points[i], textureRect, i + firstVisiblePointIndex);
|
drawPointQuad(renderer, points[i], textureRect, i + firstVisiblePointIndex);
|
||||||
|
|
||||||
UnbindTextureShader(renderer);
|
UnbindTextureShader(renderer);
|
||||||
renderer.PopLocalMatrix();
|
renderer.PopLocalMatrix();
|
||||||
@ -325,7 +325,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
private float getRotation(int index) => max_rotation * (StatelessRNG.NextSingle(rotationSeed, index) * 2 - 1);
|
private float getRotation(int index) => max_rotation * (StatelessRNG.NextSingle(rotationSeed, index) * 2 - 1);
|
||||||
|
|
||||||
private void drawPointQuad(SmokePoint point, RectangleF textureRect, int index)
|
private void drawPointQuad(IRenderer renderer, SmokePoint point, RectangleF textureRect, int index)
|
||||||
{
|
{
|
||||||
Debug.Assert(quadBatch != null);
|
Debug.Assert(quadBatch != null);
|
||||||
|
|
||||||
@ -347,25 +347,25 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
var localBotLeft = point.Position + ortho - dir;
|
var localBotLeft = point.Position + ortho - dir;
|
||||||
var localBotRight = point.Position + ortho + dir;
|
var localBotRight = point.Position + ortho + dir;
|
||||||
|
|
||||||
quadBatch.Add(new TexturedVertex2D
|
quadBatch.Add(new TexturedVertex2D(renderer)
|
||||||
{
|
{
|
||||||
Position = localTopLeft,
|
Position = localTopLeft,
|
||||||
TexturePosition = textureRect.TopLeft,
|
TexturePosition = textureRect.TopLeft,
|
||||||
Colour = Color4Extensions.Multiply(ColourAtPosition(localTopLeft), colour),
|
Colour = Color4Extensions.Multiply(ColourAtPosition(localTopLeft), colour),
|
||||||
});
|
});
|
||||||
quadBatch.Add(new TexturedVertex2D
|
quadBatch.Add(new TexturedVertex2D(renderer)
|
||||||
{
|
{
|
||||||
Position = localTopRight,
|
Position = localTopRight,
|
||||||
TexturePosition = textureRect.TopRight,
|
TexturePosition = textureRect.TopRight,
|
||||||
Colour = Color4Extensions.Multiply(ColourAtPosition(localTopRight), colour),
|
Colour = Color4Extensions.Multiply(ColourAtPosition(localTopRight), colour),
|
||||||
});
|
});
|
||||||
quadBatch.Add(new TexturedVertex2D
|
quadBatch.Add(new TexturedVertex2D(renderer)
|
||||||
{
|
{
|
||||||
Position = localBotRight,
|
Position = localBotRight,
|
||||||
TexturePosition = textureRect.BottomRight,
|
TexturePosition = textureRect.BottomRight,
|
||||||
Colour = Color4Extensions.Multiply(ColourAtPosition(localBotRight), colour),
|
Colour = Color4Extensions.Multiply(ColourAtPosition(localBotRight), colour),
|
||||||
});
|
});
|
||||||
quadBatch.Add(new TexturedVertex2D
|
quadBatch.Add(new TexturedVertex2D(renderer)
|
||||||
{
|
{
|
||||||
Position = localBotLeft,
|
Position = localBotLeft,
|
||||||
TexturePosition = textureRect.BottomLeft,
|
TexturePosition = textureRect.BottomLeft,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Taiko.Tests.Android" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Taiko.Tests.Android" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!taiko Test" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!taiko Test" />
|
||||||
</manifest>
|
</manifest>
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Tests.Android" android:installLocation="auto">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Tests.Android" android:installLocation="auto">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!visual Test" />
|
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!visual Test" />
|
||||||
</manifest>
|
</manifest>
|
@ -1,19 +1,23 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Database
|
namespace osu.Game.Tests.Database
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class LegacyBeatmapImporterTest
|
public class LegacyBeatmapImporterTest : RealmTest
|
||||||
{
|
{
|
||||||
private readonly TestLegacyBeatmapImporter importer = new TestLegacyBeatmapImporter();
|
private readonly TestLegacyBeatmapImporter importer = new TestLegacyBeatmapImporter();
|
||||||
|
|
||||||
@ -60,6 +64,33 @@ namespace osu.Game.Tests.Database
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestStableDateAddedApplied()
|
||||||
|
{
|
||||||
|
RunTestWithRealmAsync(async (realm, storage) =>
|
||||||
|
{
|
||||||
|
using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
|
||||||
|
using (var tmpStorage = new TemporaryNativeStorage("stable-songs-folder"))
|
||||||
|
{
|
||||||
|
var stableStorage = new StableStorage(tmpStorage.GetFullPath(""), host);
|
||||||
|
var songsStorage = stableStorage.GetStorageForDirectory(StableStorage.STABLE_DEFAULT_SONGS_PATH);
|
||||||
|
|
||||||
|
ZipFile.ExtractToDirectory(TestResources.GetQuickTestBeatmapForImport(), songsStorage.GetFullPath("renatus"));
|
||||||
|
|
||||||
|
string[] beatmaps = Directory.GetFiles(songsStorage.GetFullPath("renatus"), "*.osu", SearchOption.TopDirectoryOnly);
|
||||||
|
|
||||||
|
File.SetLastWriteTimeUtc(beatmaps[beatmaps.Length / 2], new DateTime(2000, 1, 1, 12, 0, 0));
|
||||||
|
|
||||||
|
await new LegacyBeatmapImporter(new BeatmapImporter(storage, realm)).ImportFromStableAsync(stableStorage);
|
||||||
|
|
||||||
|
var importedSet = realm.Realm.All<BeatmapSetInfo>().Single();
|
||||||
|
|
||||||
|
Assert.NotNull(importedSet);
|
||||||
|
Assert.AreEqual(new DateTimeOffset(new DateTime(2000, 1, 1, 12, 0, 0, DateTimeKind.Utc)), importedSet.DateAdded);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private class TestLegacyBeatmapImporter : LegacyBeatmapImporter
|
private class TestLegacyBeatmapImporter : LegacyBeatmapImporter
|
||||||
{
|
{
|
||||||
public TestLegacyBeatmapImporter()
|
public TestLegacyBeatmapImporter()
|
||||||
|
@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -29,7 +30,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
[Cached(typeof(IBeatSnapProvider))]
|
[Cached(typeof(IBeatSnapProvider))]
|
||||||
private readonly EditorBeatmap editorBeatmap;
|
private readonly EditorBeatmap editorBeatmap;
|
||||||
|
|
||||||
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
|
protected override Container<Drawable> Content { get; } = new PopoverContainer { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
public TestSceneHitObjectComposerDistanceSnapping()
|
public TestSceneHitObjectComposerDistanceSnapping()
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@ using System.Linq;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -178,7 +178,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5);
|
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class EditorBeatmapContainer : Container
|
public partial class EditorBeatmapContainer : PopoverContainer
|
||||||
{
|
{
|
||||||
private readonly IWorkingBeatmap working;
|
private readonly IWorkingBeatmap working;
|
||||||
|
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
public partial class TestSceneSliderWithTextBoxInput : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
private SliderWithTextBoxInput<float> sliderWithTextBoxInput = null!;
|
||||||
|
|
||||||
|
private OsuSliderBar<float> slider => sliderWithTextBoxInput.ChildrenOfType<OsuSliderBar<float>>().Single();
|
||||||
|
private Nub nub => sliderWithTextBoxInput.ChildrenOfType<Nub>().Single();
|
||||||
|
private OsuTextBox textBox => sliderWithTextBoxInput.ChildrenOfType<OsuTextBox>().Single();
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("create slider", () => Child = sliderWithTextBoxInput = new SliderWithTextBoxInput<float>("Test Slider")
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Width = 0.5f,
|
||||||
|
Current = new BindableFloat
|
||||||
|
{
|
||||||
|
MinValue = -5,
|
||||||
|
MaxValue = 5,
|
||||||
|
Precision = 0.2f
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNonInstantaneousMode()
|
||||||
|
{
|
||||||
|
AddStep("set instantaneous to false", () => sliderWithTextBoxInput.Instantaneous = false);
|
||||||
|
|
||||||
|
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||||
|
AddStep("change text", () => textBox.Text = "3");
|
||||||
|
AddAssert("slider not moved", () => slider.Current.Value, () => Is.Zero);
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.Zero);
|
||||||
|
|
||||||
|
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
||||||
|
AddAssert("slider moved", () => slider.Current.Value, () => Is.EqualTo(3));
|
||||||
|
AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3));
|
||||||
|
|
||||||
|
AddStep("move mouse to nub", () => InputManager.MoveMouseTo(nub));
|
||||||
|
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
AddStep("move mouse to minimum", () => InputManager.MoveMouseTo(sliderWithTextBoxInput.ScreenSpaceDrawQuad.BottomLeft));
|
||||||
|
AddAssert("textbox not changed", () => textBox.Current.Value, () => Is.EqualTo("3"));
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3));
|
||||||
|
|
||||||
|
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
AddAssert("textbox changed", () => textBox.Current.Value, () => Is.EqualTo("-5"));
|
||||||
|
AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
|
||||||
|
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||||
|
AddStep("set text to invalid", () => textBox.Text = "garbage");
|
||||||
|
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
|
||||||
|
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
||||||
|
AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5"));
|
||||||
|
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
|
||||||
|
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||||
|
AddStep("set text to invalid", () => textBox.Text = "garbage");
|
||||||
|
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
|
||||||
|
AddStep("lose focus", () => InputManager.ChangeFocus(null));
|
||||||
|
AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5"));
|
||||||
|
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestInstantaneousMode()
|
||||||
|
{
|
||||||
|
AddStep("set instantaneous to true", () => sliderWithTextBoxInput.Instantaneous = true);
|
||||||
|
|
||||||
|
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||||
|
AddStep("change text", () => textBox.Text = "3");
|
||||||
|
AddAssert("slider moved", () => slider.Current.Value, () => Is.EqualTo(3));
|
||||||
|
AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3));
|
||||||
|
|
||||||
|
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
||||||
|
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(3));
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3));
|
||||||
|
|
||||||
|
AddStep("move mouse to nub", () => InputManager.MoveMouseTo(nub));
|
||||||
|
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||||
|
AddStep("move mouse to minimum", () => InputManager.MoveMouseTo(sliderWithTextBoxInput.ScreenSpaceDrawQuad.BottomLeft));
|
||||||
|
AddAssert("textbox changed", () => textBox.Current.Value, () => Is.EqualTo("-5"));
|
||||||
|
AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
|
||||||
|
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||||
|
AddAssert("textbox not changed", () => textBox.Current.Value, () => Is.EqualTo("-5"));
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
|
||||||
|
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||||
|
AddStep("set text to invalid", () => textBox.Text = "garbage");
|
||||||
|
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
|
||||||
|
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
||||||
|
AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5"));
|
||||||
|
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
|
||||||
|
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||||
|
AddStep("set text to invalid", () => textBox.Text = "garbage");
|
||||||
|
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
|
||||||
|
AddStep("lose focus", () => InputManager.ChangeFocus(null));
|
||||||
|
AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5"));
|
||||||
|
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,6 @@ using osu.Game.Extensions;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
using osu.Game.Tournament.Models;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -22,14 +21,14 @@ namespace osu.Game.Tournament.Components
|
|||||||
{
|
{
|
||||||
public partial class SongBar : CompositeDrawable
|
public partial class SongBar : CompositeDrawable
|
||||||
{
|
{
|
||||||
private TournamentBeatmap? beatmap;
|
private IBeatmapInfo? beatmap;
|
||||||
|
|
||||||
public const float HEIGHT = 145 / 2f;
|
public const float HEIGHT = 145 / 2f;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
|
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
|
||||||
|
|
||||||
public TournamentBeatmap? Beatmap
|
public IBeatmapInfo? Beatmap
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
@ -37,7 +36,7 @@ namespace osu.Game.Tournament.Components
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
beatmap = value;
|
beatmap = value;
|
||||||
update();
|
refreshContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +48,7 @@ namespace osu.Game.Tournament.Components
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
mods = value;
|
mods = value;
|
||||||
update();
|
refreshContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,19 +70,25 @@ namespace osu.Game.Tournament.Components
|
|||||||
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
|
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 5;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Colour = colours.Gray3,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
flow = new FillFlowContainer
|
flow = new FillFlowContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
LayoutDuration = 500,
|
|
||||||
LayoutEasing = Easing.OutQuint,
|
|
||||||
Direction = FillDirection.Full,
|
Direction = FillDirection.Full,
|
||||||
Anchor = Anchor.BottomRight,
|
Anchor = Anchor.BottomRight,
|
||||||
Origin = Anchor.BottomRight,
|
Origin = Anchor.BottomRight,
|
||||||
@ -93,7 +98,7 @@ namespace osu.Game.Tournament.Components
|
|||||||
Expanded = true;
|
Expanded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update()
|
private void refreshContent()
|
||||||
{
|
{
|
||||||
if (beatmap == null)
|
if (beatmap == null)
|
||||||
{
|
{
|
||||||
@ -229,7 +234,7 @@ namespace osu.Game.Tournament.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new TournamentBeatmapPanel(beatmap)
|
new UnmaskedTournamentBeatmapPanel(beatmap)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Width = 0.5f,
|
Width = 0.5f,
|
||||||
@ -272,4 +277,18 @@ namespace osu.Game.Tournament.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal partial class UnmaskedTournamentBeatmapPanel : TournamentBeatmapPanel
|
||||||
|
{
|
||||||
|
public UnmaskedTournamentBeatmapPanel(IBeatmapInfo? beatmap, string mod = "")
|
||||||
|
: base(beatmap, mod)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Masking = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Tournament.Components
|
|||||||
{
|
{
|
||||||
public partial class TournamentBeatmapPanel : CompositeDrawable
|
public partial class TournamentBeatmapPanel : CompositeDrawable
|
||||||
{
|
{
|
||||||
public readonly TournamentBeatmap? Beatmap;
|
public readonly IBeatmapInfo? Beatmap;
|
||||||
|
|
||||||
private readonly string mod;
|
private readonly string mod;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ namespace osu.Game.Tournament.Components
|
|||||||
|
|
||||||
private Box flash = null!;
|
private Box flash = null!;
|
||||||
|
|
||||||
public TournamentBeatmapPanel(TournamentBeatmap? beatmap, string mod = "")
|
public TournamentBeatmapPanel(IBeatmapInfo? beatmap, string mod = "")
|
||||||
{
|
{
|
||||||
Beatmap = beatmap;
|
Beatmap = beatmap;
|
||||||
this.mod = mod;
|
this.mod = mod;
|
||||||
@ -58,7 +58,7 @@ namespace osu.Game.Tournament.Components
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = OsuColour.Gray(0.5f),
|
Colour = OsuColour.Gray(0.5f),
|
||||||
OnlineInfo = Beatmap,
|
OnlineInfo = (Beatmap as IBeatmapSetOnlineInfo),
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Tournament.IPC
|
|||||||
public Bindable<LegacyMods> Mods { get; } = new Bindable<LegacyMods>();
|
public Bindable<LegacyMods> Mods { get; } = new Bindable<LegacyMods>();
|
||||||
public Bindable<TourneyState> State { get; } = new Bindable<TourneyState>();
|
public Bindable<TourneyState> State { get; } = new Bindable<TourneyState>();
|
||||||
public Bindable<string> ChatChannel { get; } = new Bindable<string>();
|
public Bindable<string> ChatChannel { get; } = new Bindable<string>();
|
||||||
public BindableInt Score1 { get; } = new BindableInt();
|
public BindableLong Score1 { get; } = new BindableLong();
|
||||||
public BindableInt Score2 { get; } = new BindableInt();
|
public BindableLong Score2 { get; } = new BindableLong();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,181 +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.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.Sprites;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Tournament.IPC;
|
using osu.Game.Tournament.IPC;
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Tournament.Screens.Gameplay.Components
|
namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||||
{
|
{
|
||||||
// TODO: Update to derive from osu-side class?
|
public partial class TournamentMatchScoreDisplay : MatchScoreDisplay
|
||||||
public partial class TournamentMatchScoreDisplay : CompositeDrawable
|
|
||||||
{
|
{
|
||||||
private const float bar_height = 18;
|
|
||||||
|
|
||||||
private readonly BindableInt score1 = new BindableInt();
|
|
||||||
private readonly BindableInt score2 = new BindableInt();
|
|
||||||
|
|
||||||
private readonly MatchScoreCounter score1Text;
|
|
||||||
private readonly MatchScoreCounter score2Text;
|
|
||||||
|
|
||||||
private readonly MatchScoreDiffCounter scoreDiffText;
|
|
||||||
|
|
||||||
private readonly Drawable score1Bar;
|
|
||||||
private readonly Drawable score2Bar;
|
|
||||||
|
|
||||||
public TournamentMatchScoreDisplay()
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
AutoSizeAxes = Axes.Y;
|
|
||||||
|
|
||||||
InternalChildren = new[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Name = "top bar red (static)",
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = bar_height / 4,
|
|
||||||
Width = 0.5f,
|
|
||||||
Colour = TournamentGame.COLOUR_RED,
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopRight
|
|
||||||
},
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Name = "top bar blue (static)",
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = bar_height / 4,
|
|
||||||
Width = 0.5f,
|
|
||||||
Colour = TournamentGame.COLOUR_BLUE,
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopLeft
|
|
||||||
},
|
|
||||||
score1Bar = new Box
|
|
||||||
{
|
|
||||||
Name = "top bar red",
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = bar_height,
|
|
||||||
Width = 0,
|
|
||||||
Colour = TournamentGame.COLOUR_RED,
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopRight
|
|
||||||
},
|
|
||||||
score1Text = new MatchScoreCounter
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre
|
|
||||||
},
|
|
||||||
score2Bar = new Box
|
|
||||||
{
|
|
||||||
Name = "top bar blue",
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = bar_height,
|
|
||||||
Width = 0,
|
|
||||||
Colour = TournamentGame.COLOUR_BLUE,
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopLeft
|
|
||||||
},
|
|
||||||
score2Text = new MatchScoreCounter
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Origin = Anchor.TopCentre
|
|
||||||
},
|
|
||||||
scoreDiffText = new MatchScoreDiffCounter
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopCentre,
|
|
||||||
Margin = new MarginPadding
|
|
||||||
{
|
|
||||||
Top = bar_height / 4,
|
|
||||||
Horizontal = 8
|
|
||||||
},
|
|
||||||
Alpha = 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(MatchIPCInfo ipc)
|
private void load(MatchIPCInfo ipc)
|
||||||
{
|
{
|
||||||
score1.BindValueChanged(_ => updateScores());
|
Team1Score.BindTo(ipc.Score1);
|
||||||
score1.BindTo(ipc.Score1);
|
Team2Score.BindTo(ipc.Score2);
|
||||||
|
|
||||||
score2.BindValueChanged(_ => updateScores());
|
|
||||||
score2.BindTo(ipc.Score2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateScores()
|
|
||||||
{
|
|
||||||
score1Text.Current.Value = score1.Value;
|
|
||||||
score2Text.Current.Value = score2.Value;
|
|
||||||
|
|
||||||
var winningText = score1.Value > score2.Value ? score1Text : score2Text;
|
|
||||||
var losingText = score1.Value <= score2.Value ? score1Text : score2Text;
|
|
||||||
|
|
||||||
winningText.Winning = true;
|
|
||||||
losingText.Winning = false;
|
|
||||||
|
|
||||||
var winningBar = score1.Value > score2.Value ? score1Bar : score2Bar;
|
|
||||||
var losingBar = score1.Value <= score2.Value ? score1Bar : score2Bar;
|
|
||||||
|
|
||||||
int diff = Math.Max(score1.Value, score2.Value) - Math.Min(score1.Value, score2.Value);
|
|
||||||
|
|
||||||
losingBar.ResizeWidthTo(0, 400, Easing.OutQuint);
|
|
||||||
winningBar.ResizeWidthTo(Math.Min(0.4f, MathF.Pow(diff / 1500000f, 0.5f) / 2), 400, Easing.OutQuint);
|
|
||||||
|
|
||||||
scoreDiffText.Alpha = diff != 0 ? 1 : 0;
|
|
||||||
scoreDiffText.Current.Value = -diff;
|
|
||||||
scoreDiffText.Origin = score1.Value > score2.Value ? Anchor.TopLeft : Anchor.TopRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateAfterChildren()
|
|
||||||
{
|
|
||||||
base.UpdateAfterChildren();
|
|
||||||
score1Text.X = -Math.Max(5 + score1Text.DrawWidth / 2, score1Bar.DrawWidth);
|
|
||||||
score2Text.X = Math.Max(5 + score2Text.DrawWidth / 2, score2Bar.DrawWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
private partial class MatchScoreCounter : CommaSeparatedScoreCounter
|
|
||||||
{
|
|
||||||
private OsuSpriteText displayedSpriteText = null!;
|
|
||||||
|
|
||||||
public MatchScoreCounter()
|
|
||||||
{
|
|
||||||
Margin = new MarginPadding { Top = bar_height, Horizontal = 10 };
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Winning
|
|
||||||
{
|
|
||||||
set => updateFont(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s =>
|
|
||||||
{
|
|
||||||
displayedSpriteText = s;
|
|
||||||
displayedSpriteText.Spacing = new Vector2(-6);
|
|
||||||
updateFont(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
private void updateFont(bool winning)
|
|
||||||
=> displayedSpriteText.Font = winning
|
|
||||||
? OsuFont.Torus.With(weight: FontWeight.Bold, size: 50, fixedWidth: true)
|
|
||||||
: OsuFont.Torus.With(weight: FontWeight.Regular, size: 40, fixedWidth: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private partial class MatchScoreDiffCounter : CommaSeparatedScoreCounter
|
|
||||||
{
|
|
||||||
protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s =>
|
|
||||||
{
|
|
||||||
s.Spacing = new Vector2(-2);
|
|
||||||
s.Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: bar_height, fixedWidth: true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,6 +152,8 @@ namespace osu.Game.Beatmaps
|
|||||||
if (archive != null)
|
if (archive != null)
|
||||||
beatmapSet.Beatmaps.AddRange(createBeatmapDifficulties(beatmapSet, realm));
|
beatmapSet.Beatmaps.AddRange(createBeatmapDifficulties(beatmapSet, realm));
|
||||||
|
|
||||||
|
beatmapSet.DateAdded = getDateAdded(archive);
|
||||||
|
|
||||||
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
|
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
|
||||||
{
|
{
|
||||||
b.BeatmapSet = beatmapSet;
|
b.BeatmapSet = beatmapSet;
|
||||||
@ -305,11 +307,36 @@ namespace osu.Game.Beatmaps
|
|||||||
return new BeatmapSetInfo
|
return new BeatmapSetInfo
|
||||||
{
|
{
|
||||||
OnlineID = beatmap.BeatmapInfo.BeatmapSet?.OnlineID ?? -1,
|
OnlineID = beatmap.BeatmapInfo.BeatmapSet?.OnlineID ?? -1,
|
||||||
// Metadata = beatmap.Metadata,
|
|
||||||
DateAdded = DateTimeOffset.UtcNow
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determine the date a given beatmapset has been added to the game.
|
||||||
|
/// For legacy imports, we can use the oldest file write time for any `.osu` file in the directory.
|
||||||
|
/// For any other import types, use "now".
|
||||||
|
/// </summary>
|
||||||
|
private DateTimeOffset getDateAdded(ArchiveReader? reader)
|
||||||
|
{
|
||||||
|
DateTimeOffset dateAdded = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
if (reader is LegacyDirectoryArchiveReader legacyReader)
|
||||||
|
{
|
||||||
|
var beatmaps = reader.Filenames.Where(f => f.EndsWith(".osu", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
dateAdded = File.GetLastWriteTimeUtc(legacyReader.GetFullPath(beatmaps.First()));
|
||||||
|
|
||||||
|
foreach (string beatmapName in beatmaps)
|
||||||
|
{
|
||||||
|
var currentDateAdded = File.GetLastWriteTimeUtc(legacyReader.GetFullPath(beatmapName));
|
||||||
|
|
||||||
|
if (currentDateAdded < dateAdded)
|
||||||
|
dateAdded = currentDateAdded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateAdded;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create all required <see cref="BeatmapInfo"/>s for the provided archive.
|
/// Create all required <see cref="BeatmapInfo"/>s for the provided archive.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -70,7 +70,22 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
hitObject.StartTime = Math.Floor(hitObject.StartTime);
|
hitObject.StartTime = Math.Floor(hitObject.StartTime);
|
||||||
|
|
||||||
if (hitObject is not IHasPath hasPath || BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1) continue;
|
if (hitObject is not IHasPath hasPath) continue;
|
||||||
|
|
||||||
|
// stable's hit object parsing expects the entire slider to use only one type of curve,
|
||||||
|
// and happens to use the last non-empty curve type read for the entire slider.
|
||||||
|
// this clear of the last control point type handles an edge case
|
||||||
|
// wherein the last control point of an otherwise-single-segment slider path has a different type than previous,
|
||||||
|
// which would lead to sliders being mangled when exported back to stable.
|
||||||
|
// normally, that would be handled by the `BezierConverter.ConvertToModernBezier()` call below,
|
||||||
|
// which outputs a slider path containing only Bezier control points,
|
||||||
|
// but a non-inherited last control point is (rightly) not considered to be starting a new segment,
|
||||||
|
// therefore it would fail to clear the `CountSegments() <= 1` check.
|
||||||
|
// by clearing explicitly we both fix the issue and avoid unnecessary conversions to Bezier.
|
||||||
|
if (hasPath.Path.ControlPoints.Count > 1)
|
||||||
|
hasPath.Path.ControlPoints[^1].Type = null;
|
||||||
|
|
||||||
|
if (BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1) continue;
|
||||||
|
|
||||||
var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints);
|
var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints);
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
|
|||||||
|
|
||||||
public string Text
|
public string Text
|
||||||
{
|
{
|
||||||
|
get => Component.Text;
|
||||||
set => Component.Text = value;
|
set => Component.Text = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
145
osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs
Normal file
145
osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Graphics.UserInterfaceV2
|
||||||
|
{
|
||||||
|
public partial class SliderWithTextBoxInput<T> : CompositeDrawable, IHasCurrentValue<T>
|
||||||
|
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A custom step value for each key press which actuates a change on this control.
|
||||||
|
/// </summary>
|
||||||
|
public float KeyboardStep
|
||||||
|
{
|
||||||
|
get => slider.KeyboardStep;
|
||||||
|
set => slider.KeyboardStep = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bindable<T> Current
|
||||||
|
{
|
||||||
|
get => slider.Current;
|
||||||
|
set => slider.Current = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool instantaneous;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether changes to the slider should instantaneously transfer to the text box (and vice versa).
|
||||||
|
/// If <see langword="false"/>, the transfer will happen on text box commit (explicit, or implicit via focus loss), or on slider drag end.
|
||||||
|
/// </summary>
|
||||||
|
public bool Instantaneous
|
||||||
|
{
|
||||||
|
get => instantaneous;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
instantaneous = value;
|
||||||
|
slider.TransferValueOnCommit = !instantaneous;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly SettingsSlider<T> slider;
|
||||||
|
private readonly LabelledTextBox textBox;
|
||||||
|
|
||||||
|
public SliderWithTextBoxInput(LocalisableString labelText)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(20),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
textBox = new LabelledTextBox
|
||||||
|
{
|
||||||
|
Label = labelText,
|
||||||
|
},
|
||||||
|
slider = new SettingsSlider<T>
|
||||||
|
{
|
||||||
|
TransferValueOnCommit = true,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
textBox.OnCommit += textCommitted;
|
||||||
|
textBox.Current.BindValueChanged(textChanged);
|
||||||
|
|
||||||
|
Current.BindValueChanged(updateTextBoxFromSlider, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TakeFocus() => GetContainingInputManager().ChangeFocus(textBox);
|
||||||
|
|
||||||
|
private bool updatingFromTextBox;
|
||||||
|
|
||||||
|
private void textChanged(ValueChangedEvent<string> change)
|
||||||
|
{
|
||||||
|
if (!instantaneous) return;
|
||||||
|
|
||||||
|
tryUpdateSliderFromTextBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void textCommitted(TextBox t, bool isNew)
|
||||||
|
{
|
||||||
|
tryUpdateSliderFromTextBox();
|
||||||
|
|
||||||
|
// If the attempted update above failed, restore text box to match the slider.
|
||||||
|
Current.TriggerChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryUpdateSliderFromTextBox()
|
||||||
|
{
|
||||||
|
updatingFromTextBox = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (slider.Current)
|
||||||
|
{
|
||||||
|
case Bindable<int> bindableInt:
|
||||||
|
bindableInt.Value = int.Parse(textBox.Current.Value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Bindable<double> bindableDouble:
|
||||||
|
bindableDouble.Value = double.Parse(textBox.Current.Value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
slider.Current.Parse(textBox.Current.Value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignore parsing failures.
|
||||||
|
// sane state will eventually be restored by a commit (either explicit, or implicit via focus loss).
|
||||||
|
}
|
||||||
|
|
||||||
|
updatingFromTextBox = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTextBoxFromSlider(ValueChangedEvent<T> _)
|
||||||
|
{
|
||||||
|
if (updatingFromTextBox) return;
|
||||||
|
|
||||||
|
decimal decimalValue = slider.Current.Value.ToDecimal(NumberFormatInfo.InvariantInfo);
|
||||||
|
textBox.Text = decimalValue.ToString($@"N{FormatUtils.FindPrecision(decimalValue)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,9 @@ namespace osu.Game.IO.Archives
|
|||||||
this.path = Path.GetFullPath(path);
|
this.path = Path.GetFullPath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Stream GetStream(string name) => File.OpenRead(Path.Combine(path, name));
|
public override Stream GetStream(string name) => File.OpenRead(GetFullPath(name));
|
||||||
|
|
||||||
|
public string GetFullPath(string filename) => Path.Combine(path, filename);
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
{
|
{
|
||||||
|
@ -105,6 +105,7 @@ namespace osu.Game.Input.Bindings
|
|||||||
// See https://github.com/ppy/osu-framework/blob/master/osu.Framework/Input/StateChanges/MouseScrollRelativeInput.cs#L37-L38.
|
// See https://github.com/ppy/osu-framework/blob/master/osu.Framework/Input/StateChanges/MouseScrollRelativeInput.cs#L37-L38.
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelRight }, GlobalAction.EditorCyclePreviousBeatSnapDivisor),
|
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelRight }, GlobalAction.EditorCyclePreviousBeatSnapDivisor),
|
||||||
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelLeft }, GlobalAction.EditorCycleNextBeatSnapDivisor),
|
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelLeft }, GlobalAction.EditorCycleNextBeatSnapDivisor),
|
||||||
|
new KeyBinding(new[] { InputKey.Control, InputKey.R }, GlobalAction.EditorToggleRotateControl),
|
||||||
};
|
};
|
||||||
|
|
||||||
public IEnumerable<KeyBinding> InGameKeyBindings => new[]
|
public IEnumerable<KeyBinding> InGameKeyBindings => new[]
|
||||||
@ -378,5 +379,8 @@ namespace osu.Game.Input.Bindings
|
|||||||
|
|
||||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameLeaderboard))]
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.ToggleInGameLeaderboard))]
|
||||||
ToggleInGameLeaderboard,
|
ToggleInGameLeaderboard,
|
||||||
|
|
||||||
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleRotateControl))]
|
||||||
|
EditorToggleRotateControl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -344,6 +344,11 @@ namespace osu.Game.Localisation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString ExportReplay => new TranslatableString(getKey(@"export_replay"), @"Export replay");
|
public static LocalisableString ExportReplay => new TranslatableString(getKey(@"export_replay"), @"Export replay");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Toggle rotate control"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString EditorToggleRotateControl => new TranslatableString(getKey(@"editor_toggle_rotate_control"), @"Toggle rotate control");
|
||||||
|
|
||||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
107
osu.Game/Screens/Edit/Components/EditorToolButton.cs
Normal file
107
osu.Game/Screens/Edit/Components/EditorToolButton.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit.Components
|
||||||
|
{
|
||||||
|
public partial class EditorToolButton : OsuButton, IHasPopover
|
||||||
|
{
|
||||||
|
public BindableBool Selected { get; } = new BindableBool();
|
||||||
|
|
||||||
|
private readonly Func<Drawable> createIcon;
|
||||||
|
private readonly Func<Popover?> createPopover;
|
||||||
|
|
||||||
|
private Color4 defaultBackgroundColour;
|
||||||
|
private Color4 defaultIconColour;
|
||||||
|
private Color4 selectedBackgroundColour;
|
||||||
|
private Color4 selectedIconColour;
|
||||||
|
|
||||||
|
private Drawable icon = null!;
|
||||||
|
|
||||||
|
public EditorToolButton(LocalisableString text, Func<Drawable> createIcon, Func<Popover?> createPopover)
|
||||||
|
{
|
||||||
|
Text = text;
|
||||||
|
this.createIcon = createIcon;
|
||||||
|
this.createPopover = createPopover;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
defaultBackgroundColour = colourProvider.Background3;
|
||||||
|
selectedBackgroundColour = colourProvider.Background1;
|
||||||
|
|
||||||
|
defaultIconColour = defaultBackgroundColour.Darken(0.5f);
|
||||||
|
selectedIconColour = selectedBackgroundColour.Lighten(0.5f);
|
||||||
|
|
||||||
|
Add(icon = createIcon().With(b =>
|
||||||
|
{
|
||||||
|
b.Blending = BlendingParameters.Additive;
|
||||||
|
b.Anchor = Anchor.CentreLeft;
|
||||||
|
b.Origin = Anchor.CentreLeft;
|
||||||
|
b.Size = new Vector2(20);
|
||||||
|
b.X = 10;
|
||||||
|
}));
|
||||||
|
|
||||||
|
Action = Selected.Toggle;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
Selected.BindValueChanged(_ => updateSelectionState(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSelectionState()
|
||||||
|
{
|
||||||
|
if (!IsLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BackgroundColour = Selected.Value ? selectedBackgroundColour : defaultBackgroundColour;
|
||||||
|
icon.Colour = Selected.Value ? selectedIconColour : defaultIconColour;
|
||||||
|
|
||||||
|
if (Selected.Value)
|
||||||
|
this.ShowPopover();
|
||||||
|
else
|
||||||
|
this.HidePopover();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override SpriteText CreateText() => new OsuSpriteText
|
||||||
|
{
|
||||||
|
Depth = -1,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
X = 40f
|
||||||
|
};
|
||||||
|
|
||||||
|
public Popover? GetPopover() => Enabled.Value
|
||||||
|
? createPopover()?.With(p =>
|
||||||
|
{
|
||||||
|
p.State.BindValueChanged(state =>
|
||||||
|
{
|
||||||
|
if (state.NewValue == Visibility.Hidden)
|
||||||
|
Selected.Value = false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
@ -34,7 +34,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
public Container<SelectionBlueprint<T>> SelectionBlueprints { get; private set; }
|
public Container<SelectionBlueprint<T>> SelectionBlueprints { get; private set; }
|
||||||
|
|
||||||
protected SelectionHandler<T> SelectionHandler { get; private set; }
|
public SelectionHandler<T> SelectionHandler { get; private set; }
|
||||||
|
|
||||||
private readonly Dictionary<T, SelectionBlueprint<T>> blueprintMap = new Dictionary<T, SelectionBlueprint<T>>();
|
private readonly Dictionary<T, SelectionBlueprint<T>> blueprintMap = new Dictionary<T, SelectionBlueprint<T>>();
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
protected IEditorChangeHandler ChangeHandler { get; private set; }
|
protected IEditorChangeHandler ChangeHandler { get; private set; }
|
||||||
|
|
||||||
protected SelectionRotationHandler RotationHandler { get; private set; }
|
public SelectionRotationHandler RotationHandler { get; private set; }
|
||||||
|
|
||||||
protected SelectionHandler()
|
protected SelectionHandler()
|
||||||
{
|
{
|
||||||
|
@ -147,13 +147,25 @@ namespace osu.Game.Screens.Edit.Timing
|
|||||||
trackedType = null;
|
trackedType = null;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If the selected group only has one control point, update the tracking type.
|
switch (selectedGroup.Value.ControlPoints.Count)
|
||||||
if (selectedGroup.Value.ControlPoints.Count == 1)
|
{
|
||||||
trackedType = selectedGroup.Value?.ControlPoints.Single().GetType();
|
// If the selected group has no control points, clear the tracked type.
|
||||||
// If the selected group has more than one control point, choose the first as the tracking type
|
// Otherwise the user will be unable to select a group with no control points.
|
||||||
// if we don't already have a singular tracked type.
|
case 0:
|
||||||
else if (trackedType == null)
|
trackedType = null;
|
||||||
trackedType = selectedGroup.Value?.ControlPoints.FirstOrDefault()?.GetType();
|
break;
|
||||||
|
|
||||||
|
// If the selected group only has one control point, update the tracking type.
|
||||||
|
case 1:
|
||||||
|
trackedType = selectedGroup.Value?.ControlPoints.Single().GetType();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// If the selected group has more than one control point, choose the first as the tracking type
|
||||||
|
// if we don't already have a singular tracked type.
|
||||||
|
default:
|
||||||
|
trackedType ??= selectedGroup.Value?.ControlPoints.FirstOrDefault()?.GetType();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackedType != null)
|
if (trackedType != null)
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Globalization;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.UserInterface;
|
|
||||||
using osu.Framework.Localisation;
|
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
|
||||||
using osu.Game.Overlays.Settings;
|
|
||||||
using osu.Game.Utils;
|
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.Edit.Timing
|
|
||||||
{
|
|
||||||
public partial class SliderWithTextBoxInput<T> : CompositeDrawable, IHasCurrentValue<T>
|
|
||||||
where T : struct, IEquatable<T>, IComparable<T>, IConvertible
|
|
||||||
{
|
|
||||||
private readonly SettingsSlider<T> slider;
|
|
||||||
|
|
||||||
public SliderWithTextBoxInput(LocalisableString labelText)
|
|
||||||
{
|
|
||||||
LabelledTextBox textBox;
|
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
AutoSizeAxes = Axes.Y;
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
|
||||||
new FillFlowContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
AutoSizeAxes = Axes.Y,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Spacing = new Vector2(20),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
textBox = new LabelledTextBox
|
|
||||||
{
|
|
||||||
Label = labelText,
|
|
||||||
},
|
|
||||||
slider = new SettingsSlider<T>
|
|
||||||
{
|
|
||||||
TransferValueOnCommit = true,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
textBox.OnCommit += (t, isNew) =>
|
|
||||||
{
|
|
||||||
if (!isNew) return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
switch (slider.Current)
|
|
||||||
{
|
|
||||||
case Bindable<int> bindableInt:
|
|
||||||
bindableInt.Value = int.Parse(t.Text);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Bindable<double> bindableDouble:
|
|
||||||
bindableDouble.Value = double.Parse(t.Text);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
slider.Current.Parse(t.Text);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// TriggerChange below will restore the previous text value on failure.
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is run regardless of parsing success as the parsed number may not actually trigger a change
|
|
||||||
// due to bindable clamping. Even in such a case we want to update the textbox to a sane visual state.
|
|
||||||
Current.TriggerChange();
|
|
||||||
};
|
|
||||||
|
|
||||||
Current.BindValueChanged(_ =>
|
|
||||||
{
|
|
||||||
decimal decimalValue = slider.Current.Value.ToDecimal(NumberFormatInfo.InvariantInfo);
|
|
||||||
textBox.Text = decimalValue.ToString($@"N{FormatUtils.FindPrecision(decimalValue)}");
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A custom step value for each key press which actuates a change on this control.
|
|
||||||
/// </summary>
|
|
||||||
public float KeyboardStep
|
|
||||||
{
|
|
||||||
get => slider.KeyboardStep;
|
|
||||||
set => slider.KeyboardStep = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bindable<T> Current
|
|
||||||
{
|
|
||||||
get => slider.Current;
|
|
||||||
set => slider.Current = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -24,11 +22,13 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
public BindableLong Team1Score = new BindableLong();
|
public BindableLong Team1Score = new BindableLong();
|
||||||
public BindableLong Team2Score = new BindableLong();
|
public BindableLong Team2Score = new BindableLong();
|
||||||
|
|
||||||
protected MatchScoreCounter Score1Text;
|
protected MatchScoreCounter Score1Text = null!;
|
||||||
protected MatchScoreCounter Score2Text;
|
protected MatchScoreCounter Score2Text = null!;
|
||||||
|
|
||||||
private Drawable score1Bar;
|
private Drawable score1Bar = null!;
|
||||||
private Drawable score2Bar;
|
private Drawable score2Bar = null!;
|
||||||
|
|
||||||
|
private MatchScoreDiffCounter scoreDiffText = null!;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
@ -98,6 +98,16 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
scoreDiffText = new MatchScoreDiffCounter
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Margin = new MarginPadding
|
||||||
|
{
|
||||||
|
Top = bar_height / 4,
|
||||||
|
Horizontal = 8
|
||||||
|
},
|
||||||
|
Alpha = 0
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +149,10 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
losingBar.ResizeWidthTo(0, 400, Easing.OutQuint);
|
losingBar.ResizeWidthTo(0, 400, Easing.OutQuint);
|
||||||
winningBar.ResizeWidthTo(Math.Min(0.4f, MathF.Pow(diff / 1500000f, 0.5f) / 2), 400, Easing.OutQuint);
|
winningBar.ResizeWidthTo(Math.Min(0.4f, MathF.Pow(diff / 1500000f, 0.5f) / 2), 400, Easing.OutQuint);
|
||||||
|
|
||||||
|
scoreDiffText.Alpha = diff != 0 ? 1 : 0;
|
||||||
|
scoreDiffText.Current.Value = -diff;
|
||||||
|
scoreDiffText.Origin = Team1Score.Value > Team2Score.Value ? Anchor.TopLeft : Anchor.TopRight;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateAfterChildren()
|
protected override void UpdateAfterChildren()
|
||||||
@ -150,7 +164,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
protected partial class MatchScoreCounter : CommaSeparatedScoreCounter
|
protected partial class MatchScoreCounter : CommaSeparatedScoreCounter
|
||||||
{
|
{
|
||||||
private OsuSpriteText displayedSpriteText;
|
private OsuSpriteText displayedSpriteText = null!;
|
||||||
|
|
||||||
public MatchScoreCounter()
|
public MatchScoreCounter()
|
||||||
{
|
{
|
||||||
@ -174,5 +188,14 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
? OsuFont.Torus.With(weight: FontWeight.Bold, size: font_size, fixedWidth: true)
|
? OsuFont.Torus.With(weight: FontWeight.Bold, size: font_size, fixedWidth: true)
|
||||||
: OsuFont.Torus.With(weight: FontWeight.Regular, size: font_size * 0.8f, fixedWidth: true);
|
: OsuFont.Torus.With(weight: FontWeight.Regular, size: font_size * 0.8f, fixedWidth: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private partial class MatchScoreDiffCounter : CommaSeparatedScoreCounter
|
||||||
|
{
|
||||||
|
protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s =>
|
||||||
|
{
|
||||||
|
s.Spacing = new Vector2(-2);
|
||||||
|
s.Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: bar_height, fixedWidth: true);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="11.1.2" />
|
<PackageReference Include="Realm" Version="11.1.2" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2023.815.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2023.817.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.719.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.719.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.28.1" />
|
<PackageReference Include="Sentry" Version="3.28.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||||
|
@ -23,6 +23,6 @@
|
|||||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.815.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.817.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
Loading…
Reference in New Issue
Block a user