1
0
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:
Dean Herbert 2021-06-09 18:00:11 +09:00
commit 8fda04c2d9
30 changed files with 293 additions and 192 deletions

View File

@ -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>

View File

@ -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()
{

View File

@ -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)
{

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View 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);
}
}
}

View File

@ -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>

View File

@ -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)

View File

@ -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();
}
},

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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()
{

View File

@ -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;
}
}
}
}

View File

@ -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();
}

View File

@ -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>

View File

@ -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))

View File

@ -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;

View File

@ -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" />

View File

@ -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" />