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

Merge branch 'master' into slider-ball-scaling

This commit is contained in:
Dean Herbert 2019-07-30 07:54:40 +09:00
commit 4ee20bae30
49 changed files with 694 additions and 318 deletions

View File

@ -63,6 +63,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.723.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2019.729.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -71,7 +71,7 @@ namespace osu.Desktop
switch (newScreen) switch (newScreen)
{ {
case Intro _: case IntroScreen _:
case MainMenu _: case MainMenu _:
versionManager?.Show(); versionManager?.Show();
break; break;

View File

@ -27,6 +27,8 @@ namespace osu.Desktop.Updater
public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited(); public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited();
private static readonly Logger logger = Logger.GetLogger("updater");
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuGameBase game) private void load(NotificationOverlay notification, OsuGameBase game)
{ {
@ -77,7 +79,7 @@ namespace osu.Desktop.Updater
{ {
if (useDeltaPatching) if (useDeltaPatching)
{ {
Logger.Error(e, @"delta patching failed!"); logger.Add(@"delta patching failed; will attempt full download!");
//could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959) //could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959)
//try again without deltas. //try again without deltas.
@ -163,16 +165,11 @@ namespace osu.Desktop.Updater
{ {
public LogLevel Level { get; set; } = LogLevel.Info; public LogLevel Level { get; set; } = LogLevel.Info;
private Logger logger;
public void Write(string message, LogLevel logLevel) public void Write(string message, LogLevel logLevel)
{ {
if (logLevel < Level) if (logLevel < Level)
return; return;
if (logger == null)
logger = Logger.GetLogger("updater");
logger.Add(message); logger.Add(message);
} }

View File

@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
public override string Description => @"Play with no approach circles and fading circles/sliders."; public override string Description => @"Play with no approach circles and fading circles/sliders.";
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn) };
private const double fade_in_duration_multiplier = 0.4; private const double fade_in_duration_multiplier = 0.4;
private const double fade_out_duration_multiplier = 0.3; private const double fade_out_duration_multiplier = 0.3;

View File

@ -0,0 +1,92 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osuTK;
namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModSpinIn : Mod, IApplicableToDrawableHitObjects, IReadFromConfig
{
public override string Name => "Spin In";
public override string Acronym => "SI";
public override IconUsage Icon => FontAwesome.Solid.Undo;
public override ModType Type => ModType.Fun;
public override string Description => "Circles spin in. No approach circles.";
public override double ScoreMultiplier => 1;
// todo: this mod should be able to be compatible with hidden with a bit of further implementation.
public override Type[] IncompatibleMods => new[] { typeof(OsuModeObjectScaleTween), typeof(OsuModHidden) };
private const int rotate_offset = 360;
private const float rotate_starting_width = 2;
private Bindable<bool> increaseFirstObjectVisibility = new Bindable<bool>();
public void ReadFromConfig(OsuConfigManager config)
{
increaseFirstObjectVisibility = config.GetBindable<bool>(OsuSetting.IncreaseFirstObjectVisibility);
}
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
{
foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0))
{
switch (drawable)
{
case DrawableSpinner _:
continue;
default:
drawable.ApplyCustomUpdateState += applyZoomState;
break;
}
}
}
private void applyZoomState(DrawableHitObject drawable, ArmedState state)
{
var h = (OsuHitObject)drawable.HitObject;
switch (drawable)
{
case DrawableHitCircle circle:
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
{
circle.ApproachCircle.Hide();
circle.RotateTo(rotate_offset).Then().RotateTo(0, h.TimePreempt, Easing.InOutSine);
circle.ScaleTo(new Vector2(rotate_starting_width, 0)).Then().ScaleTo(1, h.TimePreempt, Easing.InOutSine);
// bypass fade in.
if (state == ArmedState.Idle)
circle.FadeIn();
}
break;
case DrawableSlider slider:
using (slider.BeginAbsoluteSequence(h.StartTime - h.TimePreempt))
{
slider.ScaleTo(0).Then().ScaleTo(1, h.TimePreempt, Easing.InOutSine);
// bypass fade in.
if (state == ArmedState.Idle)
slider.FadeIn();
}
break;
}
}
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -28,6 +29,8 @@ namespace osu.Game.Rulesets.Osu.Mods
private Bindable<bool> increaseFirstObjectVisibility = new Bindable<bool>(); private Bindable<bool> increaseFirstObjectVisibility = new Bindable<bool>();
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn) };
public void ReadFromConfig(OsuConfigManager config) public void ReadFromConfig(OsuConfigManager config)
{ {
increaseFirstObjectVisibility = config.GetBindable<bool>(OsuSetting.IncreaseFirstObjectVisibility); increaseFirstObjectVisibility = config.GetBindable<bool>(OsuSetting.IncreaseFirstObjectVisibility);
@ -64,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Mods
case DrawableSlider _: case DrawableSlider _:
case DrawableHitCircle _: case DrawableHitCircle _:
{ {
using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt))
drawable.ScaleTo(StartScale).Then().ScaleTo(EndScale, h.TimePreempt, Easing.OutSine); drawable.ScaleTo(StartScale).Then().ScaleTo(EndScale, h.TimePreempt, Easing.OutSine);
break; break;
} }
@ -75,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
case DrawableHitCircle circle: case DrawableHitCircle circle:
// we don't want to see the approach circle // we don't want to see the approach circle
using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true)) using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt))
circle.ApproachCircle.Hide(); circle.ApproachCircle.Hide();
break; break;
} }

View File

@ -97,13 +97,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
Position = pointStartPosition, Position = pointStartPosition,
Rotation = rotation, Rotation = rotation,
Alpha = 0, Alpha = 0,
Scale = new Vector2(1.5f), Scale = new Vector2(1.5f * currHitObject.Scale),
}); });
using (fp.BeginAbsoluteSequence(fadeInTime)) using (fp.BeginAbsoluteSequence(fadeInTime))
{ {
fp.FadeIn(currHitObject.TimeFadeIn); fp.FadeIn(currHitObject.TimeFadeIn);
fp.ScaleTo(1, currHitObject.TimeFadeIn, Easing.Out); fp.ScaleTo(currHitObject.Scale, currHitObject.TimeFadeIn, Easing.Out);
fp.MoveTo(pointEndPosition, currHitObject.TimeFadeIn, Easing.Out); fp.MoveTo(pointEndPosition, currHitObject.TimeFadeIn, Easing.Out);

View File

@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.UpdateInitialTransforms(); base.UpdateInitialTransforms();
ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt)); ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt));
ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt); ApproachCircle.ScaleTo(1f, HitObject.TimePreempt);
ApproachCircle.Expire(true); ApproachCircle.Expire(true);
} }
@ -169,6 +169,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
.FadeOut(100); .FadeOut(100);
explode.FadeIn(flash_in); explode.FadeIn(flash_in);
explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad);
using (BeginDelayedSequence(flash_in, true)) using (BeginDelayedSequence(flash_in, true))
{ {
@ -178,7 +179,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
number.FadeOut(); number.FadeOut();
this.FadeOut(800); this.FadeOut(800);
explodeContainer.ScaleTo(1.5f, 400, Easing.OutQuad);
} }
Expire(); Expire();

View File

