Merge branch 'master' into mod-muted

This commit is contained in:
Henry Lin
2021-07-29 21:14:03 +08:00
25 changed files with 343 additions and 280 deletions

View File

@ -52,7 +52,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.723.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2021.728.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Transitive Dependencies"> <ItemGroup Label="Transitive Dependencies">
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. --> <!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->

View File

@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Tests
var skin = new TestSkin { FlipCatcherPlate = flip }; var skin = new TestSkin { FlipCatcherPlate = flip };
container.Child = new SkinProvidingContainer(skin) container.Child = new SkinProvidingContainer(skin)
{ {
Child = catcher = new Catcher(new Container(), new DroppedObjectContainer()) Child = catcher = new Catcher(new DroppedObjectContainer())
{ {
Anchor = Anchor.Centre Anchor = Anchor.Centre
} }

View File

@ -31,8 +31,6 @@ namespace osu.Game.Rulesets.Catch.Tests
[Resolved] [Resolved]
private OsuConfigManager config { get; set; } private OsuConfigManager config { get; set; }
private Container trailContainer;
private DroppedObjectContainer droppedObjectContainer; private DroppedObjectContainer droppedObjectContainer;
private TestCatcher catcher; private TestCatcher catcher;
@ -45,7 +43,6 @@ namespace osu.Game.Rulesets.Catch.Tests
CircleSize = 0, CircleSize = 0,
}; };
trailContainer = new Container();
droppedObjectContainer = new DroppedObjectContainer(); droppedObjectContainer = new DroppedObjectContainer();
Child = new Container Child = new Container
@ -54,8 +51,7 @@ namespace osu.Game.Rulesets.Catch.Tests
Children = new Drawable[] Children = new Drawable[]
{ {
droppedObjectContainer, droppedObjectContainer,
catcher = new TestCatcher(trailContainer, droppedObjectContainer, difficulty), catcher = new TestCatcher(droppedObjectContainer, difficulty),
trailContainer,
} }
}; };
}); });
@ -294,8 +290,8 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
public IEnumerable<CaughtObject> CaughtObjects => this.ChildrenOfType<CaughtObject>(); public IEnumerable<CaughtObject> CaughtObjects => this.ChildrenOfType<CaughtObject>();
public TestCatcher(Container trailsTarget, DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty) public TestCatcher(DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty)
: base(trailsTarget, droppedObjectTarget, difficulty) : base(droppedObjectTarget, difficulty)
{ {
} }
} }

View File

@ -122,10 +122,9 @@ namespace osu.Game.Rulesets.Catch.Tests
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
{ {
var droppedObjectContainer = new DroppedObjectContainer(); var droppedObjectContainer = new DroppedObjectContainer();
Add(droppedObjectContainer); Add(droppedObjectContainer);
Catcher = new Catcher(this, droppedObjectContainer, beatmapDifficulty) Catcher = new Catcher(droppedObjectContainer, beatmapDifficulty)
{ {
X = CatchPlayfield.CENTER_X X = CatchPlayfield.CENTER_X
}; };

View File

@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Catch.Tests
} }
[Test] [Test]
public void TestCustomEndGlowColour() public void TestCustomAfterImageColour()
{ {
var skin = new TestSkin var skin = new TestSkin
{ {
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Catch.Tests
} }
[Test] [Test]
public void TestCustomEndGlowColourPriority() public void TestCustomAfterImageColourPriority()
{ {
var skin = new TestSkin var skin = new TestSkin
{ {
@ -111,39 +111,37 @@ namespace osu.Game.Rulesets.Catch.Tests
checkHyperDashFruitColour(skin, skin.HyperDashColour); checkHyperDashFruitColour(skin, skin.HyperDashColour);
} }
private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedEndGlowColour = null) private void checkHyperDashCatcherColour(ISkin skin, Color4 expectedCatcherColour, Color4? expectedAfterImageColour = null)
{ {
Container trailsContainer = null;
Catcher catcher = null;
CatcherTrailDisplay trails = null; CatcherTrailDisplay trails = null;
Catcher catcher = null;
AddStep("create hyper-dashing catcher", () => AddStep("create hyper-dashing catcher", () =>
{ {
trailsContainer = new Container(); CatcherArea catcherArea;
Child = setupSkinHierarchy(new Container Child = setupSkinHierarchy(new Container
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Children = new Drawable[] Child = catcherArea = new CatcherArea
{ {
catcher = new Catcher(trailsContainer, new DroppedObjectContainer()) Catcher = catcher = new Catcher(new DroppedObjectContainer())
{ {
Scale = new Vector2(4) Scale = new Vector2(4)
}, }
trailsContainer
} }
}, skin); }, skin);
trails = catcherArea.ChildrenOfType<CatcherTrailDisplay>().Single();
}); });
AddStep("get trails container", () => AddStep("start hyper-dash", () =>
{ {
trails = trailsContainer.OfType<CatcherTrailDisplay>().Single();
catcher.SetHyperDashState(2); catcher.SetHyperDashState(2);
}); });
AddUntilStep("catcher colour is correct", () => catcher.Colour == expectedCatcherColour); AddUntilStep("catcher colour is correct", () => catcher.Colour == expectedCatcherColour);
AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour); AddAssert("catcher trails colours are correct", () => trails.HyperDashTrailsColour == expectedCatcherColour);
AddAssert("catcher end-glow colours are correct", () => trails.EndGlowSpritesColour == (expectedEndGlowColour ?? expectedCatcherColour)); AddAssert("catcher after-image colours are correct", () => trails.HyperDashAfterImageColour == (expectedAfterImageColour ?? expectedCatcherColour));
AddStep("finish hyper-dashing", () => AddStep("finish hyper-dashing", () =>
{ {

View File

@ -3,7 +3,6 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Objects.Drawables;
@ -45,14 +44,9 @@ namespace osu.Game.Rulesets.Catch.UI
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
var trailContainer = new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft
};
var droppedObjectContainer = new DroppedObjectContainer(); var droppedObjectContainer = new DroppedObjectContainer();
Catcher = new Catcher(trailContainer, droppedObjectContainer, difficulty) Catcher = new Catcher(droppedObjectContainer, difficulty)
{ {
X = CENTER_X X = CENTER_X
}; };
@ -70,7 +64,6 @@ namespace osu.Game.Rulesets.Catch.UI
Origin = Anchor.TopLeft, Origin = Anchor.TopLeft,
Catcher = Catcher, Catcher = Catcher,
}, },
trailContainer,
HitObjectContainer, HitObjectContainer,
}); });

