1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 04:13:00 +08:00

Merge branch 'ppy:master' into Liswiera-FL-changes

This commit is contained in:
MK56 2022-01-17 15:19:29 +01:00 committed by GitHub
commit 31592e2995
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 340 additions and 155 deletions
osu.Desktop
osu.Game.Rulesets.Mania.Tests
Resources/special-skin
Skinning
osu.Game.Rulesets.Taiko.Tests
osu.Game.Rulesets.Taiko/Objects/Drawables
osu.Game.Tests/Visual
osu.Game
OsuGame.cs
Overlays
BeatmapSet/Scores
Settings/Sections/Input
VersionManager.cs
Skinning

View File

@ -10,14 +10,11 @@ using System.Runtime.Versioning;
using System.Threading.Tasks;
using Microsoft.Win32;
using osu.Desktop.Security;
using osu.Desktop.Overlays;
using osu.Framework.Platform;
using osu.Game;
using osu.Desktop.Updater;
using osu.Framework;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Game.Screens.Menu;
using osu.Game.Updater;
using osu.Desktop.Windows;
using osu.Framework.Threading;
@ -27,13 +24,9 @@ namespace osu.Desktop
{
internal class OsuGameDesktop : OsuGame
{
private readonly bool noVersionOverlay;
private VersionManager versionManager;
public OsuGameDesktop(string[] args = null)
: base(args)
{
noVersionOverlay = args?.Any(a => a == "--no-version-overlay") ?? false;
}
public override StableStorage GetStorageForStableInstall()
@ -114,9 +107,6 @@ namespace osu.Desktop
{
base.LoadComplete();
if (!noVersionOverlay)
LoadComponentAsync(versionManager = new VersionManager { Depth = int.MinValue }, ScreenContainer.Add);
LoadComponentAsync(new DiscordRichPresence(), Add);
if (RuntimeInfo.OS == RuntimeInfo.Platform.Windows)
@ -125,23 +115,6 @@ namespace osu.Desktop
LoadComponentAsync(new ElevatedPrivilegesChecker(), Add);
}
protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)
{
base.ScreenChanged(lastScreen, newScreen);
switch (newScreen)
{
case IntroScreen _:
case MainMenu _:
versionManager?.Show();
break;
default:
versionManager?.Hide();
break;
}
}
public override void SetHost(GameHost host)
{
base.SetHost(host);

View File

@ -4,11 +4,14 @@ Version: 2.5
[Mania]
Keys: 4
ColumnLineWidth: 3,1,3,1,1
Hit0: mania/hit0
Hit50: mania/hit50
Hit100: mania/hit100
Hit200: mania/hit200
Hit300: mania/hit300
Hit300g: mania/hit300g
// some skins found in the wild had configuration keys where the @2x suffix was included in the values.
// the expected compatibility behaviour is that the presence of the @2x suffix shouldn't change anything
// if @2x assets are present.
Hit0: mania/hit0@2x
Hit50: mania/hit50@2x
Hit100: mania/hit100@2x
Hit200: mania/hit200@2x
Hit300: mania/hit300@2x
Hit300g: mania/hit300g@2x
StageLeft: mania/stage-left
StageRight: mania/stage-right

View File

@ -5,8 +5,10 @@ using System;
using System.Linq;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Mania.Skinning.Legacy;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
@ -23,7 +25,9 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{
if (hitWindows.IsHitResultAllowed(result))
{
AddStep("Show " + result.GetDescription(), () => SetContents(_ =>
AddStep("Show " + result.GetDescription(), () =>
{
SetContents(_ =>
new DrawableManiaJudgement(new JudgementResult(new HitObject { StartTime = Time.Current }, new Judgement())
{
Type = result
@ -31,7 +35,14 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}));
});
// for test purposes, undo the Y adjustment related to the `ScorePosition` legacy positioning config value
// (see `LegacyManiaJudgementPiece.load()`).
// this prevents the judgements showing somewhere below or above the bounding box of the judgement.
foreach (var legacyPiece in this.ChildrenOfType<LegacyManiaJudgementPiece>())
legacyPiece.Y = 0;
});
}
}
}

View File

@ -0,0 +1,36 @@
// 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.Game.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.Tests
{
public class TestSceneDrumRollJudgements : TestSceneTaikoPlayer
{
[Test]
public void TestStrongDrumRollFullyJudgedOnKilled()
{
AddUntilStep("gameplay finished", () => Player.ScoreProcessor.HasCompleted.Value);
AddAssert("all judgements are misses", () => Player.Results.All(r => r.Type == r.Judgement.MinResult));
}
protected override bool Autoplay => false;
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap<TaikoHitObject>
{
BeatmapInfo = { Ruleset = new TaikoRuleset().RulesetInfo },
HitObjects =
{
new DrumRoll
{
StartTime = 1000,
Duration = 1000,
IsStrong = true
}
}
};
}
}

View File

@ -197,6 +197,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
}
public override void OnKilled()
{
base.OnKilled();
if (Time.Current > ParentHitObject.HitObject.GetEndTime() && !Judged)
ApplyResult(r => r.Type = r.Judgement.MinResult);
}
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
}
}

