mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 09:27:29 +08:00
Merge branch 'master' into bspline-sliders
This commit is contained in:
commit
b6e3e42445
@ -21,10 +21,10 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ppy.localisationanalyser.tools": {
|
"ppy.localisationanalyser.tools": {
|
||||||
"version": "2023.712.0",
|
"version": "2023.1117.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"localisation"
|
"localisation"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
.github/workflows/diffcalc.yml
vendored
4
.github/workflows/diffcalc.yml
vendored
@ -185,9 +185,11 @@ jobs:
|
|||||||
|
|
||||||
- name: Add comment environment
|
- name: Add comment environment
|
||||||
if: ${{ github.event_name == 'issue_comment' }}
|
if: ${{ github.event_name == 'issue_comment' }}
|
||||||
|
env:
|
||||||
|
COMMENT_BODY: ${{ github.event.comment.body }}
|
||||||
run: |
|
run: |
|
||||||
# Add comment environment
|
# Add comment environment
|
||||||
echo '${{ github.event.comment.body }}' | sed -r 's/\r$//' | grep -E '^\w+=' | while read -r line; do
|
echo $COMMENT_BODY | sed -r 's/\r$//' | grep -E '^\w+=' | while read -r line; do
|
||||||
opt=$(echo ${line} | cut -d '=' -f1)
|
opt=$(echo ${line} | cut -d '=' -f1)
|
||||||
sed -i "s;^${opt}=.*$;${line};" "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
sed -i "s;^${opt}=.*$;${line};" "${{ needs.directory.outputs.GENERATOR_ENV }}"
|
||||||
done
|
done
|
||||||
|
@ -70,7 +70,7 @@ namespace osu.Android
|
|||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = MouseSettingsStrings.DisableMouseButtons,
|
LabelText = MouseSettingsStrings.DisableClicksDuringGameplay,
|
||||||
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons),
|
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Input.Handlers;
|
|||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game;
|
using osu.Game;
|
||||||
using osu.Game.Overlays.Settings;
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Overlays.Settings.Sections.Input;
|
||||||
using osu.Game.Updater;
|
using osu.Game.Updater;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
|
|
||||||
@ -97,6 +98,9 @@ namespace osu.Android
|
|||||||
case AndroidJoystickHandler jh:
|
case AndroidJoystickHandler jh:
|
||||||
return new AndroidJoystickSettings(jh);
|
return new AndroidJoystickSettings(jh);
|
||||||
|
|
||||||
|
case AndroidTouchHandler th:
|
||||||
|
return new TouchSettings(th);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return base.CreateSettingsSubsectionFor(handler);
|
return base.CreateSettingsSubsectionFor(handler);
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ using osu.Game;
|
|||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using Squirrel;
|
|
||||||
using Squirrel.SimpleSplat;
|
using Squirrel.SimpleSplat;
|
||||||
|
using Squirrel.Sources;
|
||||||
using LogLevel = Squirrel.SimpleSplat.LogLevel;
|
using LogLevel = Squirrel.SimpleSplat.LogLevel;
|
||||||
using UpdateManager = osu.Game.Updater.UpdateManager;
|
using UpdateManager = osu.Game.Updater.UpdateManager;
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ namespace osu.Desktop.Updater
|
|||||||
if (localUserInfo?.IsPlaying.Value == true)
|
if (localUserInfo?.IsPlaying.Value == true)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
updateManager ??= new GithubUpdateManager(@"https://github.com/ppy/osu", false, github_token, @"osulazer");
|
updateManager ??= new Squirrel.UpdateManager(new GithubSource(@"https://github.com/ppy/osu", github_token, false), @"osulazer");
|
||||||
|
|
||||||
var info = await updateManager.CheckForUpdate(!useDeltaPatching).ConfigureAwait(false);
|
var info = await updateManager.CheckForUpdate(!useDeltaPatching).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Clowd.Squirrel" Version="2.9.42" />
|
<PackageReference Include="Clowd.Squirrel" Version="2.10.2" />
|
||||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||||
<PackageReference Include="System.IO.Packaging" Version="7.0.0" />
|
<PackageReference Include="System.IO.Packaging" Version="7.0.0" />
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
|
@ -28,9 +28,9 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
|
|
||||||
private class TestLegacySkin : LegacySkin
|
private class TestLegacySkin : LegacySkin
|
||||||
{
|
{
|
||||||
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> storage)
|
public TestLegacySkin(SkinInfo skin, IResourceStore<byte[]> fallbackStore)
|
||||||
// Bypass LegacySkinResourceStore to avoid returning null for retrieving files due to bad skin info (SkinInfo.Files = null).
|
// Bypass LegacySkinResourceStore to avoid returning null for retrieving files due to bad skin info (SkinInfo.Files = null).
|
||||||
: base(skin, null, storage)
|
: base(skin, null, fallbackStore)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
204
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs
Normal file
204
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Input;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Input;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||||
|
{
|
||||||
|
public partial class TestSceneOsuModTouchDevice : RateAdjustedBeatmapTestScene
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private SessionStatics statics { get; set; } = null!;
|
||||||
|
|
||||||
|
private ScoreAccessibleSoloPlayer currentPlayer = null!;
|
||||||
|
private readonly ManualClock manualClock = new ManualClock { Rate = 0 };
|
||||||
|
|
||||||
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null)
|
||||||
|
=> new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(manualClock), Audio);
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Add(new TouchInputInterceptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("reset static", () => statics.SetValue(Static.TouchInputActive, false));
|
||||||
|
base.SetUpSteps();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestUserAlreadyHasTouchDeviceActive()
|
||||||
|
{
|
||||||
|
loadPlayer();
|
||||||
|
// it is presumed that a previous screen (i.e. song select) will set this up
|
||||||
|
AddStep("set up touchscreen user", () =>
|
||||||
|
{
|
||||||
|
currentPlayer.Score.ScoreInfo.Mods = currentPlayer.Score.ScoreInfo.Mods.Append(new OsuModTouchDevice()).ToArray();
|
||||||
|
statics.SetValue(Static.TouchInputActive, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("seek to 0", () => currentPlayer.GameplayClockContainer.Seek(0));
|
||||||
|
AddUntilStep("wait until 0", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0));
|
||||||
|
AddStep("touch circle", () =>
|
||||||
|
{
|
||||||
|
var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||||
|
InputManager.BeginTouch(touch);
|
||||||
|
InputManager.EndTouch(touch);
|
||||||
|
});
|
||||||
|
AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf<OsuModTouchDevice>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTouchDuringBreak()
|
||||||
|
{
|
||||||
|
loadPlayer();
|
||||||
|
AddStep("seek to 2000", () => currentPlayer.GameplayClockContainer.Seek(2000));
|
||||||
|
AddUntilStep("wait until 2000", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(2000));
|
||||||
|
AddUntilStep("wait until break entered", () => currentPlayer.IsBreakTime.Value);
|
||||||
|
AddStep("touch playfield", () =>
|
||||||
|
{
|
||||||
|
var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||||
|
InputManager.BeginTouch(touch);
|
||||||
|
InputManager.EndTouch(touch);
|
||||||
|
});
|
||||||
|
AddAssert("touch device mod not activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.None.InstanceOf<OsuModTouchDevice>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTouchMiss()
|
||||||
|
{
|
||||||
|
loadPlayer();
|
||||||
|
// ensure mouse is active (and that it's not suppressed due to touches in previous tests)
|
||||||
|
AddStep("click mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddStep("seek to 200", () => currentPlayer.GameplayClockContainer.Seek(200));
|
||||||
|
AddUntilStep("wait until 200", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(200));
|
||||||
|
AddStep("touch playfield", () =>
|
||||||
|
{
|
||||||
|
var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||||
|
InputManager.BeginTouch(touch);
|
||||||
|
InputManager.EndTouch(touch);
|
||||||
|
});
|
||||||
|
AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf<OsuModTouchDevice>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestIncompatibleModActive()
|
||||||
|
{
|
||||||
|
loadPlayer();
|
||||||
|
// this is only a veneer of enabling autopilot as having it actually active from the start is annoying to make happen
|
||||||
|
// given the tests' structure.
|
||||||
|
AddStep("enable autopilot", () => currentPlayer.Score.ScoreInfo.Mods = new Mod[] { new OsuModAutopilot() });
|
||||||
|
|
||||||
|
AddStep("seek to 0", () => currentPlayer.GameplayClockContainer.Seek(0));
|
||||||
|
AddUntilStep("wait until 0", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0));
|
||||||
|
AddStep("touch playfield", () =>
|
||||||
|
{
|
||||||
|
var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||||
|
InputManager.BeginTouch(touch);
|
||||||
|
InputManager.EndTouch(touch);
|
||||||
|
});
|
||||||
|
AddAssert("touch device mod not activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.None.InstanceOf<OsuModTouchDevice>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSecondObjectTouched()
|
||||||
|
{
|
||||||
|
loadPlayer();
|
||||||
|
// ensure mouse is active (and that it's not suppressed due to touches in previous tests)
|
||||||
|
AddStep("click mouse", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddStep("seek to 0", () => currentPlayer.GameplayClockContainer.Seek(0));
|
||||||
|
AddUntilStep("wait until 0", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(0));
|
||||||
|
AddStep("click circle", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("touch device mod not activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.None.InstanceOf<OsuModTouchDevice>());
|
||||||
|
|
||||||
|
AddStep("seek to 5000", () => currentPlayer.GameplayClockContainer.Seek(5000));
|
||||||
|
AddUntilStep("wait until 5000", () => currentPlayer.GameplayClockContainer.CurrentTime, () => Is.GreaterThanOrEqualTo(5000));
|
||||||
|
AddStep("touch playfield", () =>
|
||||||
|
{
|
||||||
|
var touch = new Touch(TouchSource.Touch1, currentPlayer.DrawableRuleset.Playfield.ScreenSpaceDrawQuad.Centre);
|
||||||
|
InputManager.BeginTouch(touch);
|
||||||
|
InputManager.EndTouch(touch);
|
||||||
|
});
|
||||||
|
AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf<OsuModTouchDevice>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPlayer()
|
||||||
|
{
|
||||||
|
AddStep("load player", () =>
|
||||||
|
{
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(new OsuBeatmap
|
||||||
|
{
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||||
|
StartTime = 0,
|
||||||
|
},
|
||||||
|
new HitCircle
|
||||||
|
{
|
||||||
|
Position = OsuPlayfield.BASE_SIZE / 2,
|
||||||
|
StartTime = 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new BreakPeriod(2000, 3000)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var p = new ScoreAccessibleSoloPlayer();
|
||||||
|
|
||||||
|
LoadScreen(currentPlayer = p);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
||||||
|
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class ScoreAccessibleSoloPlayer : SoloPlayer
|
||||||
|
{
|
||||||
|
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
|
||||||
|
|
||||||
|
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
|
||||||
|
|
||||||
|
protected override bool PauseOnFocusLost => false;
|
||||||
|
|
||||||
|
public ScoreAccessibleSoloPlayer()
|
||||||
|
: base(new PlayerConfiguration
|
||||||
|
{
|
||||||
|
AllowPause = false,
|
||||||
|
ShowResults = false,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,22 +15,22 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||||
|
|
||||||
[TestCase(6.710442985146793d, 206, "diffcalc-test")]
|
[TestCase(6.710442985146793d, 239, "diffcalc-test")]
|
||||||
[TestCase(1.4386882251130073d, 45, "zero-length-sliders")]
|
[TestCase(1.4386882251130073d, 54, "zero-length-sliders")]
|
||||||
[TestCase(0.42506480230838789d, 2, "very-fast-slider")]
|
[TestCase(0.42506480230838789d, 4, "very-fast-slider")]
|
||||||
[TestCase(0.14102693012101306d, 1, "nan-slider")]
|
[TestCase(0.14102693012101306d, 2, "nan-slider")]
|
||||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||||
|
|
||||||
[TestCase(8.9742952703071666d, 206, "diffcalc-test")]
|
[TestCase(8.9742952703071666d, 239, "diffcalc-test")]
|
||||||
[TestCase(0.55071082800473514d, 2, "very-fast-slider")]
|
[TestCase(1.743180218215227d, 54, "zero-length-sliders")]
|
||||||
[TestCase(1.743180218215227d, 45, "zero-length-sliders")]
|
[TestCase(0.55071082800473514d, 4, "very-fast-slider")]
|
||||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModDoubleTime());
|
||||||
|
|
||||||
[TestCase(6.710442985146793d, 239, "diffcalc-test")]
|
[TestCase(6.710442985146793d, 239, "diffcalc-test")]
|
||||||
[TestCase(0.42506480230838789d, 4, "very-fast-slider")]
|
|
||||||
[TestCase(1.4386882251130073d, 54, "zero-length-sliders")]
|
[TestCase(1.4386882251130073d, 54, "zero-length-sliders")]
|
||||||
|
[TestCase(0.42506480230838789d, 4, "very-fast-slider")]
|
||||||
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
|
public void TestClassicMod(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new OsuModClassic());
|
||||||
|
|
||||||
|
@ -133,8 +133,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSimpleInput()
|
public void TestSimpleInput([Values] bool disableMouseButtons)
|
||||||
{
|
{
|
||||||
|
// OsuSetting.MouseDisableButtons should not affect touch taps
|
||||||
|
AddStep($"{(disableMouseButtons ? "disable" : "enable")} mouse buttons", () => config.SetValue(OsuSetting.MouseDisableButtons, disableMouseButtons));
|
||||||
|
|
||||||
beginTouch(TouchSource.Touch1);
|
beginTouch(TouchSource.Touch1);
|
||||||
|
|
||||||
assertKeyCounter(1, 0);
|
assertKeyCounter(1, 0);
|
||||||
@ -468,7 +471,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestInputWhileMouseButtonsDisabled()
|
public void TestInputWhileMouseButtonsDisabled()
|
||||||
{
|
{
|
||||||
AddStep("Disable mouse buttons", () => config.SetValue(OsuSetting.MouseDisableButtons, true));
|
AddStep("Disable gameplay taps", () => config.SetValue(OsuSetting.TouchDisableGameplayTaps, true));
|
||||||
|
|
||||||
beginTouch(TouchSource.Touch1);
|
beginTouch(TouchSource.Touch1);
|
||||||
|
|
||||||
@ -620,6 +623,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddStep("Release all touches", () =>
|
AddStep("Release all touches", () =>
|
||||||
{
|
{
|
||||||
config.SetValue(OsuSetting.MouseDisableButtons, false);
|
config.SetValue(OsuSetting.MouseDisableButtons, false);
|
||||||
|
config.SetValue(OsuSetting.TouchDisableGameplayTaps, false);
|
||||||
foreach (TouchSource source in InputManager.CurrentState.Touch.ActiveSources)
|
foreach (TouchSource source in InputManager.CurrentState.Touch.ActiveSources)
|
||||||
InputManager.EndTouch(new Touch(source, osuInputManager.ScreenSpaceDrawQuad.Centre));
|
InputManager.EndTouch(new Touch(source, osuInputManager.ScreenSpaceDrawQuad.Centre));
|
||||||
});
|
});
|
||||||
|
@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
if (hit)
|
if (hit)
|
||||||
assertAllMaxJudgements();
|
assertAllMaxJudgements();
|
||||||
else
|
else
|
||||||
AddAssert("Tracking dropped", assertMidSliderJudgementFail);
|
assertMidSliderJudgementFail();
|
||||||
|
|
||||||
AddAssert("Head judgement is first", () => judgementResults.First().HitObject is SliderHeadCircle);
|
AddAssert("Head judgement is first", () => judgementResults.First().HitObject is SliderHeadCircle);
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_1 },
|
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_1 },
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("Tracking lost", assertMidSliderJudgementFail);
|
assertMidSliderJudgementFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -278,7 +278,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_before_slider },
|
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_before_slider },
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("Tracking retained, sliderhead miss", assertHeadMissTailTracked);
|
assertHeadMissTailTracked();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -302,7 +302,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("Tracking re-acquired", assertMidSliderJudgements);
|
assertMidSliderJudgements();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -328,7 +328,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("Tracking lost", assertMidSliderJudgementFail);
|
assertMidSliderJudgementFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -350,7 +350,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("Tracking acquired", assertMidSliderJudgements);
|
assertMidSliderJudgements();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -373,7 +373,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_2 },
|
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_2 },
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("Tracking acquired", assertMidSliderJudgements);
|
assertMidSliderJudgements();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -387,7 +387,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_2 },
|
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.RightButton }, Time = time_during_slide_2 },
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("Tracking acquired", assertMidSliderJudgements);
|
assertMidSliderJudgements();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -412,7 +412,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
new OsuReplayFrame { Position = new Vector2(0, 0), Actions = { OsuAction.LeftButton }, Time = time_during_slide_4 },
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("Tracking acquired", assertMidSliderJudgements);
|
assertMidSliderJudgements();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -454,7 +454,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.201f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
|
new OsuReplayFrame { Position = new Vector2(slider_path_length, OsuHitObject.OBJECT_RADIUS * 1.201f), Actions = { OsuAction.LeftButton }, Time = time_slider_end },
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("Tracking dropped", assertMidSliderJudgementFail);
|
assertMidSliderJudgementFail();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertAllMaxJudgements()
|
private void assertAllMaxJudgements()
|
||||||
@ -465,11 +465,21 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
}, () => Is.EqualTo(judgementResults.Select(j => (j.HitObject, j.Judgement.MaxResult))));
|
}, () => Is.EqualTo(judgementResults.Select(j => (j.HitObject, j.Judgement.MaxResult))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.SmallTickHit && !judgementResults.First().IsHit;
|
private void assertHeadMissTailTracked()
|
||||||
|
{
|
||||||
|
AddAssert("Tracking retained", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.LargeTickHit));
|
||||||
|
AddAssert("Slider head missed", () => judgementResults.First().IsHit, () => Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.SmallTickHit;
|
private void assertMidSliderJudgements()
|
||||||
|
{
|
||||||
|
AddAssert("Tracking acquired", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.LargeTickHit));
|
||||||
|
}
|
||||||
|
|
||||||
private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.SmallTickMiss;
|
private void assertMidSliderJudgementFail()
|
||||||
|
{
|
||||||
|
AddAssert("Tracking lost", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.IgnoreMiss));
|
||||||
|
}
|
||||||
|
|
||||||
private void performTest(List<ReplayFrame> frames, Slider? slider = null, double? bpm = null, int? tickRate = null)
|
private void performTest(List<ReplayFrame> frames, Slider? slider = null, double? bpm = null, int? tickRate = null)
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -36,6 +37,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddSliderStep("Spin rate", 0.5, 5, 1, val => spinRate.Value = val);
|
AddSliderStep("Spin rate", 0.5, 5, 1, val => spinRate.Value = val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("Reset rate", () => spinRate.Value = 1);
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase(true)]
|
[TestCase(true)]
|
||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public void TestVariousSpinners(bool autoplay)
|
public void TestVariousSpinners(bool autoplay)
|
||||||
@ -46,6 +53,36 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddStep($"{term} Small", () => SetContents(_ => testSingle(7, autoplay)));
|
AddStep($"{term} Small", () => SetContents(_ => testSingle(7, autoplay)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSpinnerNoBonus()
|
||||||
|
{
|
||||||
|
AddStep("Set high spin rate", () => spinRate.Value = 5);
|
||||||
|
|
||||||
|
Spinner spinner;
|
||||||
|
|
||||||
|
AddStep("add spinner", () => SetContents(_ =>
|
||||||
|
{
|
||||||
|
spinner = new Spinner
|
||||||
|
{
|
||||||
|
StartTime = Time.Current,
|
||||||
|
EndTime = Time.Current + 750,
|
||||||
|
Samples = new List<HitSampleInfo>
|
||||||
|
{
|
||||||
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { OverallDifficulty = 0 });
|
||||||
|
|
||||||
|
return drawableSpinner = new TestDrawableSpinner(spinner, true, spinRate)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Depth = depthIndex++,
|
||||||
|
Scale = new Vector2(0.75f)
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSpinningSamplePitchShift()
|
public void TestSpinningSamplePitchShift()
|
||||||
{
|
{
|
||||||
@ -153,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
if (auto)
|
if (auto)
|
||||||
RotationTracker.AddRotation((float)(Clock.ElapsedFrameTime * spinRate.Value));
|
RotationTracker.AddRotation((float)Math.Min(180, Clock.ElapsedFrameTime * spinRate.Value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
SelectionBox.CanFlipX = SelectionBox.CanScaleX = quad.Width > 0;
|
SelectionBox.CanFlipX = SelectionBox.CanScaleX = quad.Width > 0;
|
||||||
SelectionBox.CanFlipY = SelectionBox.CanScaleY = quad.Height > 0;
|
SelectionBox.CanFlipY = SelectionBox.CanScaleY = quad.Height > 0;
|
||||||
|
SelectionBox.CanScaleDiagonally = SelectionBox.CanScaleX && SelectionBox.CanScaleY;
|
||||||
SelectionBox.CanReverse = EditorBeatmap.SelectedHitObjects.Count > 1 || EditorBeatmap.SelectedHitObjects.Any(s => s is Slider);
|
SelectionBox.CanReverse = EditorBeatmap.SelectedHitObjects.Count > 1 || EditorBeatmap.SelectedHitObjects.Any(s => s is Slider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
typeof(ModNoFail),
|
typeof(ModNoFail),
|
||||||
typeof(ModAutoplay),
|
typeof(ModAutoplay),
|
||||||
typeof(OsuModMagnetised),
|
typeof(OsuModMagnetised),
|
||||||
typeof(OsuModRepel)
|
typeof(OsuModRepel),
|
||||||
|
typeof(ModTouchDevice)
|
||||||
};
|
};
|
||||||
|
|
||||||
public bool PerformFail() => false;
|
public bool PerformFail() => false;
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Localisation;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModTouchDevice : Mod
|
public class OsuModTouchDevice : ModTouchDevice
|
||||||
{
|
{
|
||||||
public override string Name => "Touch Device";
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
|
||||||
public override string Acronym => "TD";
|
|
||||||
public override LocalisableString Description => "Automatically applied to plays on devices with a touchscreen.";
|
|
||||||
public override double ScoreMultiplier => 1;
|
|
||||||
|
|
||||||
public override ModType Type => ModType.System;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -45,6 +46,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private const float spinning_sample_initial_frequency = 1.0f;
|
private const float spinning_sample_initial_frequency = 1.0f;
|
||||||
private const float spinning_sample_modulated_base_frequency = 0.5f;
|
private const float spinning_sample_modulated_base_frequency = 0.5f;
|
||||||
|
|
||||||
|
private SkinnableSound maxBonusSample;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of bonus score gained from spinning after the required number of spins, for display purposes.
|
/// The amount of bonus score gained from spinning after the required number of spins, for display purposes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -109,6 +112,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
MinimumSampleVolume = MINIMUM_SAMPLE_VOLUME,
|
MinimumSampleVolume = MINIMUM_SAMPLE_VOLUME,
|
||||||
Looping = true,
|
Looping = true,
|
||||||
Frequency = { Value = spinning_sample_initial_frequency }
|
Frequency = { Value = spinning_sample_initial_frequency }
|
||||||
|
},
|
||||||
|
maxBonusSample = new SkinnableSound
|
||||||
|
{
|
||||||
|
MinimumSampleVolume = MINIMUM_SAMPLE_VOLUME,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -128,6 +135,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
base.OnFree();
|
base.OnFree();
|
||||||
|
|
||||||
spinningSample.ClearSamples();
|
spinningSample.ClearSamples();
|
||||||
|
maxBonusSample.ClearSamples();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadSamples()
|
protected override void LoadSamples()
|
||||||
@ -136,6 +144,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
spinningSample.Samples = HitObject.CreateSpinningSamples().Cast<ISampleInfo>().ToArray();
|
spinningSample.Samples = HitObject.CreateSpinningSamples().Cast<ISampleInfo>().ToArray();
|
||||||
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
||||||
|
|
||||||
|
maxBonusSample.Samples = new ISampleInfo[] { new SpinnerBonusMaxSampleInfo(HitObject.CreateHitSampleInfo()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateSpinningSample(ValueChangedEvent<bool> tracking)
|
private void updateSpinningSample(ValueChangedEvent<bool> tracking)
|
||||||
@ -157,6 +167,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.StopAllSamples();
|
base.StopAllSamples();
|
||||||
spinningSample?.Stop();
|
spinningSample?.Stop();
|
||||||
|
maxBonusSample?.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddNestedHitObject(DrawableHitObject hitObject)
|
protected override void AddNestedHitObject(DrawableHitObject hitObject)
|
||||||
@ -322,10 +333,38 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
var tick = ticks.FirstOrDefault(t => !t.Result.HasResult);
|
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.
|
||||||
tick?.TriggerResult(true);
|
if (tick == null)
|
||||||
|
{
|
||||||
|
// we still want to play a sound. this will probably be a new sound in the future, but for now let's continue playing the bonus sound.
|
||||||
|
// TODO: this doesn't concurrency. i can't figure out how to make it concurrency. samples are bad and need a refactor.
|
||||||
|
maxBonusSample.Play();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tick.TriggerResult(true);
|
||||||
|
|
||||||
completedFullSpins.Value++;
|
completedFullSpins.Value++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SpinnerBonusMaxSampleInfo : HitSampleInfo
|
||||||
|
{
|
||||||
|
public override IEnumerable<string> LookupNames
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
foreach (string name in base.LookupNames)
|
||||||
|
yield return name;
|
||||||
|
|
||||||
|
foreach (string name in base.LookupNames)
|
||||||
|
yield return name.Replace("-max", string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpinnerBonusMaxSampleInfo(HitSampleInfo sampleInfo)
|
||||||
|
: base("spinnerbonus-max", sampleInfo.Bank, sampleInfo.Suffix, sampleInfo.Volume)
|
||||||
|
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,6 +135,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
classicSliderBehaviour = value;
|
classicSliderBehaviour = value;
|
||||||
if (HeadCircle != null)
|
if (HeadCircle != null)
|
||||||
HeadCircle.ClassicSliderBehaviour = value;
|
HeadCircle.ClassicSliderBehaviour = value;
|
||||||
|
if (TailCircle != null)
|
||||||
|
TailCircle.ClassicSliderBehaviour = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +220,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
StartTime = e.Time,
|
StartTime = e.Time,
|
||||||
Position = EndPosition,
|
Position = EndPosition,
|
||||||
StackHeight = StackHeight,
|
StackHeight = StackHeight,
|
||||||
|
ClassicSliderBehaviour = ClassicSliderBehaviour,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -273,9 +276,9 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => ClassicSliderBehaviour
|
public override Judgement CreateJudgement() => ClassicSliderBehaviour
|
||||||
// See logic in `DrawableSlider.CheckForResult()`
|
// Final combo is provided by the slider itself - see logic in `DrawableSlider.CheckForResult()`
|
||||||
? new OsuJudgement()
|
? new OsuJudgement()
|
||||||
// Of note, this creates a combo discrepancy for non-classic-mod sliders (there is no combo increase for tail or slider judgement).
|
// Final combo is provided by the tail circle - see `SliderTailCircle`
|
||||||
: new OsuIgnoreJudgement();
|
: new OsuIgnoreJudgement();
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.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
|
||||||
@ -43,5 +45,12 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||||
|
|
||||||
|
public override Judgement CreateJudgement() => new SliderEndJudgement();
|
||||||
|
|
||||||
|
public class SliderEndJudgement : OsuJudgement
|
||||||
|
{
|
||||||
|
public override HitResult MaxResult => HitResult.LargeTickHit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +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.Game.Rulesets.Judgements;
|
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects
|
namespace osu.Game.Rulesets.Osu.Objects
|
||||||
{
|
{
|
||||||
public class SliderRepeat : SliderEndCircle
|
public class SliderRepeat : SliderEndCircle
|
||||||
@ -13,12 +9,5 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
: base(slider)
|
: base(slider)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new SliderRepeatJudgement();
|
|
||||||
|
|
||||||
public class SliderRepeatJudgement : OsuJudgement
|
|
||||||
{
|
|
||||||
public override HitResult MaxResult => HitResult.LargeTickHit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,16 +9,28 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
public class SliderTailCircle : SliderEndCircle
|
public class SliderTailCircle : SliderEndCircle
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to treat this <see cref="SliderHeadCircle"/> as a normal <see cref="HitCircle"/> for judgement purposes.
|
||||||
|
/// If <c>false</c>, this <see cref="SliderHeadCircle"/> will be judged as a <see cref="SliderTick"/> instead.
|
||||||
|
/// </summary>
|
||||||
|
public bool ClassicSliderBehaviour;
|
||||||
|
|
||||||
public SliderTailCircle(Slider slider)
|
public SliderTailCircle(Slider slider)
|
||||||
: base(slider)
|
: base(slider)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new SliderTailJudgement();
|
public override Judgement CreateJudgement() => ClassicSliderBehaviour ? new LegacyTailJudgement() : new TailJudgement();
|
||||||
|
|
||||||
public class SliderTailJudgement : OsuJudgement
|
public class LegacyTailJudgement : OsuJudgement
|
||||||
{
|
{
|
||||||
public override HitResult MaxResult => HitResult.SmallTickHit;
|
public override HitResult MaxResult => HitResult.SmallTickHit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TailJudgement : SliderEndJudgement
|
||||||
|
{
|
||||||
|
public override HitResult MaxResult => HitResult.LargeTickHit;
|
||||||
|
public override HitResult MinResult => HitResult.IgnoreMiss;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
private readonly OsuInputManager osuInputManager;
|
private readonly OsuInputManager osuInputManager;
|
||||||
|
|
||||||
private Bindable<bool> mouseDisabled = null!;
|
private Bindable<bool> tapsDisabled = null!;
|
||||||
|
|
||||||
public OsuTouchInputMapper(OsuInputManager inputManager)
|
public OsuTouchInputMapper(OsuInputManager inputManager)
|
||||||
{
|
{
|
||||||
@ -43,9 +43,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config)
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
// The mouse button disable setting affects touch. It's a bit weird.
|
tapsDisabled = config.GetBindable<bool>(OsuSetting.TouchDisableGameplayTaps);
|
||||||
// This is mostly just doing the same as what is done in RulesetInputManager to match behaviour.
|
|
||||||
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Required to handle touches outside of the playfield when screen scaling is enabled.
|
// Required to handle touches outside of the playfield when screen scaling is enabled.
|
||||||
@ -64,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
: OsuAction.LeftButton;
|
: OsuAction.LeftButton;
|
||||||
|
|
||||||
// Ignore any taps which trigger an action which is already handled. But track them for potential positional input in the future.
|
// Ignore any taps which trigger an action which is already handled. But track them for potential positional input in the future.
|
||||||
bool shouldResultInAction = osuInputManager.AllowGameplayInputs && !mouseDisabled.Value && trackedTouches.All(t => t.Action != action);
|
bool shouldResultInAction = osuInputManager.AllowGameplayInputs && !tapsDisabled.Value && trackedTouches.All(t => t.Action != action);
|
||||||
|
|
||||||
// If we can actually accept as an action, check whether this tap was on a circle's receptor.
|
// If we can actually accept as an action, check whether this tap was on a circle's receptor.
|
||||||
// This case gets special handling to allow for empty-space stream tapping.
|
// This case gets special handling to allow for empty-space stream tapping.
|
||||||
|
@ -174,8 +174,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
|
|
||||||
private class TestLegacySkin : LegacySkin
|
private class TestLegacySkin : LegacySkin
|
||||||
{
|
{
|
||||||
public TestLegacySkin(IResourceStore<byte[]> storage, string fileName)
|
public TestLegacySkin(IResourceStore<byte[]> fallbackStore, string fileName)
|
||||||
: base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, null, storage, fileName)
|
: base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, null, fallbackStore, fileName)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,11 +147,11 @@ namespace osu.Game.Tests.Mods
|
|||||||
new Mod[] { new OsuModDeflate(), new OsuModApproachDifferent() },
|
new Mod[] { new OsuModDeflate(), new OsuModApproachDifferent() },
|
||||||
new[] { typeof(OsuModDeflate), typeof(OsuModApproachDifferent) }
|
new[] { typeof(OsuModDeflate), typeof(OsuModApproachDifferent) }
|
||||||
},
|
},
|
||||||
// system mod.
|
// system mod not applicable in lazer.
|
||||||
new object[]
|
new object[]
|
||||||
{
|
{
|
||||||
new Mod[] { new OsuModHidden(), new OsuModTouchDevice() },
|
new Mod[] { new OsuModHidden(), new ModScoreV2() },
|
||||||
new[] { typeof(OsuModTouchDevice) }
|
new[] { typeof(ModScoreV2) }
|
||||||
},
|
},
|
||||||
// multi mod.
|
// multi mod.
|
||||||
new object[]
|
new object[]
|
||||||
|
BIN
osu.Game.Tests/Resources/Archives/modified-argon-20231108.osk
Normal file
BIN
osu.Game.Tests/Resources/Archives/modified-argon-20231108.osk
Normal file
Binary file not shown.
@ -11,9 +11,9 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class HitResultTest
|
public class HitResultTest
|
||||||
{
|
{
|
||||||
[TestCase(new[] { HitResult.Perfect, HitResult.Great, HitResult.Good, HitResult.Ok, HitResult.Meh }, new[] { HitResult.Miss })]
|
[TestCase(new[] { HitResult.Perfect, HitResult.Great, HitResult.Good, HitResult.Ok, HitResult.Meh }, new[] { HitResult.Miss, HitResult.IgnoreMiss })]
|
||||||
[TestCase(new[] { HitResult.LargeTickHit }, new[] { HitResult.LargeTickMiss })]
|
[TestCase(new[] { HitResult.LargeTickHit }, new[] { HitResult.LargeTickMiss, HitResult.IgnoreMiss })]
|
||||||
[TestCase(new[] { HitResult.SmallTickHit }, new[] { HitResult.SmallTickMiss })]
|
[TestCase(new[] { HitResult.SmallTickHit }, new[] { HitResult.SmallTickMiss, HitResult.IgnoreMiss })]
|
||||||
[TestCase(new[] { HitResult.LargeBonus, HitResult.SmallBonus }, new[] { HitResult.IgnoreMiss })]
|
[TestCase(new[] { HitResult.LargeBonus, HitResult.SmallBonus }, new[] { HitResult.IgnoreMiss })]
|
||||||
[TestCase(new[] { HitResult.IgnoreHit }, new[] { HitResult.IgnoreMiss, HitResult.ComboBreak })]
|
[TestCase(new[] { HitResult.IgnoreHit }, new[] { HitResult.IgnoreMiss, HitResult.ComboBreak })]
|
||||||
public void TestValidResultPairs(HitResult[] maxResults, HitResult[] minResults)
|
public void TestValidResultPairs(HitResult[] maxResults, HitResult[] minResults)
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Database;
|
|||||||
using osu.Game.Extensions;
|
using osu.Game.Extensions;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
using SharpCompress.Archives.Zip;
|
using SharpCompress.Archives.Zip;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Skins.IO
|
namespace osu.Game.Tests.Skins.IO
|
||||||
@ -21,6 +22,25 @@ namespace osu.Game.Tests.Skins.IO
|
|||||||
{
|
{
|
||||||
#region Testing filename metadata inclusion
|
#region Testing filename metadata inclusion
|
||||||
|
|
||||||
|
[TestCase("Archives/modified-classic-20220723.osk")]
|
||||||
|
[TestCase("Archives/modified-default-20230117.osk")]
|
||||||
|
[TestCase("Archives/modified-argon-20231106.osk")]
|
||||||
|
public Task TestImportModifiedSkinHasResources(string archive) => runSkinTest(async osu =>
|
||||||
|
{
|
||||||
|
using (var stream = TestResources.OpenResource(archive))
|
||||||
|
{
|
||||||
|
var imported = await loadSkinIntoOsu(osu, new ImportTask(stream, "skin.osk"));
|
||||||
|
|
||||||
|
// When the import filename doesn't match, it should be appended (and update the skin.ini).
|
||||||
|
|
||||||
|
var skinManager = osu.Dependencies.Get<SkinManager>();
|
||||||
|
|
||||||
|
skinManager.CurrentSkinInfo.Value = imported;
|
||||||
|
|
||||||
|
Assert.That(skinManager.CurrentSkin.Value.LayoutInfos.Count, Is.EqualTo(2));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public Task TestSingleImportDifferentFilename() => runSkinTest(async osu =>
|
public Task TestSingleImportDifferentFilename() => runSkinTest(async osu =>
|
||||||
{
|
{
|
||||||
|
@ -15,6 +15,7 @@ using osu.Game.IO.Archives;
|
|||||||
using osu.Game.Screens.Play.HUD;
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Skinning.Components;
|
||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Skins
|
namespace osu.Game.Tests.Skins
|
||||||
@ -57,6 +58,8 @@ namespace osu.Game.Tests.Skins
|
|||||||
"Archives/modified-argon-pro-20231001.osk",
|
"Archives/modified-argon-pro-20231001.osk",
|
||||||
// Covers player name text component.
|
// Covers player name text component.
|
||||||
"Archives/modified-argon-20231106.osk",
|
"Archives/modified-argon-20231106.osk",
|
||||||
|
// Covers "Argon" accuracy/score/combo counters, and wedges
|
||||||
|
"Archives/modified-argon-20231108.osk",
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -100,6 +103,20 @@ namespace osu.Game.Tests.Skins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDeserialiseModifiedArgon()
|
||||||
|
{
|
||||||
|
using (var stream = TestResources.OpenResource("Archives/modified-argon-20231106.osk"))
|
||||||
|
using (var storage = new ZipArchiveReader(stream))
|
||||||
|
{
|
||||||
|
var skin = new TestSkin(new SkinInfo(), null, storage);
|
||||||
|
|
||||||
|
Assert.That(skin.LayoutInfos, Has.Count.EqualTo(2));
|
||||||
|
Assert.That(skin.LayoutInfos[SkinComponentsContainerLookup.TargetArea.MainHUDComponents].AllDrawables.ToArray(), Has.Length.EqualTo(10));
|
||||||
|
Assert.That(skin.LayoutInfos[SkinComponentsContainerLookup.TargetArea.MainHUDComponents].AllDrawables.Select(i => i.Type), Contains.Item(typeof(PlayerName)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDeserialiseModifiedClassic()
|
public void TestDeserialiseModifiedClassic()
|
||||||
{
|
{
|
||||||
@ -132,8 +149,8 @@ namespace osu.Game.Tests.Skins
|
|||||||
|
|
||||||
private class TestSkin : Skin
|
private class TestSkin : Skin
|
||||||
{
|
{
|
||||||
public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? storage = null, string configurationFilename = "skin.ini")
|
public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? fallbackStore = null, string configurationFilename = "skin.ini")
|
||||||
: base(skin, resources, storage, configurationFilename)
|
: base(skin, resources, fallbackStore, configurationFilename)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,8 +95,8 @@ namespace osu.Game.Tests.Skins
|
|||||||
{
|
{
|
||||||
public const string SAMPLE_NAME = "test-sample";
|
public const string SAMPLE_NAME = "test-sample";
|
||||||
|
|
||||||
public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? storage = null, string configurationFilename = "skin.ini")
|
public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? fallbackStore = null, string configurationFilename = "skin.ini")
|
||||||
: base(skin, resources, storage, configurationFilename)
|
: base(skin, resources, fallbackStore, configurationFilename)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +181,54 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
AddStep("restore default beatmap", () => Beatmap.SetDefault());
|
AddStep("restore default beatmap", () => Beatmap.SetDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatmapBackgroundWithStoryboardUnloadedOnSuspension()
|
||||||
|
{
|
||||||
|
BackgroundScreenBeatmap nestedScreen = null;
|
||||||
|
|
||||||
|
setSupporter(true);
|
||||||
|
setSourceMode(BackgroundSource.BeatmapWithStoryboard);
|
||||||
|
|
||||||
|
AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithStoryboard());
|
||||||
|
AddAssert("background changed", () => screen.CheckLastLoadChange() == true);
|
||||||
|
AddUntilStep("wait for beatmap background to be loaded", () => getCurrentBackground()?.GetType() == typeof(BeatmapBackgroundWithStoryboard));
|
||||||
|
|
||||||
|
AddUntilStep("storyboard present", () => screen.ChildrenOfType<DrawableStoryboard>().SingleOrDefault()?.IsLoaded == true);
|
||||||
|
|
||||||
|
AddStep("push new background to stack", () => stack.Push(nestedScreen = new BackgroundScreenBeatmap(Beatmap.Value)));
|
||||||
|
AddUntilStep("wait for screen to load", () => nestedScreen.IsLoaded && nestedScreen.IsCurrentScreen());
|
||||||
|
|
||||||
|
AddUntilStep("storyboard unloaded", () => !screen.ChildrenOfType<DrawableStoryboard>().Any());
|
||||||
|
|
||||||
|
AddStep("go back", () => screen.MakeCurrent());
|
||||||
|
|
||||||
|
AddUntilStep("storyboard reloaded", () => screen.ChildrenOfType<DrawableStoryboard>().SingleOrDefault()?.IsLoaded == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatmapBackgroundWithStoryboardButBeatmapHasNone()
|
||||||
|
{
|
||||||
|
BackgroundScreenBeatmap nestedScreen = null;
|
||||||
|
|
||||||
|
setSupporter(true);
|
||||||
|
setSourceMode(BackgroundSource.BeatmapWithStoryboard);
|
||||||
|
|
||||||
|
AddStep("change beatmap", () => Beatmap.Value = createTestWorkingBeatmapWithUniqueBackground());
|
||||||
|
AddAssert("background changed", () => screen.CheckLastLoadChange() == true);
|
||||||
|
AddUntilStep("wait for beatmap background to be loaded", () => getCurrentBackground()?.GetType() == typeof(BeatmapBackgroundWithStoryboard));
|
||||||
|
|
||||||
|
AddUntilStep("no storyboard loaded", () => !screen.ChildrenOfType<DrawableStoryboard>().Any());
|
||||||
|
|
||||||
|
AddStep("push new background to stack", () => stack.Push(nestedScreen = new BackgroundScreenBeatmap(Beatmap.Value)));
|
||||||
|
AddUntilStep("wait for screen to load", () => nestedScreen.IsLoaded && nestedScreen.IsCurrentScreen());
|
||||||
|
|
||||||
|
AddUntilStep("still no storyboard", () => !screen.ChildrenOfType<DrawableStoryboard>().Any());
|
||||||
|
|
||||||
|
AddStep("go back", () => screen.MakeCurrent());
|
||||||
|
|
||||||
|
AddUntilStep("still no storyboard", () => !screen.ChildrenOfType<DrawableStoryboard>().Any());
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBackgroundTypeSwitch()
|
public void TestBackgroundTypeSwitch()
|
||||||
{
|
{
|
||||||
|
@ -47,6 +47,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
CanScaleX = true,
|
CanScaleX = true,
|
||||||
CanScaleY = true,
|
CanScaleY = true,
|
||||||
|
CanScaleDiagonally = true,
|
||||||
CanFlipX = true,
|
CanFlipX = true,
|
||||||
CanFlipY = true,
|
CanFlipY = true,
|
||||||
|
|
||||||
|
@ -47,17 +47,17 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
AddSliderStep("Width", 0, 1f, 1f, val =>
|
|
||||||
{
|
|
||||||
if (healthDisplay.IsNotNull())
|
|
||||||
healthDisplay.BarLength.Value = val;
|
|
||||||
});
|
|
||||||
|
|
||||||
AddSliderStep("Height", 0, 64, 0, val =>
|
AddSliderStep("Height", 0, 64, 0, val =>
|
||||||
{
|
{
|
||||||
if (healthDisplay.IsNotNull())
|
if (healthDisplay.IsNotNull())
|
||||||
healthDisplay.BarHeight.Value = val;
|
healthDisplay.BarHeight.Value = val;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddSliderStep("Width", 0, 1f, 0.98f, val =>
|
||||||
|
{
|
||||||
|
if (healthDisplay.IsNotNull())
|
||||||
|
healthDisplay.Width = val;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
@ -96,7 +97,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddStep("Begin drag top left", () =>
|
AddStep("Begin drag top left", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.TopLeft - new Vector2(box1.ScreenSpaceDrawQuad.Width / 4));
|
InputManager.MoveMouseTo(box1.ScreenSpaceDrawQuad.TopLeft - new Vector2(box1.ScreenSpaceDrawQuad.Width / 4, box1.ScreenSpaceDrawQuad.Height / 8));
|
||||||
InputManager.PressButton(MouseButton.Left);
|
InputManager.PressButton(MouseButton.Left);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -146,8 +147,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
AddStep("Add big black box", () =>
|
AddStep("Add big black box", () =>
|
||||||
{
|
{
|
||||||
InputManager.MoveMouseTo(skinEditor.ChildrenOfType<BigBlackBox>().First());
|
skinEditor.ChildrenOfType<SkinComponentToolbox.ToolboxComponentButton>().First(b => b.ChildrenOfType<BigBlackBox>().FirstOrDefault() != null).TriggerClick();
|
||||||
InputManager.Click(MouseButton.Left);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("store box", () =>
|
AddStep("store box", () =>
|
||||||
@ -243,7 +243,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
void revertAndCheckUnchanged()
|
void revertAndCheckUnchanged()
|
||||||
{
|
{
|
||||||
AddStep("Revert changes", () => changeHandler.RestoreState(int.MinValue));
|
AddStep("Revert changes", () => changeHandler.RestoreState(int.MinValue));
|
||||||
AddAssert("Current state is same as default", () => defaultState.SequenceEqual(changeHandler.GetCurrentState()));
|
AddAssert("Current state is same as default",
|
||||||
|
() => Encoding.UTF8.GetString(defaultState),
|
||||||
|
() => Is.EqualTo(Encoding.UTF8.GetString(changeHandler.GetCurrentState())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Cached]
|
[Cached]
|
||||||
private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset());
|
private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset());
|
||||||
|
|
||||||
|
protected override Drawable CreateArgonImplementation() => new ArgonAccuracyCounter();
|
||||||
protected override Drawable CreateDefaultImplementation() => new DefaultAccuracyCounter();
|
protected override Drawable CreateDefaultImplementation() => new DefaultAccuracyCounter();
|
||||||
protected override Drawable CreateLegacyImplementation() => new LegacyAccuracyCounter();
|
protected override Drawable CreateLegacyImplementation() => new LegacyAccuracyCounter();
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Cached]
|
[Cached]
|
||||||
private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset());
|
private ScoreProcessor scoreProcessor = new ScoreProcessor(new OsuRuleset());
|
||||||
|
|
||||||
|
protected override Drawable CreateArgonImplementation() => new ArgonComboCounter();
|
||||||
protected override Drawable CreateDefaultImplementation() => new DefaultComboCounter();
|
protected override Drawable CreateDefaultImplementation() => new DefaultComboCounter();
|
||||||
protected override Drawable CreateLegacyImplementation() => new LegacyComboCounter();
|
protected override Drawable CreateLegacyImplementation() => new LegacyComboCounter();
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Cached(typeof(HealthProcessor))]
|
[Cached(typeof(HealthProcessor))]
|
||||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||||
|
|
||||||
protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay { Scale = new Vector2(0.6f) };
|
protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay { Scale = new Vector2(0.6f), Width = 1f };
|
||||||
protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay { Scale = new Vector2(0.6f) };
|
protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay { Scale = new Vector2(0.6f) };
|
||||||
protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay { Scale = new Vector2(0.6f) };
|
protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay { Scale = new Vector2(0.6f) };
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Cached(typeof(ScoreProcessor))]
|
[Cached(typeof(ScoreProcessor))]
|
||||||
private ScoreProcessor scoreProcessor = TestGameplayState.Create(new OsuRuleset()).ScoreProcessor;
|
private ScoreProcessor scoreProcessor = TestGameplayState.Create(new OsuRuleset()).ScoreProcessor;
|
||||||
|
|
||||||
|
protected override Drawable CreateArgonImplementation() => new ArgonScoreCounter();
|
||||||
protected override Drawable CreateDefaultImplementation() => new DefaultScoreCounter();
|
protected override Drawable CreateDefaultImplementation() => new DefaultScoreCounter();
|
||||||
protected override Drawable CreateLegacyImplementation() => new LegacyScoreCounter();
|
protected override Drawable CreateLegacyImplementation() => new LegacyScoreCounter();
|
||||||
|
|
||||||
|
@ -133,6 +133,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
private bool assertAllAvailableModsSelected()
|
private bool assertAllAvailableModsSelected()
|
||||||
{
|
{
|
||||||
var allAvailableMods = availableMods.Value
|
var allAvailableMods = availableMods.Value
|
||||||
|
.Where(pair => pair.Key != ModType.System)
|
||||||
.SelectMany(pair => pair.Value)
|
.SelectMany(pair => pair.Value)
|
||||||
.Where(mod => mod.UserPlayable && mod.HasImplementation)
|
.Where(mod => mod.UserPlayable && mod.HasImplementation)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
@ -6,11 +6,13 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets.Mania;
|
using osu.Game.Rulesets.Mania;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
@ -232,6 +234,35 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
() => Is.EqualTo(beatmapSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0).ID));
|
() => Is.EqualTo(beatmapSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0).ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCreateNewDifficultyOnNonExistentBeatmap()
|
||||||
|
{
|
||||||
|
AddUntilStep("wait for dialog overlay", () => Game.ChildrenOfType<DialogOverlay>().SingleOrDefault() != null);
|
||||||
|
|
||||||
|
AddStep("open editor", () => Game.ChildrenOfType<ButtonSystem>().Single().OnEdit.Invoke());
|
||||||
|
AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.IsLoaded);
|
||||||
|
AddStep("click on file", () =>
|
||||||
|
{
|
||||||
|
var item = getEditor().ChildrenOfType<Menu.DrawableMenuItem>().Single(i => i.Item.Text.Value.ToString() == "File");
|
||||||
|
item.TriggerClick();
|
||||||
|
});
|
||||||
|
AddStep("click on create new difficulty", () =>
|
||||||
|
{
|
||||||
|
var item = getEditor().ChildrenOfType<Menu.DrawableMenuItem>().Single(i => i.Item.Text.Value.ToString() == "Create new difficulty");
|
||||||
|
item.TriggerClick();
|
||||||
|
});
|
||||||
|
AddStep("click on catch", () =>
|
||||||
|
{
|
||||||
|
var item = getEditor().ChildrenOfType<Menu.DrawableMenuItem>().Single(i => i.Item.Text.Value.ToString() == "osu!catch");
|
||||||
|
item.TriggerClick();
|
||||||
|
});
|
||||||
|
AddAssert("save dialog displayed", () => Game.ChildrenOfType<DialogOverlay>().Single().CurrentDialog is SaveRequiredPopupDialog);
|
||||||
|
|
||||||
|
AddStep("press save", () => Game.ChildrenOfType<DialogOverlay>().Single().CurrentDialog!.PerformOkAction());
|
||||||
|
AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.IsLoaded);
|
||||||
|
AddAssert("editor beatmap uses catch ruleset", () => getEditorBeatmap().BeatmapInfo.Ruleset.ShortName == "fruits");
|
||||||
|
}
|
||||||
|
|
||||||
private EditorBeatmap getEditorBeatmap() => getEditor().ChildrenOfType<EditorBeatmap>().Single();
|
private EditorBeatmap getEditorBeatmap() => getEditor().ChildrenOfType<EditorBeatmap>().Single();
|
||||||
|
|
||||||
private Editor getEditor() => (Editor)Game.ScreenStack.CurrentScreen;
|
private Editor getEditor() => (Editor)Game.ScreenStack.CurrentScreen;
|
||||||
|
@ -12,6 +12,7 @@ using osu.Framework.Extensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -835,6 +836,110 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
AddAssert("exit dialog is shown", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog is ConfirmExitDialog);
|
AddAssert("exit dialog is shown", () => Game.Dependencies.Get<IDialogOverlay>().CurrentDialog is ConfirmExitDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTouchScreenDetectionAtSongSelect()
|
||||||
|
{
|
||||||
|
AddStep("touch logo", () =>
|
||||||
|
{
|
||||||
|
var button = Game.ChildrenOfType<OsuLogo>().Single();
|
||||||
|
var touch = new Touch(TouchSource.Touch1, button.ScreenSpaceDrawQuad.Centre);
|
||||||
|
InputManager.BeginTouch(touch);
|
||||||
|
InputManager.EndTouch(touch);
|
||||||
|
});
|
||||||
|
AddAssert("touch screen detected active", () => Game.Dependencies.Get<SessionStatics>().Get<bool>(Static.TouchInputActive), () => Is.True);
|
||||||
|
|
||||||
|
AddStep("click settings button", () =>
|
||||||
|
{
|
||||||
|
var button = Game.ChildrenOfType<MainMenuButton>().Last();
|
||||||
|
InputManager.MoveMouseTo(button);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddAssert("touch screen detected inactive", () => Game.Dependencies.Get<SessionStatics>().Get<bool>(Static.TouchInputActive), () => Is.False);
|
||||||
|
|
||||||
|
AddStep("close settings sidebar", () => InputManager.Key(Key.Escape));
|
||||||
|
|
||||||
|
Screens.Select.SongSelect songSelect = null;
|
||||||
|
AddRepeatStep("go to solo", () => InputManager.Key(Key.P), 3);
|
||||||
|
AddUntilStep("wait for song select", () => (songSelect = Game.ScreenStack.CurrentScreen as Screens.Select.SongSelect) != null);
|
||||||
|
AddUntilStep("wait for beatmap sets loaded", () => songSelect.BeatmapSetsLoaded);
|
||||||
|
|
||||||
|
AddStep("switch to osu! ruleset", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.LControl);
|
||||||
|
InputManager.Key(Key.Number1);
|
||||||
|
InputManager.ReleaseKey(Key.LControl);
|
||||||
|
});
|
||||||
|
AddStep("touch beatmap wedge", () =>
|
||||||
|
{
|
||||||
|
var wedge = Game.ChildrenOfType<BeatmapInfoWedge>().Single();
|
||||||
|
var touch = new Touch(TouchSource.Touch2, wedge.ScreenSpaceDrawQuad.Centre);
|
||||||
|
InputManager.BeginTouch(touch);
|
||||||
|
InputManager.EndTouch(touch);
|
||||||
|
});
|
||||||
|
AddUntilStep("touch device mod activated", () => Game.SelectedMods.Value, () => Has.One.InstanceOf<ModTouchDevice>());
|
||||||
|
|
||||||
|
AddStep("switch to mania ruleset", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.LControl);
|
||||||
|
InputManager.Key(Key.Number4);
|
||||||
|
InputManager.ReleaseKey(Key.LControl);
|
||||||
|
});
|
||||||
|
AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf<ModTouchDevice>());
|
||||||
|
AddStep("touch beatmap wedge", () =>
|
||||||
|
{
|
||||||
|
var wedge = Game.ChildrenOfType<BeatmapInfoWedge>().Single();
|
||||||
|
var touch = new Touch(TouchSource.Touch2, wedge.ScreenSpaceDrawQuad.Centre);
|
||||||
|
InputManager.BeginTouch(touch);
|
||||||
|
InputManager.EndTouch(touch);
|
||||||
|
});
|
||||||
|
AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf<ModTouchDevice>());
|
||||||
|
|
||||||
|
AddStep("switch to osu! ruleset", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.LControl);
|
||||||
|
InputManager.Key(Key.Number1);
|
||||||
|
InputManager.ReleaseKey(Key.LControl);
|
||||||
|
});
|
||||||
|
AddUntilStep("touch device mod activated", () => Game.SelectedMods.Value, () => Has.One.InstanceOf<ModTouchDevice>());
|
||||||
|
|
||||||
|
AddStep("click beatmap wedge", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(Game.ChildrenOfType<BeatmapInfoWedge>().Single());
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
AddUntilStep("touch device mod not activated", () => Game.SelectedMods.Value, () => Has.None.InstanceOf<ModTouchDevice>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTouchScreenDetectionInGame()
|
||||||
|
{
|
||||||
|
PushAndConfirm(() => new TestPlaySongSelect());
|
||||||
|
AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely());
|
||||||
|
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
||||||
|
AddStep("select", () => InputManager.Key(Key.Enter));
|
||||||
|
|
||||||
|
Player player = null;
|
||||||
|
|
||||||
|
AddUntilStep("wait for player", () =>
|
||||||
|
{
|
||||||
|
DismissAnyNotifications();
|
||||||
|
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for track playing", () => Game.Beatmap.Value.Track.IsRunning);
|
||||||
|
|
||||||
|
AddStep("touch", () =>
|
||||||
|
{
|
||||||
|
var touch = new Touch(TouchSource.Touch2, Game.ScreenSpaceDrawQuad.Centre);
|
||||||
|
InputManager.BeginTouch(touch);
|
||||||
|
InputManager.EndTouch(touch);
|
||||||
|
});
|
||||||
|
AddUntilStep("touch device mod added to score", () => player.Score.ScoreInfo.Mods, () => Has.One.InstanceOf<ModTouchDevice>());
|
||||||
|
|
||||||
|
AddStep("exit player", () => player.Exit());
|
||||||
|
AddUntilStep("touch device mod still active", () => Game.SelectedMods.Value, () => Has.One.InstanceOf<ModTouchDevice>());
|
||||||
|
}
|
||||||
|
|
||||||
private Func<Player> playToResults()
|
private Func<Player> playToResults()
|
||||||
{
|
{
|
||||||
var player = playToCompletion();
|
var player = playToCompletion();
|
||||||
|
@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
new[] { "Plain", "This is plain comment" },
|
new[] { "Plain", "This is plain comment" },
|
||||||
new[] { "Pinned", "This is pinned comment" },
|
new[] { "Pinned", "This is pinned comment" },
|
||||||
new[] { "Link", "Please visit https://osu.ppy.sh" },
|
new[] { "Link", "Please visit https://osu.ppy.sh" },
|
||||||
new[] { "Big Image", "![](Backgrounds/bg1)" },
|
new[] { "Big Image", "![](Backgrounds/bg1 \"Big Image\")" },
|
||||||
new[] { "Small Image", "![](Cursor/cursortrail)" },
|
new[] { "Small Image", "![](Cursor/cursortrail)" },
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
|
@ -17,18 +17,16 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
{
|
{
|
||||||
BarGraph graph;
|
BarGraph graph;
|
||||||
|
|
||||||
Children = new[]
|
Child = graph = new BarGraph
|
||||||
{
|
{
|
||||||
graph = new BarGraph
|
RelativeSizeAxes = Axes.Both,
|
||||||
{
|
Anchor = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
Origin = Anchor.Centre,
|
||||||
Anchor = Anchor.Centre,
|
Size = new Vector2(0.5f),
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Size = new Vector2(0.5f),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i));
|
AddStep("values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Select(i => (float)i));
|
||||||
|
AddStep("small values", () => graph.Values = Enumerable.Range(1, 10).Select(i => i * 0.01f).Concat(new[] { 100f }));
|
||||||
AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i));
|
AddStep("values from 1-100", () => graph.Values = Enumerable.Range(1, 100).Select(i => (float)i));
|
||||||
AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i));
|
AddStep("reversed values from 1-10", () => graph.Values = Enumerable.Range(1, 10).Reverse().Select(i => (float)i));
|
||||||
AddStep("empty values", () => graph.Values = Array.Empty<float>());
|
AddStep("empty values", () => graph.Values = Array.Empty<float>());
|
||||||
@ -36,6 +34,14 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
AddStep("Top to bottom", () => graph.Direction = BarDirection.TopToBottom);
|
AddStep("Top to bottom", () => graph.Direction = BarDirection.TopToBottom);
|
||||||
AddStep("Left to right", () => graph.Direction = BarDirection.LeftToRight);
|
AddStep("Left to right", () => graph.Direction = BarDirection.LeftToRight);
|
||||||
AddStep("Right to left", () => graph.Direction = BarDirection.RightToLeft);
|
AddStep("Right to left", () => graph.Direction = BarDirection.RightToLeft);
|
||||||
|
|
||||||
|
AddToggleStep("Toggle movement", enabled =>
|
||||||
|
{
|
||||||
|
if (enabled)
|
||||||
|
graph.MoveToY(-10, 1000).Then().MoveToY(10, 1000).Loop();
|
||||||
|
else
|
||||||
|
graph.ClearTransforms();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
86
osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs
Normal file
86
osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// 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.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Graphics.Cursor;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Users;
|
||||||
|
using osu.Game.Users.Drawables;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public partial class TestSceneUserClickableAvatar : OsuManualInputManagerTestScene
|
||||||
|
{
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
Child = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Spacing = new Vector2(10f),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
generateUser(@"peppy", 2, CountryCode.AU, @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", false, "99EB47"),
|
||||||
|
generateUser(@"flyte", 3103765, CountryCode.JP, @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", true),
|
||||||
|
generateUser(@"joshika39", 17032217, CountryCode.RS, @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", false),
|
||||||
|
new UpdateableAvatar(),
|
||||||
|
new UpdateableAvatar()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestClickableAvatarHover()
|
||||||
|
{
|
||||||
|
AddStep("hover avatar with user panel", () => InputManager.MoveMouseTo(this.ChildrenOfType<ClickableAvatar>().ElementAt(1)));
|
||||||
|
AddUntilStep("wait for tooltip to show", () => this.ChildrenOfType<ClickableAvatar.UserCardTooltip>().FirstOrDefault()?.State.Value == Visibility.Visible);
|
||||||
|
AddStep("hover out", () => InputManager.MoveMouseTo(new Vector2(0)));
|
||||||
|
AddUntilStep("wait for tooltip to hide", () => this.ChildrenOfType<ClickableAvatar.UserCardTooltip>().FirstOrDefault()?.State.Value == Visibility.Hidden);
|
||||||
|
|
||||||
|
AddStep("hover avatar without user panel", () => InputManager.MoveMouseTo(this.ChildrenOfType<ClickableAvatar>().ElementAt(0)));
|
||||||
|
AddUntilStep("wait for tooltip to show", () => this.ChildrenOfType<OsuTooltipContainer.OsuTooltip>().FirstOrDefault()?.State.Value == Visibility.Visible);
|
||||||
|
AddStep("hover out", () => InputManager.MoveMouseTo(new Vector2(0)));
|
||||||
|
AddUntilStep("wait for tooltip to hide", () => this.ChildrenOfType<OsuTooltipContainer.OsuTooltip>().FirstOrDefault()?.State.Value == Visibility.Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable generateUser(string username, int id, CountryCode countryCode, string cover, bool showPanel, string? color = null)
|
||||||
|
{
|
||||||
|
var user = new APIUser
|
||||||
|
{
|
||||||
|
Username = username,
|
||||||
|
Id = id,
|
||||||
|
CountryCode = countryCode,
|
||||||
|
CoverUrl = cover,
|
||||||
|
Colour = color ?? "000000",
|
||||||
|
Status =
|
||||||
|
{
|
||||||
|
Value = new UserStatusOnline()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return new ClickableAvatar(user, showPanel)
|
||||||
|
{
|
||||||
|
Width = 50,
|
||||||
|
Height = 50,
|
||||||
|
CornerRadius = 10,
|
||||||
|
Masking = true,
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Radius = 1,
|
||||||
|
Colour = Color4.Black.Opacity(0.2f),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,6 @@ 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.Containers.Markdown;
|
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
@ -298,7 +297,7 @@ This is a line after the fenced code block!
|
|||||||
{
|
{
|
||||||
public LinkInline Link;
|
public LinkInline Link;
|
||||||
|
|
||||||
public override MarkdownTextFlowContainer CreateTextFlow() => new TestMarkdownTextFlowContainer
|
public override OsuMarkdownTextFlowContainer CreateTextFlow() => new TestMarkdownTextFlowContainer
|
||||||
{
|
{
|
||||||
UrlAdded = link => Link = link,
|
UrlAdded = link => Link = link,
|
||||||
};
|
};
|
||||||
|
@ -203,6 +203,7 @@ namespace osu.Game.Tests.Visual.Ranking
|
|||||||
|
|
||||||
public IBeatmap Beatmap { get; }
|
public IBeatmap Beatmap { get; }
|
||||||
|
|
||||||
|
// ReSharper disable once NotNullOrRequiredMemberIsNotInitialized
|
||||||
public TestBeatmapConverter(IBeatmap beatmap)
|
public TestBeatmapConverter(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
Beatmap = beatmap;
|
Beatmap = beatmap;
|
||||||
|
@ -167,7 +167,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAddingFlow()
|
public void TestAddingFlow([Values] bool withSystemModActive)
|
||||||
{
|
{
|
||||||
ModPresetColumn modPresetColumn = null!;
|
ModPresetColumn modPresetColumn = null!;
|
||||||
|
|
||||||
@ -181,7 +181,13 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddUntilStep("items loaded", () => modPresetColumn.IsLoaded && modPresetColumn.ItemsLoaded);
|
AddUntilStep("items loaded", () => modPresetColumn.IsLoaded && modPresetColumn.ItemsLoaded);
|
||||||
AddAssert("add preset button disabled", () => !this.ChildrenOfType<AddPresetButton>().Single().Enabled.Value);
|
AddAssert("add preset button disabled", () => !this.ChildrenOfType<AddPresetButton>().Single().Enabled.Value);
|
||||||
|
|
||||||
AddStep("set mods", () => SelectedMods.Value = new Mod[] { new OsuModDaycore(), new OsuModClassic() });
|
AddStep("set mods", () =>
|
||||||
|
{
|
||||||
|
var newMods = new Mod[] { new OsuModDaycore(), new OsuModClassic() };
|
||||||
|
if (withSystemModActive)
|
||||||
|
newMods = newMods.Append(new OsuModTouchDevice()).ToArray();
|
||||||
|
SelectedMods.Value = newMods;
|
||||||
|
});
|
||||||
AddAssert("add preset button enabled", () => this.ChildrenOfType<AddPresetButton>().Single().Enabled.Value);
|
AddAssert("add preset button enabled", () => this.ChildrenOfType<AddPresetButton>().Single().Enabled.Value);
|
||||||
|
|
||||||
AddStep("click add preset button", () =>
|
AddStep("click add preset button", () =>
|
||||||
@ -209,6 +215,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
});
|
});
|
||||||
AddUntilStep("popover closed", () => !this.ChildrenOfType<OsuPopover>().Any());
|
AddUntilStep("popover closed", () => !this.ChildrenOfType<OsuPopover>().Any());
|
||||||
AddUntilStep("preset creation occurred", () => this.ChildrenOfType<ModPresetPanel>().Count() == 4);
|
AddUntilStep("preset creation occurred", () => this.ChildrenOfType<ModPresetPanel>().Count() == 4);
|
||||||
|
AddAssert("preset has correct mods",
|
||||||
|
() => this.ChildrenOfType<ModPresetPanel>().Single(panel => panel.Preset.Value.Name == "new preset").Preset.Value.Mods,
|
||||||
|
() => Has.Count.EqualTo(2));
|
||||||
|
|
||||||
AddStep("click add preset button", () =>
|
AddStep("click add preset button", () =>
|
||||||
{
|
{
|
||||||
|
@ -86,6 +86,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
AddStep("set mods to HD+HR+DT", () => SelectedMods.Value = new Mod[] { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime() });
|
AddStep("set mods to HD+HR+DT", () => SelectedMods.Value = new Mod[] { new OsuModHidden(), new OsuModHardRock(), new OsuModDoubleTime() });
|
||||||
AddAssert("panel is not active", () => !panel.AsNonNull().Active.Value);
|
AddAssert("panel is not active", () => !panel.AsNonNull().Active.Value);
|
||||||
|
|
||||||
|
// system mods are not included in presets.
|
||||||
|
AddStep("set mods to HR+DT+TD", () => SelectedMods.Value = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime(), new OsuModTouchDevice() });
|
||||||
|
AddAssert("panel is active", () => panel.AsNonNull().Active.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -113,6 +117,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
AddStep("set customised mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
|
AddStep("set customised mod", () => SelectedMods.Value = new[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
|
||||||
AddStep("activate panel", () => panel.AsNonNull().TriggerClick());
|
AddStep("activate panel", () => panel.AsNonNull().TriggerClick());
|
||||||
assertSelectedModsEquivalentTo(new Mod[] { new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } });
|
assertSelectedModsEquivalentTo(new Mod[] { new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } });
|
||||||
|
|
||||||
|
AddStep("set system mod", () => SelectedMods.Value = new[] { new OsuModTouchDevice() });
|
||||||
|
AddStep("activate panel", () => panel.AsNonNull().TriggerClick());
|
||||||
|
assertSelectedModsEquivalentTo(new Mod[] { new OsuModTouchDevice(), new OsuModHardRock(), new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertSelectedModsEquivalentTo(IEnumerable<Mod> mods)
|
private void assertSelectedModsEquivalentTo(IEnumerable<Mod> mods)
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -17,6 +19,11 @@ namespace osu.Game.Tournament.Components
|
|||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
var players = team?.Players ?? new BindableList<TournamentUser>();
|
||||||
|
|
||||||
|
// split the players into two even columns, favouring the first column if odd.
|
||||||
|
int split = (int)Math.Ceiling(players.Count / 2f);
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
@ -39,13 +46,13 @@ namespace osu.Game.Tournament.Components
|
|||||||
{
|
{
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
ChildrenEnumerable = team?.Players.Select(createPlayerText).Take(5) ?? Enumerable.Empty<Drawable>()
|
ChildrenEnumerable = players.Take(split).Select(createPlayerText),
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
ChildrenEnumerable = team?.Players.Select(createPlayerText).Skip(5) ?? Enumerable.Empty<Drawable>()
|
ChildrenEnumerable = players.Skip(split).Select(createPlayerText),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -74,7 +74,7 @@ namespace osu.Game.Beatmaps.Drawables.Cards.Statistics
|
|||||||
|
|
||||||
#region Tooltip implementation
|
#region Tooltip implementation
|
||||||
|
|
||||||
public virtual ITooltip GetCustomTooltip() => null;
|
public virtual ITooltip GetCustomTooltip() => null!;
|
||||||
public virtual object TooltipContent => null;
|
public virtual object TooltipContent => null;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -108,6 +108,8 @@ namespace osu.Game.Configuration
|
|||||||
SetDefault(OsuSetting.MouseDisableWheel, false);
|
SetDefault(OsuSetting.MouseDisableWheel, false);
|
||||||
SetDefault(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay);
|
SetDefault(OsuSetting.ConfineMouseMode, OsuConfineMouseMode.DuringGameplay);
|
||||||
|
|
||||||
|
SetDefault(OsuSetting.TouchDisableGameplayTaps, false);
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
SetDefault(OsuSetting.ShowFpsDisplay, false);
|
SetDefault(OsuSetting.ShowFpsDisplay, false);
|
||||||
|
|
||||||
@ -330,6 +332,10 @@ namespace osu.Game.Configuration
|
|||||||
|
|
||||||
ShowHealthDisplayWhenCantFail,
|
ShowHealthDisplayWhenCantFail,
|
||||||
FadePlayfieldWhenHealthLow,
|
FadePlayfieldWhenHealthLow,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables mouse buttons clicks during gameplay.
|
||||||
|
/// </summary>
|
||||||
MouseDisableButtons,
|
MouseDisableButtons,
|
||||||
MouseDisableWheel,
|
MouseDisableWheel,
|
||||||
ConfineMouseMode,
|
ConfineMouseMode,
|
||||||
@ -408,6 +414,7 @@ namespace osu.Game.Configuration
|
|||||||
EditorLimitedDistanceSnap,
|
EditorLimitedDistanceSnap,
|
||||||
ReplaySettingsOverlay,
|
ReplaySettingsOverlay,
|
||||||
AutomaticallyDownloadMissingBeatmaps,
|
AutomaticallyDownloadMissingBeatmaps,
|
||||||
EditorShowSpeedChanges
|
EditorShowSpeedChanges,
|
||||||
|
TouchDisableGameplayTaps,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using osu.Framework;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Input;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
@ -24,6 +26,7 @@ namespace osu.Game.Configuration
|
|||||||
SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null);
|
SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null);
|
||||||
SetDefault(Static.LastModSelectPanelSamplePlaybackTime, (double?)null);
|
SetDefault(Static.LastModSelectPanelSamplePlaybackTime, (double?)null);
|
||||||
SetDefault<APISeasonalBackgrounds>(Static.SeasonalBackgrounds, null);
|
SetDefault<APISeasonalBackgrounds>(Static.SeasonalBackgrounds, null);
|
||||||
|
SetDefault(Static.TouchInputActive, RuntimeInfo.IsMobile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -63,6 +66,12 @@ namespace osu.Game.Configuration
|
|||||||
/// The last playback time in milliseconds of an on/off sample (from <see cref="ModSelectPanel"/>).
|
/// The last playback time in milliseconds of an on/off sample (from <see cref="ModSelectPanel"/>).
|
||||||
/// Used to debounce <see cref="ModSelectPanel"/> on/off sounds game-wide to avoid volume saturation, especially in activating mod presets with many mods.
|
/// Used to debounce <see cref="ModSelectPanel"/> on/off sounds game-wide to avoid volume saturation, especially in activating mod presets with many mods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
LastModSelectPanelSamplePlaybackTime
|
LastModSelectPanelSamplePlaybackTime,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the last positional input received was a touch input.
|
||||||
|
/// Used in touchscreen detection scenarios (<see cref="TouchInputInterceptor"/>).
|
||||||
|
/// </summary>
|
||||||
|
TouchInputActive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -10,6 +12,7 @@ using osu.Framework.Timing;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Screens;
|
||||||
using osu.Game.Storyboards.Drawables;
|
using osu.Game.Storyboards.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Backgrounds
|
namespace osu.Game.Graphics.Backgrounds
|
||||||
@ -18,6 +21,10 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
{
|
{
|
||||||
private readonly InterpolatingFramedClock storyboardClock;
|
private readonly InterpolatingFramedClock storyboardClock;
|
||||||
|
|
||||||
|
private AudioContainer storyboardContainer = null!;
|
||||||
|
private DrawableStoryboard? drawableStoryboard;
|
||||||
|
private CancellationTokenSource? loadCancellationSource = new CancellationTokenSource();
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private MusicController? musicController { get; set; }
|
private MusicController? musicController { get; set; }
|
||||||
|
|
||||||
@ -33,18 +40,59 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
if (!Beatmap.Storyboard.HasDrawable)
|
AddInternal(storyboardContainer = new AudioContainer
|
||||||
return;
|
|
||||||
|
|
||||||
if (Beatmap.Storyboard.ReplacesBackground)
|
|
||||||
Sprite.Alpha = 0;
|
|
||||||
|
|
||||||
LoadComponentAsync(new AudioContainer
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Volume = { Value = 0 },
|
Volume = { Value = 0 },
|
||||||
Child = new DrawableStoryboard(Beatmap.Storyboard, mods.Value) { Clock = storyboardClock }
|
});
|
||||||
}, AddInternal);
|
|
||||||
|
LoadStoryboard(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadStoryboard(bool async = true)
|
||||||
|
{
|
||||||
|
Debug.Assert(drawableStoryboard == null);
|
||||||
|
|
||||||
|
if (!Beatmap.Storyboard.HasDrawable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
drawableStoryboard = new DrawableStoryboard(Beatmap.Storyboard, mods.Value)
|
||||||
|
{
|
||||||
|
Clock = storyboardClock
|
||||||
|
};
|
||||||
|
|
||||||
|
if (async)
|
||||||
|
LoadComponentAsync(drawableStoryboard, finishLoad, (loadCancellationSource = new CancellationTokenSource()).Token);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LoadComponent(drawableStoryboard);
|
||||||
|
finishLoad(drawableStoryboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void finishLoad(DrawableStoryboard s)
|
||||||
|
{
|
||||||
|
if (Beatmap.Storyboard.ReplacesBackground)
|
||||||
|
Sprite.FadeOut(BackgroundScreen.TRANSITION_LENGTH, Easing.InQuint);
|
||||||
|
|
||||||
|
storyboardContainer.FadeInFromZero(BackgroundScreen.TRANSITION_LENGTH, Easing.OutQuint);
|
||||||
|
storyboardContainer.Add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnloadStoryboard()
|
||||||
|
{
|
||||||
|
if (drawableStoryboard == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
loadCancellationSource?.Cancel();
|
||||||
|
loadCancellationSource = null;
|
||||||
|
|
||||||
|
// clear is intentionally used here for the storyboard to be disposed asynchronously.
|
||||||
|
storyboardContainer.Clear();
|
||||||
|
|
||||||
|
drawableStoryboard = null;
|
||||||
|
|
||||||
|
Sprite.Alpha = 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
|
@ -5,7 +5,6 @@ using Markdig.Extensions.Footnotes;
|
|||||||
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.Containers.Markdown;
|
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
@ -62,7 +61,7 @@ namespace osu.Game.Graphics.Containers.Markdown.Footnotes
|
|||||||
lastFootnote = Text = footnote;
|
lastFootnote = Text = footnote;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override MarkdownTextFlowContainer CreateTextFlow() => new FootnoteMarkdownTextFlowContainer();
|
public override OsuMarkdownTextFlowContainer CreateTextFlow() => new FootnoteMarkdownTextFlowContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class FootnoteMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer
|
private partial class FootnoteMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer
|
||||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Graphics.Containers.Markdown
|
|||||||
Font = OsuFont.GetFont(Typeface.Inter, size: 14, weight: FontWeight.Regular),
|
Font = OsuFont.GetFont(Typeface.Inter, size: 14, weight: FontWeight.Regular),
|
||||||
};
|
};
|
||||||
|
|
||||||
public override MarkdownTextFlowContainer CreateTextFlow() => new OsuMarkdownTextFlowContainer();
|
public override OsuMarkdownTextFlowContainer CreateTextFlow() => new OsuMarkdownTextFlowContainer();
|
||||||
|
|
||||||
protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new OsuMarkdownHeading(headingBlock);
|
protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new OsuMarkdownHeading(headingBlock);
|
||||||
|
|
||||||
|
@ -145,6 +145,13 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
float barHeight = drawSize.Y * ((direction == BarDirection.TopToBottom || direction == BarDirection.BottomToTop) ? lengths[i] : barBreadth);
|
float barHeight = drawSize.Y * ((direction == BarDirection.TopToBottom || direction == BarDirection.BottomToTop) ? lengths[i] : barBreadth);
|
||||||
float barWidth = drawSize.X * ((direction == BarDirection.LeftToRight || direction == BarDirection.RightToLeft) ? lengths[i] : barBreadth);
|
float barWidth = drawSize.X * ((direction == BarDirection.LeftToRight || direction == BarDirection.RightToLeft) ? lengths[i] : barBreadth);
|
||||||
|
|
||||||
|
if (barHeight == 0 || barWidth == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Apply minimum sizing to hide the fact that we don't have fractional anti-aliasing.
|
||||||
|
barHeight = Math.Max(barHeight, 1.5f);
|
||||||
|
barWidth = Math.Max(barWidth, 1.5f);
|
||||||
|
|
||||||
Vector2 topLeft;
|
Vector2 topLeft;
|
||||||
|
|
||||||
switch (direction)
|
switch (direction)
|
||||||
|
72
osu.Game/Input/TouchInputInterceptor.cs
Normal file
72
osu.Game/Input/TouchInputInterceptor.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// 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.Diagnostics;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.TypeExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Input.StateChanges;
|
||||||
|
using osu.Framework.Logging;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Input
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Intercepts all positional input events and sets the appropriate <see cref="Static.TouchInputActive"/> value
|
||||||
|
/// for consumption by particular game screens.
|
||||||
|
/// </summary>
|
||||||
|
public partial class TouchInputInterceptor : Component
|
||||||
|
{
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
|
||||||
|
|
||||||
|
private readonly BindableBool touchInputActive = new BindableBool();
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(SessionStatics statics)
|
||||||
|
{
|
||||||
|
statics.BindWith(Static.TouchInputActive, touchInputActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool Handle(UIEvent e)
|
||||||
|
{
|
||||||
|
bool touchInputWasActive = touchInputActive.Value;
|
||||||
|
|
||||||
|
switch (e)
|
||||||
|
{
|
||||||
|
case MouseEvent:
|
||||||
|
if (e.CurrentState.Mouse.LastSource is not ISourcedFromTouch)
|
||||||
|
{
|
||||||
|
if (touchInputWasActive)
|
||||||
|
Logger.Log($@"Touch input deactivated due to received {e.GetType().ReadableName()}", LoggingTarget.Input);
|
||||||
|
touchInputActive.Value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TouchEvent:
|
||||||
|
if (!touchInputWasActive)
|
||||||
|
Logger.Log($@"Touch input activated due to received {e.GetType().ReadableName()}", LoggingTarget.Input);
|
||||||
|
touchInputActive.Value = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KeyDownEvent keyDown:
|
||||||
|
if (keyDown.Key == Key.T && keyDown.ControlPressed && keyDown.ShiftPressed)
|
||||||
|
debugToggleTouchInputActive();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Conditional("TOUCH_INPUT_DEBUG")]
|
||||||
|
private void debugToggleTouchInputActive()
|
||||||
|
{
|
||||||
|
Logger.Log($@"Debug-toggling touch input to {(touchInputActive.Value ? @"inactive" : @"active")}", LoggingTarget.Information, LogLevel.Important);
|
||||||
|
touchInputActive.Toggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,14 +40,14 @@ namespace osu.Game.Localisation
|
|||||||
public static LocalisableString DisableMouseWheelVolumeAdjust => new TranslatableString(getKey(@"disable_mouse_wheel_volume_adjust"), @"Disable mouse wheel adjusting volume during gameplay");
|
public static LocalisableString DisableMouseWheelVolumeAdjust => new TranslatableString(getKey(@"disable_mouse_wheel_volume_adjust"), @"Disable mouse wheel adjusting volume during gameplay");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Volume can still be adjusted using the mouse wheel by holding "Alt""
|
/// "Volume can still be adjusted using the mouse wheel by holding "Alt""
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString DisableMouseWheelVolumeAdjustTooltip => new TranslatableString(getKey(@"disable_mouse_wheel_volume_adjust_tooltip"), @"Volume can still be adjusted using the mouse wheel by holding ""Alt""");
|
public static LocalisableString DisableMouseWheelVolumeAdjustTooltip => new TranslatableString(getKey(@"disable_mouse_wheel_volume_adjust_tooltip"), @"Volume can still be adjusted using the mouse wheel by holding ""Alt""");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Disable mouse buttons during gameplay"
|
/// "Disable clicks during gameplay"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString DisableMouseButtons => new TranslatableString(getKey(@"disable_mouse_buttons"), @"Disable mouse buttons during gameplay");
|
public static LocalisableString DisableClicksDuringGameplay => new TranslatableString(getKey(@"disable_clicks"), @"Disable clicks during gameplay");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Enable high precision mouse to adjust sensitivity"
|
/// "Enable high precision mouse to adjust sensitivity"
|
||||||
|
@ -12,43 +12,53 @@ namespace osu.Game.Localisation.SkinComponents
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Sprite name"
|
/// "Sprite name"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString SpriteName => new TranslatableString(getKey(@"sprite_name"), "Sprite name");
|
public static LocalisableString SpriteName => new TranslatableString(getKey(@"sprite_name"), @"Sprite name");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "The filename of the sprite"
|
/// "The filename of the sprite"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString SpriteNameDescription => new TranslatableString(getKey(@"sprite_name_description"), "The filename of the sprite");
|
public static LocalisableString SpriteNameDescription => new TranslatableString(getKey(@"sprite_name_description"), @"The filename of the sprite");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Font"
|
/// "Font"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString Font => new TranslatableString(getKey(@"font"), "Font");
|
public static LocalisableString Font => new TranslatableString(getKey(@"font"), @"Font");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "The font to use."
|
/// "The font to use."
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString FontDescription => new TranslatableString(getKey(@"font_description"), "The font to use.");
|
public static LocalisableString FontDescription => new TranslatableString(getKey(@"font_description"), @"The font to use.");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Text"
|
/// "Text"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString TextElementText => new TranslatableString(getKey(@"text_element_text"), "Text");
|
public static LocalisableString TextElementText => new TranslatableString(getKey(@"text_element_text"), @"Text");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "The text to be displayed."
|
/// "The text to be displayed."
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString TextElementTextDescription => new TranslatableString(getKey(@"text_element_text_description"), "The text to be displayed.");
|
public static LocalisableString TextElementTextDescription => new TranslatableString(getKey(@"text_element_text_description"), @"The text to be displayed.");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "Corner radius"
|
/// "Corner radius"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString CornerRadius => new TranslatableString(getKey(@"corner_radius"), "Corner radius");
|
public static LocalisableString CornerRadius => new TranslatableString(getKey(@"corner_radius"), @"Corner radius");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// "How rounded the corners should be."
|
/// "How rounded the corners should be."
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static LocalisableString CornerRadiusDescription => new TranslatableString(getKey(@"corner_radius_description"), "How rounded the corners should be.");
|
public static LocalisableString CornerRadiusDescription => new TranslatableString(getKey(@"corner_radius_description"), @"How rounded the corners should be.");
|
||||||
|
|
||||||
private static string getKey(string key) => $"{prefix}:{key}";
|
/// <summary>
|
||||||
|
/// "Show label"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ShowLabel => new TranslatableString(getKey(@"show_label"), @"Show label");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Whether the component's label should be shown."
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString ShowLabelDescription => new TranslatableString(getKey(@"show_label_description"), @"Whether the component's label should be shown.");
|
||||||
|
|
||||||
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
24
osu.Game/Localisation/TouchSettingsStrings.cs
Normal file
24
osu.Game/Localisation/TouchSettingsStrings.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 osu.Framework.Localisation;
|
||||||
|
|
||||||
|
namespace osu.Game.Localisation
|
||||||
|
{
|
||||||
|
public static class TouchSettingsStrings
|
||||||
|
{
|
||||||
|
private const string prefix = @"osu.Game.Resources.Localisation.TouchSettings";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Touch"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString Touch => new TranslatableString(getKey(@"touch"), @"Touch");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Disable taps during gameplay"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString DisableTapsDuringGameplay => new TranslatableString(getKey(@"disable_taps_during_gameplay"), @"Disable taps during gameplay");
|
||||||
|
|
||||||
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
|
}
|
||||||
|
}
|
@ -407,6 +407,8 @@ namespace osu.Game
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
base.Content.Add(new TouchInputInterceptor());
|
||||||
|
|
||||||
KeyBindingStore = new RealmKeyBindingStore(realm, keyCombinationProvider);
|
KeyBindingStore = new RealmKeyBindingStore(realm, keyCombinationProvider);
|
||||||
KeyBindingStore.Register(globalBindings, RulesetStore.AvailableRulesets);
|
KeyBindingStore.Register(globalBindings, RulesetStore.AvailableRulesets);
|
||||||
|
|
||||||
@ -575,14 +577,14 @@ namespace osu.Game
|
|||||||
|
|
||||||
case JoystickHandler jh:
|
case JoystickHandler jh:
|
||||||
return new JoystickSettings(jh);
|
return new JoystickSettings(jh);
|
||||||
|
|
||||||
case TouchHandler th:
|
|
||||||
return new TouchSettings(th);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (handler)
|
switch (handler)
|
||||||
{
|
{
|
||||||
|
case TouchHandler th:
|
||||||
|
return new TouchSettings(th);
|
||||||
|
|
||||||
case MidiHandler:
|
case MidiHandler:
|
||||||
return new InputSection.HandlerSection(handler);
|
return new InputSection.HandlerSection(handler);
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
CornerRadius = 4,
|
CornerRadius = 4,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
Child = avatar = new UpdateableAvatar(showGuestOnNull: false)
|
Child = avatar = new UpdateableAvatar(showUserPanelOnHover: true, showGuestOnNull: false)
|
||||||
{
|
{
|
||||||
Size = new Vector2(height),
|
Size = new Vector2(height),
|
||||||
},
|
},
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
|
|
||||||
protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new CommentMarkdownHeading(headingBlock);
|
protected override MarkdownHeading CreateHeading(HeadingBlock headingBlock) => new CommentMarkdownHeading(headingBlock);
|
||||||
|
|
||||||
public override MarkdownTextFlowContainer CreateTextFlow() => new CommentMarkdownTextFlowContainer();
|
public override OsuMarkdownTextFlowContainer CreateTextFlow() => new CommentMarkdownTextFlowContainer();
|
||||||
|
|
||||||
private partial class CommentMarkdownHeading : OsuMarkdownHeading
|
private partial class CommentMarkdownHeading : OsuMarkdownHeading
|
||||||
{
|
{
|
||||||
@ -49,14 +49,14 @@ namespace osu.Game.Overlays.Comments
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class CommentMarkdownTextFlowContainer : MarkdownTextFlowContainer
|
private partial class CommentMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer
|
||||||
{
|
{
|
||||||
protected override void AddImage(LinkInline linkInline) => AddDrawable(new CommentMarkdownImage(linkInline.Url));
|
protected override void AddImage(LinkInline linkInline) => AddDrawable(new CommentMarkdownImage(linkInline));
|
||||||
|
|
||||||
private partial class CommentMarkdownImage : MarkdownImage
|
private partial class CommentMarkdownImage : OsuMarkdownImage
|
||||||
{
|
{
|
||||||
public CommentMarkdownImage(string url)
|
public CommentMarkdownImage(LinkInline linkInline)
|
||||||
: base(url)
|
: base(linkInline)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = 20 },
|
Padding = new MarginPadding { Horizontal = WaveOverlayContainer.HORIZONTAL_PADDING, Vertical = 20 },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
avatar = new UpdateableAvatar(api.LocalUser.Value)
|
avatar = new UpdateableAvatar(api.LocalUser.Value, isInteractive: false)
|
||||||
{
|
{
|
||||||
Size = new Vector2(50),
|
Size = new Vector2(50),
|
||||||
CornerExponent = 2,
|
CornerExponent = 2,
|
||||||
|
@ -144,7 +144,7 @@ namespace osu.Game.Overlays.Comments
|
|||||||
Size = new Vector2(avatar_size),
|
Size = new Vector2(avatar_size),
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new UpdateableAvatar(Comment.User)
|
new UpdateableAvatar(Comment.User, showUserPanelOnHover: true)
|
||||||
{
|
{
|
||||||
Size = new Vector2(avatar_size),
|
Size = new Vector2(avatar_size),
|
||||||
Masking = true,
|
Masking = true,
|
||||||
|
@ -115,7 +115,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
Name = nameTextBox.Current.Value,
|
Name = nameTextBox.Current.Value,
|
||||||
Description = descriptionTextBox.Current.Value,
|
Description = descriptionTextBox.Current.Value,
|
||||||
Mods = selectedMods.Value.ToArray(),
|
Mods = selectedMods.Value.Where(mod => mod.Type != ModType.System).ToArray(),
|
||||||
Ruleset = r.Find<RulesetInfo>(ruleset.Value.ShortName)!
|
Ruleset = r.Find<RulesetInfo>(ruleset.Value.ShortName)!
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private void useCurrentMods()
|
private void useCurrentMods()
|
||||||
{
|
{
|
||||||
saveableMods = selectedMods.Value.ToHashSet();
|
saveableMods = selectedMods.Value.Where(mod => mod.Type != ModType.System).ToHashSet();
|
||||||
updateState();
|
updateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
if (!selectedMods.Value.Any())
|
if (!selectedMods.Value.Any())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return !saveableMods.SetEquals(selectedMods.Value);
|
return !saveableMods.SetEquals(selectedMods.Value.Where(mod => mod.Type != ModType.System));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void save()
|
private void save()
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -56,17 +55,14 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
protected override void Select()
|
protected override void Select()
|
||||||
{
|
{
|
||||||
// if the preset is not active at the point of the user click, then set the mods using the preset directly, discarding any previous selections,
|
var selectedSystemMods = selectedMods.Value.Where(mod => mod.Type == ModType.System);
|
||||||
// which will also have the side effect of activating the preset (see `updateActiveState()`).
|
// will also have the side effect of activating the preset (see `updateActiveState()`).
|
||||||
selectedMods.Value = Preset.Value.Mods.ToArray();
|
selectedMods.Value = Preset.Value.Mods.Concat(selectedSystemMods).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Deselect()
|
protected override void Deselect()
|
||||||
{
|
{
|
||||||
// if the preset is active when the user has clicked it, then it means that the set of active mods is exactly equal to the set of mods in the preset
|
selectedMods.Value = selectedMods.Value.Except(Preset.Value.Mods).ToArray();
|
||||||
// (there are no other active mods than what the preset specifies, and the mod settings match exactly).
|
|
||||||
// therefore it's safe to just clear selected mods, since it will have the effect of toggling the preset off.
|
|
||||||
selectedMods.Value = Array.Empty<Mod>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void selectedModsChanged()
|
private void selectedModsChanged()
|
||||||
@ -79,7 +75,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private void updateActiveState()
|
private void updateActiveState()
|
||||||
{
|
{
|
||||||
Active.Value = new HashSet<Mod>(Preset.Value.Mods).SetEquals(selectedMods.Value);
|
Active.Value = new HashSet<Mod>(Preset.Value.Mods).SetEquals(selectedMods.Value.Where(mod => mod.Type != ModType.System));
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Filtering support
|
#region Filtering support
|
||||||
|
@ -41,6 +41,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
private void updateEnabledState()
|
private void updateEnabledState()
|
||||||
{
|
{
|
||||||
Enabled.Value = availableMods.Value
|
Enabled.Value = availableMods.Value
|
||||||
|
.Where(pair => pair.Key != ModType.System)
|
||||||
.SelectMany(pair => pair.Value)
|
.SelectMany(pair => pair.Value)
|
||||||
.Any(modState => !modState.Active.Value && modState.Visible);
|
.Any(modState => !modState.Active.Value && modState.Visible);
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
|||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = MouseSettingsStrings.DisableMouseButtons,
|
LabelText = MouseSettingsStrings.DisableClicksDuringGameplay,
|
||||||
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons)
|
Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -3,38 +3,48 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Input.Handlers;
|
||||||
using osu.Framework.Input.Handlers.Touch;
|
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Settings.Sections.Input
|
namespace osu.Game.Overlays.Settings.Sections.Input
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Touch input settings subsection common to all touch handlers (even on different platforms).
|
||||||
|
/// </summary>
|
||||||
public partial class TouchSettings : SettingsSubsection
|
public partial class TouchSettings : SettingsSubsection
|
||||||
{
|
{
|
||||||
private readonly TouchHandler handler;
|
private readonly InputHandler handler;
|
||||||
|
|
||||||
public TouchSettings(TouchHandler handler)
|
protected override LocalisableString Header => TouchSettingsStrings.Touch;
|
||||||
|
|
||||||
|
public TouchSettings(InputHandler handler)
|
||||||
{
|
{
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OsuConfigManager osuConfig)
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
if (!RuntimeInfo.IsMobile) // don't allow disabling the only input method (touch) on mobile.
|
||||||
{
|
{
|
||||||
new SettingsCheckbox
|
Add(new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = CommonStrings.Enabled,
|
LabelText = CommonStrings.Enabled,
|
||||||
Current = handler.Enabled
|
Current = handler.Enabled
|
||||||
},
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
Add(new SettingsCheckbox
|
||||||
|
{
|
||||||
|
LabelText = TouchSettingsStrings.DisableTapsDuringGameplay,
|
||||||
|
Current = osuConfig.GetBindable<bool>(OsuSetting.TouchDisableGameplayTaps)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { @"touchscreen" });
|
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { @"touchscreen" });
|
||||||
|
|
||||||
protected override LocalisableString Header => handler.Description;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -153,6 +154,8 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
Items = new[]
|
Items = new[]
|
||||||
{
|
{
|
||||||
new EditorMenuItem(Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()),
|
new EditorMenuItem(Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()),
|
||||||
|
new EditorMenuItem(CommonStrings.Export, MenuItemType.Standard, () => skins.ExportCurrentSkin()) { Action = { Disabled = !RuntimeInfo.IsDesktop } },
|
||||||
|
new EditorMenuItemSpacer(),
|
||||||
new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, () => dialogOverlay?.Push(new RevertConfirmDialog(revert))),
|
new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, () => dialogOverlay?.Push(new RevertConfirmDialog(revert))),
|
||||||
new EditorMenuItemSpacer(),
|
new EditorMenuItemSpacer(),
|
||||||
new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, () => skinEditorOverlay?.Hide()),
|
new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, () => skinEditorOverlay?.Hide()),
|
||||||
@ -406,7 +409,14 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
cp.Colour = colours.Yellow;
|
cp.Colour = colours.Yellow;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
changeHandler?.Dispose();
|
||||||
|
|
||||||
skins.EnsureMutableSkin();
|
skins.EnsureMutableSkin();
|
||||||
|
|
||||||
|
var targetContainer = getTarget(selectedTarget.Value);
|
||||||
|
|
||||||
|
if (targetContainer != null)
|
||||||
|
changeHandler = new SkinEditorChangeHandler(targetContainer);
|
||||||
hasBegunMutating = true;
|
hasBegunMutating = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.EnumExtensions;
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
@ -31,8 +32,44 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
UpdatePosition = updateDrawablePosition
|
UpdatePosition = updateDrawablePosition
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private bool allSelectedSupportManualSizing(Axes axis) => SelectedItems.All(b => (b as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(axis) == false);
|
||||||
|
|
||||||
public override bool HandleScale(Vector2 scale, Anchor anchor)
|
public override bool HandleScale(Vector2 scale, Anchor anchor)
|
||||||
{
|
{
|
||||||
|
Axes adjustAxis;
|
||||||
|
|
||||||
|
switch (anchor)
|
||||||
|
{
|
||||||
|
// for corners, adjust scale.
|
||||||
|
case Anchor.TopLeft:
|
||||||
|
case Anchor.TopRight:
|
||||||
|
case Anchor.BottomLeft:
|
||||||
|
case Anchor.BottomRight:
|
||||||
|
adjustAxis = Axes.Both;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// for edges, adjust size.
|
||||||
|
// autosize elements can't be easily handled so just disable sizing for now.
|
||||||
|
case Anchor.TopCentre:
|
||||||
|
case Anchor.BottomCentre:
|
||||||
|
if (!allSelectedSupportManualSizing(Axes.Y))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
adjustAxis = Axes.Y;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Anchor.CentreLeft:
|
||||||
|
case Anchor.CentreRight:
|
||||||
|
if (!allSelectedSupportManualSizing(Axes.X))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
adjustAxis = Axes.X;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(anchor), anchor, null);
|
||||||
|
}
|
||||||
|
|
||||||
// convert scale to screen space
|
// convert scale to screen space
|
||||||
scale = ToScreenSpace(scale) - ToScreenSpace(Vector2.Zero);
|
scale = ToScreenSpace(scale) - ToScreenSpace(Vector2.Zero);
|
||||||
|
|
||||||
@ -120,7 +157,20 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
if (Precision.AlmostEquals(MathF.Abs(drawableItem.Rotation) % 180, 90))
|
if (Precision.AlmostEquals(MathF.Abs(drawableItem.Rotation) % 180, 90))
|
||||||
currentScaledDelta = new Vector2(scaledDelta.Y, scaledDelta.X);
|
currentScaledDelta = new Vector2(scaledDelta.Y, scaledDelta.X);
|
||||||
|
|
||||||
drawableItem.Scale *= currentScaledDelta;
|
switch (adjustAxis)
|
||||||
|
{
|
||||||
|
case Axes.X:
|
||||||
|
drawableItem.Width *= currentScaledDelta.X;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Axes.Y:
|
||||||
|
drawableItem.Height *= currentScaledDelta.Y;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Axes.Both:
|
||||||
|
drawableItem.Scale *= currentScaledDelta;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -169,8 +219,9 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
{
|
{
|
||||||
base.OnSelectionChanged();
|
base.OnSelectionChanged();
|
||||||
|
|
||||||
SelectionBox.CanScaleX = true;
|
SelectionBox.CanScaleX = allSelectedSupportManualSizing(Axes.X);
|
||||||
SelectionBox.CanScaleY = true;
|
SelectionBox.CanScaleY = allSelectedSupportManualSizing(Axes.Y);
|
||||||
|
SelectionBox.CanScaleDiagonally = true;
|
||||||
SelectionBox.CanFlipX = true;
|
SelectionBox.CanFlipX = true;
|
||||||
SelectionBox.CanFlipY = true;
|
SelectionBox.CanFlipY = true;
|
||||||
SelectionBox.CanReverse = false;
|
SelectionBox.CanReverse = false;
|
||||||
@ -215,7 +266,15 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
yield return new OsuMenuItem("Reset scale", MenuItemType.Standard, () =>
|
yield return new OsuMenuItem("Reset scale", MenuItemType.Standard, () =>
|
||||||
{
|
{
|
||||||
foreach (var blueprint in SelectedBlueprints)
|
foreach (var blueprint in SelectedBlueprints)
|
||||||
((Drawable)blueprint.Item).Scale = Vector2.One;
|
{
|
||||||
|
var blueprintItem = ((Drawable)blueprint.Item);
|
||||||
|
blueprintItem.Scale = Vector2.One;
|
||||||
|
|
||||||
|
if (blueprintItem.RelativeSizeAxes.HasFlagFast(Axes.X))
|
||||||
|
blueprintItem.Width = 1;
|
||||||
|
if (blueprintItem.RelativeSizeAxes.HasFlagFast(Axes.Y))
|
||||||
|
blueprintItem.Height = 1;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
yield return new EditorMenuItemSpacer();
|
yield return new EditorMenuItemSpacer();
|
||||||
|
@ -7,7 +7,6 @@ using Markdig.Extensions.Yaml;
|
|||||||
using Markdig.Syntax;
|
using Markdig.Syntax;
|
||||||
using Markdig.Syntax.Inlines;
|
using Markdig.Syntax.Inlines;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Containers.Markdown;
|
|
||||||
using osu.Game.Graphics.Containers.Markdown;
|
using osu.Game.Graphics.Containers.Markdown;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.Wiki.Markdown
|
namespace osu.Game.Overlays.Wiki.Markdown
|
||||||
@ -53,7 +52,7 @@ namespace osu.Game.Overlays.Wiki.Markdown
|
|||||||
base.AddMarkdownComponent(markdownObject, container, level);
|
base.AddMarkdownComponent(markdownObject, container, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override MarkdownTextFlowContainer CreateTextFlow() => new WikiMarkdownTextFlowContainer();
|
public override OsuMarkdownTextFlowContainer CreateTextFlow() => new WikiMarkdownTextFlowContainer();
|
||||||
|
|
||||||
private partial class WikiMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer
|
private partial class WikiMarkdownTextFlowContainer : OsuMarkdownTextFlowContainer
|
||||||
{
|
{
|
||||||
|
@ -93,7 +93,7 @@ namespace osu.Game.Overlays.Wiki
|
|||||||
|
|
||||||
public override SpriteText CreateSpriteText() => base.CreateSpriteText().With(t => t.Font = t.Font.With(Typeface.Torus, weight: FontWeight.Bold));
|
public override SpriteText CreateSpriteText() => base.CreateSpriteText().With(t => t.Font = t.Font.With(Typeface.Torus, weight: FontWeight.Bold));
|
||||||
|
|
||||||
public override MarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => f.TextAnchor = Anchor.TopCentre);
|
public override OsuMarkdownTextFlowContainer CreateTextFlow() => base.CreateTextFlow().With(f => f.TextAnchor = Anchor.TopCentre);
|
||||||
|
|
||||||
protected override MarkdownParagraph CreateParagraph(ParagraphBlock paragraphBlock, int level)
|
protected override MarkdownParagraph CreateParagraph(ParagraphBlock paragraphBlock, int level)
|
||||||
=> base.CreateParagraph(paragraphBlock, level).With(p => p.Margin = new MarginPadding { Bottom = 10 });
|
=> base.CreateParagraph(paragraphBlock, level).With(p => p.Margin = new MarginPadding { Bottom = 10 });
|
||||||
|
@ -59,6 +59,13 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
bool ValidForMultiplayerAsFreeMod { get; }
|
bool ValidForMultiplayerAsFreeMod { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that this mod is always permitted in scenarios wherein a user is submitting a score regardless of other circumstances.
|
||||||
|
/// Intended for mods that are informational in nature and do not really affect gameplay by themselves,
|
||||||
|
/// but are more of a gauge of increased/decreased difficulty due to the user's configuration (e.g. <see cref="ModTouchDevice"/>).
|
||||||
|
/// </summary>
|
||||||
|
bool AlwaysValidForSubmission { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a fresh <see cref="Mod"/> instance based on this mod.
|
/// Create a fresh <see cref="Mod"/> instance based on this mod.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -156,6 +156,10 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public virtual bool ValidForMultiplayerAsFreeMod => true;
|
public virtual bool ValidForMultiplayerAsFreeMod => true;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
[JsonIgnore]
|
||||||
|
public virtual bool AlwaysValidForSubmission => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether this mod requires configuration to apply changes to the game.
|
/// Whether this mod requires configuration to apply changes to the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public sealed override bool ValidForMultiplayer => false;
|
public sealed override bool ValidForMultiplayer => false;
|
||||||
public sealed override bool ValidForMultiplayerAsFreeMod => false;
|
public sealed override bool ValidForMultiplayerAsFreeMod => false;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModCinema), typeof(ModRelax), typeof(ModAdaptiveSpeed) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModCinema), typeof(ModRelax), typeof(ModAdaptiveSpeed), typeof(ModTouchDevice) };
|
||||||
|
|
||||||
public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
|
public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
|
||||||
|
|
||||||
|
24
osu.Game/Rulesets/Mods/ModTouchDevice.cs
Normal file
24
osu.Game/Rulesets/Mods/ModTouchDevice.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.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mods
|
||||||
|
{
|
||||||
|
public class ModTouchDevice : Mod, IApplicableMod
|
||||||
|
{
|
||||||
|
public sealed override string Name => "Touch Device";
|
||||||
|
public sealed override string Acronym => "TD";
|
||||||
|
public sealed override IconUsage? Icon => OsuIcon.PlayStyleTouch;
|
||||||
|
public sealed override LocalisableString Description => "Automatically applied to plays on devices with a touchscreen.";
|
||||||
|
public sealed override double ScoreMultiplier => 1;
|
||||||
|
public sealed override ModType Type => ModType.System;
|
||||||
|
public sealed override bool ValidForMultiplayer => false;
|
||||||
|
public sealed override bool ValidForMultiplayerAsFreeMod => false;
|
||||||
|
public sealed override bool AlwaysValidForSubmission => true;
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(ICreateReplayData) };
|
||||||
|
}
|
||||||
|
}
|
@ -204,6 +204,8 @@ namespace osu.Game.Rulesets
|
|||||||
|
|
||||||
public ModAutoplay? GetAutoplayMod() => CreateMod<ModAutoplay>();
|
public ModAutoplay? GetAutoplayMod() => CreateMod<ModAutoplay>();
|
||||||
|
|
||||||
|
public ModTouchDevice? GetTouchDeviceMod() => CreateMod<ModTouchDevice>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a transformer which adds lookups specific to a ruleset to skin sources.
|
/// Create a transformer which adds lookups specific to a ruleset to skin sources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -350,6 +350,9 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
if (maxResult.IsBonus() && minResult != HitResult.IgnoreMiss)
|
if (maxResult.IsBonus() && minResult != HitResult.IgnoreMiss)
|
||||||
throw new ArgumentOutOfRangeException(nameof(minResult), $"{HitResult.IgnoreMiss} is the only valid minimum result for a {maxResult} judgement.");
|
throw new ArgumentOutOfRangeException(nameof(minResult), $"{HitResult.IgnoreMiss} is the only valid minimum result for a {maxResult} judgement.");
|
||||||
|
|
||||||
|
if (minResult == HitResult.IgnoreMiss)
|
||||||
|
return;
|
||||||
|
|
||||||
if (maxResult == HitResult.LargeTickHit && minResult != HitResult.LargeTickMiss)
|
if (maxResult == HitResult.LargeTickHit && minResult != HitResult.LargeTickMiss)
|
||||||
throw new ArgumentOutOfRangeException(nameof(minResult), $"{HitResult.LargeTickMiss} is the only valid minimum result for a {maxResult} judgement.");
|
throw new ArgumentOutOfRangeException(nameof(minResult), $"{HitResult.LargeTickMiss} is the only valid minimum result for a {maxResult} judgement.");
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
private void load(OsuConfigManager config)
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
mouseDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
|
||||||
|
tapsDisabled = config.GetBindable<bool>(OsuSetting.TouchDisableGameplayTaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Action mapping (for replays)
|
#region Action mapping (for replays)
|
||||||
@ -124,6 +125,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
#region Setting application (disables etc.)
|
#region Setting application (disables etc.)
|
||||||
|
|
||||||
private Bindable<bool> mouseDisabled;
|
private Bindable<bool> mouseDisabled;
|
||||||
|
private Bindable<bool> tapsDisabled;
|
||||||
|
|
||||||
protected override bool Handle(UIEvent e)
|
protected override bool Handle(UIEvent e)
|
||||||
{
|
{
|
||||||
@ -147,9 +149,9 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
protected override bool HandleMouseTouchStateChange(TouchStateChangeEvent e)
|
protected override bool HandleMouseTouchStateChange(TouchStateChangeEvent e)
|
||||||
{
|
{
|
||||||
if (mouseDisabled.Value)
|
if (tapsDisabled.Value)
|
||||||
{
|
{
|
||||||
// Only propagate positional data when mouse buttons are disabled.
|
// Only propagate positional data when taps are disabled.
|
||||||
e = new TouchStateChangeEvent(e.State, e.Input, e.Touch, false, e.LastPosition);
|
e = new TouchStateChangeEvent(e.State, e.Input, e.Touch, false, e.LastPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,8 @@ namespace osu.Game.Screens
|
|||||||
{
|
{
|
||||||
public abstract partial class BackgroundScreen : Screen, IEquatable<BackgroundScreen>
|
public abstract partial class BackgroundScreen : Screen, IEquatable<BackgroundScreen>
|
||||||
{
|
{
|
||||||
protected const float TRANSITION_LENGTH = 500;
|
public const float TRANSITION_LENGTH = 500;
|
||||||
|
|
||||||
private const float x_movement_amount = 50;
|
private const float x_movement_amount = 50;
|
||||||
|
|
||||||
private readonly bool animateOnEnter;
|
private readonly bool animateOnEnter;
|
||||||
|
@ -3,11 +3,14 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
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.Logging;
|
using osu.Framework.Logging;
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -34,6 +37,9 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private IBindable<WorkingBeatmap> beatmap { get; set; }
|
private IBindable<WorkingBeatmap> beatmap { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private GameHost gameHost { get; set; }
|
||||||
|
|
||||||
protected virtual bool AllowStoryboardBackground => true;
|
protected virtual bool AllowStoryboardBackground => true;
|
||||||
|
|
||||||
public BackgroundScreenDefault(bool animateOnEnter = true)
|
public BackgroundScreenDefault(bool animateOnEnter = true)
|
||||||
@ -71,6 +77,34 @@ namespace osu.Game.Screens.Backgrounds
|
|||||||
void next() => Next();
|
void next() => Next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ScheduledDelegate storyboardUnloadDelegate;
|
||||||
|
|
||||||
|
public override void OnSuspending(ScreenTransitionEvent e)
|
||||||
|
{
|
||||||
|
var backgroundScreenStack = Parent as BackgroundScreenStack;
|
||||||
|
Debug.Assert(backgroundScreenStack != null);
|
||||||
|
|
||||||
|
if (background is BeatmapBackgroundWithStoryboard storyboardBackground)
|
||||||
|
storyboardUnloadDelegate = gameHost.UpdateThread.Scheduler.AddDelayed(storyboardBackground.UnloadStoryboard, TRANSITION_LENGTH);
|
||||||
|
|
||||||
|
base.OnSuspending(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnResuming(ScreenTransitionEvent e)
|
||||||
|
{
|
||||||
|
if (background is BeatmapBackgroundWithStoryboard storyboardBackground)
|
||||||
|
{
|
||||||
|
if (storyboardUnloadDelegate?.Completed == false)
|
||||||
|
storyboardUnloadDelegate.Cancel();
|
||||||
|
else
|
||||||
|
storyboardBackground.LoadStoryboard();
|
||||||
|
|
||||||
|
storyboardUnloadDelegate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnResuming(e);
|
||||||
|
}
|
||||||
|
|
||||||
private ScheduledDelegate nextTask;
|
private ScheduledDelegate nextTask;
|
||||||
private CancellationTokenSource cancellationTokenSource;
|
private CancellationTokenSource cancellationTokenSource;
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
private bool canScaleX;
|
private bool canScaleX;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether horizontal scaling support should be enabled.
|
/// Whether horizontal scaling (from the left or right edge) support should be enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CanScaleX
|
public bool CanScaleX
|
||||||
{
|
{
|
||||||
@ -77,7 +77,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
private bool canScaleY;
|
private bool canScaleY;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether vertical scaling support should be enabled.
|
/// Whether vertical scaling (from the top or bottom edge) support should be enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CanScaleY
|
public bool CanScaleY
|
||||||
{
|
{
|
||||||
@ -91,6 +91,27 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool canScaleDiagonally;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether diagonal scaling (from a corner) support should be enabled.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// There are some cases where we only want to allow proportional resizing, and not allow
|
||||||
|
/// one or both explicit directions of scale.
|
||||||
|
/// </remarks>
|
||||||
|
public bool CanScaleDiagonally
|
||||||
|
{
|
||||||
|
get => canScaleDiagonally;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (canScaleDiagonally == value) return;
|
||||||
|
|
||||||
|
canScaleDiagonally = value;
|
||||||
|
recreate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool canFlipX;
|
private bool canFlipX;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -245,7 +266,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (CanScaleX) addXScaleComponents();
|
if (CanScaleX) addXScaleComponents();
|
||||||
if (CanScaleX && CanScaleY) addFullScaleComponents();
|
if (CanScaleDiagonally) addFullScaleComponents();
|
||||||
if (CanScaleY) addYScaleComponents();
|
if (CanScaleY) addYScaleComponents();
|
||||||
if (CanFlipX) addXFlipComponents();
|
if (CanFlipX) addXFlipComponents();
|
||||||
if (CanFlipY) addYFlipComponents();
|
if (CanFlipY) addYFlipComponents();
|
||||||
|
@ -1095,6 +1095,19 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
protected void CreateNewDifficulty(RulesetInfo rulesetInfo)
|
protected void CreateNewDifficulty(RulesetInfo rulesetInfo)
|
||||||
{
|
{
|
||||||
|
if (isNewBeatmap)
|
||||||
|
{
|
||||||
|
dialogOverlay.Push(new SaveRequiredPopupDialog("This beatmap will be saved in order to create another difficulty.", () =>
|
||||||
|
{
|
||||||
|
if (!Save())
|
||||||
|
return;
|
||||||
|
|
||||||
|
CreateNewDifficulty(rulesetInfo);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!rulesetInfo.Equals(editorBeatmap.BeatmapInfo.Ruleset))
|
if (!rulesetInfo.Equals(editorBeatmap.BeatmapInfo.Ruleset))
|
||||||
{
|
{
|
||||||
switchToNewDifficulty(rulesetInfo, false);
|
switchToNewDifficulty(rulesetInfo, false);
|
||||||
|
@ -85,7 +85,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
private readonly List<MainMenuButton> buttonsTopLevel = new List<MainMenuButton>();
|
private readonly List<MainMenuButton> buttonsTopLevel = new List<MainMenuButton>();
|
||||||
private readonly List<MainMenuButton> buttonsPlay = new List<MainMenuButton>();
|
private readonly List<MainMenuButton> buttonsPlay = new List<MainMenuButton>();
|
||||||
|
|
||||||
private Sample sampleBack;
|
private Sample sampleBackToLogo;
|
||||||
|
private Sample sampleLogoSwoosh;
|
||||||
|
|
||||||
private readonly LogoTrackingContainer logoTrackingContainer;
|
private readonly LogoTrackingContainer logoTrackingContainer;
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
buttonArea.AddRange(new Drawable[]
|
buttonArea.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, FontAwesome.Solid.Cog, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O),
|
new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, FontAwesome.Solid.Cog, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O),
|
||||||
backButton = new MainMenuButton(ButtonSystemStrings.Back, @"button-back-select", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel,
|
backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel,
|
||||||
-WEDGE_WIDTH)
|
-WEDGE_WIDTH)
|
||||||
{
|
{
|
||||||
VisibleState = ButtonSystemState.Play,
|
VisibleState = ButtonSystemState.Play,
|
||||||
@ -127,14 +128,14 @@ namespace osu.Game.Screens.Menu
|
|||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(AudioManager audio, IdleTracker idleTracker, GameHost host)
|
private void load(AudioManager audio, IdleTracker idleTracker, GameHost host)
|
||||||
{
|
{
|
||||||
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-solo-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P));
|
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P));
|
||||||
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-generic-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M));
|
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M));
|
||||||
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-generic-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L));
|
buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L));
|
||||||
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
|
buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play);
|
||||||
|
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P));
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P));
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-edit-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E));
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-default-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E));
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-direct-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D));
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D));
|
||||||
|
|
||||||
if (host.CanExit)
|
if (host.CanExit)
|
||||||
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q));
|
buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q));
|
||||||
@ -155,7 +156,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle);
|
if (idleTracker != null) isIdle.BindTo(idleTracker.IsIdle);
|
||||||
|
|
||||||
sampleBack = audio.Samples.Get(@"Menu/button-back-select");
|
sampleBackToLogo = audio.Samples.Get(@"Menu/back-to-logo");
|
||||||
|
sampleLogoSwoosh = audio.Samples.Get(@"Menu/osu-logo-swoosh");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMultiplayer()
|
private void onMultiplayer()
|
||||||
@ -197,6 +199,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
if (State == ButtonSystemState.Initial)
|
if (State == ButtonSystemState.Initial)
|
||||||
{
|
{
|
||||||
|
StopSamplePlayback();
|
||||||
logo?.TriggerClick();
|
logo?.TriggerClick();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -260,10 +263,15 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
case ButtonSystemState.TopLevel:
|
case ButtonSystemState.TopLevel:
|
||||||
State = ButtonSystemState.Initial;
|
State = ButtonSystemState.Initial;
|
||||||
sampleBack?.Play();
|
|
||||||
|
// Samples are explicitly played here in response to user interaction and not when transitioning due to idle.
|
||||||
|
StopSamplePlayback();
|
||||||
|
sampleBackToLogo?.Play();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case ButtonSystemState.Play:
|
case ButtonSystemState.Play:
|
||||||
|
StopSamplePlayback();
|
||||||
backButton.TriggerClick();
|
backButton.TriggerClick();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -272,6 +280,13 @@ namespace osu.Game.Screens.Menu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void StopSamplePlayback()
|
||||||
|
{
|
||||||
|
buttonsPlay.ForEach(button => button.StopSamplePlayback());
|
||||||
|
buttonsTopLevel.ForEach(button => button.StopSamplePlayback());
|
||||||
|
logo?.StopSamplePlayback();
|
||||||
|
}
|
||||||
|
|
||||||
private bool onOsuLogo()
|
private bool onOsuLogo()
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
@ -346,6 +361,9 @@ namespace osu.Game.Screens.Menu
|
|||||||
logo?.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo);
|
logo?.MoveTo(new Vector2(0.5f), 800, Easing.OutExpo);
|
||||||
logo?.ScaleTo(1, 800, Easing.OutExpo);
|
logo?.ScaleTo(1, 800, Easing.OutExpo);
|
||||||
}, buttonArea.Alpha * 150);
|
}, buttonArea.Alpha * 150);
|
||||||
|
|
||||||
|
if (lastState == ButtonSystemState.TopLevel)
|
||||||
|
sampleLogoSwoosh?.Play();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ButtonSystemState.TopLevel:
|
case ButtonSystemState.TopLevel:
|
||||||
|
@ -7,6 +7,8 @@ using System;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Sample;
|
||||||
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;
|
||||||
@ -89,8 +91,10 @@ namespace osu.Game.Screens.Menu
|
|||||||
private SongTicker songTicker;
|
private SongTicker songTicker;
|
||||||
private Container logoTarget;
|
private Container logoTarget;
|
||||||
|
|
||||||
|
private Sample reappearSampleSwoosh;
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics)
|
private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics, AudioManager audio)
|
||||||
{
|
{
|
||||||
holdDelay = config.GetBindable<double>(OsuSetting.UIHoldActivationDelay);
|
holdDelay = config.GetBindable<double>(OsuSetting.UIHoldActivationDelay);
|
||||||
loginDisplayed = statics.GetBindable<bool>(Static.LoginOverlayDisplayed);
|
loginDisplayed = statics.GetBindable<bool>(Static.LoginOverlayDisplayed);
|
||||||
@ -162,6 +166,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
Buttons.OnSettings = () => settings?.ToggleVisibility();
|
Buttons.OnSettings = () => settings?.ToggleVisibility();
|
||||||
Buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility();
|
Buttons.OnBeatmapListing = () => beatmapListing?.ToggleVisibility();
|
||||||
|
|
||||||
|
reappearSampleSwoosh = audio.Samples.Get(@"Menu/reappear-swoosh");
|
||||||
|
|
||||||
preloadSongSelect();
|
preloadSongSelect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,6 +297,10 @@ namespace osu.Game.Screens.Menu
|
|||||||
{
|
{
|
||||||
base.OnResuming(e);
|
base.OnResuming(e);
|
||||||
|
|
||||||
|
// Ensures any playing `ButtonSystem` samples are stopped when returning to MainMenu (as to not overlap with the 'back' sample)
|
||||||
|
Buttons.StopSamplePlayback();
|
||||||
|
reappearSampleSwoosh?.Play();
|
||||||
|
|
||||||
ApplyToBackground(b => (b as BackgroundScreenDefault)?.Next());
|
ApplyToBackground(b => (b as BackgroundScreenDefault)?.Next());
|
||||||
|
|
||||||
// we may have consumed our preloaded instance, so let's make another.
|
// we may have consumed our preloaded instance, so let's make another.
|
||||||
|
@ -51,6 +51,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
private readonly Action clickAction;
|
private readonly Action clickAction;
|
||||||
private Sample sampleClick;
|
private Sample sampleClick;
|
||||||
private Sample sampleHover;
|
private Sample sampleHover;
|
||||||
|
private SampleChannel sampleChannel;
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
@ -225,7 +226,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
private void trigger()
|
private void trigger()
|
||||||
{
|
{
|
||||||
sampleClick?.Play();
|
sampleChannel = sampleClick?.GetChannel();
|
||||||
|
sampleChannel?.Play();
|
||||||
|
|
||||||
clickAction?.Invoke();
|
clickAction?.Invoke();
|
||||||
|
|
||||||
@ -237,6 +239,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
public override bool HandleNonPositionalInput => state == ButtonState.Expanded;
|
public override bool HandleNonPositionalInput => state == ButtonState.Expanded;
|
||||||
public override bool HandlePositionalInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f;
|
public override bool HandlePositionalInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f;
|
||||||
|
|
||||||
|
public void StopSamplePlayback() => sampleChannel?.Stop();
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
iconText.Alpha = Math.Clamp((box.Scale.X - 0.5f) / 0.3f, 0, 1);
|
iconText.Alpha = Math.Clamp((box.Scale.X - 0.5f) / 0.3f, 0, 1);
|
||||||
|
@ -52,6 +52,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
private readonly IntroSequence intro;
|
private readonly IntroSequence intro;
|
||||||
|
|
||||||
private Sample sampleClick;
|
private Sample sampleClick;
|
||||||
|
private SampleChannel sampleClickChannel;
|
||||||
|
|
||||||
private Sample sampleBeat;
|
private Sample sampleBeat;
|
||||||
private Sample sampleDownbeat;
|
private Sample sampleDownbeat;
|
||||||
|
|
||||||
@ -391,7 +393,11 @@ namespace osu.Game.Screens.Menu
|
|||||||
flashLayer.FadeOut(1500, Easing.OutExpo);
|
flashLayer.FadeOut(1500, Easing.OutExpo);
|
||||||
|
|
||||||
if (Action?.Invoke() == true)
|
if (Action?.Invoke() == true)
|
||||||
sampleClick.Play();
|
{
|
||||||
|
StopSamplePlayback();
|
||||||
|
sampleClickChannel = sampleClick.GetChannel();
|
||||||
|
sampleClickChannel.Play();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -440,6 +446,8 @@ namespace osu.Game.Screens.Menu
|
|||||||
private Container currentProxyTarget;
|
private Container currentProxyTarget;
|
||||||
private Drawable proxy;
|
private Drawable proxy;
|
||||||
|
|
||||||
|
public void StopSamplePlayback() => sampleClickChannel?.Stop();
|
||||||
|
|
||||||
public Drawable ProxyToContainer(Container c)
|
public Drawable ProxyToContainer(Container c)
|
||||||
{
|
{
|
||||||
if (currentProxyTarget != null)
|
if (currentProxyTarget != null)
|
||||||
|
@ -115,7 +115,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4Extensions.FromHex(@"27252d"),
|
Colour = Color4Extensions.FromHex(@"27252d"),
|
||||||
},
|
},
|
||||||
avatar = new UpdateableAvatar(showUsernameTooltip: true) { RelativeSizeAxes = Axes.Both },
|
avatar = new UpdateableAvatar(showUserPanelOnHover: true) { RelativeSizeAxes = Axes.Both },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,7 +289,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|||||||
set => avatar.User = value;
|
set => avatar.User = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly UpdateableAvatar avatar = new UpdateableAvatar(showUsernameTooltip: true) { RelativeSizeAxes = Axes.Both };
|
private readonly UpdateableAvatar avatar = new UpdateableAvatar(showUserPanelOnHover: true) { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OverlayColourProvider colours)
|
private void load(OverlayColourProvider colours)
|
||||||
|
@ -10,8 +10,6 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
public partial class ArgonKeyCounterDisplay : KeyCounterDisplay
|
public partial class ArgonKeyCounterDisplay : KeyCounterDisplay
|
||||||
{
|
{
|
||||||
private const int duration = 100;
|
|
||||||
|
|
||||||
protected override FillFlowContainer<KeyCounter> KeyFlow { get; }
|
protected override FillFlowContainer<KeyCounter> KeyFlow { get; }
|
||||||
|
|
||||||
public ArgonKeyCounterDisplay()
|
public ArgonKeyCounterDisplay()
|
||||||
@ -25,16 +23,6 @@ namespace osu.Game.Screens.Play
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
Size = KeyFlow.Size;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override KeyCounter CreateCounter(InputTrigger trigger) => new ArgonKeyCounter(trigger);
|
protected override KeyCounter CreateCounter(InputTrigger trigger) => new ArgonKeyCounter(trigger);
|
||||||
|
|
||||||
protected override void UpdateVisibility()
|
|
||||||
=> KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
110
osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs
Normal file
110
osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Localisation.SkinComponents;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play.HUD
|
||||||
|
{
|
||||||
|
public partial class ArgonAccuracyCounter : GameplayAccuracyCounter, ISerialisableDrawable
|
||||||
|
{
|
||||||
|
protected override double RollingDuration => 500;
|
||||||
|
protected override Easing RollingEasing => Easing.OutQuint;
|
||||||
|
|
||||||
|
[SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")]
|
||||||
|
public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f)
|
||||||
|
{
|
||||||
|
Precision = 0.01f,
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.ShowLabel), nameof(SkinnableComponentStrings.ShowLabelDescription))]
|
||||||
|
public Bindable<bool> ShowLabel { get; } = new BindableBool(true);
|
||||||
|
|
||||||
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
protected override IHasText CreateText() => new ArgonAccuracyTextComponent
|
||||||
|
{
|
||||||
|
WireframeOpacity = { BindTarget = WireframeOpacity },
|
||||||
|
ShowLabel = { BindTarget = ShowLabel },
|
||||||
|
};
|
||||||
|
|
||||||
|
private partial class ArgonAccuracyTextComponent : CompositeDrawable, IHasText
|
||||||
|
{
|
||||||
|
private readonly ArgonCounterTextComponent wholePart;
|
||||||
|
private readonly ArgonCounterTextComponent fractionPart;
|
||||||
|
private readonly ArgonCounterTextComponent percentText;
|
||||||
|
|
||||||
|
public IBindable<float> WireframeOpacity { get; } = new BindableFloat();
|
||||||
|
|
||||||
|
public Bindable<bool> ShowLabel { get; } = new BindableBool();
|
||||||
|
|
||||||
|
public LocalisableString Text
|
||||||
|
{
|
||||||
|
get => wholePart.Text;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string[] split = value.ToString().Replace("%", string.Empty).Split(".");
|
||||||
|
|
||||||
|
wholePart.Text = split[0];
|
||||||
|
fractionPart.Text = "." + split[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgonAccuracyTextComponent()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Child = wholePart = new ArgonCounterTextComponent(Anchor.TopRight, BeatmapsetsStrings.ShowScoreboardHeadersAccuracy.ToUpper())
|
||||||
|
{
|
||||||
|
RequiredDisplayDigits = { Value = 3 },
|
||||||
|
WireframeOpacity = { BindTarget = WireframeOpacity },
|
||||||
|
ShowLabel = { BindTarget = ShowLabel },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fractionPart = new ArgonCounterTextComponent(Anchor.TopLeft)
|
||||||
|
{
|
||||||
|
WireframeOpacity = { BindTarget = WireframeOpacity },
|
||||||
|
Scale = new Vector2(0.5f),
|
||||||
|
},
|
||||||
|
percentText = new ArgonCounterTextComponent(Anchor.TopLeft)
|
||||||
|
{
|
||||||
|
Text = @"%",
|
||||||
|
WireframeOpacity = { BindTarget = WireframeOpacity }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
ShowLabel.BindValueChanged(s =>
|
||||||
|
{
|
||||||
|
fractionPart.Margin = new MarginPadding { Top = s.NewValue ? 12f * 2f + 4f : 4f }; // +4 to account for the extra spaces above the digits.
|
||||||
|
percentText.Margin = new MarginPadding { Top = s.NewValue ? 12f : 0 };
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
osu.Game/Screens/Play/HUD/ArgonComboCounter.cs
Normal file
68
osu.Game/Screens/Play/HUD/ArgonComboCounter.cs
Normal file
@ -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;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Localisation.SkinComponents;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play.HUD
|
||||||
|
{
|
||||||
|
public partial class ArgonComboCounter : ComboCounter
|
||||||
|
{
|
||||||
|
private ArgonCounterTextComponent text = null!;
|
||||||
|
|
||||||
|
protected override double RollingDuration => 500;
|
||||||
|
protected override Easing RollingEasing => Easing.OutQuint;
|
||||||
|
|
||||||
|
[SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")]
|
||||||
|
public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f)
|
||||||
|
{
|
||||||
|
Precision = 0.01f,
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.ShowLabel), nameof(SkinnableComponentStrings.ShowLabelDescription))]
|
||||||
|
public Bindable<bool> ShowLabel { get; } = new BindableBool(true);
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ScoreProcessor scoreProcessor)
|
||||||
|
{
|
||||||
|
Current.BindTo(scoreProcessor.Combo);
|
||||||
|
Current.BindValueChanged(combo =>
|
||||||
|
{
|
||||||
|
bool wasIncrease = combo.NewValue > combo.OldValue;
|
||||||
|
bool wasMiss = combo.OldValue > 1 && combo.NewValue == 0;
|
||||||
|
|
||||||
|
float newScale = Math.Clamp(text.NumberContainer.Scale.X * (wasIncrease ? 1.1f : 0.8f), 0.6f, 1.4f);
|
||||||
|
|
||||||
|
float duration = wasMiss ? 2000 : 500;
|
||||||
|
|
||||||
|
text.NumberContainer
|
||||||
|
.ScaleTo(new Vector2(newScale))
|
||||||
|
.ScaleTo(Vector2.One, duration, Easing.OutQuint);
|
||||||
|
|
||||||
|
if (wasMiss)
|
||||||
|
text.FlashColour(Color4.Red, duration, Easing.OutQuint);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override LocalisableString FormatCount(int count) => $@"{count}x";
|
||||||
|
|
||||||
|
protected override IHasText CreateText() => text = new ArgonCounterTextComponent(Anchor.TopLeft, MatchesStrings.MatchScoreStatsCombo.ToUpper())
|
||||||
|
{
|
||||||
|
WireframeOpacity = { BindTarget = WireframeOpacity },
|
||||||
|
ShowLabel = { BindTarget = ShowLabel },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
173
osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs
Normal file
173
osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
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.Graphics.Textures;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Framework.Text;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play.HUD
|
||||||
|
{
|
||||||
|
public partial class ArgonCounterTextComponent : CompositeDrawable, IHasText
|
||||||
|
{
|
||||||
|
private readonly ArgonCounterSpriteText wireframesPart;
|
||||||
|
private readonly ArgonCounterSpriteText textPart;
|
||||||
|
private readonly OsuSpriteText labelText;
|
||||||
|
|
||||||
|
public IBindable<float> WireframeOpacity { get; } = new BindableFloat();
|
||||||
|
public Bindable<int> RequiredDisplayDigits { get; } = new BindableInt();
|
||||||
|
public Bindable<bool> ShowLabel { get; } = new BindableBool();
|
||||||
|
|
||||||
|
public Container NumberContainer { get; private set; }
|
||||||
|
|
||||||
|
public LocalisableString Text
|
||||||
|
{
|
||||||
|
get => textPart.Text;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
int remainingCount = RequiredDisplayDigits.Value - value.ToString().Count(char.IsDigit);
|
||||||
|
string remainingText = remainingCount > 0 ? new string('#', remainingCount) : string.Empty;
|
||||||
|
|
||||||
|
wireframesPart.Text = remainingText + value;
|
||||||
|
textPart.Text = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgonCounterTextComponent(Anchor anchor, LocalisableString? label = null)
|
||||||
|
{
|
||||||
|
Anchor = anchor;
|
||||||
|
Origin = anchor;
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
labelText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Alpha = 0,
|
||||||
|
Text = label.GetValueOrDefault(),
|
||||||
|
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold),
|
||||||
|
Margin = new MarginPadding { Left = 2.5f },
|
||||||
|
},
|
||||||
|
NumberContainer = new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
wireframesPart = new ArgonCounterSpriteText(wireframesLookup)
|
||||||
|
{
|
||||||
|
Anchor = anchor,
|
||||||
|
Origin = anchor,
|
||||||
|
},
|
||||||
|
textPart = new ArgonCounterSpriteText(textLookup)
|
||||||
|
{
|
||||||
|
Anchor = anchor,
|
||||||
|
Origin = anchor,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string textLookup(char c)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '.':
|
||||||
|
return @"dot";
|
||||||
|
|
||||||
|
case '%':
|
||||||
|
return @"percentage";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return c.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string wireframesLookup(char c)
|
||||||
|
{
|
||||||
|
if (c == '.') return @"dot";
|
||||||
|
|
||||||
|
return @"wireframes";
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
labelText.Colour = colours.Blue0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
WireframeOpacity.BindValueChanged(v => wireframesPart.Alpha = v.NewValue, true);
|
||||||
|
ShowLabel.BindValueChanged(s => labelText.Alpha = s.NewValue ? 1 : 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class ArgonCounterSpriteText : OsuSpriteText
|
||||||
|
{
|
||||||
|
private readonly Func<char, string> getLookup;
|
||||||
|
|
||||||
|
private GlyphStore glyphStore = null!;
|
||||||
|
|
||||||
|
protected override char FixedWidthReferenceCharacter => '5';
|
||||||
|
|
||||||
|
public ArgonCounterSpriteText(Func<char, string> getLookup)
|
||||||
|
{
|
||||||
|
this.getLookup = getLookup;
|
||||||
|
|
||||||
|
Shadow = false;
|
||||||
|
UseFullGlyphHeight = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(TextureStore textures)
|
||||||
|
{
|
||||||
|
Spacing = new Vector2(-2f, 0f);
|
||||||
|
Font = new FontUsage(@"argon-counter", 1);
|
||||||
|
glyphStore = new GlyphStore(textures, getLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore);
|
||||||
|
|
||||||
|
private class GlyphStore : ITexturedGlyphLookupStore
|
||||||
|
{
|
||||||
|
private readonly TextureStore textures;
|
||||||
|
private readonly Func<char, string> getLookup;
|
||||||
|
|
||||||
|
public GlyphStore(TextureStore textures, Func<char, string> getLookup)
|
||||||
|
{
|
||||||
|
this.textures = textures;
|
||||||
|
this.getLookup = getLookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITexturedCharacterGlyph? Get(string fontName, char character)
|
||||||
|
{
|
||||||
|
string lookup = getLookup(character);
|
||||||
|
var texture = textures.Get($"Gameplay/Fonts/{fontName}-{lookup}");
|
||||||
|
|
||||||
|
if (texture == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 0.125f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<ITexturedCharacterGlyph?> GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -35,13 +35,8 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
Precision = 1
|
Precision = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
[SettingSource("Bar length")]
|
[SettingSource("Use relative size")]
|
||||||
public BindableFloat BarLength { get; } = new BindableFloat(0.98f)
|
public BindableBool UseRelativeSize { get; } = new BindableBool(true);
|
||||||
{
|
|
||||||
MinValue = 0.2f,
|
|
||||||
MaxValue = 1,
|
|
||||||
Precision = 0.01f,
|
|
||||||
};
|
|
||||||
|
|
||||||
private BarPath mainBar = null!;
|
private BarPath mainBar = null!;
|
||||||
|
|
||||||
@ -92,12 +87,30 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const float main_path_radius = 10f;
|
public const float MAIN_PATH_RADIUS = 10f;
|
||||||
|
|
||||||
|
private const float curve_start_offset = 70;
|
||||||
|
private const float curve_end_offset = 40;
|
||||||
|
private const float padding = MAIN_PATH_RADIUS * 2;
|
||||||
|
private const float curve_smoothness = 10;
|
||||||
|
|
||||||
|
private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize);
|
||||||
|
|
||||||
|
public ArgonHealthDisplay()
|
||||||
|
{
|
||||||
|
AddLayout(drawSizeLayout);
|
||||||
|
|
||||||
|
// sane default width specification.
|
||||||
|
// this only matters if the health display isn't part of the default skin
|
||||||
|
// (in which case width will be set to 300 via `ArgonSkin.GetDrawableComponent()`),
|
||||||
|
// and if the user hasn't applied their own modifications
|
||||||
|
// (which are applied via `SerialisedDrawableInfo.ApplySerialisedInfo()`).
|
||||||
|
Width = 0.98f;
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
InternalChild = new Container
|
InternalChild = new Container
|
||||||
@ -107,7 +120,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
{
|
{
|
||||||
background = new BackgroundPath
|
background = new BackgroundPath
|
||||||
{
|
{
|
||||||
PathRadius = main_path_radius,
|
PathRadius = MAIN_PATH_RADIUS,
|
||||||
},
|
},
|
||||||
glowBar = new BarPath
|
glowBar = new BarPath
|
||||||
{
|
{
|
||||||
@ -127,7 +140,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
Blending = BlendingParameters.Additive,
|
Blending = BlendingParameters.Additive,
|
||||||
BarColour = main_bar_colour,
|
BarColour = main_bar_colour,
|
||||||
GlowColour = main_bar_glow_colour,
|
GlowColour = main_bar_glow_colour,
|
||||||
PathRadius = main_path_radius,
|
PathRadius = MAIN_PATH_RADIUS,
|
||||||
GlowPortion = 0.6f,
|
GlowPortion = 0.6f,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -140,17 +153,15 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
Current.BindValueChanged(_ => Scheduler.AddOnce(updateCurrent), true);
|
Current.BindValueChanged(_ => Scheduler.AddOnce(updateCurrent), true);
|
||||||
|
|
||||||
BarLength.BindValueChanged(l => Width = l.NewValue, true);
|
// we're about to set `RelativeSizeAxes` depending on the value of `UseRelativeSize`.
|
||||||
BarHeight.BindValueChanged(_ => updatePath());
|
// setting `RelativeSizeAxes` internally transforms absolute sizing to relative and back to keep the size the same,
|
||||||
updatePath();
|
// but that is not what we want in this case, since the width at this point is valid in the *target* sizing mode.
|
||||||
}
|
// to counteract this, store the numerical value here, and restore it after setting the correct initial relative sizing axes.
|
||||||
|
float previousWidth = Width;
|
||||||
|
UseRelativeSize.BindValueChanged(v => RelativeSizeAxes = v.NewValue ? Axes.X : Axes.None, true);
|
||||||
|
Width = previousWidth;
|
||||||
|
|
||||||
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
|
BarHeight.BindValueChanged(_ => updatePath(), true);
|
||||||
{
|
|
||||||
if ((invalidation & Invalidation.DrawSize) > 0)
|
|
||||||
updatePath();
|
|
||||||
|
|
||||||
return base.OnInvalidate(invalidation, source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateCurrent()
|
private void updateCurrent()
|
||||||
@ -168,6 +179,12 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
|
if (!drawSizeLayout.IsValid)
|
||||||
|
{
|
||||||
|
updatePath();
|
||||||
|
drawSizeLayout.Validate();
|
||||||
|
}
|
||||||
|
|
||||||
mainBar.Alpha = (float)Interpolation.DampContinuously(mainBar.Alpha, Current.Value > 0 ? 1 : 0, 40, Time.Elapsed);
|
mainBar.Alpha = (float)Interpolation.DampContinuously(mainBar.Alpha, Current.Value > 0 ? 1 : 0, 40, Time.Elapsed);
|
||||||
glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, GlowBarValue > 0 ? 1 : 0, 40, Time.Elapsed);
|
glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, GlowBarValue > 0 ? 1 : 0, 40, Time.Elapsed);
|
||||||
}
|
}
|
||||||
@ -236,11 +253,17 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
private void updatePath()
|
private void updatePath()
|
||||||
{
|
{
|
||||||
float barLength = DrawWidth - main_path_radius * 2;
|
float usableWidth = DrawWidth - padding;
|
||||||
float curveStart = barLength - 70;
|
|
||||||
float curveEnd = barLength - 40;
|
|
||||||
|
|
||||||
const float curve_smoothness = 10;
|
if (usableWidth < 0) enforceMinimumWidth();
|
||||||
|
|
||||||
|
// the display starts curving at `curve_start_offset` units from the right and ends curving at `curve_end_offset`.
|
||||||
|
// to ensure that the curve is symmetric when it starts being narrow enough, add a `curve_end_offset` to the left side too.
|
||||||
|
const float rescale_cutoff = curve_start_offset + curve_end_offset;
|
||||||
|
|
||||||
|
float barLength = Math.Max(DrawWidth - padding, rescale_cutoff);
|
||||||
|
float curveStart = barLength - curve_start_offset;
|
||||||
|
float curveEnd = barLength - curve_end_offset;
|
||||||
|
|
||||||
Vector2 diagonalDir = (new Vector2(curveEnd, BarHeight.Value) - new Vector2(curveStart, 0)).Normalized();
|
Vector2 diagonalDir = (new Vector2(curveEnd, BarHeight.Value) - new Vector2(curveStart, 0)).Normalized();
|
||||||
|
|
||||||
@ -256,6 +279,9 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
new PathControlPoint(new Vector2(barLength, BarHeight.Value)),
|
new PathControlPoint(new Vector2(barLength, BarHeight.Value)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (DrawWidth - padding < rescale_cutoff)
|
||||||
|
rescalePathProportionally();
|
||||||
|
|
||||||
List<Vector2> vertices = new List<Vector2>();
|
List<Vector2> vertices = new List<Vector2>();
|
||||||
barPath.GetPathToProgress(vertices, 0.0, 1.0);
|
barPath.GetPathToProgress(vertices, 0.0, 1.0);
|
||||||
|
|
||||||
@ -264,6 +290,24 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
glowBar.Vertices = vertices;
|
glowBar.Vertices = vertices;
|
||||||
|
|
||||||
updatePathVertices();
|
updatePathVertices();
|
||||||
|
|
||||||
|
void enforceMinimumWidth()
|
||||||
|
{
|
||||||
|
// Switch to absolute in order to be able to define a minimum width.
|
||||||
|
// Then switch back is required. Framework will handle the conversion for us.
|
||||||
|
Axes relativeAxes = RelativeSizeAxes;
|
||||||
|
RelativeSizeAxes = Axes.None;
|
||||||
|
|
||||||
|
Width = padding;
|
||||||
|
|
||||||
|
RelativeSizeAxes = relativeAxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rescalePathProportionally()
|
||||||
|
{
|
||||||
|
foreach (var point in barPath.ControlPoints)
|
||||||
|
point.Position = new Vector2(point.Position.X / barLength * (DrawWidth - padding), point.Position.Y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePathVertices()
|
private void updatePathVertices()
|
||||||
|
51
osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs
Normal file
51
osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Framework.Extensions.LocalisationExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Localisation.SkinComponents;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play.HUD
|
||||||
|
{
|
||||||
|
public partial class ArgonScoreCounter : GameplayScoreCounter, ISerialisableDrawable
|
||||||
|
{
|
||||||
|
protected override double RollingDuration => 500;
|
||||||
|
protected override Easing RollingEasing => Easing.OutQuint;
|
||||||
|
|
||||||
|
[SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")]
|
||||||
|
public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f)
|
||||||
|
{
|
||||||
|
Precision = 0.01f,
|
||||||
|
MinValue = 0,
|
||||||
|
MaxValue = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource(typeof(SkinnableComponentStrings), nameof(SkinnableComponentStrings.ShowLabel), nameof(SkinnableComponentStrings.ShowLabelDescription))]
|
||||||
|
public Bindable<bool> ShowLabel { get; } = new BindableBool(true);
|
||||||
|
|
||||||
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
protected override LocalisableString FormatCount(long count) => count.ToLocalisableString();
|
||||||
|
|
||||||
|
protected override IHasText CreateText() => new ArgonScoreTextComponent(Anchor.TopRight, BeatmapsetsStrings.ShowScoreboardHeadersScore.ToUpper())
|
||||||
|
{
|
||||||
|
RequiredDisplayDigits = { BindTarget = RequiredDisplayDigits },
|
||||||
|
WireframeOpacity = { BindTarget = WireframeOpacity },
|
||||||
|
ShowLabel = { BindTarget = ShowLabel },
|
||||||
|
};
|
||||||
|
|
||||||
|
private partial class ArgonScoreTextComponent : ArgonCounterTextComponent
|
||||||
|
{
|
||||||
|
public ArgonScoreTextComponent(Anchor anchor, LocalisableString? label = null)
|
||||||
|
: base(anchor, label)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
private readonly ArgonSongProgressGraph graph;
|
private readonly ArgonSongProgressGraph graph;
|
||||||
private readonly ArgonSongProgressBar bar;
|
private readonly ArgonSongProgressBar bar;
|
||||||
private readonly Container graphContainer;
|
private readonly Container graphContainer;
|
||||||
|
private readonly Container content;
|
||||||
|
|
||||||
private const float bar_height = 10;
|
private const float bar_height = 10;
|
||||||
|
|
||||||
@ -30,43 +31,50 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
public ArgonSongProgress()
|
public ArgonSongProgress()
|
||||||
{
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
Anchor = Anchor.BottomCentre;
|
Anchor = Anchor.BottomCentre;
|
||||||
Origin = Anchor.BottomCentre;
|
Origin = Anchor.BottomCentre;
|
||||||
Masking = true;
|
Masking = true;
|
||||||
CornerRadius = 5;
|
CornerRadius = 5;
|
||||||
Children = new Drawable[]
|
|
||||||
|
Child = content = new Container
|
||||||
{
|
{
|
||||||
info = new SongProgressInfo
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Origin = Anchor.TopLeft,
|
info = new SongProgressInfo
|
||||||
Name = "Info",
|
|
||||||
Anchor = Anchor.TopLeft,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
ShowProgress = false
|
|
||||||
},
|
|
||||||
bar = new ArgonSongProgressBar(bar_height)
|
|
||||||
{
|
|
||||||
Name = "Seek bar",
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
OnSeek = time => player?.Seek(time),
|
|
||||||
},
|
|
||||||
graphContainer = new Container
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
Masking = true,
|
|
||||||
CornerRadius = 5,
|
|
||||||
Child = graph = new ArgonSongProgressGraph
|
|
||||||
{
|
{
|
||||||
Name = "Difficulty graph",
|
Origin = Anchor.TopLeft,
|
||||||
RelativeSizeAxes = Axes.Both,
|
Name = "Info",
|
||||||
Blending = BlendingParameters.Additive
|
Anchor = Anchor.TopLeft,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
ShowProgress = false
|
||||||
},
|
},
|
||||||
RelativeSizeAxes = Axes.X,
|
bar = new ArgonSongProgressBar(bar_height)
|
||||||
},
|
{
|
||||||
|
Name = "Seek bar",
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
OnSeek = time => player?.Seek(time),
|
||||||
|
},
|
||||||
|
graphContainer = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 5,
|
||||||
|
Child = graph = new ArgonSongProgressGraph
|
||||||
|
{
|
||||||
|
Name = "Difficulty graph",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Blending = BlendingParameters.Additive
|
||||||
|
},
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -100,7 +108,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
Height = bar.Height + bar_height + info.Height;
|
content.Height = bar.Height + bar_height + info.Height;
|
||||||
graphContainer.Height = bar.Height;
|
graphContainer.Height = bar.Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
51
osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs
Normal file
51
osu.Game/Screens/Play/HUD/ArgonWedgePiece.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play.HUD
|
||||||
|
{
|
||||||
|
public partial class ArgonWedgePiece : CompositeDrawable, ISerialisableDrawable
|
||||||
|
{
|
||||||
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
[SettingSource("Inverted shear")]
|
||||||
|
public BindableBool InvertShear { get; } = new BindableBool();
|
||||||
|
|
||||||
|
public ArgonWedgePiece()
|
||||||
|
{
|
||||||
|
CornerRadius = 10f;
|
||||||
|
|
||||||
|
Size = new Vector2(400, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
Masking = true;
|
||||||
|
Shear = new Vector2(0.8f, 0f);
|
||||||
|
|
||||||
|
InternalChild = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#66CCFF").Opacity(0.0f), Color4Extensions.FromHex("#66CCFF").Opacity(0.25f)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
InvertShear.BindValueChanged(v => Shear = new Vector2(0.8f, 0f) * (v.NewValue ? -1 : 1), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,6 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
{
|
{
|
||||||
public partial class DefaultKeyCounterDisplay : KeyCounterDisplay
|
public partial class DefaultKeyCounterDisplay : KeyCounterDisplay
|
||||||
{
|
{
|
||||||
private const int duration = 100;
|
|
||||||
private const double key_fade_time = 80;
|
private const double key_fade_time = 80;
|
||||||
|
|
||||||
protected override FillFlowContainer<KeyCounter> KeyFlow { get; }
|
protected override FillFlowContainer<KeyCounter> KeyFlow { get; }
|
||||||
@ -25,15 +24,6 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
// Don't use autosize as it will shrink to zero when KeyFlow is hidden.
|
|
||||||
// In turn this can cause the display to be masked off screen and never become visible again.
|
|
||||||
Size = KeyFlow.Size;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override KeyCounter CreateCounter(InputTrigger trigger) => new DefaultKeyCounter(trigger)
|
protected override KeyCounter CreateCounter(InputTrigger trigger) => new DefaultKeyCounter(trigger)
|
||||||
{
|
{
|
||||||
FadeTime = key_fade_time,
|
FadeTime = key_fade_time,
|
||||||
@ -41,10 +31,6 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
KeyUpTextColor = KeyUpTextColor,
|
KeyUpTextColor = KeyUpTextColor,
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override void UpdateVisibility() =>
|
|
||||||
// Isolate changing visibility of the key counters from fading this component.
|
|
||||||
KeyFlow.FadeTo(AlwaysVisible.Value || ConfigVisibility.Value ? 1 : 0, duration);
|
|
||||||
|
|
||||||
private Color4 keyDownTextColor = Color4.DarkGray;
|
private Color4 keyDownTextColor = Color4.DarkGray;
|
||||||
|
|
||||||
public Color4 KeyDownTextColor
|
public Color4 KeyDownTextColor
|
||||||
|
@ -5,6 +5,7 @@ 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.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -27,6 +28,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
private readonly DefaultSongProgressBar bar;
|
private readonly DefaultSongProgressBar bar;
|
||||||
private readonly DefaultSongProgressGraph graph;
|
private readonly DefaultSongProgressGraph graph;
|
||||||
private readonly SongProgressInfo info;
|
private readonly SongProgressInfo info;
|
||||||
|
private readonly Container content;
|
||||||
|
|
||||||
[SettingSource(typeof(SongProgressStrings), nameof(SongProgressStrings.ShowGraph), nameof(SongProgressStrings.ShowGraphDescription))]
|
[SettingSource(typeof(SongProgressStrings), nameof(SongProgressStrings.ShowGraph), nameof(SongProgressStrings.ShowGraphDescription))]
|
||||||
public Bindable<bool> ShowGraph { get; } = new BindableBool(true);
|
public Bindable<bool> ShowGraph { get; } = new BindableBool(true);
|
||||||
@ -37,31 +39,36 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
public DefaultSongProgress()
|
public DefaultSongProgress()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
Anchor = Anchor.BottomRight;
|
Anchor = Anchor.BottomRight;
|
||||||
Origin = Anchor.BottomRight;
|
Origin = Anchor.BottomRight;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Child = content = new Container
|
||||||
{
|
{
|
||||||
info = new SongProgressInfo
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Origin = Anchor.BottomLeft,
|
info = new SongProgressInfo
|
||||||
Anchor = Anchor.BottomLeft,
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
Origin = Anchor.BottomLeft,
|
||||||
},
|
Anchor = Anchor.BottomLeft,
|
||||||
graph = new DefaultSongProgressGraph
|
RelativeSizeAxes = Axes.X,
|
||||||
{
|
},
|
||||||
RelativeSizeAxes = Axes.X,
|
graph = new DefaultSongProgressGraph
|
||||||
Origin = Anchor.BottomLeft,
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = graph_height,
|
Origin = Anchor.BottomLeft,
|
||||||
Margin = new MarginPadding { Bottom = bottom_bar_height },
|
Anchor = Anchor.BottomLeft,
|
||||||
},
|
Height = graph_height,
|
||||||
bar = new DefaultSongProgressBar(bottom_bar_height, graph_height, handle_size)
|
Margin = new MarginPadding { Bottom = bottom_bar_height },
|
||||||
{
|
},
|
||||||
Anchor = Anchor.BottomLeft,
|
bar = new DefaultSongProgressBar(bottom_bar_height, graph_height, handle_size)
|
||||||
Origin = Anchor.BottomLeft,
|
{
|
||||||
OnSeek = time => player?.Seek(time),
|
Anchor = Anchor.BottomLeft,
|
||||||
},
|
Origin = Anchor.BottomLeft,
|
||||||
|
OnSeek = time => player?.Seek(time),
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +114,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
float newHeight = bottom_bar_height + graph_height + handle_size.Y + info.Height - graph.Y;
|
float newHeight = bottom_bar_height + graph_height + handle_size.Y + info.Height - graph.Y;
|
||||||
|
|
||||||
if (!Precision.AlmostEquals(Height, newHeight, 5f))
|
if (!Precision.AlmostEquals(Height, newHeight, 5f))
|
||||||
Height = newHeight;
|
content.Height = newHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBarVisibility()
|
private void updateBarVisibility()
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user