View File

@ -36,8 +36,7 @@ namespace osu.Game.Rulesets.Catch.UI
public const float ALLOWED_CATCH_RANGE = 0.8f; public const float ALLOWED_CATCH_RANGE = 0.8f;
/// <summary> /// <summary>
/// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail /// The default colour used to tint hyper-dash fruit, along with the moving catcher, its trail and after-image during a hyper-dash.
/// and end glow/after-image during a hyper-dash.
/// </summary> /// </summary>
public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red; public static readonly Color4 DEFAULT_HYPER_DASH_COLOUR = Color4.Red;
@ -71,11 +70,6 @@ namespace osu.Game.Rulesets.Catch.UI
/// </summary> /// </summary>
private const float caught_fruit_scale_adjust = 0.5f; private const float caught_fruit_scale_adjust = 0.5f;
[NotNull]
private readonly Container trailsTarget;
private CatcherTrailDisplay trails;
/// <summary> /// <summary>
/// Contains caught objects on the plate. /// Contains caught objects on the plate.
/// </summary> /// </summary>
@ -88,30 +82,22 @@ namespace osu.Game.Rulesets.Catch.UI
public CatcherAnimationState CurrentState public CatcherAnimationState CurrentState
{ {
get => Body.AnimationState.Value; get => body.AnimationState.Value;
private set => Body.AnimationState.Value = value; private set => body.AnimationState.Value = value;
} }
private bool dashing; /// <summary>
/// Whether the catcher is currently dashing.
public bool Dashing /// </summary>
{ public bool Dashing { get; set; }
get => dashing;
set
{
if (value == dashing) return;
dashing = value;
updateTrailVisibility();
}
}
/// <summary> /// <summary>
/// The currently facing direction. /// The currently facing direction.
/// </summary> /// </summary>
public Direction VisualDirection { get; set; } = Direction.Right; public Direction VisualDirection { get; set; } = Direction.Right;
public Vector2 BodyScale => Scale * body.Scale;
/// <summary> /// <summary>
/// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed. /// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
/// </summary> /// </summary>
@ -122,10 +108,9 @@ namespace osu.Game.Rulesets.Catch.UI
/// </summary> /// </summary>
private readonly float catchWidth; private readonly float catchWidth;
internal readonly SkinnableCatcher Body; private readonly SkinnableCatcher body;
private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR; private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR;
private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR;
private double hyperDashModifier = 1; private double hyperDashModifier = 1;
private int hyperDashDirection; private int hyperDashDirection;
@ -138,9 +123,8 @@ namespace osu.Game.Rulesets.Catch.UI
private readonly DrawablePool<CaughtBanana> caughtBananaPool; private readonly DrawablePool<CaughtBanana> caughtBananaPool;
private readonly DrawablePool<CaughtDroplet> caughtDropletPool; private readonly DrawablePool<CaughtDroplet> caughtDropletPool;
public Catcher([NotNull] Container trailsTarget, [NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null) public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null)
{ {
this.trailsTarget = trailsTarget;
this.droppedObjectTarget = droppedObjectTarget; this.droppedObjectTarget = droppedObjectTarget;
Origin = Anchor.TopCentre; Origin = Anchor.TopCentre;
@ -164,7 +148,7 @@ namespace osu.Game.Rulesets.Catch.UI
// offset fruit vertically to better place "above" the plate. // offset fruit vertically to better place "above" the plate.
Y = -5 Y = -5
}, },
Body = new SkinnableCatcher(), body = new SkinnableCatcher(),
hitExplosionContainer = new HitExplosionContainer hitExplosionContainer = new HitExplosionContainer
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
@ -177,15 +161,6 @@ namespace osu.Game.Rulesets.Catch.UI
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
hitLighting = config.GetBindable<bool>(OsuSetting.HitLighting); hitLighting = config.GetBindable<bool>(OsuSetting.HitLighting);
trails = new CatcherTrailDisplay(this);
}
protected override void LoadComplete()
{
base.LoadComplete();
// don't add in above load as we may potentially modify a parent in an unsafe manner.
trailsTarget.Add(trails);
} }
/// <summary> /// <summary>
@ -307,10 +282,7 @@ namespace osu.Game.Rulesets.Catch.UI
hyperDashTargetPosition = targetPosition; hyperDashTargetPosition = targetPosition;
if (!wasHyperDashing) if (!wasHyperDashing)
{
trails.DisplayEndGlow();
runHyperDashStateTransition(true); runHyperDashStateTransition(true);
}
} }
} }
@ -326,13 +298,9 @@ namespace osu.Game.Rulesets.Catch.UI
private void runHyperDashStateTransition(bool hyperDashing) private void runHyperDashStateTransition(bool hyperDashing)
{ {
updateTrailVisibility();
this.FadeColour(hyperDashing ? hyperDashColour : Color4.White, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint); this.FadeColour(hyperDashing ? hyperDashColour : Color4.White, HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
} }
private void updateTrailVisibility() => trails.DisplayTrail = Dashing || HyperDashing;
protected override void SkinChanged(ISkinSource skin) protected override void SkinChanged(ISkinSource skin)
{ {
base.SkinChanged(skin); base.SkinChanged(skin);
@ -341,13 +309,6 @@ namespace osu.Game.Rulesets.Catch.UI
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ?? skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
DEFAULT_HYPER_DASH_COLOUR; DEFAULT_HYPER_DASH_COLOUR;
hyperDashEndGlowColour =
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashAfterImage)?.Value ??
hyperDashColour;
trails.HyperDashTrailsColour = hyperDashColour;
trails.EndGlowSpritesColour = hyperDashEndGlowColour;
flipCatcherPlate = skin.GetConfig<CatchSkinConfiguration, bool>(CatchSkinConfiguration.FlipCatcherPlate)?.Value ?? true; flipCatcherPlate = skin.GetConfig<CatchSkinConfiguration, bool>(CatchSkinConfiguration.FlipCatcherPlate)?.Value ?? true;
runHyperDashStateTransition(HyperDashing); runHyperDashStateTransition(HyperDashing);
@ -358,7 +319,7 @@ namespace osu.Game.Rulesets.Catch.UI
base.Update(); base.Update();
var scaleFromDirection = new Vector2((int)VisualDirection, 1); var scaleFromDirection = new Vector2((int)VisualDirection, 1);
Body.Scale = scaleFromDirection; body.Scale = scaleFromDirection;
caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One; caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
// Correct overshooting. // Correct overshooting.

View File

@ -25,17 +25,15 @@ namespace osu.Game.Rulesets.Catch.UI
public Catcher Catcher public Catcher Catcher
{ {
get => catcher; get => catcher;
set set => catcherContainer.Child = catcher = value;
{
if (catcher != null)
Remove(catcher);
Add(catcher = value);
}
} }
private readonly Container<Catcher> catcherContainer;
private readonly CatchComboDisplay comboDisplay; private readonly CatchComboDisplay comboDisplay;
private readonly CatcherTrailDisplay catcherTrails;
private Catcher catcher; private Catcher catcher;
/// <summary> /// <summary>
@ -45,20 +43,28 @@ namespace osu.Game.Rulesets.Catch.UI
/// </summary> /// </summary>
private int currentDirection; private int currentDirection;
// TODO: support replay rewind
private bool lastHyperDashState;
/// <remarks> /// <remarks>
/// <see cref="Catcher"/> must be set before loading. /// <see cref="Catcher"/> must be set before loading.
/// </remarks> /// </remarks>
public CatcherArea() public CatcherArea()
{ {
Size = new Vector2(CatchPlayfield.WIDTH, Catcher.BASE_SIZE); Size = new Vector2(CatchPlayfield.WIDTH, Catcher.BASE_SIZE);
Child = comboDisplay = new CatchComboDisplay Children = new Drawable[]
{ {
RelativeSizeAxes = Axes.None, catcherContainer = new Container<Catcher> { RelativeSizeAxes = Axes.Both },
AutoSizeAxes = Axes.Both, catcherTrails = new CatcherTrailDisplay(),
Anchor = Anchor.TopLeft, comboDisplay = new CatchComboDisplay
Origin = Anchor.Centre, {
Margin = new MarginPadding { Bottom = 350f }, RelativeSizeAxes = Axes.None,
X = CatchPlayfield.CENTER_X AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopLeft,
Origin = Anchor.Centre,
Margin = new MarginPadding { Bottom = 350f },
X = CatchPlayfield.CENTER_X
}
}; };
} }
@ -102,6 +108,19 @@ namespace osu.Game.Rulesets.Catch.UI
base.UpdateAfterChildren(); base.UpdateAfterChildren();
comboDisplay.X = Catcher.X; comboDisplay.X = Catcher.X;
if (!lastHyperDashState && Catcher.HyperDashing && Time.Elapsed > 0)
catcherTrails.DisplayHyperDashAfterImage(Catcher.CurrentState, Catcher.X, Catcher.BodyScale);
if (Catcher.Dashing || Catcher.HyperDashing)
{
double generationInterval = Catcher.HyperDashing ? 25 : 50;
if (Time.Current - catcherTrails.LastDashTrailTime >= generationInterval)
catcherTrails.DisplayDashTrail(Catcher.CurrentState, Catcher.X, Catcher.BodyScale, Catcher.HyperDashing);
}
lastHyperDashState = Catcher.HyperDashing;
} }
public void SetCatcherPosition(float X) public void SetCatcherPosition(float X)