View File

@ -5,6 +5,7 @@ using System;
using JetBrains.Annotations;
using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Skinning.Default;
using osu.Game.Skinning;
@ -52,6 +53,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
ApplyResult(r => r.Type = r.Judgement.MaxResult);
}
public override void OnKilled()
{
base.OnKilled();
if (Time.Current > HitObject.GetEndTime() && !Judged)
ApplyResult(r => r.Type = r.Judgement.MinResult);
}
protected override void UpdateHitStateTransforms(ArmedState state)
{
switch (state)
@ -92,6 +101,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
ApplyResult(r => r.Type = ParentHitObject.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult);
}
public override void OnKilled()
{
base.OnKilled();
if (Time.Current > ParentHitObject.HitObject.GetEndTime() && !Judged)
ApplyResult(r => r.Type = r.Judgement.MinResult);
}
public override bool OnPressed(KeyBindingPressEvent<TaikoAction> e) => false;
}
}

View File

@ -0,0 +1,88 @@
// 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.Extensions;
using osu.Framework.Input.Bindings;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings;
using osu.Game.Overlays.Settings.Sections.Input;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Tests.Beatmaps.IO;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Navigation
{
public class TestSceneChangeAndUseGameplayBindings : OsuGameTestScene
{
[Test]
public void TestGameplayKeyBindings()
{
AddAssert("databased key is default", () => firstOsuRulesetKeyBindings.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Z }));
AddStep("open settings", () => { Game.Settings.Show(); });
// Until step requires as settings has a delayed load.
AddUntilStep("wait for button", () => configureBindingsButton?.Enabled.Value == true);
AddStep("scroll to section", () => Game.Settings.SectionsContainer.ScrollTo(configureBindingsButton));
AddStep("press button", () => configureBindingsButton.TriggerClick());
AddUntilStep("wait for panel", () => keyBindingPanel?.IsLoaded == true);
AddUntilStep("wait for osu subsection", () => osuBindingSubsection?.IsLoaded == true);
AddStep("scroll to section", () => keyBindingPanel.SectionsContainer.ScrollTo(osuBindingSubsection));
AddWaitStep("wait for scroll to end", 3);
AddStep("start rebinding first osu! key", () =>
{
var button = osuBindingSubsection.ChildrenOfType<KeyBindingRow>().First();
InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});
AddStep("Press 's'", () => InputManager.Key(Key.S));
AddUntilStep("wait for database updated", () => firstOsuRulesetKeyBindings.KeyCombination.Keys.SequenceEqual(new[] { InputKey.S }));
AddStep("close settings", () => Game.Settings.Hide());
AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).WaitSafely());
PushAndConfirm(() => new PlaySongSelect());
AddStep("enter gameplay", () => InputManager.Key(Key.Enter));
AddUntilStep("wait for gameplay", () => player?.IsBreakTime.Value == false);
AddStep("press 'z'", () => InputManager.Key(Key.Z));
AddAssert("key counter didn't increase", () => keyCounter.CountPresses == 0);
AddStep("press 's'", () => InputManager.Key(Key.S));
AddAssert("key counter did increase", () => keyCounter.CountPresses == 1);
}
private KeyBindingsSubsection osuBindingSubsection => keyBindingPanel
.ChildrenOfType<VariantBindingsSubsection>()
.FirstOrDefault(s => s.Ruleset.ShortName == "osu");
private OsuButton configureBindingsButton => Game.Settings
.ChildrenOfType<BindingSettings>().SingleOrDefault()?
.ChildrenOfType<OsuButton>()?
.First(b => b.Text.ToString() == "Configure");
private KeyBindingPanel keyBindingPanel => Game.Settings
.ChildrenOfType<KeyBindingPanel>().SingleOrDefault();
private RealmKeyBinding firstOsuRulesetKeyBindings => Game.Dependencies
.Get<RealmContextFactory>().Context
.All<RealmKeyBinding>()
.AsEnumerable()
.First(k => k.RulesetName == "osu" && k.ActionInt == 0);
private Player player => Game.ScreenStack.CurrentScreen as Player;
private KeyCounter keyCounter => player.ChildrenOfType<KeyCounter>().First();
}
}