@ -3,6 +3,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
@ -20,27 +22,40 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private double animDuration; private double animDuration;
private readonly SkinnableDrawable scaleContainer;
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
: base(repeatPoint) : base(repeatPoint)
{ {
this.repeatPoint = repeatPoint; this.repeatPoint = repeatPoint;
this.drawableSlider = drawableSlider; this.drawableSlider = drawableSlider;
Size = new Vector2(45 * repeatPoint.Scale); Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Blending = BlendingMode.Additive; Blending = BlendingMode.Additive;
Origin = Anchor.Centre; Origin = Anchor.Centre;
InternalChildren = new Drawable[] InternalChild = scaleContainer = new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon
{
new SkinnableDrawable("Play/osu/reversearrow", _ => new SpriteIcon
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Icon = FontAwesome.Solid.ChevronRight Icon = FontAwesome.Solid.ChevronRight,
}) Size = new Vector2(0.35f)
}, confineMode: ConfineMode.NoScaling)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}; };
} }
private readonly IBindable<float> scaleBindable = new Bindable<float>();
[BackgroundDependencyLoader]
private void load()
{
scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
scaleBindable.BindTo(HitObject.ScaleBindable);
}
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (repeatPoint.StartTime <= Time.Current) if (repeatPoint.StartTime <= Time.Current)

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osuTK; using osuTK;
@ -16,25 +18,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
public const double ANIM_DURATION = 150; public const double ANIM_DURATION = 150;
private const float default_tick_size = 16;
public bool Tracking { get; set; } public bool Tracking { get; set; }
public override bool DisplayResult => false; public override bool DisplayResult => false;
private readonly SkinnableDrawable scaleContainer;
public DrawableSliderTick(SliderTick sliderTick) public DrawableSliderTick(SliderTick sliderTick)
: base(sliderTick) : base(sliderTick)
{ {
Size = new Vector2(16 * sliderTick.Scale); Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Origin = Anchor.Centre; Origin = Anchor.Centre;
InternalChildren = new Drawable[] InternalChild = scaleContainer = new SkinnableDrawable("Play/osu/sliderscorepoint", _ => new CircularContainer
{
new SkinnableDrawable("Play/osu/sliderscorepoint", _ => new Container
{ {
Masking = true, Masking = true,
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre, Origin = Anchor.Centre,
CornerRadius = Size.X / 2, Size = new Vector2(default_tick_size),
BorderThickness = 2, BorderThickness = default_tick_size / 4,
BorderColour = Color4.White, BorderColour = Color4.White,
Child = new Box Child = new Box
{ {
@ -43,9 +46,21 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Alpha = 0.3f, Alpha = 0.3f,
} }
}) })
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}; };
} }
private readonly IBindable<float> scaleBindable = new Bindable<float>();
[BackgroundDependencyLoader]
private void load()
{
scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
scaleBindable.BindTo(HitObject.ScaleBindable);
}
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (timeOffset >= 0) if (timeOffset >= 0)

View File

@ -6,6 +6,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
@ -24,7 +25,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(TextureStore textures) private void load(TextureStore textures)
{ {
Child = new SkinnableSprite("Play/osu/approachcircle"); Child = new SkinnableApproachCircle();
}
private class SkinnableApproachCircle : SkinnableSprite
{
public SkinnableApproachCircle()
: base("Play/osu/approachcircle")
{
}
protected override Drawable CreateDefault(string name)
{
var drawable = base.CreateDefault(name);
// account for the sprite being used for the default approach circle being taken from stable,
// when hitcircles have 5px padding on each size. this should be removed if we update the sprite.
drawable.Scale = new Vector2(128 / 118f);
return drawable;
}
} }
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Lines;
@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
protected Path Path => path; protected Path Path => path;
public float PathRadius public virtual float PathRadius
{ {
get => path.PathRadius; get => path.PathRadius;
set => path.PathRadius = value; set => path.PathRadius = value;
@ -75,22 +76,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
protected SliderBody() protected SliderBody()
{ {
InternalChild = path = new SliderPath(); RecyclePath();
} }
/// <summary> /// <summary>
/// Initialises a new <see cref="SliderPath"/>, releasing all resources retained by the old one. /// Initialises a new <see cref="SliderPath"/>, releasing all resources retained by the old one.
/// </summary> /// </summary>
public void RecyclePath() public virtual void RecyclePath()
{ {
InternalChild = path = new SliderPath InternalChild = path = new SliderPath
{ {
Position = path.Position, Position = path?.Position ?? Vector2.Zero,
PathRadius = path.PathRadius, PathRadius = path?.PathRadius ?? 10,
AccentColour = path.AccentColour, AccentColour = path?.AccentColour ?? Color4.White,
BorderColour = path.BorderColour, BorderColour = path?.BorderColour ?? Color4.White,
BorderSize = path.BorderSize, BorderSize = path?.BorderSize ?? DEFAULT_BORDER_SIZE,
Vertices = path.Vertices Vertices = path?.Vertices ?? Array.Empty<Vector2>()
}; };
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osuTK; using osuTK;
@ -23,6 +24,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public double? SnakedStart { get; private set; } public double? SnakedStart { get; private set; }
public double? SnakedEnd { get; private set; } public double? SnakedEnd { get; private set; }
public override float PathRadius
{
get => base.PathRadius;
set
{
if (base.PathRadius == value)
return;
base.PathRadius = value;
Refresh();
}
}
public override Vector2 PathOffset => snakedPathOffset; public override Vector2 PathOffset => snakedPathOffset;
/// <summary> /// <summary>
@ -78,9 +93,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
slider.Path.GetPathToProgress(CurrentCurve, 0, 1); slider.Path.GetPathToProgress(CurrentCurve, 0, 1);
SetVertices(CurrentCurve); SetVertices(CurrentCurve);
// The body is sized to the full path size to avoid excessive autosize computations // Force the body to be the final path size to avoid excessive autosize computations
Path.AutoSizeAxes = Axes.Both;
Size = Path.Size; Size = Path.Size;
updatePathSize();
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero); snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]); snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
@ -93,6 +111,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
setRange(lastSnakedStart, lastSnakedEnd); setRange(lastSnakedStart, lastSnakedEnd);
} }
public override void RecyclePath()
{
base.RecyclePath();
updatePathSize();
}
private void updatePathSize()
{
// Force the path to its final size to avoid excessive framebuffer resizes
Path.AutoSizeAxes = Axes.None;
Path.Size = Size;
}
private void setRange(double p0, double p1) private void setRange(double p0, double p1)
{ {
if (p0 > p1) if (p0 > p1)

View File

@ -134,6 +134,7 @@ namespace osu.Game.Rulesets.Osu
{ {
new OsuModTransform(), new OsuModTransform(),
new OsuModWiggle(), new OsuModWiggle(),
new OsuModSpinIn(),
new MultiMod(new OsuModGrow(), new OsuModDeflate()), new MultiMod(new OsuModGrow(), new OsuModDeflate()),
new MultiMod(new ModWindUp<OsuHitObject>(), new ModWindDown<OsuHitObject>()), new MultiMod(new ModWindUp<OsuHitObject>(), new ModWindDown<OsuHitObject>()),
}; };

View File

@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Osu.UI
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;
// Calculated from osu!stable as 512 (default gamefield size) / 640 (default window size)
Size = new Vector2(0.8f); Size = new Vector2(0.8f);
InternalChild = new Container InternalChild = new Container

View File

@ -0,0 +1,69 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Menus
{
[TestFixture]
public abstract class IntroTestScene : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(StartupScreen),
typeof(IntroScreen),
typeof(OsuScreen),
typeof(IntroTestScene),
};
[Cached]
private OsuLogo logo;
protected IntroTestScene()
{
Drawable introStack = null;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Depth = float.MaxValue,
Colour = Color4.Black,
},
logo = new OsuLogo
{
Alpha = 0,
RelativePositionAxes = Axes.Both,
Depth = float.MinValue,
Position = new Vector2(0.5f),
}
};
AddStep("restart sequence", () =>
{
logo.FinishTransforms();
logo.IsTracking = false;
introStack?.Expire();
Add(introStack = new OsuScreenStack(CreateScreen())
{
RelativeSizeAxes = Axes.Both,
});
});
}
protected abstract IScreen CreateScreen();
}
}