View File

@ -2,10 +2,11 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using JetBrains.Annotations;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Pooling;
using osu.Game.Rulesets.Catch.Skinning;
using osu.Game.Skinning;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -15,70 +16,27 @@ namespace osu.Game.Rulesets.Catch.UI
/// Represents a component responsible for displaying /// Represents a component responsible for displaying
/// the appropriate catcher trails when requested to. /// the appropriate catcher trails when requested to.
/// </summary> /// </summary>
public class CatcherTrailDisplay : CompositeDrawable public class CatcherTrailDisplay : SkinReloadableDrawable
{ {
private readonly Catcher catcher; /// <summary>
/// The most recent time a dash trail was added to this container.
/// Only alive (not faded out) trails are considered.
/// Returns <see cref="double.NegativeInfinity"/> if no dash trail is alive.
/// </summary>
public double LastDashTrailTime => getLastDashTrailTime();
public Color4 HyperDashTrailsColour => hyperDashTrails.Colour;
public Color4 HyperDashAfterImageColour => hyperDashAfterImages.Colour;
private readonly DrawablePool<CatcherTrail> trailPool; private readonly DrawablePool<CatcherTrail> trailPool;
private readonly Container<CatcherTrail> dashTrails; private readonly Container<CatcherTrail> dashTrails;
private readonly Container<CatcherTrail> hyperDashTrails; private readonly Container<CatcherTrail> hyperDashTrails;
private readonly Container<CatcherTrail> endGlowSprites; private readonly Container<CatcherTrail> hyperDashAfterImages;
private Color4 hyperDashTrailsColour = Catcher.DEFAULT_HYPER_DASH_COLOUR; public CatcherTrailDisplay()
public Color4 HyperDashTrailsColour
{ {
get => hyperDashTrailsColour;
set
{
if (hyperDashTrailsColour == value)
return;
hyperDashTrailsColour = value;
hyperDashTrails.Colour = hyperDashTrailsColour;
}
}
private Color4 endGlowSpritesColour = Catcher.DEFAULT_HYPER_DASH_COLOUR;
public Color4 EndGlowSpritesColour
{
get => endGlowSpritesColour;
set
{
if (endGlowSpritesColour == value)
return;
endGlowSpritesColour = value;
endGlowSprites.Colour = endGlowSpritesColour;
}
}
private bool trail;
/// <summary>
/// Whether to start displaying trails following the catcher.
/// </summary>
public bool DisplayTrail
{
get => trail;
set
{
if (trail == value)
return;
trail = value;
if (trail)
displayTrail();
}
}
public CatcherTrailDisplay([NotNull] Catcher catcher)
{
this.catcher = catcher ?? throw new ArgumentNullException(nameof(catcher));
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
@ -86,47 +44,68 @@ namespace osu.Game.Rulesets.Catch.UI
trailPool = new DrawablePool<CatcherTrail>(30), trailPool = new DrawablePool<CatcherTrail>(30),
dashTrails = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both }, dashTrails = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both },
hyperDashTrails = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, hyperDashTrails = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
endGlowSprites = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR }, hyperDashAfterImages = new Container<CatcherTrail> { RelativeSizeAxes = Axes.Both, Colour = Catcher.DEFAULT_HYPER_DASH_COLOUR },
}; };
} }
protected override void SkinChanged(ISkinSource skin)
{
base.SkinChanged(skin);
hyperDashTrails.Colour = skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ?? Catcher.DEFAULT_HYPER_DASH_COLOUR;
hyperDashAfterImages.Colour = skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashAfterImage)?.Value ?? hyperDashTrails.Colour;
}
/// <summary> /// <summary>
/// Displays a single end-glow catcher sprite. /// Displays a hyper-dash after-image of the catcher.
/// </summary> /// </summary>
public void DisplayEndGlow() public void DisplayHyperDashAfterImage(CatcherAnimationState animationState, float x, Vector2 scale)
{ {
var endGlow = createTrailSprite(endGlowSprites); var trail = createTrail(animationState, x, scale);
endGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In); hyperDashAfterImages.Add(trail);
endGlow.ScaleTo(endGlow.Scale * 0.95f).ScaleTo(endGlow.Scale * 1.2f, 1200, Easing.In);
endGlow.FadeOut(1200); trail.MoveToOffset(new Vector2(0, -10), 1200, Easing.In);
endGlow.Expire(true); trail.ScaleTo(trail.Scale * 0.95f).ScaleTo(trail.Scale * 1.2f, 1200, Easing.In);
trail.FadeOut(1200);
trail.Expire(true);
} }
private void displayTrail() public void DisplayDashTrail(CatcherAnimationState animationState, float x, Vector2 scale, bool hyperDashing)
{ {
if (!DisplayTrail) var trail = createTrail(animationState, x, scale);
return;
var sprite = createTrailSprite(catcher.HyperDashing ? hyperDashTrails : dashTrails); if (hyperDashing)
hyperDashTrails.Add(trail);
else
dashTrails.Add(trail);
sprite.FadeTo(0.4f).FadeOut(800, Easing.OutQuint); trail.FadeTo(0.4f).FadeOut(800, Easing.OutQuint);
sprite.Expire(true); trail.Expire(true);
Scheduler.AddDelayed(displayTrail, catcher.HyperDashing ? 25 : 50);
} }
private CatcherTrail createTrailSprite(Container<CatcherTrail> target) private CatcherTrail createTrail(CatcherAnimationState animationState, float x, Vector2 scale)
{ {
CatcherTrail sprite = trailPool.Get(); CatcherTrail trail = trailPool.Get();
sprite.AnimationState = catcher.CurrentState; trail.AnimationState = animationState;
sprite.Scale = catcher.Scale * catcher.Body.Scale; trail.Scale = scale;
sprite.Position = catcher.Position; trail.Position = new Vector2(x, 0);
target.Add(sprite); return trail;
}
return sprite; private double getLastDashTrailTime()
{
double maxTime = double.NegativeInfinity;
foreach (var trail in dashTrails)
maxTime = Math.Max(maxTime, trail.LifetimeStart);
foreach (var trail in hyperDashTrails)
maxTime = Math.Max(maxTime, trail.LifetimeStart);
return maxTime;
} }
} }
} }