View File

@ -1,11 +1,15 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing;
using osu.Framework.Utils;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
@ -24,10 +28,11 @@ namespace osu.Game.Tests.Visual.Online
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
public TestSceneScoresContainer()
{
TestScoresContainer scoresContainer;
private TestScoresContainer scoresContainer;
[SetUpSteps]
public void SetUp() => Schedule(() =>
{
Child = new Container
{
Anchor = Anchor.TopCentre,
@ -41,16 +46,110 @@ namespace osu.Game.Tests.Visual.Online
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
scoresContainer = new TestScoresContainer(),
scoresContainer = new TestScoresContainer
{
Beatmap = { Value = CreateAPIBeatmap() }
}
}
};
});
var allScores = new APIScoresCollection
[Test]
public void TestNoUserBest()
{
AddStep("Scores with no user best", () =>
{
var allScores = createScores();
allScores.UserScore = null;
scoresContainer.Scores = allScores;
});
AddUntilStep("wait for scores displayed", () => scoresContainer.ChildrenOfType<ScoreTableRowBackground>().Any());
AddAssert("no user best displayed", () => scoresContainer.ChildrenOfType<DrawableTopScore>().Count() == 1);
AddStep("Load null scores", () => scoresContainer.Scores = null);
AddUntilStep("wait for scores not displayed", () => !scoresContainer.ChildrenOfType<ScoreTableRowBackground>().Any());
AddAssert("no best score displayed", () => !scoresContainer.ChildrenOfType<DrawableTopScore>().Any());
AddStep("Load only one score", () =>
{
var allScores = createScores();
allScores.Scores.RemoveRange(1, allScores.Scores.Count - 1);
scoresContainer.Scores = allScores;
});
AddUntilStep("wait for scores not displayed", () => scoresContainer.ChildrenOfType<ScoreTableRowBackground>().Count() == 1);
AddAssert("no best score displayed", () => scoresContainer.ChildrenOfType<DrawableTopScore>().Count() == 1);
}
[Test]
public void TestUserBest()
{
AddStep("Load scores with personal best", () =>
{
var allScores = createScores();
allScores.UserScore = createUserBest();
scoresContainer.Scores = allScores;
});
AddUntilStep("wait for scores displayed", () => scoresContainer.ChildrenOfType<ScoreTableRowBackground>().Any());
AddAssert("best score displayed", () => scoresContainer.ChildrenOfType<DrawableTopScore>().Count() == 2);
AddStep("Load scores with personal best (null position)", () =>
{
var allScores = createScores();
var userBest = createUserBest();
userBest.Position = null;
allScores.UserScore = userBest;
scoresContainer.Scores = allScores;
});
AddUntilStep("wait for scores displayed", () => scoresContainer.ChildrenOfType<ScoreTableRowBackground>().Any());
AddAssert("best score displayed", () => scoresContainer.ChildrenOfType<DrawableTopScore>().Count() == 2);
AddStep("Load scores with personal best (first place)", () =>
{
var allScores = createScores();
allScores.UserScore = new APIScoreWithPosition
{
Score = allScores.Scores.First(),
Position = 1,
};
scoresContainer.Scores = allScores;
});
AddUntilStep("wait for scores displayed", () => scoresContainer.ChildrenOfType<ScoreTableRowBackground>().Any());
AddAssert("best score displayed", () => scoresContainer.ChildrenOfType<DrawableTopScore>().Count() == 1);
AddStep("Scores with no user best", () =>
{
var allScores = createScores();
allScores.UserScore = null;
scoresContainer.Scores = allScores;
});
AddUntilStep("best score not displayed", () => scoresContainer.ChildrenOfType<DrawableTopScore>().Count() == 1);
}
private int onlineID = 1;
private APIScoresCollection createScores()
{
var scores = new APIScoresCollection
{
Scores = new List<APIScore>
{
new APIScore
{
Date = DateTimeOffset.Now,
OnlineID = onlineID++,
User = new APIUser
{
Id = 6602580,
@ -76,6 +175,8 @@ namespace osu.Game.Tests.Visual.Online
},
new APIScore
{
Date = DateTimeOffset.Now,
OnlineID = onlineID++,
User = new APIUser
{
Id = 4608074,
@ -100,6 +201,8 @@ namespace osu.Game.Tests.Visual.Online
},
new APIScore
{
Date = DateTimeOffset.Now,
OnlineID = onlineID++,
User = new APIUser
{
Id = 1014222,
@ -123,6 +226,8 @@ namespace osu.Game.Tests.Visual.Online
},
new APIScore
{
Date = DateTimeOffset.Now,
OnlineID = onlineID++,
User = new APIUser
{
Id = 1541390,
@ -145,6 +250,8 @@ namespace osu.Game.Tests.Visual.Online
},
new APIScore
{
Date = DateTimeOffset.Now,
OnlineID = onlineID++,
User = new APIUser
{
Id = 7151382,
@ -164,10 +271,26 @@ namespace osu.Game.Tests.Visual.Online
}
};
var myBestScore = new APIScoreWithPosition
foreach (var s in scores.Scores)
{
s.Statistics = new Dictionary<string, int>
{
{ "count_300", RNG.Next(2000) },
{ "count_100", RNG.Next(2000) },
{ "count_50", RNG.Next(2000) },
{ "count_miss", RNG.Next(2000) }
};
}
return scores;
}
private APIScoreWithPosition createUserBest() => new APIScoreWithPosition
{
Score = new APIScore
{
Date = DateTimeOffset.Now,
OnlineID = onlineID++,
User = new APIUser
{
Id = 7151382,
@ -187,92 +310,6 @@ namespace osu.Game.Tests.Visual.Online
Position = 1337,
};
var myBestScoreWithNullPosition = new APIScoreWithPosition
{
Score = new APIScore
{
User = new APIUser
{
Id = 7151382,
Username = @"Mayuri Hana",
Country = new Country
{
FullName = @"Thailand",
FlagName = @"TH",
},
},
Rank = ScoreRank.D,
PP = 160,
MaxCombo = 1234,
TotalScore = 123456,
Accuracy = 0.6543,
},
Position = null,
};
var oneScore = new APIScoresCollection
{
Scores = new List<APIScore>
{
new APIScore
{
User = new APIUser
{
Id = 6602580,
Username = @"waaiiru",
Country = new Country
{
FullName = @"Spain",
FlagName = @"ES",
},
},
Mods = new[]
{
new APIMod { Acronym = new OsuModDoubleTime().Acronym },
new APIMod { Acronym = new OsuModHidden().Acronym },
new APIMod { Acronym = new OsuModFlashlight().Acronym },
new APIMod { Acronym = new OsuModHardRock().Acronym },
},
Rank = ScoreRank.XH,
PP = 200,
MaxCombo = 1234,
TotalScore = 1234567890,
Accuracy = 1,
}
}
};
foreach (var s in allScores.Scores)
{
s.Statistics = new Dictionary<string, int>
{
{ "count_300", RNG.Next(2000) },
{ "count_100", RNG.Next(2000) },
{ "count_50", RNG.Next(2000) },
{ "count_miss", RNG.Next(2000) }
};
}
AddStep("Load all scores", () =>
{
allScores.UserScore = null;
scoresContainer.Scores = allScores;
});
AddStep("Load null scores", () => scoresContainer.Scores = null);
AddStep("Load only one score", () => scoresContainer.Scores = oneScore);
AddStep("Load scores with my best", () =>
{
allScores.UserScore = myBestScore;
scoresContainer.Scores = allScores;
});
AddStep("Load scores with null my best position", () =>
{
allScores.UserScore = myBestScoreWithNullPosition;
scoresContainer.Scores = allScores;
});
}
private class TestScoresContainer : ScoresContainer
{
public new APIScoresCollection Scores

View File

@ -154,6 +154,8 @@ namespace osu.Game
private MainMenu menuScreen;
private VersionManager versionManager;
[CanBeNull]
private IntroScreen introScreen;
@ -743,6 +745,9 @@ namespace osu.Game
ScreenStack.ScreenPushed += screenPushed;
ScreenStack.ScreenExited += screenExited;
if (!args?.Any(a => a == @"--no-version-overlay") ?? true)
loadComponentSingleFile(versionManager = new VersionManager { Depth = int.MinValue }, ScreenContainer.Add);
loadComponentSingleFile(osuLogo, logo =>
{
logoContainer.Add(logo);
@ -1126,10 +1131,16 @@ namespace osu.Game
{
case IntroScreen intro:
introScreen = intro;
versionManager?.Show();
break;
case MainMenu menu:
menuScreen = menu;
versionManager?.Show();
break;
default:
versionManager?.Hide();
break;
}

View File

@ -65,6 +65,9 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
scoreTable.ClearScores();
scoreTable.Hide();
loading.Hide();
loading.FinishTransforms();
if (value?.Scores.Any() != true)
return;
@ -258,9 +261,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
{
Scores = null;
notSupporterPlaceholder.Show();
loading.Hide();
loading.FinishTransforms();
return;
}
@ -272,9 +272,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
getScoresRequest = new GetScoresRequest(Beatmap.Value, Beatmap.Value.Ruleset, scope.Value, modSelector.SelectedMods);
getScoresRequest.Success += scores =>
{
loading.Hide();
loading.FinishTransforms();
Scores = scores;
if (!scores.Scores.Any())

View File

@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
protected IEnumerable<Framework.Input.Bindings.KeyBinding> Defaults;
protected RulesetInfo Ruleset;
public RulesetInfo Ruleset { get; protected set; }
private readonly int? variant;

View File

@ -7,13 +7,12 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osuTK;
using osuTK.Graphics;
namespace osu.Desktop.Overlays
namespace osu.Game.Overlays
{
public class VersionManager : VisibilityContainer
{

View File

@ -474,13 +474,18 @@ namespace osu.Game.Skinning
{
foreach (string name in getFallbackNames(componentName))
{
// some component names (especially user-controlled ones, like `HitX` in mania)
// may contain `@2x` scale specifications.
// stable happens to check for that and strip them, so do the same to match stable behaviour.
string lookupName = name.Replace(@"@2x", string.Empty);
float ratio = 2;
var texture = Textures?.Get($"{name}@2x", wrapModeS, wrapModeT);
var texture = Textures?.Get(@$"{lookupName}@2x", wrapModeS, wrapModeT);
if (texture == null)
{
ratio = 1;
texture = Textures?.Get(name, wrapModeS, wrapModeT);
texture = Textures?.Get(lookupName, wrapModeS, wrapModeT);
}
if (texture == null)