View File

@ -0,0 +1,15 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
using osu.Framework.Screens;
using osu.Game.Screens.Menu;
namespace osu.Game.Tests.Visual.Menus
{
[TestFixture]
public class TestSceneIntroCircles : IntroTestScene
{
protected override IScreen CreateScreen() => new IntroCircles();
}
}

View File

@ -1,54 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Timing;
using osu.Game.Screens.Menu;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Menus
{
[TestFixture]
public class TestSceneIntroSequence : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuLogo),
};
public TestSceneIntroSequence()
{
OsuLogo logo;
var rateAdjustClock = new StopwatchClock(true);
var framedClock = new FramedClock(rateAdjustClock);
framedClock.ProcessFrame();
Add(new Container
{
RelativeSizeAxes = Axes.Both,
Clock = framedClock,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
logo = new OsuLogo
{
Anchor = Anchor.Centre,
}
}
});
AddStep(@"Restart", logo.PlayIntro);
AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v);
}
}
}

View File

@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Online
private TestChatOverlay chatOverlay; private TestChatOverlay chatOverlay;
private ChannelManager channelManager; private ChannelManager channelManager;
private readonly Channel channel1 = new Channel(new User()) { Name = "test1" }; private readonly Channel channel1 = new Channel(new User()) { Name = "test really long username" };
private readonly Channel channel2 = new Channel(new User()) { Name = "test2" }; private readonly Channel channel2 = new Channel(new User()) { Name = "test2" };
[SetUp] [SetUp]

View File

@ -47,8 +47,8 @@ namespace osu.Game.Tournament.Screens.MapPool
mapFlows = new FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>> mapFlows = new FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>>
{ {
Y = 100, Y = 100,
Spacing = new Vector2(10, 20), Spacing = new Vector2(10, 10),
Padding = new MarginPadding(50), Padding = new MarginPadding(25),
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
@ -218,7 +218,7 @@ namespace osu.Game.Tournament.Screens.MapPool
{ {
mapFlows.Add(currentFlow = new FillFlowContainer<TournamentBeatmapPanel> mapFlows.Add(currentFlow = new FillFlowContainer<TournamentBeatmapPanel>
{ {
Spacing = new Vector2(10, 20), Spacing = new Vector2(10, 5),
Direction = FillDirection.Full, Direction = FillDirection.Full,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y AutoSizeAxes = Axes.Y

View File

@ -386,7 +386,7 @@ namespace osu.Game.Beatmaps
beatmap.OnlineBeatmapID = res.OnlineBeatmapID; beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
}; };
req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap}", e); }; req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); };
// intentionally blocking to limit web request concurrency // intentionally blocking to limit web request concurrency
req.Perform(api); req.Perform(api);

View File

@ -253,7 +253,7 @@ namespace osu.Game.Database
using (Stream s = reader.GetStream(file)) using (Stream s = reader.GetStream(file))
s.CopyTo(hashable); s.CopyTo(hashable);
return hashable.ComputeSHA2Hash(); return hashable.Length > 0 ? hashable.ComputeSHA2Hash() : null;
} }
/// <summary> /// <summary>

View File

@ -76,7 +76,12 @@ namespace osu.Game.Graphics.UserInterface
{ {
Masking = true, Masking = true,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = path = new SmoothPath { RelativeSizeAxes = Axes.Both, PathRadius = 1 } Child = path = new SmoothPath
{
AutoSizeAxes = Axes.None,
RelativeSizeAxes = Axes.Both,
PathRadius = 1
}
}); });
} }

View File