View File

@ -10,13 +10,9 @@ using osu.Game.Rulesets.Mania.Beatmaps;
namespace osu.Game.Rulesets.Mania.Mods namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModMirror : Mod, IApplicableToBeatmap public class ManiaModMirror : ModMirror, IApplicableToBeatmap
{ {
public override string Name => "Mirror";
public override string Acronym => "MR";
public override ModType Type => ModType.Conversion;
public override string Description => "Notes are flipped horizontally."; public override string Description => "Notes are flipped horizontally.";
public override double ScoreMultiplier => 1;
public void ApplyToBeatmap(IBeatmap beatmap) public void ApplyToBeatmap(IBeatmap beatmap)
{ {

View File

@ -1,13 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq; using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.Utils;
using osuTK;
namespace osu.Game.Rulesets.Osu.Mods namespace osu.Game.Rulesets.Osu.Mods
{ {
@ -15,23 +14,13 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModMirror)).ToArray();
public void ApplyToHitObject(HitObject hitObject) public void ApplyToHitObject(HitObject hitObject)
{ {
var osuObject = (OsuHitObject)hitObject; var osuObject = (OsuHitObject)hitObject;
osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y); OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
if (!(hitObject is Slider slider))
return;
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
foreach (var point in controlPoints)
point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
} }
} }
} }

