mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 10:07:52 +08:00
Merge branch 'master' into results-screen-sfx
This commit is contained in:
commit
8fda04c2d9
@ -52,6 +52,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.604.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.607.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.608.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -4,10 +4,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Catch.Mods;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
@ -21,12 +19,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public class TestSceneCatchModHidden : ModTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
LocalConfig.SetValue(OsuSetting.IncreaseFirstObjectVisibility, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestJuiceStream()
|
||||
{
|
||||
|
@ -29,8 +29,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
}
|
||||
|
||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
}
|
||||
=> ApplyNormalVisibilityState(hitObject, state);
|
||||
|
||||
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
{
|
||||
|
@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
{
|
||||
isLegacySkin = new Lazy<bool>(() => FindProvider(s => s.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null) != null);
|
||||
hasKeyTexture = new Lazy<bool>(() => FindProvider(s => s.GetAnimation(
|
||||
this.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value
|
||||
s.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.KeyImage, 0)?.Value
|
||||
?? "mania-key1", true, true) != null) != null);
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Utils;
|
||||
@ -30,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
private static readonly float border_distance_x = OsuPlayfield.BASE_SIZE.X * playfield_edge_ratio;
|
||||
private static readonly float border_distance_y = OsuPlayfield.BASE_SIZE.Y * playfield_edge_ratio;
|
||||
|
||||
private static readonly Vector2 playfield_middle = Vector2.Divide(OsuPlayfield.BASE_SIZE, 2);
|
||||
private static readonly Vector2 playfield_middle = OsuPlayfield.BASE_SIZE / 2;
|
||||
|
||||
private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast;
|
||||
|
||||
@ -74,22 +75,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
// update end position as it may have changed as a result of the position update.
|
||||
current.EndPositionRandomised = current.PositionRandomised;
|
||||
|
||||
switch (hitObject)
|
||||
{
|
||||
case Slider slider:
|
||||
// Shift nested objects the same distance as the slider got shifted in the randomisation process
|
||||
// so that moveSliderIntoPlayfield() can determine their relative distances to slider.Position and thus minMargin
|
||||
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, current.PositionOriginal));
|
||||
|
||||
var oldPos = new Vector2(slider.Position.X, slider.Position.Y);
|
||||
|
||||
moveSliderIntoPlayfield(slider, current);
|
||||
|
||||
// Shift them again to move them to their final position after the slider got moved into the playfield
|
||||
shiftNestedObjects(slider, Vector2.Subtract(slider.Position, oldPos));
|
||||
|
||||
break;
|
||||
}
|
||||
if (hitObject is Slider slider)
|
||||
moveSliderIntoPlayfield(slider, current);
|
||||
|
||||
previous = current;
|
||||
}
|
||||
@ -131,7 +118,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X);
|
||||
|
||||
var position = Vector2.Add(previous.EndPositionRandomised, posRelativeToPrev);
|
||||
var position = previous.EndPositionRandomised + posRelativeToPrev;
|
||||
|
||||
// Move hit objects back into the playfield if they are outside of it,
|
||||
// which would sometimes happen during big jumps otherwise.
|
||||
@ -146,34 +133,41 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
/// </summary>
|
||||
private void moveSliderIntoPlayfield(Slider slider, RandomObjectInfo currentObjectInfo)
|
||||
{
|
||||
// Min. distances from the slider's position to the playfield border
|
||||
var minMargin = new MarginPadding();
|
||||
var minMargin = getMinSliderMargin(slider);
|
||||
|
||||
foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderEndCircle))
|
||||
{
|
||||
if (!(hitObject is OsuHitObject osuHitObject))
|
||||
continue;
|
||||
|
||||
var relativePos = Vector2.Subtract(osuHitObject.Position, slider.Position);
|
||||
|
||||
minMargin.Left = Math.Max(minMargin.Left, -relativePos.X);
|
||||
minMargin.Right = Math.Max(minMargin.Right, relativePos.X);
|
||||
minMargin.Top = Math.Max(minMargin.Top, -relativePos.Y);
|
||||
minMargin.Bottom = Math.Max(minMargin.Bottom, relativePos.Y);
|
||||
}
|
||||
|
||||
if (slider.Position.X < minMargin.Left)
|
||||
slider.Position = new Vector2(minMargin.Left, slider.Position.Y);
|
||||
else if (slider.Position.X + minMargin.Right > OsuPlayfield.BASE_SIZE.X)
|
||||
slider.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - minMargin.Right, slider.Position.Y);
|
||||
|
||||
if (slider.Position.Y < minMargin.Top)
|
||||
slider.Position = new Vector2(slider.Position.X, minMargin.Top);
|
||||
else if (slider.Position.Y + minMargin.Bottom > OsuPlayfield.BASE_SIZE.Y)
|
||||
slider.Position = new Vector2(slider.Position.X, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom);
|
||||
slider.Position = new Vector2(
|
||||
Math.Clamp(slider.Position.X, minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right),
|
||||
Math.Clamp(slider.Position.Y, minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom)
|
||||
);
|
||||
|
||||
currentObjectInfo.PositionRandomised = slider.Position;
|
||||
currentObjectInfo.EndPositionRandomised = slider.EndPosition;
|
||||
|
||||
shiftNestedObjects(slider, currentObjectInfo.PositionRandomised - currentObjectInfo.PositionOriginal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the min. distances from the <see cref="Slider"/>'s position to the playfield border for the slider to be fully inside of the playfield.
|
||||
/// </summary>
|
||||
private MarginPadding getMinSliderMargin(Slider slider)
|
||||
{
|
||||
var pathPositions = new List<Vector2>();
|
||||
slider.Path.GetPathToProgress(pathPositions, 0, 1);
|
||||
|
||||
var minMargin = new MarginPadding();
|
||||
|
||||
foreach (var pos in pathPositions)
|
||||
{
|
||||
minMargin.Left = Math.Max(minMargin.Left, -pos.X);
|
||||
minMargin.Right = Math.Max(minMargin.Right, pos.X);
|
||||
minMargin.Top = Math.Max(minMargin.Top, -pos.Y);
|
||||
minMargin.Bottom = Math.Max(minMargin.Bottom, pos.Y);
|
||||
}
|
||||
|
||||
minMargin.Left = Math.Min(minMargin.Left, OsuPlayfield.BASE_SIZE.X - minMargin.Right);
|
||||
minMargin.Top = Math.Min(minMargin.Top, OsuPlayfield.BASE_SIZE.Y - minMargin.Bottom);
|
||||
|
||||
return minMargin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -188,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
if (!(hitObject is OsuHitObject osuHitObject))
|
||||
continue;
|
||||
|
||||
osuHitObject.Position = Vector2.Add(osuHitObject.Position, shift);
|
||||
osuHitObject.Position += shift;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,5 +128,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
spmContainer.FadeIn(drawableSpinner.HitObject.TimeFadeIn);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (drawableSpinner != null)
|
||||
drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public class MainCirclePiece : CompositeDrawable
|
||||
{
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
|
||||
private readonly CirclePiece circle;
|
||||
private readonly RingPiece ring;
|
||||
private readonly FlashPiece flash;
|
||||
|
@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
public class LegacyMainCirclePiece : CompositeDrawable
|
||||
{
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
|
||||
private readonly string priorityLookup;
|
||||
private readonly bool hasNumber;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
@ -11,6 +12,7 @@ using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Background
|
||||
@ -23,6 +25,9 @@ namespace osu.Game.Tests.Visual.Background
|
||||
|
||||
private Graphics.Backgrounds.Background getCurrentBackground() => screen.ChildrenOfType<Graphics.Backgrounds.Background>().FirstOrDefault();
|
||||
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
|
||||
@ -35,7 +40,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTogglingStoryboardSwitchesBackgroundType()
|
||||
public void TestBackgroundTypeSwitch()
|
||||
{
|
||||
setSupporter(true);
|
||||
|
||||
@ -44,6 +49,12 @@ namespace osu.Game.Tests.Visual.Background
|
||||
|
||||
setSourceMode(BackgroundSource.BeatmapWithStoryboard);
|
||||
AddUntilStep("is storyboard background", () => getCurrentBackground() is BeatmapBackgroundWithStoryboard);
|
||||
|
||||
setSourceMode(BackgroundSource.Skin);
|
||||
AddUntilStep("is default background", () => getCurrentBackground().GetType() == typeof(Graphics.Backgrounds.Background));
|
||||
|
||||
setCustomSkin();
|
||||
AddUntilStep("is skin background", () => getCurrentBackground() is SkinBackground);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -61,15 +72,19 @@ namespace osu.Game.Tests.Visual.Background
|
||||
AddUntilStep("is beatmap background", () => getCurrentBackground() is BeatmapBackground);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapDoesntReloadOnNoChange()
|
||||
[TestCase(BackgroundSource.Beatmap, typeof(BeatmapBackground))]
|
||||
[TestCase(BackgroundSource.BeatmapWithStoryboard, typeof(BeatmapBackgroundWithStoryboard))]
|
||||
[TestCase(BackgroundSource.Skin, typeof(SkinBackground))]
|
||||
public void TestBackgroundDoesntReloadOnNoChange(BackgroundSource source, Type backgroundType)
|
||||
{
|
||||
BeatmapBackground last = null;
|
||||
Graphics.Backgrounds.Background last = null;
|
||||
|
||||
setSourceMode(BackgroundSource.Beatmap);
|
||||
setSourceMode(source);
|
||||
setSupporter(true);
|
||||
if (source == BackgroundSource.Skin)
|
||||
setCustomSkin();
|
||||
|
||||
AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground() as BeatmapBackground) != null);
|
||||
AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground())?.GetType() == backgroundType);
|
||||
AddAssert("next doesn't load new background", () => screen.Next() == false);
|
||||
|
||||
// doesn't really need to be checked but might as well.
|
||||
@ -77,8 +92,25 @@ namespace osu.Game.Tests.Visual.Background
|
||||
AddUntilStep("ensure same background instance", () => last == getCurrentBackground());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBackgroundCyclingOnDefaultSkin([Values] bool supporter)
|
||||
{
|
||||
Graphics.Backgrounds.Background last = null;
|
||||
|
||||
setSourceMode(BackgroundSource.Skin);
|
||||
setSupporter(supporter);
|
||||
setDefaultSkin();
|
||||
|
||||
AddUntilStep("wait for beatmap background to be loaded", () => (last = getCurrentBackground())?.GetType() == typeof(Graphics.Backgrounds.Background));
|
||||
AddAssert("next cycles background", () => screen.Next());
|
||||
|
||||
// doesn't really need to be checked but might as well.
|
||||
AddWaitStep("wait a bit", 5);
|
||||
AddUntilStep("ensure different background instance", () => last != getCurrentBackground());
|
||||
}
|
||||
|
||||
private void setSourceMode(BackgroundSource source) =>
|
||||
AddStep("set background mode to beatmap", () => config.SetValue(OsuSetting.MenuBackgroundSource, source));
|
||||
AddStep($"set background mode to {source}", () => config.SetValue(OsuSetting.MenuBackgroundSource, source));
|
||||
|
||||
private void setSupporter(bool isSupporter) =>
|
||||
AddStep($"set supporter {isSupporter}", () => ((DummyAPIAccess)API).LocalUser.Value = new User
|
||||
@ -86,5 +118,16 @@ namespace osu.Game.Tests.Visual.Background
|
||||
IsSupporter = isSupporter,
|
||||
Id = API.LocalUser.Value.Id + 1,
|
||||
});
|
||||
|
||||
private void setCustomSkin()
|
||||
{
|
||||
// feign a skin switch. this doesn't do anything except force CurrentSkin to become a LegacySkin.
|
||||
AddStep("set custom skin", () => skins.CurrentSkinInfo.Value = new SkinInfo { ID = 5 });
|
||||
}
|
||||
|
||||
private void setDefaultSkin() => AddStep("set default skin", () => skins.CurrentSkinInfo.SetDefault());
|
||||
|
||||
[TearDownSteps]
|
||||
public void TearDown() => setDefaultSkin();
|
||||
}
|
||||
}
|
||||
|
@ -9,16 +9,19 @@ using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Overlays.Toolbar;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Options;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
using osu.Game.Tests.Visual.Multiplayer;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -152,6 +155,14 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddUntilStep("wait for track", () => !Game.MusicController.CurrentTrack.IsDummyDevice && Game.MusicController.IsPlaying);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPushSongSelectAndPressBackButtonImmediately()
|
||||
{
|
||||
AddStep("push song select", () => Game.ScreenStack.Push(new TestPlaySongSelect()));
|
||||
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action());
|
||||
AddWaitStep("wait two frames", 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitSongSelectWithClick()
|
||||
{
|
||||
@ -298,6 +309,18 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddAssert("Toolbar is hidden", () => Game.Toolbar.State.Value == Visibility.Hidden);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPushMatchSubScreenAndPressBackButtonImmediately()
|
||||
{
|
||||
TestMultiplayer multiplayer = null;
|
||||
|
||||
PushAndConfirm(() => multiplayer = new TestMultiplayer());
|
||||
|
||||
AddStep("open room", () => multiplayer.OpenNewRoom());
|
||||
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action());
|
||||
AddWaitStep("wait two frames", 2);
|
||||
}
|
||||
|
||||
private void pushEscape() =>
|
||||
AddStep("Press escape", () => InputManager.Key(Key.Escape));
|
||||
|
||||
@ -322,5 +345,18 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
protected override bool DisplayStableImportPrompt => false;
|
||||
}
|
||||
|
||||
private class TestMultiplayer : Screens.OnlinePlay.Multiplayer.Multiplayer
|
||||
{
|
||||
[Cached(typeof(MultiplayerClient))]
|
||||
public readonly TestMultiplayerClient Client;
|
||||
|
||||
public TestMultiplayer()
|
||||
{
|
||||
Client = new TestMultiplayerClient((TestMultiplayerRoomManager)RoomManager);
|
||||
}
|
||||
|
||||
protected override RoomManager CreateRoomManager() => new TestMultiplayerRoomManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -264,14 +264,18 @@ namespace osu.Game.Collections
|
||||
using (var sw = new SerializationWriter(storage.GetStream(database_name, FileAccess.Write)))
|
||||
{
|
||||
sw.Write(database_version);
|
||||
sw.Write(Collections.Count);
|
||||
|
||||
foreach (var c in Collections)
|
||||
var collectionsCopy = Collections.ToArray();
|
||||
sw.Write(collectionsCopy.Length);
|
||||
|
||||
foreach (var c in collectionsCopy)
|
||||
{
|
||||
sw.Write(c.Name.Value);
|
||||
sw.Write(c.Beatmaps.Count);
|
||||
|
||||
foreach (var b in c.Beatmaps)
|
||||
var beatmapsCopy = c.Beatmaps.ToArray();
|
||||
sw.Write(beatmapsCopy.Length);
|
||||
|
||||
foreach (var b in beatmapsCopy)
|
||||
sw.Write(b.MD5Hash);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -14,7 +15,7 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
/// <summary>
|
||||
/// A background which offers blurring via a <see cref="BufferedContainer"/> on demand.
|
||||
/// </summary>
|
||||
public class Background : CompositeDrawable
|
||||
public class Background : CompositeDrawable, IEquatable<Background>
|
||||
{
|
||||
private const float blur_scale = 0.5f;
|
||||
|
||||
@ -71,5 +72,14 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
|
||||
bufferedContainer?.BlurTo(newBlurSigma * blur_scale, duration, easing);
|
||||
}
|
||||
|
||||
public virtual bool Equals(Background other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
|
||||
return other.GetType() == GetType()
|
||||
&& other.textureName == textureName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,5 +24,14 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
Sprite.Texture = Beatmap?.Background ?? textures.Get(fallbackTextureName);
|
||||
}
|
||||
|
||||
public override bool Equals(Background other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
|
||||
return other.GetType() == GetType()
|
||||
&& ((BeatmapBackground)other).Beatmap == Beatmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
34
osu.Game/Graphics/Backgrounds/SkinBackground.cs
Normal file
34
osu.Game/Graphics/Backgrounds/SkinBackground.cs
Normal file
@ -0,0 +1,34 @@
|
||||
// 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.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Graphics.Backgrounds
|
||||
{
|
||||
internal class SkinBackground : Background
|
||||
{
|
||||
private readonly Skin skin;
|
||||
|
||||
public SkinBackground(Skin skin, string fallbackTextureName)
|
||||
: base(fallbackTextureName)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Sprite.Texture = skin.GetTexture("menu-background") ?? Sprite.Texture;
|
||||
}
|
||||
|
||||
public override bool Equals(Background other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
|
||||
return other.GetType() == GetType()
|
||||
&& ((SkinBackground)other).skin.SkinInfo.Equals(skin.SkinInfo);
|
||||
}
|
||||
}
|
||||
}
|
@ -57,12 +57,6 @@ namespace osu.Game.Graphics.Backgrounds
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether we want to expire triangles as they exit our draw area completely.
|
||||
/// </summary>
|
||||
[Obsolete("Unused.")] // Can be removed 20210518
|
||||
protected virtual bool ExpireOffScreenTriangles => true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we should create new triangles as others expire.
|
||||
/// </summary>
|
||||
|
@ -21,7 +21,12 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
{
|
||||
var ruleset = rulesets.GetRuleset(OnlineRulesetID);
|
||||
|
||||
var mods = Mods != null ? ruleset.CreateInstance().GetAllMods().Where(mod => Mods.Contains(mod.Acronym)).ToArray() : Array.Empty<Mod>();
|
||||
var rulesetInstance = ruleset.CreateInstance();
|
||||
|
||||
var mods = Mods != null ? rulesetInstance.GetAllMods().Where(mod => Mods.Contains(mod.Acronym)).ToArray() : Array.Empty<Mod>();
|
||||
|
||||
// all API scores provided by this class are considered to be legacy.
|
||||
mods = mods.Append(rulesetInstance.GetAllMods().OfType<ModClassic>().Single()).ToArray();
|
||||
|
||||
var scoreInfo = new ScoreInfo
|
||||
{
|
||||
@ -38,7 +43,6 @@ namespace osu.Game.Online.API.Requests.Responses
|
||||
Rank = Rank,
|
||||
Ruleset = ruleset,
|
||||
Mods = mods,
|
||||
IsLegacyScore = true
|
||||
};
|
||||
|
||||
if (Statistics != null)
|
||||
|
@ -651,9 +651,10 @@ namespace osu.Game
|
||||
Origin = Anchor.BottomLeft,
|
||||
Action = () =>
|
||||
{
|
||||
var currentScreen = ScreenStack.CurrentScreen as IOsuScreen;
|
||||
if (!(ScreenStack.CurrentScreen is IOsuScreen currentScreen))
|
||||
return;
|
||||
|
||||
if (currentScreen?.AllowBackButton == true && !currentScreen.OnBackButton())
|
||||
if (!((Drawable)currentScreen).IsLoaded || (currentScreen.AllowBackButton && !currentScreen.OnBackButton()))
|
||||
ScreenStack.Exit();
|
||||
}
|
||||
},
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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.Diagnostics;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
@ -32,18 +31,6 @@ namespace osu.Game.Rulesets.Judgements
|
||||
|
||||
private readonly Container aboveHitObjectsContent;
|
||||
|
||||
/// <summary>
|
||||
/// Duration of initial fade in.
|
||||
/// </summary>
|
||||
[Obsolete("Apply any animations manually via ApplyHitAnimations / ApplyMissAnimations. Defaults were moved inside skinned components.")]
|
||||
protected virtual double FadeInDuration => 100;
|
||||
|
||||
/// <summary>
|
||||
/// Duration to wait until fade out begins. Defaults to <see cref="FadeInDuration"/>.
|
||||
/// </summary>
|
||||
[Obsolete("Apply any animations manually via ApplyHitAnimations / ApplyMissAnimations. Defaults were moved inside skinned components.")]
|
||||
protected virtual double FadeOutDelay => FadeInDuration;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a drawable which visualises a <see cref="Judgements.Judgement"/>.
|
||||
/// </summary>
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
@ -69,14 +68,6 @@ namespace osu.Game.Rulesets.Judgements
|
||||
/// </summary>
|
||||
public double MaxHealthIncrease => HealthIncreaseFor(MaxResult);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the numeric score representation of a <see cref="HitResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="result">The <see cref="HitResult"/> to find the numeric score representation for.</param>
|
||||
/// <returns>The numeric score representation of <paramref name="result"/>.</returns>
|
||||
[Obsolete("Has no effect. Use ToNumericResult(HitResult) (standardised across all rulesets).")] // Can be made non-virtual 20210328
|
||||
protected virtual int NumericResultFor(HitResult result) => ToNumericResult(result);
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the numeric score representation of a <see cref="JudgementResult"/>.
|
||||
/// </summary>
|
||||
|
@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// <summary>
|
||||
/// Applies a hit object to be represented by this <see cref="DrawableHitObject"/>.
|
||||
/// </summary>
|
||||
[Obsolete("Use either overload of Apply that takes a single argument of type HitObject or HitObjectLifetimeEntry")]
|
||||
[Obsolete("Use either overload of Apply that takes a single argument of type HitObject or HitObjectLifetimeEntry")] // Can be removed 20211021.
|
||||
public void Apply([NotNull] HitObject hitObject, [CanBeNull] HitObjectLifetimeEntry lifetimeEntry)
|
||||
{
|
||||
if (lifetimeEntry != null)
|
||||
@ -409,11 +409,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
using (BeginAbsoluteSequence(StateUpdateTime, true))
|
||||
UpdateStartTimeStateTransforms();
|
||||
|
||||
#pragma warning disable 618
|
||||
using (BeginAbsoluteSequence(StateUpdateTime + (Result?.TimeOffset ?? 0), true))
|
||||
UpdateStateTransforms(newState);
|
||||
#pragma warning restore 618
|
||||
|
||||
using (BeginAbsoluteSequence(HitStateUpdateTime, true))
|
||||
UpdateHitStateTransforms(newState);
|
||||
|
||||
@ -447,7 +442,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// By default, this will fade in the object from zero with no duration.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is called once before every <see cref="UpdateStateTransforms"/>. This is to ensure a good state in the case
|
||||
/// This is called once before every <see cref="UpdateHitStateTransforms"/>. This is to ensure a good state in the case
|
||||
/// the <see cref="JudgementResult.TimeOffset"/> was negative and potentially altered the pre-hit transforms.
|
||||
/// </remarks>
|
||||
protected virtual void UpdateInitialTransforms()
|
||||
@ -455,16 +450,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
this.FadeInFromZero();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply transforms based on the current <see cref="ArmedState"/>. Previous states are automatically cleared.
|
||||
/// In the case of a non-idle <see cref="ArmedState"/>, and if <see cref="Drawable.LifetimeEnd"/> was not set during this call, <see cref="Drawable.Expire"/> will be invoked.
|
||||
/// </summary>
|
||||
/// <param name="state">The new armed state.</param>
|
||||
[Obsolete("Use UpdateStartTimeStateTransforms and UpdateHitStateTransforms instead")] // Can be removed 20210504
|
||||
protected virtual void UpdateStateTransforms(ArmedState state)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply passive transforms at the <see cref="HitObject"/>'s StartTime.
|
||||
/// This is called each time <see cref="State"/> changes.
|
||||
@ -520,23 +505,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
AccentColour.Value = combo.GetComboColour(comboColours);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to retrieve the combo colour. Automatically assigned to <see cref="AccentColour"/>.
|
||||
/// Defaults to using <see cref="IHasComboInformation.ComboIndex"/> to decide on a colour.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will only be called if the <see cref="HitObject"/> implements <see cref="IHasComboInformation"/>.
|
||||
/// </remarks>
|
||||
/// <param name="comboColours">A list of combo colours provided by the beatmap or skin. Can be null if not available.</param>
|
||||
[Obsolete("Unused. Implement IHasComboInformation and IHasComboInformation.GetComboColour() on the HitObject model instead.")] // Can be removed 20210527
|
||||
protected virtual Color4 GetComboColour(IReadOnlyList<Color4> comboColours)
|
||||
{
|
||||
if (!(HitObject is IHasComboInformation combo))
|
||||
throw new InvalidOperationException($"{nameof(HitObject)} must implement {nameof(IHasComboInformation)}");
|
||||
|
||||
return comboColours?.Count > 0 ? comboColours[combo.ComboIndex % comboColours.Count] : Color4.White;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a change is made to the skin.
|
||||
/// </summary>
|
||||
@ -630,7 +598,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
|
||||
/// <summary>
|
||||
/// The time at which state transforms should be applied that line up to <see cref="HitObject"/>'s StartTime.
|
||||
/// This is used to offset calls to <see cref="UpdateStateTransforms"/>.
|
||||
/// This is used to offset calls to <see cref="UpdateStartTimeStateTransforms"/>.
|
||||
/// </summary>
|
||||
public double StateUpdateTime => HitObject.StartTime;
|
||||
|
||||
|
@ -65,6 +65,10 @@ namespace osu.Game.Scoring.Legacy
|
||||
|
||||
scoreInfo.Mods = currentRuleset.ConvertFromLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
|
||||
|
||||
// lazer replays get a really high version number.
|
||||
if (version < LegacyScoreEncoder.FIRST_LAZER_VERSION)
|
||||
scoreInfo.Mods = scoreInfo.Mods.Append(currentRuleset.GetAllMods().OfType<ModClassic>().Single()).ToArray();
|
||||
|
||||
currentBeatmap = workingBeatmap.GetPlayableBeatmap(currentRuleset.RulesetInfo, scoreInfo.Mods);
|
||||
scoreInfo.Beatmap = currentBeatmap.BeatmapInfo;
|
||||
|
||||
|
@ -15,7 +15,16 @@ namespace osu.Game.Scoring.Legacy
|
||||
{
|
||||
public class LegacyScoreEncoder
|
||||
{
|
||||
public const int LATEST_VERSION = 128;
|
||||
/// <summary>
|
||||
/// Database version in stable-compatible YYYYMMDD format.
|
||||
/// Should be incremented if any changes are made to the format/usage.
|
||||
/// </summary>
|
||||
public const int LATEST_VERSION = FIRST_LAZER_VERSION;
|
||||
|
||||
/// <summary>
|
||||
/// The first stable-compatible YYYYMMDD format version given to lazer usage of replays.
|
||||
/// </summary>
|
||||
public const int FIRST_LAZER_VERSION = 30000000;
|
||||
|
||||
private readonly Score score;
|
||||
private readonly IBeatmap beatmap;
|
||||
|
@ -76,9 +76,6 @@ namespace osu.Game.Scoring
|
||||
else if (localAPIMods != null)
|
||||
scoreMods = apiMods.Select(m => m.ToMod(rulesetInstance)).ToArray();
|
||||
|
||||
if (IsLegacyScore)
|
||||
scoreMods = scoreMods.Append(rulesetInstance.GetAllMods().OfType<ModClassic>().Single()).ToArray();
|
||||
|
||||
return scoreMods;
|
||||
}
|
||||
set
|
||||
@ -201,33 +198,12 @@ namespace osu.Game.Scoring
|
||||
[JsonProperty("position")]
|
||||
public int? Position { get; set; }
|
||||
|
||||
private bool isLegacyScore;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this <see cref="ScoreInfo"/> represents a legacy (osu!stable) score.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
[NotMapped]
|
||||
public bool IsLegacyScore
|
||||
{
|
||||
get
|
||||
{
|
||||
if (isLegacyScore)
|
||||
return true;
|
||||
|
||||
// The above check will catch legacy online scores that have an appropriate UserString + UserId.
|
||||
// For non-online scores such as those imported in, a heuristic is used based on the following table:
|
||||
//
|
||||
// Mode | UserString | UserId
|
||||
// --------------- | ---------- | ---------
|
||||
// stable | <username> | 1
|
||||
// lazer | <username> | <userid>
|
||||
// lazer (offline) | Guest | 1
|
||||
|
||||
return ID > 0 && UserID == 1 && UserString != "Guest";
|
||||
}
|
||||
set => isLegacyScore = value;
|
||||
}
|
||||
public bool IsLegacyScore => Mods.OfType<ModClassic>().Any();
|
||||
|
||||
public IEnumerable<HitResultDisplayStatistic> GetStatisticsForDisplay()
|
||||
{
|
||||
|
@ -112,17 +112,24 @@ namespace osu.Game.Screens.Backgrounds
|
||||
newBackground = new BeatmapBackgroundWithStoryboard(beatmap.Value, getBackgroundTextureName());
|
||||
newBackground ??= new BeatmapBackground(beatmap.Value, getBackgroundTextureName());
|
||||
|
||||
// this method is called in many cases where the beatmap hasn't changed (ie. on screen transitions).
|
||||
// if a background is already displayed for the requested beatmap, we don't want to load it again.
|
||||
if (background?.GetType() == newBackground.GetType() &&
|
||||
(background as BeatmapBackground)?.Beatmap == beatmap.Value)
|
||||
return background;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BackgroundSource.Skin:
|
||||
// default skins should use the default background rotation, which won't be the case if a SkinBackground is created for them.
|
||||
if (skin.Value is DefaultSkin || skin.Value is DefaultLegacySkin)
|
||||
break;
|
||||
|
||||
newBackground = new SkinBackground(skin.Value, getBackgroundTextureName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// this method is called in many cases where the background might not necessarily need to change.
|
||||
// if an equivalent background is currently being shown, we don't want to load it again.
|
||||
if (newBackground?.Equals(background) == true)
|
||||
return background;
|
||||
|
||||
newBackground ??= new Background(getBackgroundTextureName());
|
||||
newBackground.Depth = currentDisplay;
|
||||
|
||||
@ -140,22 +147,5 @@ namespace osu.Game.Screens.Backgrounds
|
||||
return $@"Menu/menu-background-{currentDisplay % background_count + 1}";
|
||||
}
|
||||
}
|
||||
|
||||
private class SkinnedBackground : Background
|
||||
{
|
||||
private readonly Skin skin;
|
||||
|
||||
public SkinnedBackground(Skin skin, string fallbackTextureName)
|
||||
: base(fallbackTextureName)
|
||||
{
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Sprite.Texture = skin.GetTexture("menu-background") ?? Sprite.Texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,8 +67,11 @@ namespace osu.Game.Screens
|
||||
/// Invoked when the back button has been pressed to close any overlays before exiting this <see cref="IOsuScreen"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this <see cref="IOsuScreen"/> has not yet finished loading, the exit will occur immediately without this method being invoked.
|
||||
/// <para>
|
||||
/// Return <c>true</c> to block this <see cref="IOsuScreen"/> from being exited after closing an overlay.
|
||||
/// Return <c>false</c> if this <see cref="IOsuScreen"/> should continue exiting.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
bool OnBackButton();
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
// Isolates beatmap/ruleset to this screen.
|
||||
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
||||
|
||||
// We are managing our own adjustments. For now, this happens inside the Player instances themselves.
|
||||
public override bool AllowRateAdjustments => false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether all spectating players have finished loading.
|
||||
/// </summary>
|
||||
|
@ -253,7 +253,10 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
|
||||
public override bool OnBackButton()
|
||||
{
|
||||
if ((screenStack.CurrentScreen as IOnlinePlaySubScreen)?.OnBackButton() == true)
|
||||
if (!(screenStack.CurrentScreen is IOnlinePlaySubScreen onlineSubScreen))
|
||||
return false;
|
||||
|
||||
if (((Drawable)onlineSubScreen).IsLoaded && onlineSubScreen.AllowBackButton && onlineSubScreen.OnBackButton())
|
||||
return true;
|
||||
|
||||
if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen))
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
@ -70,22 +71,48 @@ namespace osu.Game.Skinning
|
||||
updateSample();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
CurrentSkin.SourceChanged += skinChangedImmediate;
|
||||
}
|
||||
|
||||
private void skinChangedImmediate()
|
||||
{
|
||||
// Clean up the previous sample immediately on a source change.
|
||||
// This avoids a potential call to Play() of an already disposed sample (samples are disposed along with the skin, but SkinChanged is scheduled).
|
||||
clearPreviousSamples();
|
||||
}
|
||||
|
||||
protected override void SkinChanged(ISkinSource skin)
|
||||
{
|
||||
base.SkinChanged(skin);
|
||||
updateSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this sample was playing before a skin source change.
|
||||
/// </summary>
|
||||
private bool wasPlaying;
|
||||
|
||||
private void clearPreviousSamples()
|
||||
{
|
||||
// only run if the samples aren't already cleared.
|
||||
// this ensures the "wasPlaying" state is stored correctly even if multiple clear calls are executed.
|
||||
if (!sampleContainer.Any()) return;
|
||||
|
||||
wasPlaying = Playing;
|
||||
|
||||
sampleContainer.Clear();
|
||||
Sample = null;
|
||||
}
|
||||
|
||||
private void updateSample()
|
||||
{
|
||||
if (sampleInfo == null)
|
||||
return;
|
||||
|
||||
bool wasPlaying = Playing;
|
||||
|
||||
sampleContainer.Clear();
|
||||
Sample = null;
|
||||
|
||||
var sample = CurrentSkin.GetSample(sampleInfo);
|
||||
|
||||
if (sample == null)
|
||||
@ -146,6 +173,14 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (CurrentSkin != null)
|
||||
CurrentSkin.SourceChanged -= skinChangedImmediate;
|
||||
}
|
||||
|
||||
#region Re-expose AudioContainer
|
||||
|
||||
public BindableNumber<double> Volume => sampleContainer.Volume;
|
||||
|
@ -34,7 +34,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.607.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.608.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.604.0"/>
|
||||
<PackageReference Include="Sentry" Version="3.4.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
||||
|
@ -70,7 +70,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.607.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.608.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.604.0"/>
|
||||
</ItemGroup>
|
||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||
@ -93,7 +93,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.607.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2021.608.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.28.2" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user