@ -64,7 +64,7 @@ namespace osu.Game.Graphics.UserInterface
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
Children = new Drawable[] Children = new Drawable[]
{ {
text = new OsuSpriteText { Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold) }, text = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
icon = new SpriteIcon icon = new SpriteIcon
{ {
Size = new Vector2(14), Size = new Vector2(14),
@ -84,7 +84,11 @@ namespace osu.Game.Graphics.UserInterface
} }
}; };
Current.ValueChanged += selected => { icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle; }; Current.ValueChanged += selected =>
{
icon.Icon = selected.NewValue ? FontAwesome.Regular.CheckCircle : FontAwesome.Regular.Circle;
text.Font = text.Font.With(weight: selected.NewValue ? FontWeight.Bold : FontWeight.Medium);
};
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -87,7 +87,8 @@ namespace osu.Game
private BackButton backButton; private BackButton backButton;
private MainMenu menuScreen; private MainMenu menuScreen;
private Intro introScreen;
private IntroScreen introScreen;
private Bindable<int> configRuleset; private Bindable<int> configRuleset;
@ -589,7 +590,7 @@ namespace osu.Game
{ {
int recentLogCount = 0; int recentLogCount = 0;
const double debounce = 5000; const double debounce = 60000;
Logger.NewEntry += entry => Logger.NewEntry += entry =>
{ {
@ -761,7 +762,7 @@ namespace osu.Game
if (introScreen == null) if (introScreen == null)
return true; return true;
if (!introScreen.DidLoadMenu || !(screenStack.CurrentScreen is Intro)) if (!introScreen.DidLoadMenu || !(screenStack.CurrentScreen is IntroScreen))
{ {
Scheduler.Add(introScreen.MakeCurrent); Scheduler.Add(introScreen.MakeCurrent);
return true; return true;
@ -796,7 +797,7 @@ namespace osu.Game
{ {
switch (newScreen) switch (newScreen)
{ {
case Intro intro: case IntroScreen intro:
introScreen = intro; introScreen = intro;
break; break;

View File

@ -13,6 +13,8 @@ namespace osu.Game.Overlays.Chat.Tabs
public override bool IsSwitchable => false; public override bool IsSwitchable => false;
protected override bool IsBoldWhenActive => false;
public ChannelSelectorTabItem() public ChannelSelectorTabItem()
: base(new ChannelSelectorTabChannel()) : base(new ChannelSelectorTabChannel())
{ {
@ -22,7 +24,7 @@ namespace osu.Game.Overlays.Chat.Tabs
Icon.Alpha = 0; Icon.Alpha = 0;
Text.Font = Text.Font.With(size: 45); Text.Font = Text.Font.With(size: 45);
TextBold.Font = Text.Font.With(size: 45); Text.Truncate = false;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -16,6 +16,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osuTK.Input;
namespace osu.Game.Overlays.Chat.Tabs namespace osu.Game.Overlays.Chat.Tabs
{ {
@ -28,7 +29,6 @@ namespace osu.Game.Overlays.Chat.Tabs
public override bool IsRemovable => !Pinned; public override bool IsRemovable => !Pinned;
protected readonly SpriteText Text; protected readonly SpriteText Text;
protected readonly SpriteText TextBold;
protected readonly ClickableContainer CloseButton; protected readonly ClickableContainer CloseButton;
private readonly Box box; private readonly Box box;
private readonly Box highlightBox; private readonly Box highlightBox;
@ -87,20 +87,17 @@ namespace osu.Game.Overlays.Chat.Tabs
}, },
Text = new OsuSpriteText Text = new OsuSpriteText
{ {
Margin = new MarginPadding(5),
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Text = value.ToString(), Text = value.ToString(),
Font = OsuFont.GetFont(size: 18) Font = OsuFont.GetFont(size: 18),
}, Padding = new MarginPadding(5)
TextBold = new OsuSpriteText
{ {
Alpha = 0, Left = LeftTextPadding,
Margin = new MarginPadding(5), Right = RightTextPadding,
Origin = Anchor.CentreLeft, },
Anchor = Anchor.CentreLeft, RelativeSizeAxes = Axes.X,
Text = value.ToString(), Truncate = true,
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold)
}, },
CloseButton = new TabCloseButton CloseButton = new TabCloseButton
{ {
@ -118,10 +115,16 @@ namespace osu.Game.Overlays.Chat.Tabs
}; };
} }
protected virtual float LeftTextPadding => 5;
protected virtual float RightTextPadding => IsRemovable ? 40 : 5;
protected virtual IconUsage DisplayIcon => FontAwesome.Solid.Hashtag; protected virtual IconUsage DisplayIcon => FontAwesome.Solid.Hashtag;
protected virtual bool ShowCloseOnHover => true; protected virtual bool ShowCloseOnHover => true;
protected virtual bool IsBoldWhenActive => true;
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)
{ {
if (IsRemovable && ShowCloseOnHover) if (IsRemovable && ShowCloseOnHover)
@ -138,6 +141,19 @@ namespace osu.Game.Overlays.Chat.Tabs
updateState(); updateState();
} }
protected override bool OnMouseUp(MouseUpEvent e)
{
switch (e.Button)
{
case MouseButton.Middle:
CloseButton.Click();
return true;
default:
return false;
}
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
@ -189,8 +205,7 @@ namespace osu.Game.Overlays.Chat.Tabs
box.FadeColour(BackgroundActive, TRANSITION_LENGTH, Easing.OutQuint); box.FadeColour(BackgroundActive, TRANSITION_LENGTH, Easing.OutQuint);
highlightBox.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); highlightBox.FadeIn(TRANSITION_LENGTH, Easing.OutQuint);
Text.FadeOut(TRANSITION_LENGTH, Easing.OutQuint); if (IsBoldWhenActive) Text.Font = Text.Font.With(weight: FontWeight.Bold);
TextBold.FadeIn(TRANSITION_LENGTH, Easing.OutQuint);
} }
protected virtual void FadeInactive() protected virtual void FadeInactive()
@ -202,8 +217,7 @@ namespace osu.Game.Overlays.Chat.Tabs
box.FadeColour(BackgroundInactive, TRANSITION_LENGTH, Easing.OutQuint); box.FadeColour(BackgroundInactive, TRANSITION_LENGTH, Easing.OutQuint);
highlightBox.FadeOut(TRANSITION_LENGTH, Easing.OutQuint); highlightBox.FadeOut(TRANSITION_LENGTH, Easing.OutQuint);
Text.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); Text.Font = Text.Font.With(weight: FontWeight.Medium);
TextBold.FadeOut(TRANSITION_LENGTH, Easing.OutQuint);
} }
protected override void OnActivated() => updateState(); protected override void OnActivated() => updateState();

View File

@ -62,11 +62,10 @@ namespace osu.Game.Overlays.Chat.Tabs
}); });
avatar.OnLoadComplete += d => d.FadeInFromZero(300, Easing.OutQuint); avatar.OnLoadComplete += d => d.FadeInFromZero(300, Easing.OutQuint);
Text.X = ChatOverlay.TAB_AREA_HEIGHT;
TextBold.X = ChatOverlay.TAB_AREA_HEIGHT;
} }
protected override float LeftTextPadding => base.LeftTextPadding + ChatOverlay.TAB_AREA_HEIGHT;
protected override bool ShowCloseOnHover => false; protected override bool ShowCloseOnHover => false;
protected override void FadeActive() protected override void FadeActive()

View File

@ -148,12 +148,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
if (State.Value == newState && !force) if (State.Value == newState && !force)
return; return;
// apply any custom state overrides
ApplyCustomUpdateState?.Invoke(this, newState);
if (newState == ArmedState.Hit)
PlaySamples();
if (UseTransformStateManagement) if (UseTransformStateManagement)
{ {
double transformTime = HitObject.StartTime - InitialLifetimeOffset; double transformTime = HitObject.StartTime - InitialLifetimeOffset;
@ -178,6 +172,12 @@ namespace osu.Game.Rulesets.Objects.Drawables
state.Value = newState; state.Value = newState;
UpdateState(newState); UpdateState(newState);
// apply any custom state overrides
ApplyCustomUpdateState?.Invoke(this, newState);
if (newState == ArmedState.Hit)
PlaySamples();
} }
/// <summary> /// <summary>

View File

@ -80,6 +80,9 @@ namespace osu.Game.Scoring.Legacy
else if (version >= 20121008) else if (version >= 20121008)
scoreInfo.OnlineScoreID = sr.ReadInt32(); scoreInfo.OnlineScoreID = sr.ReadInt32();
if (scoreInfo.OnlineScoreID <= 0)
scoreInfo.OnlineScoreID = null;
if (compressedReplay?.Length > 0) if (compressedReplay?.Length > 0)
{ {
using (var replayInStream = new MemoryStream(compressedReplay)) using (var replayInStream = new MemoryStream(compressedReplay))

View File

@ -45,7 +45,15 @@ namespace osu.Game.Screens
private OsuScreen loadableScreen; private OsuScreen loadableScreen;
private ShaderPrecompiler precompiler; private ShaderPrecompiler precompiler;
protected virtual OsuScreen CreateLoadableScreen() => showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro(); protected virtual OsuScreen CreateLoadableScreen()
{
if (showDisclaimer)
return new Disclaimer(getIntroSequence());
return getIntroSequence();
}
private IntroScreen getIntroSequence() => new IntroCircles();
protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler(); protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler();

View File

@ -21,7 +21,6 @@ namespace osu.Game.Screens.Menu
{ {
public class Disclaimer : StartupScreen public class Disclaimer : StartupScreen
{ {
private Intro intro;
private SpriteIcon icon; private SpriteIcon icon;
private Color4 iconColour; private Color4 iconColour;
private LinkFlowContainer textFlow; private LinkFlowContainer textFlow;
@ -32,10 +31,13 @@ namespace osu.Game.Screens.Menu
private const float icon_y = -85; private const float icon_y = -85;
private const float icon_size = 30; private const float icon_size = 30;
private readonly OsuScreen nextScreen;
private readonly Bindable<User> currentUser = new Bindable<User>(); private readonly Bindable<User> currentUser = new Bindable<User>();
public Disclaimer() public Disclaimer(OsuScreen nextScreen = null)
{ {
this.nextScreen = nextScreen;
ValidForResume = false; ValidForResume = false;
} }
@ -146,7 +148,8 @@ namespace osu.Game.Screens.Menu
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
LoadComponentAsync(intro = new Intro()); if (nextScreen != null)
LoadComponentAsync(nextScreen);
} }
public override void OnEntering(IScreen last) public override void OnEntering(IScreen last)
@ -170,7 +173,7 @@ namespace osu.Game.Screens.Menu
.Then(5500) .Then(5500)
.FadeOut(250) .FadeOut(250)
.ScaleTo(0.9f, 250, Easing.InQuint) .ScaleTo(0.9f, 250, Easing.InQuint)
.Finally(d => this.Push(intro)); .Finally(d => this.Push(nextScreen));
} }
} }
} }