View File

@ -0,0 +1,50 @@
// 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.Bindables;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Utils;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModMirror : ModMirror, IApplicableToHitObject
{
public override string Description => "Flip objects on the chosen axes.";
public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) };
[SettingSource("Mirrored axes", "Choose which axes objects are mirrored over.")]
public Bindable<MirrorType> Reflection { get; } = new Bindable<MirrorType>();
public void ApplyToHitObject(HitObject hitObject)
{
var osuObject = (OsuHitObject)hitObject;
switch (Reflection.Value)
{
case MirrorType.Horizontal:
OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject);
break;
case MirrorType.Vertical:
OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
break;
case MirrorType.Both:
OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject);
OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
break;
}
}
public enum MirrorType
{
Horizontal,
Vertical,
Both
}
}
}

View File

@ -4,8 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
@ -16,7 +14,6 @@ using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -29,7 +26,6 @@ using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Osu.Utils; using osu.Game.Rulesets.Osu.Utils;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -67,11 +63,6 @@ namespace osu.Game.Rulesets.Osu.Mods
/// </summary> /// </summary>
private const float distance_cap = 380f; private const float distance_cap = 380f;
// The distances from the hit objects to the borders of the playfield they start to "turn around" and curve towards the middle.
// The closer the hit objects draw to the border, the sharper the turn
private const byte border_distance_x = 192;
private const byte border_distance_y = 144;
/// <summary> /// <summary>
/// The extent of rotation towards playfield centre when a circle is near the edge /// The extent of rotation towards playfield centre when a circle is near the edge
/// </summary> /// </summary>
@ -341,46 +332,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset) public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
{ {
drawableRuleset.Overlays.Add(new TargetBeatContainer(drawableRuleset.Beatmap.HitObjects.First().StartTime)); drawableRuleset.Overlays.Add(new Metronome(drawableRuleset.Beatmap.HitObjects.First().StartTime));
}
public class TargetBeatContainer : BeatSyncedContainer
{
private readonly double firstHitTime;
private PausableSkinnableSound sample;
public TargetBeatContainer(double firstHitTime)
{
this.firstHitTime = firstHitTime;
AllowMistimedEventFiring = false;
Divisor = 1;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[]
{
sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana"))
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
if (!IsBeatSyncedWithTrack) return;
int timeSignature = (int)timingPoint.TimeSignature;
// play metronome from one measure before the first object.
if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature)
return;
sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f;
sample.Play();
}
} }
#endregion #endregion

View File

@ -166,6 +166,7 @@ namespace osu.Game.Rulesets.Osu
new OsuModDifficultyAdjust(), new OsuModDifficultyAdjust(),
new OsuModClassic(), new OsuModClassic(),
new OsuModRandom(), new OsuModRandom(),
new OsuModMirror(),
}; };
case ModType.Automation: case ModType.Automation:

View File

@ -2,7 +2,11 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Utils namespace osu.Game.Rulesets.Osu.Utils
@ -100,5 +104,47 @@ namespace osu.Game.Rulesets.Osu.Utils
initial.Length * MathF.Sin(finalAngleRad) initial.Length * MathF.Sin(finalAngleRad)
); );
} }
/// <summary>
/// Reflects the position of the <see cref="OsuHitObject"/> in the playfield horizontally.
/// </summary>
/// <param name="osuObject">The object to reflect.</param>
public static void ReflectHorizontally(OsuHitObject osuObject)
{
osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y);
if (!(osuObject is Slider slider))
return;
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
foreach (var point in controlPoints)
point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y);
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
}
/// <summary>
/// Reflects the position of the <see cref="OsuHitObject"/> in the playfield vertically.
/// </summary>
/// <param name="osuObject">The object to reflect.</param>
public static void ReflectVertically(OsuHitObject osuObject)
{
osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y);
if (!(osuObject is Slider slider))
return;
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
foreach (var point in controlPoints)
point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
}
} }
} }