View File

@ -2,7 +2,6 @@
// 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.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
@ -12,41 +11,24 @@ using osu.Framework.MathUtils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.IO.Archives; using osu.Game.IO.Archives;
using osu.Game.Screens.Backgrounds;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Menu namespace osu.Game.Screens.Menu
{ {
public class Intro : StartupScreen public class IntroCircles : IntroScreen
{ {
private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83"; private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83";
/// <summary>
/// Whether we have loaded the menu previously.
/// </summary>
public bool DidLoadMenu;
private MainMenu mainMenu;
private SampleChannel welcome; private SampleChannel welcome;
private SampleChannel seeya;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
private readonly BindableDouble exitingVolumeFade = new BindableDouble(1);
[Resolved]
private AudioManager audio { get; set; }
private Bindable<bool> menuVoice;
private Bindable<bool> menuMusic; private Bindable<bool> menuMusic;
private Track track; private Track track;
private WorkingBeatmap introBeatmap; private WorkingBeatmap introBeatmap;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game) private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game, ISampleStore samples)
{ {
menuVoice = config.GetBindable<bool>(OsuSetting.MenuVoice);
menuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic); menuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic);
BeatmapSetInfo setInfo = null; BeatmapSetInfo setInfo = null;
@ -75,15 +57,13 @@ namespace osu.Game.Screens.Menu
introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
track = introBeatmap.Track; track = introBeatmap.Track;
welcome = audio.Samples.Get(@"welcome"); if (config.Get<bool>(OsuSetting.MenuVoice))
seeya = audio.Samples.Get(@"seeya"); welcome = samples.Get(@"welcome");
} }
private const double delay_step_one = 2300; private const double delay_step_one = 2300;
private const double delay_step_two = 600; private const double delay_step_two = 600;
public const int EXIT_DELAY = 3000;
protected override void LogoArriving(OsuLogo logo, bool resuming) protected override void LogoArriving(OsuLogo logo, bool resuming)
{ {
base.LogoArriving(logo, resuming); base.LogoArriving(logo, resuming);
@ -93,86 +73,34 @@ namespace osu.Game.Screens.Menu
Beatmap.Value = introBeatmap; Beatmap.Value = introBeatmap;
introBeatmap = null; introBeatmap = null;
if (menuVoice.Value) welcome?.Play();
welcome.Play();
Scheduler.AddDelayed(delegate Scheduler.AddDelayed(delegate
{ {
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu. // Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu.
if (menuMusic.Value) if (menuMusic.Value)
{ {
track.Start(); track.Restart();
track = null; track = null;
} }
LoadComponentAsync(mainMenu = new MainMenu()); PrepareMenuLoad();
Scheduler.AddDelayed(delegate Scheduler.AddDelayed(LoadMenu, delay_step_one);
{
DidLoadMenu = true;
this.Push(mainMenu);
}, delay_step_one);
}, delay_step_two); }, delay_step_two);
}
logo.Colour = Color4.White;
logo.Ripple = false;
const int quick_appear = 350;
int initialMovementTime = logo.Alpha > 0.2f ? quick_appear : 0;
logo.MoveTo(new Vector2(0.5f), initialMovementTime, Easing.OutQuint);
if (!resuming)
{
logo.ScaleTo(1); logo.ScaleTo(1);
logo.FadeIn(); logo.FadeIn();
logo.PlayIntro(); logo.PlayIntro();
} }
else
{
logo.Triangles = false;
logo
.ScaleTo(1, initialMovementTime, Easing.OutQuint)
.FadeIn(quick_appear, Easing.OutQuint)
.Then()
.RotateTo(20, EXIT_DELAY * 1.5f)
.FadeOut(EXIT_DELAY);
}
} }
public override void OnSuspending(IScreen next) public override void OnSuspending(IScreen next)
{ {
track = null;
this.FadeOut(300); this.FadeOut(300);
base.OnSuspending(next); base.OnSuspending(next);
} }
public override bool OnExiting(IScreen next)
{
//cancel exiting if we haven't loaded the menu yet.
return !DidLoadMenu;
}
public override void OnResuming(IScreen last)
{
this.FadeIn(300);
double fadeOutTime = EXIT_DELAY;
//we also handle the exit transition.
if (menuVoice.Value)
seeya.Play();
else
fadeOutTime = 500;
audio.AddAdjustment(AdjustableProperty.Volume, exitingVolumeFade);
this.TransformBindableTo(exitingVolumeFade, 0, fadeOutTime).OnComplete(_ => this.Exit());
//don't want to fade out completely else we will stop running updates.
Game.FadeTo(0.01f, fadeOutTime);
base.OnResuming(last);
}
} }
} }

View File

@ -0,0 +1,114 @@
// 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;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Screens.Backgrounds;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Menu
{
public abstract class IntroScreen : StartupScreen
{
private readonly BindableDouble exitingVolumeFade = new BindableDouble(1);
public const int EXIT_DELAY = 3000;
[Resolved]
private AudioManager audio { get; set; }
private SampleChannel seeya;
private Bindable<bool> menuVoice;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
{
menuVoice = config.GetBindable<bool>(OsuSetting.MenuVoice);
seeya = audio.Samples.Get(@"seeya");
}
/// <summary>
/// Whether we have loaded the menu previously.
/// </summary>
public bool DidLoadMenu { get; private set; }
public override bool OnExiting(IScreen next)
{
//cancel exiting if we haven't loaded the menu yet.
return !DidLoadMenu;
}
public override void OnResuming(IScreen last)
{
this.FadeIn(300);
double fadeOutTime = EXIT_DELAY;
//we also handle the exit transition.
if (menuVoice.Value)
seeya.Play();
else
fadeOutTime = 500;
audio.AddAdjustment(AdjustableProperty.Volume, exitingVolumeFade);
this.TransformBindableTo(exitingVolumeFade, 0, fadeOutTime).OnComplete(_ => this.Exit());
//don't want to fade out completely else we will stop running updates.
Game.FadeTo(0.01f, fadeOutTime);
base.OnResuming(last);
}
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
base.LogoArriving(logo, resuming);
logo.Colour = Color4.White;
logo.Triangles = false;
logo.Ripple = false;
if (!resuming)
{
logo.MoveTo(new Vector2(0.5f));
logo.ScaleTo(Vector2.One);
logo.Hide();
}
else
{
const int quick_appear = 350;
int initialMovementTime = logo.Alpha > 0.2f ? quick_appear : 0;
logo.MoveTo(new Vector2(0.5f), initialMovementTime, Easing.OutQuint);
logo
.ScaleTo(1, initialMovementTime, Easing.OutQuint)
.FadeIn(quick_appear, Easing.OutQuint)
.Then()
.RotateTo(20, EXIT_DELAY * 1.5f)
.FadeOut(EXIT_DELAY);
}
}
private MainMenu mainMenu;
protected void PrepareMenuLoad()
{
LoadComponentAsync(mainMenu = new MainMenu());
}
protected void LoadMenu()
{
DidLoadMenu = true;
this.Push(mainMenu);
}
}
}