View File

@ -171,19 +171,24 @@ namespace osu.Game.Online.API
WebRequest?.Abort(); WebRequest?.Abort();
string responseString = WebRequest?.GetResponseString(); // in the case of a cancellation we don't care about whether there's an error in the response.
if (!(e is OperationCanceledException))
if (!string.IsNullOrEmpty(responseString))
{ {
try string responseString = WebRequest?.GetResponseString();
{
// attempt to decode a displayable error string. // naive check whether there's an error in the response to avoid unnecessary JSON deserialisation.
var error = JsonConvert.DeserializeObject<DisplayableError>(responseString); if (!string.IsNullOrEmpty(responseString) && responseString.Contains(@"""error"""))
if (error != null)
e = new APIException(error.ErrorMessage, e);
}
catch
{ {
try
{
// attempt to decode a displayable error string.
var error = JsonConvert.DeserializeObject<DisplayableError>(responseString);
if (error != null)
e = new APIException(error.ErrorMessage, e);
}
catch
{
}
} }
} }

View File

@ -116,10 +116,7 @@ namespace osu.Game.Online
} }
catch (Exception e) catch (Exception e)
{ {
Logger.Log($"{clientName} connection error: {e}", LoggingTarget.Network); await handleErrorAndDelay(e, cancellationToken).ConfigureAwait(false);
// retry on any failure.
await Task.Delay(5000, cancellationToken).ConfigureAwait(false);
} }
} }
} }
@ -129,6 +126,15 @@ namespace osu.Game.Online
} }
} }
/// <summary>
/// Handles an exception and delays an async flow.
/// </summary>
private async Task handleErrorAndDelay(Exception exception, CancellationToken cancellationToken)
{
Logger.Log($"{clientName} connection error: {exception}", LoggingTarget.Network);
await Task.Delay(5000, cancellationToken).ConfigureAwait(false);
}
private HubConnection buildConnection(CancellationToken cancellationToken) private HubConnection buildConnection(CancellationToken cancellationToken)
{ {
var builder = new HubConnectionBuilder() var builder = new HubConnectionBuilder()
@ -155,17 +161,18 @@ namespace osu.Game.Online
return newConnection; return newConnection;
} }
private Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken) private async Task onConnectionClosed(Exception? ex, CancellationToken cancellationToken)
{ {
isConnected.Value = false; isConnected.Value = false;
Logger.Log(ex != null ? $"{clientName} lost connection: {ex}" : $"{clientName} disconnected", LoggingTarget.Network); if (ex != null)
await handleErrorAndDelay(ex, cancellationToken).ConfigureAwait(false);
else
Logger.Log($"{clientName} disconnected", LoggingTarget.Network);
// make sure a disconnect wasn't triggered (and this is still the active connection). // make sure a disconnect wasn't triggered (and this is still the active connection).
if (!cancellationToken.IsCancellationRequested) if (!cancellationToken.IsCancellationRequested)
Task.Run(connect, default); await Task.Run(connect, default).ConfigureAwait(false);
return Task.CompletedTask;
} }
private async Task disconnect(bool takeLock) private async Task disconnect(bool takeLock)

View File