View File

@ -123,7 +123,7 @@ namespace osu.Game.Screens.Menu
var track = Beatmap.Value.Track; var track = Beatmap.Value.Track;
var metadata = Beatmap.Value.Metadata; var metadata = Beatmap.Value.Metadata;
if (last is Intro && track != null) if (last is IntroScreen && track != null)
{ {
if (!track.IsRunning) if (!track.IsRunning)
{ {

View File

@ -212,7 +212,7 @@ namespace osu.Game.Screens.Multi
public override bool OnExiting(IScreen next) public override bool OnExiting(IScreen next)
{ {
if (!(screenStack.CurrentScreen is LoungeSubScreen)) if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen))
{ {
screenStack.Exit(); screenStack.Exit();
return true; return true;

View File

@ -19,11 +19,11 @@ namespace osu.Game.Screens.Play
private const float remaining_time_container_max_size = 0.3f; private const float remaining_time_container_max_size = 0.3f;
private const int vertical_margin = 25; private const int vertical_margin = 25;
private List<BreakPeriod> breaks;
private readonly Container fadeContainer; private readonly Container fadeContainer;
public List<BreakPeriod> Breaks private IReadOnlyList<BreakPeriod> breaks;
public IReadOnlyList<BreakPeriod> Breaks
{ {
get => breaks; get => breaks;
set set

View File

@ -26,6 +26,9 @@ namespace osu.Game.Screens.Select
{ {
public class BeatmapCarousel : OsuScrollContainer public class BeatmapCarousel : OsuScrollContainer
{ {
private const float bleed_top = FilterControl.HEIGHT;
private const float bleed_bottom = Footer.HEIGHT;
/// <summary> /// <summary>
/// Triggered when the <see cref="BeatmapSets"/> loaded change and are completely loaded. /// Triggered when the <see cref="BeatmapSets"/> loaded change and are completely loaded.
/// </summary> /// </summary>
@ -81,7 +84,8 @@ namespace osu.Game.Screens.Select
itemsCache.Invalidate(); itemsCache.Invalidate();
scrollPositionCache.Invalidate(); scrollPositionCache.Invalidate();
Schedule(() => // Run on late scheduler want to ensure this runs after all pending UpdateBeatmapSet / RemoveBeatmapSet operations are run.
SchedulerAfterChildren.Add(() =>
{ {
BeatmapSetsChanged?.Invoke(); BeatmapSetsChanged?.Invoke();
BeatmapSetsLoaded = true; BeatmapSetsLoaded = true;
@ -129,9 +133,7 @@ namespace osu.Game.Screens.Select
loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable()); loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable());
} }
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
{
Schedule(() =>
{ {
var existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID); var existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID);
@ -141,7 +143,6 @@ namespace osu.Game.Screens.Select
root.RemoveChild(existingSet); root.RemoveChild(existingSet);
itemsCache.Invalidate(); itemsCache.Invalidate();
}); });
}
public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
{ {
@ -338,6 +339,25 @@ namespace osu.Game.Screens.Select
public bool AllowSelection = true; public bool AllowSelection = true;
/// <summary>
/// Half the height of the visible content.
/// <remarks>
/// This is different from the height of <see cref="ScrollContainer{T}.displayableContent"/>, since
/// the beatmap carousel bleeds into the <see cref="FilterControl"/> and the <see cref="Footer"/>
/// </remarks>
/// </summary>
private float visibleHalfHeight => (DrawHeight + bleed_bottom + bleed_top) / 2;
/// <summary>
/// The position of the lower visible bound with respect to the current scroll position.
/// </summary>
private float visibleBottomBound => Current + DrawHeight + bleed_bottom;
/// <summary>
/// The position of the upper visible bound with respect to the current scroll position.
/// </summary>
private float visibleUpperBound => Current - bleed_top;
public void FlushPendingFilterOperations() public void FlushPendingFilterOperations()
{ {
if (PendingFilter?.Completed == false) if (PendingFilter?.Completed == false)
@ -414,6 +434,8 @@ namespace osu.Game.Screens.Select
return true; return true;
} }
protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => ReceivePositionalInputAt(screenSpacePos);
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
@ -424,17 +446,15 @@ namespace osu.Game.Screens.Select
if (!scrollPositionCache.IsValid) if (!scrollPositionCache.IsValid)
updateScrollPosition(); updateScrollPosition();
float drawHeight = DrawHeight;
// Remove all items that should no longer be on-screen // Remove all items that should no longer be on-screen
scrollableContent.RemoveAll(p => p.Y < Current - p.DrawHeight || p.Y > Current + drawHeight || !p.IsPresent); scrollableContent.RemoveAll(p => p.Y < visibleUpperBound - p.DrawHeight || p.Y > visibleBottomBound || !p.IsPresent);
// Find index range of all items that should be on-screen // Find index range of all items that should be on-screen
Trace.Assert(Items.Count == yPositions.Count); Trace.Assert(Items.Count == yPositions.Count);
int firstIndex = yPositions.BinarySearch(Current - DrawableCarouselItem.MAX_HEIGHT); int firstIndex = yPositions.BinarySearch(visibleUpperBound - DrawableCarouselItem.MAX_HEIGHT);
if (firstIndex < 0) firstIndex = ~firstIndex; if (firstIndex < 0) firstIndex = ~firstIndex;
int lastIndex = yPositions.BinarySearch(Current + drawHeight); int lastIndex = yPositions.BinarySearch(visibleBottomBound);
if (lastIndex < 0) lastIndex = ~lastIndex; if (lastIndex < 0) lastIndex = ~lastIndex;
int notVisibleCount = 0; int notVisibleCount = 0;
@ -486,9 +506,8 @@ namespace osu.Game.Screens.Select
// Update externally controlled state of currently visible items // Update externally controlled state of currently visible items
// (e.g. x-offset and opacity). // (e.g. x-offset and opacity).
float halfHeight = drawHeight / 2;
foreach (DrawableCarouselItem p in scrollableContent.Children) foreach (DrawableCarouselItem p in scrollableContent.Children)
updateItem(p, halfHeight); updateItem(p);
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
@ -542,7 +561,7 @@ namespace osu.Game.Screens.Select
yPositions.Clear(); yPositions.Clear();
float currentY = DrawHeight / 2; float currentY = visibleHalfHeight;
DrawableCarouselBeatmapSet lastSet = null; DrawableCarouselBeatmapSet lastSet = null;
scrollTarget = null; scrollTarget = null;
@ -575,7 +594,6 @@ namespace osu.Game.Screens.Select
float? setY = null; float? setY = null;
if (!d.IsLoaded || beatmap.Alpha == 0) // can't use IsPresent due to DrawableCarouselItem override. if (!d.IsLoaded || beatmap.Alpha == 0) // can't use IsPresent due to DrawableCarouselItem override.
// ReSharper disable once PossibleNullReferenceException (resharper broken?)
setY = lastSet.Y + lastSet.DrawHeight + 5; setY = lastSet.Y + lastSet.DrawHeight + 5;
if (d.IsLoaded) if (d.IsLoaded)
@ -596,7 +614,7 @@ namespace osu.Game.Screens.Select
currentY += d.DrawHeight + 5; currentY += d.DrawHeight + 5;
} }
currentY += DrawHeight / 2; currentY += visibleHalfHeight;
scrollableContent.Height = currentY; scrollableContent.Height = currentY;
if (BeatmapSetsLoaded && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected)) if (BeatmapSetsLoaded && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected))
@ -637,18 +655,15 @@ namespace osu.Game.Screens.Select
/// the current scroll position. /// the current scroll position.
/// </summary> /// </summary>
/// <param name="p">The item to be updated.</param> /// <param name="p">The item to be updated.</param>
/// <param name="halfHeight">Half the draw height of the carousel container.</param> private void updateItem(DrawableCarouselItem p)
private void updateItem(DrawableCarouselItem p, float halfHeight)
{ {
var height = p.IsPresent ? p.DrawHeight : 0; float itemDrawY = p.Position.Y - visibleUpperBound + p.DrawHeight / 2;
float dist = Math.Abs(1f - itemDrawY / visibleHalfHeight);
float itemDrawY = p.Position.Y - Current + height / 2;
float dist = Math.Abs(1f - itemDrawY / halfHeight);
// Setting the origin position serves as an additive position on top of potential // Setting the origin position serves as an additive position on top of potential
// local transformation we may want to apply (e.g. when a item gets selected, we // local transformation we may want to apply (e.g. when a item gets selected, we
// may want to smoothly transform it leftwards.) // may want to smoothly transform it leftwards.)
p.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0); p.OriginPosition = new Vector2(-offsetX(dist, visibleHalfHeight), 0);
// We are applying a multiplicative alpha (which is internally done by nesting an // We are applying a multiplicative alpha (which is internally done by nesting an
// additional container and setting that container's alpha) such that we can // additional container and setting that container's alpha) such that we can

View File

@ -25,22 +25,6 @@ namespace osu.Game.Screens.Select
private Bindable<BeatmapDetailTab> selectedTab; private Bindable<BeatmapDetailTab> selectedTab;
private void invokeOnFilter()
{
OnFilter?.Invoke(tabs.Current.Value, modsCheckbox.Current.Value);
}
[BackgroundDependencyLoader]
private void load(OsuColour colour, OsuConfigManager config)
{
modsCheckbox.AccentColour = tabs.AccentColour = colour.YellowLight;
selectedTab = config.GetBindable<BeatmapDetailTab>(OsuSetting.BeatmapDetailTab);
tabs.Current.BindTo(selectedTab);
tabs.Current.TriggerChange();
}
public BeatmapDetailAreaTabControl() public BeatmapDetailAreaTabControl()
{ {
Height = HEIGHT; Height = HEIGHT;
@ -66,12 +50,31 @@ namespace osu.Game.Screens.Select
Anchor = Anchor.BottomRight, Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight, Origin = Anchor.BottomRight,
Text = @"Mods", Text = @"Mods",
Alpha = 0,
}, },
}; };
tabs.Current.ValueChanged += _ => invokeOnFilter(); tabs.Current.ValueChanged += _ => invokeOnFilter();
modsCheckbox.Current.ValueChanged += _ => invokeOnFilter(); modsCheckbox.Current.ValueChanged += _ => invokeOnFilter();
} }
[BackgroundDependencyLoader]
private void load(OsuColour colour, OsuConfigManager config)
{
modsCheckbox.AccentColour = tabs.AccentColour = colour.YellowLight;
selectedTab = config.GetBindable<BeatmapDetailTab>(OsuSetting.BeatmapDetailTab);
tabs.Current.BindTo(selectedTab);
tabs.Current.TriggerChange();
}
private void invokeOnFilter()
{
OnFilter?.Invoke(tabs.Current.Value, modsCheckbox.Current.Value);
modsCheckbox.FadeTo(tabs.Current.Value == BeatmapDetailTab.Details ? 0 : 1, 200, Easing.OutQuint);
}
} }
public enum BeatmapDetailTab public enum BeatmapDetailTab

View File

@ -14,7 +14,6 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Select.Filter; using osu.Game.Screens.Select.Filter;
using Container = osu.Framework.Graphics.Containers.Container; using Container = osu.Framework.Graphics.Containers.Container;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -22,6 +21,8 @@ namespace osu.Game.Screens.Select
{ {
public class FilterControl : Container public class FilterControl : Container
{ {
public const float HEIGHT = 100;
public Action<FilterCriteria> FilterChanged; public Action<FilterCriteria> FilterChanged;
private readonly OsuTabControl<SortMode> sortTabs; private readonly OsuTabControl<SortMode> sortTabs;
@ -187,11 +188,5 @@ namespace osu.Game.Screens.Select
} }
private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria()); private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria());
protected override bool OnMouseDown(MouseDownEvent e) => true;
protected override bool OnMouseMove(MouseMoveEvent e) => true;
protected override bool OnClick(ClickEvent e) => true;
} }
} }

View File

@ -121,7 +121,7 @@ namespace osu.Game.Screens.Select
Size = new Vector2(wedged_container_size.X, 1), Size = new Vector2(wedged_container_size.X, 1),
Padding = new MarginPadding Padding = new MarginPadding
{ {
Bottom = 50, Bottom = Footer.HEIGHT,
Top = wedged_container_size.Y + left_area_padding, Top = wedged_container_size.Y + left_area_padding,
Left = left_area_padding, Left = left_area_padding,
Right = left_area_padding * 2, Right = left_area_padding * 2,
@ -147,7 +147,15 @@ namespace osu.Game.Screens.Select
Width = 0.5f, Width = 0.5f,
Children = new Drawable[] Children = new Drawable[]
{ {
Carousel = new BeatmapCarousel new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Top = FilterControl.HEIGHT,
Bottom = Footer.HEIGHT
},
Child = Carousel = new BeatmapCarousel
{ {
Masking = false, Masking = false,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -157,10 +165,11 @@ namespace osu.Game.Screens.Select
SelectionChanged = updateSelectedBeatmap, SelectionChanged = updateSelectedBeatmap,
BeatmapSetsChanged = carouselBeatmapsLoaded, BeatmapSetsChanged = carouselBeatmapsLoaded,
}, },
},
FilterControl = new FilterControl FilterControl = new FilterControl
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 100, Height = FilterControl.HEIGHT,
FilterChanged = c => Carousel.Filter(c), FilterChanged = c => Carousel.Filter(c),
Background = { Width = 2 }, Background = { Width = 2 },
Exit = () => Exit = () =>

View File

@ -38,7 +38,7 @@ namespace osu.Game.Skinning
private void onChange() => private void onChange() =>
// schedule required to avoid calls after disposed. // schedule required to avoid calls after disposed.
// note that this has the side-effect of components only performance a skin change when they are alive. // note that this has the side-effect of components only performing a skin change when they are alive.
Scheduler.AddOnce(() => SkinChanged(skin, allowDefaultFallback)); Scheduler.AddOnce(() => SkinChanged(skin, allowDefaultFallback));
protected override void LoadAsyncComplete() protected override void LoadAsyncComplete()

View File

@ -1,4 +1,4 @@
// 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;
@ -8,23 +8,13 @@ using osuTK;
namespace osu.Game.Skinning namespace osu.Game.Skinning
{ {
public class SkinnableDrawable : SkinnableDrawable<Drawable>
{
public SkinnableDrawable(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(name, defaultImplementation, allowFallback, confineMode)
{
}
}
/// <summary> /// <summary>
/// A drawable which can be skinned via an <see cref="ISkinSource"/>. /// A drawable which can be skinned via an <see cref="ISkinSource"/>.
/// </summary> /// </summary>
/// <typeparam name="T">The type of drawable.</typeparam> public class SkinnableDrawable : SkinReloadableDrawable
public class SkinnableDrawable<T> : SkinReloadableDrawable
where T : Drawable
{ {
/// <summary> /// <summary>
/// The displayed component. May or may not be a type-<typeparamref name="T"/> member. /// The displayed component.
/// </summary> /// </summary>
protected Drawable Drawable { get; private set; } protected Drawable Drawable { get; private set; }
@ -39,7 +29,7 @@ namespace osu.Game.Skinning
/// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param> /// <param name="defaultImplementation">A function to create the default skin implementation of this element.</param>
/// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param> /// <param name="allowFallback">A conditional to decide whether to allow fallback to the default implementation if a skinned element is not present.</param>
/// <param name="confineMode">How (if at all) the <see cref="Drawable"/> should be resize to fit within our own bounds.</param> /// <param name="confineMode">How (if at all) the <see cref="Drawable"/> should be resize to fit within our own bounds.</param>
public SkinnableDrawable(string name, Func<string, T> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) public SkinnableDrawable(string name, Func<string, Drawable> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: this(name, allowFallback, confineMode) : this(name, allowFallback, confineMode)
{ {
createDefault = defaultImplementation; createDefault = defaultImplementation;
@ -54,13 +44,13 @@ namespace osu.Game.Skinning
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
} }
private readonly Func<string, T> createDefault; private readonly Func<string, Drawable> createDefault;
private readonly Cached scaling = new Cached(); private readonly Cached scaling = new Cached();
private bool isDefault; private bool isDefault;
protected virtual T CreateDefault(string name) => createDefault(name); protected virtual Drawable CreateDefault(string name) => createDefault(name);
/// <summary> /// <summary>
/// Whether to apply size restrictions (specified via <see cref="confineMode"/>) to the default implementation. /// Whether to apply size restrictions (specified via <see cref="confineMode"/>) to the default implementation.

View File

@ -3,6 +3,7 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
@ -11,7 +12,7 @@ namespace osu.Game.Skinning
/// <summary> /// <summary>
/// A skinnable element which uses a stable sprite and can therefore share implementation logic. /// A skinnable element which uses a stable sprite and can therefore share implementation logic.
/// </summary> /// </summary>
public class SkinnableSprite : SkinnableDrawable<Sprite> public class SkinnableSprite : SkinnableDrawable
{ {
protected override bool ApplySizeRestrictionsToDefault => true; protected override bool ApplySizeRestrictionsToDefault => true;
@ -23,6 +24,6 @@ namespace osu.Game.Skinning
{ {
} }
protected override Sprite CreateDefault(string name) => new Sprite { Texture = textures.Get(name) }; protected override Drawable CreateDefault(string name) => new Sprite { Texture = textures.Get(name) };
} }
} }

View File

@ -6,7 +6,7 @@ using osu.Framework.Graphics.Sprites;
namespace osu.Game.Skinning namespace osu.Game.Skinning
{ {
public class SkinnableSpriteText : SkinnableDrawable<SpriteText>, IHasText public class SkinnableSpriteText : SkinnableDrawable, IHasText
{ {
public SkinnableSpriteText(string name, Func<string, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit) public SkinnableSpriteText(string name, Func<string, SpriteText> defaultImplementation, Func<ISkinSource, bool> allowFallback = null, ConfineMode confineMode = ConfineMode.ScaleDownToFit)
: base(name, defaultImplementation, allowFallback, confineMode) : base(name, defaultImplementation, allowFallback, confineMode)

View File

@ -15,7 +15,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.4" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.723.0" /> <PackageReference Include="ppy.osu.Framework" Version="2019.729.0" />
<PackageReference Include="SharpCompress" Version="0.23.0" /> <PackageReference Include="SharpCompress" Version="0.23.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />

View File

@ -105,8 +105,8 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2019.702.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.723.0" /> <PackageReference Include="ppy.osu.Framework" Version="2019.729.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.723.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2019.729.0" />
<PackageReference Include="SharpCompress" Version="0.22.0" /> <PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />

View File

@ -1,15 +1,25 @@
// 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.Threading.Tasks;
using Foundation; using Foundation;
using osu.Framework.iOS; using osu.Framework.iOS;
using osu.Game; using osu.Game;
using UIKit;
namespace osu.iOS namespace osu.iOS
{ {
[Register("AppDelegate")] [Register("AppDelegate")]
public class AppDelegate : GameAppDelegate public class AppDelegate : GameAppDelegate
{ {
protected override Framework.Game CreateGame() => new OsuGameIOS(); private OsuGameIOS game;
protected override Framework.Game CreateGame() => game = new OsuGameIOS();
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
Task.Run(() => game.Import(url.Path));
return true;
}
} }
} }

View File

@ -40,5 +40,70 @@
</array> </array>
<key>XSAppIconAssets</key> <key>XSAppIconAssets</key>
<string>Assets.xcassets/AppIcon.appiconset</string> <string>Assets.xcassets/AppIcon.appiconset</string>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string></string>
</array>
<key>UTTypeIdentifier</key>
<string>sh.ppy.osu.items</string>
<key>UTTypeTagSpecification</key>
<dict/>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>sh.ppy.osu.items</string>
</array>
<key>UTTypeIdentifier</key>
<string>sh.ppy.osu.osr</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>osr</string>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>sh.ppy.osu.items</string>
</array>
<key>UTTypeIdentifier</key>
<string>sh.ppy.osu.osk</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>osk</string>
</dict>
</dict>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>sh.ppy.osu.items</string>
</array>
<key>UTTypeIdentifier</key>
<string>sh.ppy.osu.osz</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>osz</string>
</dict>
</dict>
</array>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>CFBundleTypeName</key>
<string>Supported osu! files</string>
<key>LSItemContentTypes</key>
<array>
<string>sh.ppy.osu.items</string>
</array>
</dict>
</array>
</dict> </dict>
</plist> </plist>