@ -18,6 +18,8 @@ using osu.Game.Scoring;
using osu.Game.Users.Drawables; using osu.Game.Users.Drawables;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Framework.Localisation;
using osu.Framework.Extensions.LocalisationExtensions;
namespace osu.Game.Overlays.BeatmapSet.Scores namespace osu.Game.Overlays.BeatmapSet.Scores
{ {
@ -211,11 +213,11 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
return content.ToArray(); return content.ToArray();
} }
protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? default);
private class HeaderText : OsuSpriteText private class HeaderText : OsuSpriteText
{ {
public HeaderText(string text) public HeaderText(LocalisableString text)
{ {
Text = text.ToUpper(); Text = text.ToUpper();
Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold); Font = OsuFont.GetFont(size: 10, weight: FontWeight.Bold);

View File

@ -13,6 +13,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Users; using osu.Game.Users;
using osu.Game.Users.Drawables; using osu.Game.Users.Drawables;
using osuTK; using osuTK;
using osu.Framework.Localisation;
namespace osu.Game.Overlays.Rankings.Tables namespace osu.Game.Overlays.Rankings.Tables
{ {
@ -74,7 +75,7 @@ namespace osu.Game.Overlays.Rankings.Tables
protected override Drawable CreateHeader(int index, TableColumn column) protected override Drawable CreateHeader(int index, TableColumn column)
{ {
var title = column?.Header ?? string.Empty; var title = column?.Header ?? default;
return new HeaderText(title, title == HighlightedColumn); return new HeaderText(title, title == HighlightedColumn);
} }
@ -109,7 +110,7 @@ namespace osu.Game.Overlays.Rankings.Tables
{ {
private readonly bool isHighlighted; private readonly bool isHighlighted;
public HeaderText(string text, bool isHighlighted) public HeaderText(LocalisableString text, bool isHighlighted)
{ {
this.isHighlighted = isHighlighted; this.isHighlighted = isHighlighted;

View File

@ -9,6 +9,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Users; using osu.Game.Users;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Framework.Localisation;
namespace osu.Game.Overlays.Rankings.Tables namespace osu.Game.Overlays.Rankings.Tables
{ {
@ -31,8 +32,8 @@ namespace osu.Game.Overlays.Rankings.Tables
protected override Drawable CreateHeader(int index, TableColumn column) protected override Drawable CreateHeader(int index, TableColumn column)
{ {
var title = column?.Header ?? string.Empty; var title = column?.Header ?? default;
return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title)); return new UserTableHeaderText(title, HighlightedColumn == title, GradeColumns.Contains(title.ToString()));
} }
protected sealed override Country GetCountry(UserStatistics item) => item.User.Country; protected sealed override Country GetCountry(UserStatistics item) => item.User.Country;
@ -66,7 +67,7 @@ namespace osu.Game.Overlays.Rankings.Tables
private class UserTableHeaderText : HeaderText private class UserTableHeaderText : HeaderText
{ {
public UserTableHeaderText(string text, bool isHighlighted, bool isGrade) public UserTableHeaderText(LocalisableString text, bool isHighlighted, bool isGrade)
: base(text, isHighlighted) : base(text, isHighlighted)
{ {
Margin = new MarginPadding Margin = new MarginPadding

View File

@ -0,0 +1,53 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Mods
{
public class Metronome : BeatSyncedContainer
{
private readonly double firstHitTime;
private PausableSkinnableSound sample;
/// <param name="firstHitTime">Start time of the first hit object, used for providing a count down.</param>
public Metronome(double firstHitTime)
{
this.firstHitTime = firstHitTime;
AllowMistimedEventFiring = false;
Divisor = 1;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[]
{
sample = new PausableSkinnableSound(new SampleInfo("Gameplay/catch-banana"))
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
if (!IsBeatSyncedWithTrack) return;
int timeSignature = (int)timingPoint.TimeSignature;
// play metronome from one measure before the first object.
if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature)
return;
sample.Frequency.Value = beatIndex % timeSignature == 0 ? 1 : 0.5f;
sample.Play();
}
}
}

View File

@ -0,0 +1,13 @@
// 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.
namespace osu.Game.Rulesets.Mods
{
public abstract class ModMirror : Mod
{
public override string Name => "Mirror";
public override string Acronym => "MR";
public override ModType Type => ModType.Conversion;
public override double ScoreMultiplier => 1;
}
}

View File

@ -2,10 +2,12 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.LocalisationExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
@ -42,11 +44,11 @@ namespace osu.Game.Screens.Edit
}); });
} }
protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? string.Empty); protected override Drawable CreateHeader(int index, TableColumn column) => new HeaderText(column?.Header ?? default);
private class HeaderText : OsuSpriteText private class HeaderText : OsuSpriteText
{ {
public HeaderText(string text) public HeaderText(LocalisableString text)
{ {
Text = text.ToUpper(); Text = text.ToUpper();
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold); Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold);

View File

@ -36,7 +36,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Realm" Version="10.3.0" /> <PackageReference Include="Realm" Version="10.3.0" />
<PackageReference Include="ppy.osu.Framework" Version="2021.723.0" /> <PackageReference Include="ppy.osu.Framework" Version="2021.728.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" />
<PackageReference Include="Sentry" Version="3.8.2" /> <PackageReference Include="Sentry" Version="3.8.2" />
<PackageReference Include="SharpCompress" Version="0.28.3" /> <PackageReference Include="SharpCompress" Version="0.28.3" />

View File

@ -70,7 +70,7 @@
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.723.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2021.728.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2021.722.0" />
</ItemGroup> </ItemGroup>
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) --> <!-- 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" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="ppy.osu.Framework" Version="2021.723.0" /> <PackageReference Include="ppy.osu.Framework" Version="2021.728.0" />
<PackageReference Include="SharpCompress" Version="0.28.3" /> <PackageReference Include="SharpCompress" Version="0.28.3" />
<PackageReference Include="NUnit" Version="3.13.2" /> <PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />