mirror of
https://github.com/ppy/osu.git
synced 2025-02-05 04:52:53 +08:00
Merge branch 'master' into fix-follow-point-transforms
This commit is contained in:
commit
7a9d878a34
@ -5,6 +5,6 @@
|
|||||||
"version": "3.1.100"
|
"version": "3.1.100"
|
||||||
},
|
},
|
||||||
"msbuild-sdks": {
|
"msbuild-sdks": {
|
||||||
"Microsoft.Build.Traversal": "2.0.24"
|
"Microsoft.Build.Traversal": "2.0.32"
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
@ -72,10 +72,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
|||||||
protected override Skill[] CreateSkills(IBeatmap beatmap)
|
protected override Skill[] CreateSkills(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty))
|
using (var catcher = new Catcher(beatmap.BeatmapInfo.BaseDifficulty))
|
||||||
{
|
|
||||||
halfCatcherWidth = catcher.CatchWidth * 0.5f;
|
halfCatcherWidth = catcher.CatchWidth * 0.5f;
|
||||||
halfCatcherWidth *= 0.8f; // We're only using 80% of the catcher's width to simulate imperfect gameplay.
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Skill[]
|
return new Skill[]
|
||||||
{
|
{
|
||||||
|
@ -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.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
@ -30,6 +31,22 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
Value = 5,
|
Value = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public override string SettingDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string circleSize = CircleSize.IsDefault ? string.Empty : $"CS {CircleSize.Value:N1}";
|
||||||
|
string approachRate = ApproachRate.IsDefault ? string.Empty : $"AR {ApproachRate.Value:N1}";
|
||||||
|
|
||||||
|
return string.Join(", ", new[]
|
||||||
|
{
|
||||||
|
circleSize,
|
||||||
|
base.SettingDescription,
|
||||||
|
approachRate
|
||||||
|
}.Where(s => !string.IsNullOrEmpty(s)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void TransferSettings(BeatmapDifficulty difficulty)
|
protected override void TransferSettings(BeatmapDifficulty difficulty)
|
||||||
{
|
{
|
||||||
base.TransferSettings(difficulty);
|
base.TransferSettings(difficulty);
|
||||||
|
@ -37,10 +37,15 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public CatcherAnimationState CurrentState { get; private set; }
|
public CatcherAnimationState CurrentState { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The width of the catcher which can receive fruit. Equivalent to "catchMargin" in osu-stable.
|
||||||
|
/// </summary>
|
||||||
|
private const float allowed_catch_range = 0.8f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Width of the area that can be used to attempt catches during gameplay.
|
/// Width of the area that can be used to attempt catches during gameplay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X);
|
internal float CatchWidth => CatcherArea.CATCHER_SIZE * Math.Abs(Scale.X) * allowed_catch_range;
|
||||||
|
|
||||||
protected bool Dashing
|
protected bool Dashing
|
||||||
{
|
{
|
||||||
@ -141,14 +146,14 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
var ourRadius = fruit.DisplayRadius;
|
var ourRadius = fruit.DisplayRadius;
|
||||||
float theirRadius = 0;
|
float theirRadius = 0;
|
||||||
|
|
||||||
const float allowance = 6;
|
const float allowance = 10;
|
||||||
|
|
||||||
while (caughtFruit.Any(f =>
|
while (caughtFruit.Any(f =>
|
||||||
f.LifetimeEnd == double.MaxValue &&
|
f.LifetimeEnd == double.MaxValue &&
|
||||||
Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2)))
|
Vector2Extensions.Distance(f.Position, fruit.Position) < (ourRadius + (theirRadius = f.DrawSize.X / 2 * f.Scale.X)) / (allowance / 2)))
|
||||||
{
|
{
|
||||||
var diff = (ourRadius + theirRadius) / allowance;
|
var diff = (ourRadius + theirRadius) / allowance;
|
||||||
fruit.X += (RNG.NextSingle() - 0.5f) * 2 * diff;
|
fruit.X += (RNG.NextSingle() - 0.5f) * diff * 2;
|
||||||
fruit.Y -= RNG.NextSingle() * diff;
|
fruit.Y -= RNG.NextSingle() * diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A container to be used in a <see cref="ManiaSkinnableTestScene"/> to provide a resolvable <see cref="Column"/> dependency.
|
||||||
|
/// </summary>
|
||||||
|
public class ColumnTestContainer : Container
|
||||||
|
{
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
|
private readonly Container content;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private readonly Column column;
|
||||||
|
|
||||||
|
public ColumnTestContainer(int column, ManiaAction action)
|
||||||
|
{
|
||||||
|
this.column = new Column(column)
|
||||||
|
{
|
||||||
|
Action = { Value = action },
|
||||||
|
AccentColour = Color4.Orange
|
||||||
|
};
|
||||||
|
|
||||||
|
InternalChild = content = new ManiaInputManager(new ManiaRuleset().RulesetInfo, 4)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
// 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.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Timing;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A test scene for a mania hitobject.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ManiaHitObjectTestScene : ManiaSkinnableTestScene
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
SetContents(() => new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Height = 0.7f,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new ColumnTestContainer(0, ManiaAction.Key1)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = 80,
|
||||||
|
Child = new ScrollingHitObjectContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Clock = new FramedClock(new StopwatchClock()),
|
||||||
|
}.With(c =>
|
||||||
|
{
|
||||||
|
c.Add(CreateHitObject().With(h => h.AccentColour.Value = Color4.Orange));
|
||||||
|
})
|
||||||
|
},
|
||||||
|
new ColumnTestContainer(1, ManiaAction.Key2)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = 80,
|
||||||
|
Child = new ScrollingHitObjectContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Clock = new FramedClock(new StopwatchClock()),
|
||||||
|
}.With(c =>
|
||||||
|
{
|
||||||
|
c.Add(CreateHitObject().With(h => h.AccentColour.Value = Color4.Orange));
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract DrawableManiaHitObject CreateHitObject();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling.Algorithms;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A test scene for skinnable mania components.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ManiaSkinnableTestScene : SkinnableTestScene
|
||||||
|
{
|
||||||
|
[Cached(Type = typeof(IScrollingInfo))]
|
||||||
|
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
|
||||||
|
|
||||||
|
protected ManiaSkinnableTestScene()
|
||||||
|
{
|
||||||
|
scrollingInfo.Direction.Value = ScrollingDirection.Down;
|
||||||
|
|
||||||
|
Add(new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.SlateGray.Opacity(0.2f),
|
||||||
|
Depth = 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestScrollingDown()
|
||||||
|
{
|
||||||
|
AddStep("change direction to down", () => scrollingInfo.Direction.Value = ScrollingDirection.Down);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestScrollingUp()
|
||||||
|
{
|
||||||
|
AddStep("change direction to up", () => scrollingInfo.Direction.Value = ScrollingDirection.Up);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestScrollingInfo : IScrollingInfo
|
||||||
|
{
|
||||||
|
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||||
|
IBindable<double> IScrollingInfo.TimeRange { get; } = new Bindable<double>(1000);
|
||||||
|
IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ConstantScrollAlgorithm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,13 +6,13 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Tests.Visual;
|
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||||
{
|
{
|
||||||
public class TestSceneDrawableJudgement : SkinnableTestScene
|
public class TestSceneDrawableJudgement : SkinnableTestScene
|
||||||
{
|
{
|
@ -16,4 +16,8 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
|
|
||||||
protected override string ComponentName => Component.ToString().ToLower();
|
protected override string ComponentName => Component.ToString().ToLower();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ManiaSkinComponents
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +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.
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania
|
|
||||||
{
|
|
||||||
public enum ManiaSkinComponents
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,6 +2,7 @@
|
|||||||
// 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.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Replays.Legacy;
|
using osu.Game.Replays.Legacy;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
@ -26,13 +27,7 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
|
|
||||||
public void FromLegacy(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
|
public void FromLegacy(LegacyReplayFrame legacyFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
|
||||||
{
|
{
|
||||||
// We don't need to fully convert, just create the converter
|
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||||
var converter = new ManiaBeatmapConverter(beatmap, new ManiaRuleset());
|
|
||||||
|
|
||||||
// NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling
|
|
||||||
// elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage.
|
|
||||||
|
|
||||||
var stage = new StageDefinition { Columns = converter.TargetColumns };
|
|
||||||
|
|
||||||
var normalAction = ManiaAction.Key1;
|
var normalAction = ManiaAction.Key1;
|
||||||
var specialAction = ManiaAction.Special1;
|
var specialAction = ManiaAction.Special1;
|
||||||
@ -42,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
|
|
||||||
while (activeColumns > 0)
|
while (activeColumns > 0)
|
||||||
{
|
{
|
||||||
var isSpecial = stage.IsSpecialColumn(counter);
|
var isSpecial = maniaBeatmap.Stages.First().IsSpecialColumn(counter);
|
||||||
|
|
||||||
if ((activeColumns & 1) > 0)
|
if ((activeColumns & 1) > 0)
|
||||||
Actions.Add(isSpecial ? specialAction : normalAction);
|
Actions.Add(isSpecial ? specialAction : normalAction);
|
||||||
@ -59,17 +54,15 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
|
|
||||||
public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
|
public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
|
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||||
|
|
||||||
int keys = 0;
|
int keys = 0;
|
||||||
|
|
||||||
var converter = new ManiaBeatmapConverter(beatmap, new ManiaRuleset());
|
|
||||||
|
|
||||||
var stage = new StageDefinition { Columns = converter.TargetColumns };
|
|
||||||
|
|
||||||
var specialColumns = new List<int>();
|
var specialColumns = new List<int>();
|
||||||
|
|
||||||
for (int i = 0; i < converter.TargetColumns; i++)
|
for (int i = 0; i < maniaBeatmap.TotalColumns; i++)
|
||||||
{
|
{
|
||||||
if (stage.IsSpecialColumn(i))
|
if (maniaBeatmap.Stages.First().IsSpecialColumn(i))
|
||||||
specialColumns.Add(i);
|
specialColumns.Add(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
41
osu.Game.Rulesets.Mania/Skinning/LegacyManiaColumnElement.cs
Normal file
41
osu.Game.Rulesets.Mania/Skinning/LegacyManiaColumnElement.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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 JetBrains.Annotations;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Skinning
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="CompositeDrawable"/> which is placed somewhere within a <see cref="Column"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class LegacyManiaColumnElement : CompositeDrawable
|
||||||
|
{
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
[CanBeNull]
|
||||||
|
protected ManiaStage Stage { get; private set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
protected Column Column { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The column index to use for texture lookups, in the case of no user-provided configuration.
|
||||||
|
/// </summary>
|
||||||
|
protected int FallbackColumnIndex { get; private set; }
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
if (Stage == null)
|
||||||
|
FallbackColumnIndex = Column.Index % 2 + 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int dist = Math.Min(Column.Index, Stage.Columns.Count - Column.Index - 1);
|
||||||
|
FallbackColumnIndex = dist % 2 + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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 osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
@ -15,9 +16,19 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
{
|
{
|
||||||
private readonly ISkin source;
|
private readonly ISkin source;
|
||||||
|
|
||||||
public ManiaLegacySkinTransformer(ISkin source)
|
private Lazy<bool> isLegacySkin;
|
||||||
|
|
||||||
|
public ManiaLegacySkinTransformer(ISkinSource source)
|
||||||
{
|
{
|
||||||
this.source = source;
|
this.source = source;
|
||||||
|
|
||||||
|
source.SourceChanged += sourceChanged;
|
||||||
|
sourceChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sourceChanged()
|
||||||
|
{
|
||||||
|
isLegacySkin = new Lazy<bool>(() => source.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version) != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Drawable GetDrawableComponent(ISkinComponent component)
|
public Drawable GetDrawableComponent(ISkinComponent component)
|
||||||
@ -26,6 +37,12 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
{
|
{
|
||||||
case GameplaySkinComponent<HitResult> resultComponent:
|
case GameplaySkinComponent<HitResult> resultComponent:
|
||||||
return getResult(resultComponent);
|
return getResult(resultComponent);
|
||||||
|
|
||||||
|
case ManiaSkinComponent _:
|
||||||
|
if (!isLegacySkin.Value)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -19,6 +19,7 @@ using osuTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
public class Column : ScrollingPlayfield, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
public class Column : ScrollingPlayfield, IKeyBindingHandler<ManiaAction>, IHasAccentColour
|
||||||
{
|
{
|
||||||
public const float COLUMN_WIDTH = 80;
|
public const float COLUMN_WIDTH = 80;
|
||||||
|
@ -23,6 +23,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A collection of <see cref="Column"/>s.
|
/// A collection of <see cref="Column"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Cached]
|
||||||
public class ManiaStage : ScrollingPlayfield
|
public class ManiaStage : ScrollingPlayfield
|
||||||
{
|
{
|
||||||
public const float COLUMN_SPACING = 1;
|
public const float COLUMN_SPACING = 1;
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
70
osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpunOut.cs
Normal file
70
osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerSpunOut.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneSpinnerSpunOut : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(SpinnerDisc),
|
||||||
|
typeof(DrawableSpinner),
|
||||||
|
typeof(DrawableOsuHitObject),
|
||||||
|
typeof(OsuModSpunOut)
|
||||||
|
};
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = new[] { new OsuModSpunOut() };
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSpunOut()
|
||||||
|
{
|
||||||
|
DrawableSpinner spinner = null;
|
||||||
|
|
||||||
|
AddStep("create spinner", () => spinner = createSpinner());
|
||||||
|
|
||||||
|
AddUntilStep("wait for end", () => Time.Current > spinner.LifetimeEnd);
|
||||||
|
|
||||||
|
AddAssert("spinner is completed", () => spinner.Progress >= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DrawableSpinner createSpinner()
|
||||||
|
{
|
||||||
|
var spinner = new Spinner
|
||||||
|
{
|
||||||
|
StartTime = Time.Current + 500,
|
||||||
|
EndTime = Time.Current + 2500
|
||||||
|
};
|
||||||
|
spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||||
|
|
||||||
|
var drawableSpinner = new DrawableSpinner(spinner)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var mod in SelectedMods.Value.OfType<IApplicableToDrawableHitObjects>())
|
||||||
|
mod.ApplyToDrawableHitObjects(new[] { drawableSpinner });
|
||||||
|
|
||||||
|
Add(drawableSpinner);
|
||||||
|
return drawableSpinner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
@ -30,6 +31,22 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
Value = 5,
|
Value = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public override string SettingDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string circleSize = CircleSize.IsDefault ? string.Empty : $"CS {CircleSize.Value:N1}";
|
||||||
|
string approachRate = ApproachRate.IsDefault ? string.Empty : $"AR {ApproachRate.Value:N1}";
|
||||||
|
|
||||||
|
return string.Join(", ", new[]
|
||||||
|
{
|
||||||
|
circleSize,
|
||||||
|
base.SettingDescription,
|
||||||
|
approachRate
|
||||||
|
}.Where(s => !string.IsNullOrEmpty(s)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void TransferSettings(BeatmapDifficulty difficulty)
|
protected override void TransferSettings(BeatmapDifficulty difficulty)
|
||||||
{
|
{
|
||||||
base.TransferSettings(difficulty);
|
base.TransferSettings(difficulty);
|
||||||
|
@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
void handleHitCircle(DrawableHitCircle circle)
|
void handleHitCircle(DrawableHitCircle circle)
|
||||||
{
|
{
|
||||||
if (!circle.IsHovered)
|
if (!circle.HitArea.IsHovered)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Debug.Assert(circle.HitObject.HitWindows != null);
|
Debug.Assert(circle.HitObject.HitWindows != null);
|
||||||
|
@ -2,21 +2,46 @@
|
|||||||
// 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.Collections.Generic;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModSpunOut : Mod
|
public class OsuModSpunOut : Mod, IApplicableToDrawableHitObjects
|
||||||
{
|
{
|
||||||
public override string Name => "Spun Out";
|
public override string Name => "Spun Out";
|
||||||
public override string Acronym => "SO";
|
public override string Acronym => "SO";
|
||||||
public override IconUsage? Icon => OsuIcon.ModSpunout;
|
public override IconUsage? Icon => OsuIcon.ModSpunout;
|
||||||
public override ModType Type => ModType.DifficultyReduction;
|
public override ModType Type => ModType.Automation;
|
||||||
public override string Description => @"Spinners will be automatically completed.";
|
public override string Description => @"Spinners will be automatically completed.";
|
||||||
public override double ScoreMultiplier => 0.9;
|
public override double ScoreMultiplier => 0.9;
|
||||||
public override bool Ranked => true;
|
public override bool Ranked => true;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) };
|
||||||
|
|
||||||
|
public void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables)
|
||||||
|
{
|
||||||
|
foreach (var hitObject in drawables)
|
||||||
|
{
|
||||||
|
if (hitObject is DrawableSpinner spinner)
|
||||||
|
{
|
||||||
|
spinner.HandleUserInput = false;
|
||||||
|
spinner.OnUpdate += onSpinnerUpdate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSpinnerUpdate(Drawable drawable)
|
||||||
|
{
|
||||||
|
var spinner = (DrawableSpinner)drawable;
|
||||||
|
|
||||||
|
spinner.Disc.Tracking = true;
|
||||||
|
spinner.Disc.Rotate(MathUtils.RadiansToDegrees((float)spinner.Clock.ElapsedFrameTime * 0.03f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
public readonly SkinnableDrawable CirclePiece;
|
public readonly SkinnableDrawable CirclePiece;
|
||||||
private readonly Container scaleContainer;
|
private readonly Container scaleContainer;
|
||||||
|
|
||||||
|
protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle;
|
||||||
|
|
||||||
public DrawableHitCircle(HitCircle h)
|
public DrawableHitCircle(HitCircle h)
|
||||||
: base(h)
|
: base(h)
|
||||||
{
|
{
|
||||||
@ -57,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
CirclePiece = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.HitCircle), _ => new MainCirclePiece()),
|
CirclePiece = new SkinnableDrawable(new OsuSkinComponent(CirclePieceComponent), _ => new MainCirclePiece()),
|
||||||
ApproachCircle = new ApproachCircle
|
ApproachCircle = new ApproachCircle
|
||||||
{
|
{
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Skinning;
|
using osu.Game.Rulesets.Osu.Skinning;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -196,6 +197,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void PlaySamples()
|
||||||
|
{
|
||||||
|
// rather than doing it this way, we should probably attach the sample to the tail circle.
|
||||||
|
// this can only be done after we stop using LegacyLastTick.
|
||||||
|
if (TailCircle.Result.Type != HitResult.Miss)
|
||||||
|
base.PlaySamples();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateStateTransforms(ArmedState state)
|
protected override void UpdateStateTransforms(ArmedState state)
|
||||||
{
|
{
|
||||||
base.UpdateStateTransforms(state);
|
base.UpdateStateTransforms(state);
|
||||||
|
@ -14,6 +14,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
private readonly IBindable<Vector2> positionBindable = new Bindable<Vector2>();
|
||||||
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
private readonly IBindable<int> pathVersion = new Bindable<int>();
|
||||||
|
|
||||||
|
protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle;
|
||||||
|
|
||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
|
|
||||||
public DrawableSliderHead(Slider slider, HitCircle h)
|
public DrawableSliderHead(Slider slider, HitCircle h)
|
||||||
|
@ -87,6 +87,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
|
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
|
||||||
{
|
{
|
||||||
|
// When the repeat is hit, the arrow should fade out on spot rather than following the slider
|
||||||
|
if (IsHit) return;
|
||||||
|
|
||||||
bool isRepeatAtEnd = sliderRepeat.RepeatIndex % 2 == 0;
|
bool isRepeatAtEnd = sliderRepeat.RepeatIndex % 2 == 0;
|
||||||
List<Vector2> curve = ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
|
List<Vector2> curve = ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
|
||||||
|
|
||||||
|
@ -176,17 +176,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
Disc.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false;
|
|
||||||
if (!SpmCounter.IsPresent && Disc.Tracking)
|
|
||||||
SpmCounter.FadeIn(HitObject.TimeFadeIn);
|
|
||||||
|
|
||||||
base.Update();
|
base.Update();
|
||||||
|
if (HandleUserInput)
|
||||||
|
Disc.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateAfterChildren()
|
protected override void UpdateAfterChildren()
|
||||||
{
|
{
|
||||||
base.UpdateAfterChildren();
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
|
if (!SpmCounter.IsPresent && Disc.Tracking)
|
||||||
|
SpmCounter.FadeIn(HitObject.TimeFadeIn);
|
||||||
|
|
||||||
circle.Rotation = Disc.Rotation;
|
circle.Rotation = Disc.Rotation;
|
||||||
Ticks.Rotation = Disc.Rotation;
|
Ticks.Rotation = Disc.Rotation;
|
||||||
SpmCounter.SetRotation(Disc.RotationAbsolute);
|
SpmCounter.SetRotation(Disc.RotationAbsolute);
|
||||||
|
@ -73,6 +73,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether currently in the correct time range to allow spinning.
|
||||||
|
/// </summary>
|
||||||
|
private bool isSpinnableTime => spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current;
|
||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
{
|
{
|
||||||
mousePosition = Parent.ToLocalSpace(e.ScreenSpaceMousePosition);
|
mousePosition = Parent.ToLocalSpace(e.ScreenSpaceMousePosition);
|
||||||
@ -93,27 +98,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
var thisAngle = -MathUtils.RadiansToDegrees(MathF.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2));
|
var thisAngle = -MathUtils.RadiansToDegrees(MathF.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2));
|
||||||
|
|
||||||
bool validAndTracking = tracking && spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current;
|
var delta = thisAngle - lastAngle;
|
||||||
|
|
||||||
if (validAndTracking)
|
if (tracking)
|
||||||
{
|
Rotate(delta);
|
||||||
if (!rotationTransferred)
|
|
||||||
{
|
|
||||||
currentRotation = Rotation * 2;
|
|
||||||
rotationTransferred = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thisAngle - lastAngle > 180)
|
|
||||||
lastAngle += 360;
|
|
||||||
else if (lastAngle - thisAngle > 180)
|
|
||||||
lastAngle -= 360;
|
|
||||||
|
|
||||||
currentRotation += thisAngle - lastAngle;
|
|
||||||
RotationAbsolute += Math.Abs(thisAngle - lastAngle) * Math.Sign(Clock.ElapsedFrameTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastAngle = thisAngle;
|
lastAngle = thisAngle;
|
||||||
|
|
||||||
@ -128,5 +118,38 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
|
|
||||||
Rotation = (float)Interpolation.Lerp(Rotation, currentRotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1));
|
Rotation = (float)Interpolation.Lerp(Rotation, currentRotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rotate the disc by the provided angle (in addition to any existing rotation).
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Will be a no-op if not a valid time to spin.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="angle">The delta angle.</param>
|
||||||
|
public void Rotate(float angle)
|
||||||
|
{
|
||||||
|
if (!isSpinnableTime)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!rotationTransferred)
|
||||||
|
{
|
||||||
|
currentRotation = Rotation * 2;
|
||||||
|
rotationTransferred = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (angle > 180)
|
||||||
|
{
|
||||||
|
lastAngle += 360;
|
||||||
|
angle -= 360;
|
||||||
|
}
|
||||||
|
else if (-angle > 180)
|
||||||
|
{
|
||||||
|
lastAngle -= 360;
|
||||||
|
angle += 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRotation += angle;
|
||||||
|
RotationAbsolute += Math.Abs(angle) * Math.Sign(Clock.ElapsedFrameTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
public class SliderRepeatJudgement : OsuJudgement
|
public class SliderRepeatJudgement : OsuJudgement
|
||||||
{
|
{
|
||||||
public override bool IsBonus => true;
|
|
||||||
|
|
||||||
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 30 : 0;
|
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 30 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
public class SliderTickJudgement : OsuJudgement
|
public class SliderTickJudgement : OsuJudgement
|
||||||
{
|
{
|
||||||
public override bool IsBonus => true;
|
|
||||||
|
|
||||||
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 10 : 0;
|
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 10 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,6 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
new OsuModEasy(),
|
new OsuModEasy(),
|
||||||
new OsuModNoFail(),
|
new OsuModNoFail(),
|
||||||
new MultiMod(new OsuModHalfTime(), new OsuModDaycore()),
|
new MultiMod(new OsuModHalfTime(), new OsuModDaycore()),
|
||||||
new OsuModSpunOut(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.DifficultyIncrease:
|
case ModType.DifficultyIncrease:
|
||||||
@ -139,6 +138,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
new MultiMod(new OsuModAutoplay(), new OsuModCinema()),
|
new MultiMod(new OsuModAutoplay(), new OsuModCinema()),
|
||||||
new OsuModRelax(),
|
new OsuModRelax(),
|
||||||
new OsuModAutopilot(),
|
new OsuModAutopilot(),
|
||||||
|
new OsuModSpunOut(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Fun:
|
case ModType.Fun:
|
||||||
|
@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
ApproachCircle,
|
ApproachCircle,
|
||||||
ReverseArrow,
|
ReverseArrow,
|
||||||
HitCircleText,
|
HitCircleText,
|
||||||
|
SliderHeadHitCircle,
|
||||||
SliderFollowCircle,
|
SliderFollowCircle,
|
||||||
SliderBall,
|
SliderBall,
|
||||||
SliderBody,
|
SliderBody,
|
||||||
|
@ -6,6 +6,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -18,8 +19,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
public class LegacyMainCirclePiece : CompositeDrawable
|
public class LegacyMainCirclePiece : CompositeDrawable
|
||||||
{
|
{
|
||||||
public LegacyMainCirclePiece()
|
private readonly string priorityLookup;
|
||||||
|
|
||||||
|
public LegacyMainCirclePiece(string priorityLookup = null)
|
||||||
{
|
{
|
||||||
|
this.priorityLookup = priorityLookup;
|
||||||
|
|
||||||
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
{
|
{
|
||||||
hitCircleSprite = new Sprite
|
hitCircleSprite = new Sprite
|
||||||
{
|
{
|
||||||
Texture = skin.GetTexture("hitcircle"),
|
Texture = getTextureWithFallback(string.Empty),
|
||||||
Colour = drawableObject.AccentColour.Value,
|
Colour = drawableObject.AccentColour.Value,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -51,12 +56,17 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
}, confineMode: ConfineMode.NoScaling),
|
}, confineMode: ConfineMode.NoScaling),
|
||||||
new Sprite
|
new Sprite
|
||||||
{
|
{
|
||||||
Texture = skin.GetTexture("hitcircleoverlay"),
|
Texture = getTextureWithFallback("overlay"),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool overlayAboveNumber = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true;
|
||||||
|
|
||||||
|
if (!overlayAboveNumber)
|
||||||
|
ChangeInternalChildDepth(hitCircleText, -float.MaxValue);
|
||||||
|
|
||||||
state.BindTo(drawableObject.State);
|
state.BindTo(drawableObject.State);
|
||||||
state.BindValueChanged(updateState, true);
|
state.BindValueChanged(updateState, true);
|
||||||
|
|
||||||
@ -65,6 +75,16 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable);
|
indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable);
|
||||||
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true);
|
||||||
|
|
||||||
|
Texture getTextureWithFallback(string name)
|
||||||
|
{
|
||||||
|
Texture tex = null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(priorityLookup))
|
||||||
|
tex = skin.GetTexture($"{priorityLookup}{name}");
|
||||||
|
|
||||||
|
return tex ?? skin.GetTexture($"hitcircle{name}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateState(ValueChangedEvent<ArmedState> state)
|
private void updateState(ValueChangedEvent<ArmedState> state)
|
||||||
|
@ -82,6 +82,12 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
case OsuSkinComponents.SliderHeadHitCircle:
|
||||||
|
if (hasHitCircle.Value)
|
||||||
|
return new LegacyMainCirclePiece("sliderstartcircle");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
case OsuSkinComponents.HitCircle:
|
case OsuSkinComponents.HitCircle:
|
||||||
if (hasHitCircle.Value)
|
if (hasHitCircle.Value)
|
||||||
return new LegacyMainCirclePiece();
|
return new LegacyMainCirclePiece();
|
||||||
|
@ -11,6 +11,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
SliderPathRadius,
|
SliderPathRadius,
|
||||||
AllowSliderBallTint,
|
AllowSliderBallTint,
|
||||||
CursorExpand,
|
CursorExpand,
|
||||||
CursorRotate
|
CursorRotate,
|
||||||
|
HitCircleOverlayAboveNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
osu.Game.Tests/Resources/mania-skin-duplicate.ini
Normal file
9
osu.Game.Tests/Resources/mania-skin-duplicate.ini
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[Mania]
|
||||||
|
Keys: 4
|
||||||
|
ColumnWidth: 10,10,10,10
|
||||||
|
HitPosition: 470
|
||||||
|
|
||||||
|
[Mania]
|
||||||
|
Keys: 4
|
||||||
|
ColumnWidth: 20,20,20,20
|
||||||
|
HitPosition: 460
|
4
osu.Game.Tests/Resources/mania-skin-extra-data.ini
Normal file
4
osu.Game.Tests/Resources/mania-skin-extra-data.ini
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[Mania]
|
||||||
|
Keys: 4
|
||||||
|
ColumnWidth: 10,10,10,10,10,10,10
|
||||||
|
HitPosition: 470
|
9
osu.Game.Tests/Resources/mania-skin-multiple.ini
Normal file
9
osu.Game.Tests/Resources/mania-skin-multiple.ini
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[Mania]
|
||||||
|
Keys: 4
|
||||||
|
ColumnWidth: 10,10,10,10
|
||||||
|
HitPosition: 470
|
||||||
|
|
||||||
|
[Mania]
|
||||||
|
Keys: 2
|
||||||
|
ColumnWidth: 20,20
|
||||||
|
HitPosition: 460
|
4
osu.Game.Tests/Resources/mania-skin-single.ini
Normal file
4
osu.Game.Tests/Resources/mania-skin-single.ini
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[Mania]
|
||||||
|
Keys: 4
|
||||||
|
ColumnWidth: 10,10,10,10
|
||||||
|
HitPosition: 470
|
31
osu.Game.Tests/Resources/storyboard_no_video.osu
Normal file
31
osu.Game.Tests/Resources/storyboard_no_video.osu
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
osu file format v14
|
||||||
|
|
||||||
|
[Events]
|
||||||
|
//Background and Video events
|
||||||
|
0,0,"BG.jpg",0,0
|
||||||
|
Video,0,"video.avi"
|
||||||
|
//Break Periods
|
||||||
|
//Storyboard Layer 0 (Background)
|
||||||
|
//Storyboard Layer 1 (Fail)
|
||||||
|
//Storyboard Layer 2 (Pass)
|
||||||
|
//Storyboard Layer 3 (Foreground)
|
||||||
|
//Storyboard Layer 4 (Overlay)
|
||||||
|
//Storyboard Sound Samples
|
||||||
|
|
||||||
|
[TimingPoints]
|
||||||
|
1674,333.333333333333,4,2,1,70,1,0
|
||||||
|
1674,-100,4,2,1,70,0,0
|
||||||
|
3340,-100,4,2,1,70,0,0
|
||||||
|
3507,-100,4,2,1,70,0,0
|
||||||
|
3673,-100,4,2,1,70,0,0
|
||||||
|
|
||||||
|
[Colours]
|
||||||
|
Combo1 : 240,80,80
|
||||||
|
Combo2 : 171,252,203
|
||||||
|
Combo3 : 128,128,255
|
||||||
|
Combo4 : 249,254,186
|
||||||
|
|
||||||
|
[HitObjects]
|
||||||
|
148,303,1674,5,6,3:2:0:0:
|
||||||
|
378,252,1840,1,0,0:0:0:0:
|
||||||
|
389,270,2340,5,2,0:1:0:0:
|
87
osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs
Normal file
87
osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// 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.Game.IO;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Skins
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class LegacyManiaSkinDecoderTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestParseSingleConfig()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyManiaSkinDecoder();
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("mania-skin-single.ini"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var configs = decoder.Decode(stream);
|
||||||
|
|
||||||
|
Assert.That(configs.Count, Is.EqualTo(1));
|
||||||
|
Assert.That(configs[0].Keys, Is.EqualTo(4));
|
||||||
|
Assert.That(configs[0].ColumnWidth, Is.EquivalentTo(new float[] { 16, 16, 16, 16 }));
|
||||||
|
Assert.That(configs[0].HitPosition, Is.EqualTo(16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestParseMultipleConfig()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyManiaSkinDecoder();
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("mania-skin-multiple.ini"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var configs = decoder.Decode(stream);
|
||||||
|
|
||||||
|
Assert.That(configs.Count, Is.EqualTo(2));
|
||||||
|
|
||||||
|
Assert.That(configs[0].Keys, Is.EqualTo(4));
|
||||||
|
Assert.That(configs[0].ColumnWidth, Is.EquivalentTo(new float[] { 16, 16, 16, 16 }));
|
||||||
|
Assert.That(configs[0].HitPosition, Is.EqualTo(16));
|
||||||
|
|
||||||
|
Assert.That(configs[1].Keys, Is.EqualTo(2));
|
||||||
|
Assert.That(configs[1].ColumnWidth, Is.EquivalentTo(new float[] { 32, 32 }));
|
||||||
|
Assert.That(configs[1].HitPosition, Is.EqualTo(32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestParseDuplicateConfig()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyManiaSkinDecoder();
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("mania-skin-single.ini"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var configs = decoder.Decode(stream);
|
||||||
|
|
||||||
|
Assert.That(configs.Count, Is.EqualTo(1));
|
||||||
|
Assert.That(configs[0].Keys, Is.EqualTo(4));
|
||||||
|
Assert.That(configs[0].ColumnWidth, Is.EquivalentTo(new float[] { 16, 16, 16, 16 }));
|
||||||
|
Assert.That(configs[0].HitPosition, Is.EqualTo(16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestParseWithUnnecessaryExtraData()
|
||||||
|
{
|
||||||
|
var decoder = new LegacyManiaSkinDecoder();
|
||||||
|
|
||||||
|
using (var resStream = TestResources.OpenResource("mania-skin-extra-data.ini"))
|
||||||
|
using (var stream = new LineBufferedReader(resStream))
|
||||||
|
{
|
||||||
|
var configs = decoder.Decode(stream);
|
||||||
|
|
||||||
|
Assert.That(configs.Count, Is.EqualTo(1));
|
||||||
|
Assert.That(configs[0].Keys, Is.EqualTo(4));
|
||||||
|
Assert.That(configs[0].ColumnWidth, Is.EquivalentTo(new float[] { 16, 16, 16, 16 }));
|
||||||
|
Assert.That(configs[0].HitPosition, Is.EqualTo(16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Screens;
|
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Catch;
|
using osu.Game.Rulesets.Catch;
|
||||||
@ -74,9 +73,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
Beatmap.Value = working;
|
Beatmap.Value = working;
|
||||||
SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) };
|
SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) };
|
||||||
|
|
||||||
Player?.Exit();
|
|
||||||
Player = null;
|
|
||||||
|
|
||||||
Player = CreatePlayer(ruleset);
|
Player = CreatePlayer(ruleset);
|
||||||
|
|
||||||
LoadScreen(Player);
|
LoadScreen(Player);
|
||||||
|
@ -3,8 +3,14 @@
|
|||||||
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Screens.Play.Break;
|
||||||
|
using osu.Game.Screens.Ranking;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
@ -15,20 +21,38 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
protected override Player CreatePlayer(Ruleset ruleset)
|
protected override Player CreatePlayer(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray();
|
SelectedMods.Value = new[] { ruleset.GetAutoplayMod() };
|
||||||
return new TestPlayer(false, false);
|
return new TestPlayer(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddCheckSteps()
|
protected override void AddCheckSteps()
|
||||||
{
|
{
|
||||||
AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
AddUntilStep("score above zero", () => Player.ScoreProcessor.TotalScore.Value > 0);
|
||||||
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
|
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 2));
|
||||||
AddStep("seek to break time", () => Player.GameplayClockContainer.Seek(Player.BreakOverlay.Breaks.First().StartTime));
|
seekToBreak(0);
|
||||||
AddUntilStep("wait for seek to complete", () =>
|
AddAssert("keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting);
|
||||||
Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= Player.BreakOverlay.Breaks.First().StartTime);
|
AddAssert("overlay displays 100% accuracy", () => Player.BreakOverlay.ChildrenOfType<BreakInfo>().Single().AccuracyDisplay.Current.Value == 1);
|
||||||
AddAssert("test keys not counting", () => !Player.HUDOverlay.KeyCounter.IsCounting);
|
|
||||||
AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
|
AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
|
||||||
AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
|
AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
|
||||||
|
|
||||||
|
seekToBreak(0);
|
||||||
|
seekToBreak(1);
|
||||||
|
|
||||||
|
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
|
||||||
|
AddUntilStep("results displayed", () => getResultsScreen() != null);
|
||||||
|
|
||||||
|
AddAssert("score has combo", () => getResultsScreen().Score.Combo > 100);
|
||||||
|
AddAssert("score has no misses", () => getResultsScreen().Score.Statistics[HitResult.Miss] == 0);
|
||||||
|
|
||||||
|
ResultsScreen getResultsScreen() => Stack.CurrentScreen as ResultsScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void seekToBreak(int breakIndex)
|
||||||
|
{
|
||||||
|
AddStep($"seek to break {breakIndex}", () => Player.GameplayClockContainer.Seek(destBreak().StartTime));
|
||||||
|
AddUntilStep("wait for seek to complete", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= destBreak().StartTime);
|
||||||
|
|
||||||
|
BreakPeriod destBreak() => Player.ChildrenOfType<BreakTracker>().First().Breaks.ElementAt(breakIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -12,14 +13,16 @@ using osu.Game.Screens.Play;
|
|||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TestSceneBreakOverlay : OsuTestScene
|
public class TestSceneBreakTracker : OsuTestScene
|
||||||
{
|
{
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
typeof(BreakOverlay),
|
typeof(BreakOverlay),
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly TestBreakOverlay breakOverlay;
|
private readonly BreakOverlay breakOverlay;
|
||||||
|
|
||||||
|
private readonly TestBreakTracker breakTracker;
|
||||||
|
|
||||||
private readonly IReadOnlyList<BreakPeriod> testBreaks = new List<BreakPeriod>
|
private readonly IReadOnlyList<BreakPeriod> testBreaks = new List<BreakPeriod>
|
||||||
{
|
{
|
||||||
@ -35,9 +38,23 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
public TestSceneBreakOverlay()
|
public TestSceneBreakTracker()
|
||||||
{
|
{
|
||||||
Add(breakOverlay = new TestBreakOverlay(true));
|
AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
breakTracker = new TestBreakTracker(),
|
||||||
|
breakOverlay = new BreakOverlay(true, null)
|
||||||
|
{
|
||||||
|
ProcessCustomClock = false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
breakOverlay.Clock = breakTracker.Clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -88,7 +105,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
loadBreaksStep("multiple breaks", testBreaks);
|
loadBreaksStep("multiple breaks", testBreaks);
|
||||||
|
|
||||||
seekAndAssertBreak("seek to break start", testBreaks[1].StartTime, true);
|
seekAndAssertBreak("seek to break start", testBreaks[1].StartTime, true);
|
||||||
AddAssert("is skipped to break #2", () => breakOverlay.CurrentBreakIndex == 1);
|
AddAssert("is skipped to break #2", () => breakTracker.CurrentBreakIndex == 1);
|
||||||
|
|
||||||
seekAndAssertBreak("seek to break middle", testBreaks[1].StartTime + testBreaks[1].Duration / 2, true);
|
seekAndAssertBreak("seek to break middle", testBreaks[1].StartTime + testBreaks[1].Duration / 2, true);
|
||||||
seekAndAssertBreak("seek to break end", testBreaks[1].EndTime, false);
|
seekAndAssertBreak("seek to break end", testBreaks[1].EndTime, false);
|
||||||
@ -110,7 +127,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private void addShowBreakStep(double seconds)
|
private void addShowBreakStep(double seconds)
|
||||||
{
|
{
|
||||||
AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = new List<BreakPeriod>
|
AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = breakTracker.Breaks = new List<BreakPeriod>
|
||||||
{
|
{
|
||||||
new BreakPeriod
|
new BreakPeriod
|
||||||
{
|
{
|
||||||
@ -122,12 +139,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private void setClock(bool useManual)
|
private void setClock(bool useManual)
|
||||||
{
|
{
|
||||||
AddStep($"set {(useManual ? "manual" : "realtime")} clock", () => breakOverlay.SwitchClock(useManual));
|
AddStep($"set {(useManual ? "manual" : "realtime")} clock", () => breakTracker.SwitchClock(useManual));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadBreaksStep(string breakDescription, IReadOnlyList<BreakPeriod> breaks)
|
private void loadBreaksStep(string breakDescription, IReadOnlyList<BreakPeriod> breaks)
|
||||||
{
|
{
|
||||||
AddStep($"load {breakDescription}", () => breakOverlay.Breaks = breaks);
|
AddStep($"load {breakDescription}", () => breakOverlay.Breaks = breakTracker.Breaks = breaks);
|
||||||
seekAndAssertBreak("seek back to 0", 0, false);
|
seekAndAssertBreak("seek back to 0", 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,17 +168,18 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
private void seekAndAssertBreak(string seekStepDescription, double time, bool shouldBeBreak)
|
private void seekAndAssertBreak(string seekStepDescription, double time, bool shouldBeBreak)
|
||||||
{
|
{
|
||||||
AddStep(seekStepDescription, () => breakOverlay.ManualClockTime = time);
|
AddStep(seekStepDescription, () => breakTracker.ManualClockTime = time);
|
||||||
AddAssert($"is{(!shouldBeBreak ? " not" : string.Empty)} break time", () =>
|
AddAssert($"is{(!shouldBeBreak ? " not" : string.Empty)} break time", () =>
|
||||||
{
|
{
|
||||||
breakOverlay.ProgressTime();
|
breakTracker.ProgressTime();
|
||||||
return breakOverlay.IsBreakTime.Value == shouldBeBreak;
|
return breakTracker.IsBreakTime.Value == shouldBeBreak;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestBreakOverlay : BreakOverlay
|
private class TestBreakTracker : BreakTracker
|
||||||
{
|
{
|
||||||
private readonly FramedClock framedManualClock;
|
public readonly FramedClock FramedManualClock;
|
||||||
|
|
||||||
private readonly ManualClock manualClock;
|
private readonly ManualClock manualClock;
|
||||||
private IFrameBasedClock originalClock;
|
private IFrameBasedClock originalClock;
|
||||||
|
|
||||||
@ -173,20 +191,19 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
set => manualClock.CurrentTime = value;
|
set => manualClock.CurrentTime = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestBreakOverlay(bool letterboxing)
|
public TestBreakTracker()
|
||||||
: base(letterboxing)
|
|
||||||
{
|
{
|
||||||
framedManualClock = new FramedClock(manualClock = new ManualClock());
|
FramedManualClock = new FramedClock(manualClock = new ManualClock());
|
||||||
ProcessCustomClock = false;
|
ProcessCustomClock = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ProgressTime()
|
public void ProgressTime()
|
||||||
{
|
{
|
||||||
framedManualClock.ProcessFrame();
|
FramedManualClock.ProcessFrame();
|
||||||
Update();
|
Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SwitchClock(bool setManual) => Clock = setManual ? framedManualClock : originalClock;
|
public void SwitchClock(bool setManual) => Clock = setManual ? FramedManualClock : originalClock;
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
@ -17,13 +17,12 @@ using osu.Game.Replays;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Tests.Visual;
|
|
||||||
using osu.Game.Tests.Visual.UserInterface;
|
using osu.Game.Tests.Visual.UserInterface;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
public class TestSceneReplayRecorder : OsuManualInputManagerTestScene
|
public class TestSceneReplayRecorder : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
@ -13,12 +13,11 @@ using osu.Game.Replays;
|
|||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Tests.Visual;
|
|
||||||
using osu.Game.Tests.Visual.UserInterface;
|
using osu.Game.Tests.Visual.UserInterface;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
public class TestSceneReplayRecording : OsuTestScene
|
public class TestSceneReplayRecording : OsuTestScene
|
||||||
{
|
{
|
@ -9,8 +9,12 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
|
using osu.Game.IO;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
using osu.Game.Storyboards.Drawables;
|
using osu.Game.Storyboards.Drawables;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
@ -54,7 +58,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
State = { Value = Visibility.Visible },
|
State = { Value = Visibility.Visible },
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestStoryboard()
|
||||||
|
{
|
||||||
AddStep("Restart", restart);
|
AddStep("Restart", restart);
|
||||||
AddToggleStep("Passing", passing =>
|
AddToggleStep("Passing", passing =>
|
||||||
{
|
{
|
||||||
@ -62,6 +70,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestStoryboardMissingVideo()
|
||||||
|
{
|
||||||
|
AddStep("Load storyboard with missing video", loadStoryboardNoVideo);
|
||||||
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
@ -94,5 +108,28 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
storyboardContainer.Add(storyboard);
|
storyboardContainer.Add(storyboard);
|
||||||
decoupledClock.ChangeSource(working.Track);
|
decoupledClock.ChangeSource(working.Track);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadStoryboardNoVideo()
|
||||||
|
{
|
||||||
|
if (storyboard != null)
|
||||||
|
storyboardContainer.Remove(storyboard);
|
||||||
|
|
||||||
|
var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true };
|
||||||
|
storyboardContainer.Clock = decoupledClock;
|
||||||
|
|
||||||
|
Storyboard sb;
|
||||||
|
|
||||||
|
using (var str = TestResources.OpenResource("storyboard_no_video.osu"))
|
||||||
|
using (var bfr = new LineBufferedReader(str))
|
||||||
|
{
|
||||||
|
var decoder = new LegacyStoryboardDecoder();
|
||||||
|
sb = decoder.Decode(bfr);
|
||||||
|
}
|
||||||
|
|
||||||
|
storyboard = sb.CreateDrawable(Beatmap.Value);
|
||||||
|
|
||||||
|
storyboardContainer.Add(storyboard);
|
||||||
|
decoupledClock.ChangeSource(Beatmap.Value.Track);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,6 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestInstantLoad()
|
public void TestInstantLoad()
|
||||||
{
|
{
|
||||||
// visual only, very impossible to test this using asserts.
|
|
||||||
|
|
||||||
AddStep("load immediately", () =>
|
AddStep("load immediately", () =>
|
||||||
{
|
{
|
||||||
loader = new TestLoader();
|
loader = new TestLoader();
|
||||||
@ -46,12 +44,17 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
LoadScreen(loader);
|
LoadScreen(loader);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("spinner did not display", () => loader.LoadingSpinner?.Alpha == 0);
|
spinnerNotPresentOrHidden();
|
||||||
|
|
||||||
AddUntilStep("loaded", () => loader.ScreenLoaded);
|
AddUntilStep("loaded", () => loader.ScreenLoaded);
|
||||||
AddUntilStep("not current", () => !loader.IsCurrentScreen());
|
AddUntilStep("not current", () => !loader.IsCurrentScreen());
|
||||||
|
|
||||||
|
spinnerNotPresentOrHidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void spinnerNotPresentOrHidden() =>
|
||||||
|
AddAssert("spinner did not display", () => loader.LoadingSpinner == null || loader.LoadingSpinner.Alpha == 0);
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDelayedLoad()
|
public void TestDelayedLoad()
|
||||||
{
|
{
|
||||||
|
@ -83,6 +83,82 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
waitForSelection(set_count, 3);
|
waitForSelection(set_count, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase(true)]
|
||||||
|
[TestCase(false)]
|
||||||
|
public void TestTraversalBeyondVisible(bool forwards)
|
||||||
|
{
|
||||||
|
var sets = new List<BeatmapSetInfo>();
|
||||||
|
|
||||||
|
const int total_set_count = 200;
|
||||||
|
|
||||||
|
for (int i = 0; i < total_set_count; i++)
|
||||||
|
sets.Add(createTestBeatmapSet(i + 1));
|
||||||
|
|
||||||
|
loadBeatmaps(sets);
|
||||||
|
|
||||||
|
for (int i = 1; i < total_set_count; i += i)
|
||||||
|
selectNextAndAssert(i);
|
||||||
|
|
||||||
|
void selectNextAndAssert(int amount)
|
||||||
|
{
|
||||||
|
setSelected(forwards ? 1 : total_set_count, 1);
|
||||||
|
|
||||||
|
AddStep($"{(forwards ? "Next" : "Previous")} beatmap {amount} times", () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < amount; i++)
|
||||||
|
{
|
||||||
|
carousel.SelectNext(forwards ? 1 : -1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
waitForSelection(forwards ? amount + 1 : total_set_count - amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestTraversalBeyondVisibleDifficulties()
|
||||||
|
{
|
||||||
|
var sets = new List<BeatmapSetInfo>();
|
||||||
|
|
||||||
|
const int total_set_count = 20;
|
||||||
|
|
||||||
|
for (int i = 0; i < total_set_count; i++)
|
||||||
|
sets.Add(createTestBeatmapSet(i + 1));
|
||||||
|
|
||||||
|
loadBeatmaps(sets);
|
||||||
|
|
||||||
|
// Selects next set once, difficulty index doesn't change
|
||||||
|
selectNextAndAssert(3, true, 2, 1);
|
||||||
|
|
||||||
|
// Selects next set 16 times (50 \ 3 == 16), difficulty index changes twice (50 % 3 == 2)
|
||||||
|
selectNextAndAssert(50, true, 17, 3);
|
||||||
|
|
||||||
|
// Travels around the carousel thrice (200 \ 60 == 3)
|
||||||
|
// continues to select 20 times (200 \ 60 == 20)
|
||||||
|
// selects next set 6 times (20 \ 3 == 6)
|
||||||
|
// difficulty index changes twice (20 % 3 == 2)
|
||||||
|
selectNextAndAssert(200, true, 7, 3);
|
||||||
|
|
||||||
|
// All same but in reverse
|
||||||
|
selectNextAndAssert(3, false, 19, 3);
|
||||||
|
selectNextAndAssert(50, false, 4, 1);
|
||||||
|
selectNextAndAssert(200, false, 14, 1);
|
||||||
|
|
||||||
|
void selectNextAndAssert(int amount, bool forwards, int expectedSet, int expectedDiff)
|
||||||
|
{
|
||||||
|
// Select very first or very last difficulty
|
||||||
|
setSelected(forwards ? 1 : 20, forwards ? 1 : 3);
|
||||||
|
|
||||||
|
AddStep($"{(forwards ? "Next" : "Previous")} difficulty {amount} times", () =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < amount; i++)
|
||||||
|
carousel.SelectNext(forwards ? 1 : -1, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
waitForSelection(expectedSet, expectedDiff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test filtering
|
/// Test filtering
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -91,13 +91,14 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
var easierMods = osu.GetModsFor(ModType.DifficultyReduction);
|
var easierMods = osu.GetModsFor(ModType.DifficultyReduction);
|
||||||
var harderMods = osu.GetModsFor(ModType.DifficultyIncrease);
|
var harderMods = osu.GetModsFor(ModType.DifficultyIncrease);
|
||||||
|
var conversionMods = osu.GetModsFor(ModType.Conversion);
|
||||||
|
|
||||||
var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail);
|
var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail);
|
||||||
var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden);
|
var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden);
|
||||||
|
|
||||||
var doubleTimeMod = harderMods.OfType<MultiMod>().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime));
|
var doubleTimeMod = harderMods.OfType<MultiMod>().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime));
|
||||||
|
|
||||||
var spunOutMod = easierMods.FirstOrDefault(m => m is OsuModSpunOut);
|
var targetMod = conversionMods.FirstOrDefault(m => m is OsuModTarget);
|
||||||
|
|
||||||
var easy = easierMods.FirstOrDefault(m => m is OsuModEasy);
|
var easy = easierMods.FirstOrDefault(m => m is OsuModEasy);
|
||||||
var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock);
|
var hardRock = harderMods.FirstOrDefault(m => m is OsuModHardRock);
|
||||||
@ -109,7 +110,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
testMultiplierTextColour(noFailMod, () => modSelect.LowMultiplierColour);
|
testMultiplierTextColour(noFailMod, () => modSelect.LowMultiplierColour);
|
||||||
testMultiplierTextColour(hiddenMod, () => modSelect.HighMultiplierColour);
|
testMultiplierTextColour(hiddenMod, () => modSelect.HighMultiplierColour);
|
||||||
|
|
||||||
testUnimplementedMod(spunOutMod);
|
testUnimplementedMod(targetMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -87,7 +87,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz";
|
protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path)?.ToLowerInvariant() == ".osz";
|
||||||
|
|
||||||
protected override Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
|
protected override async Task Populate(BeatmapSetInfo beatmapSet, ArchiveReader archive, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (archive != null)
|
if (archive != null)
|
||||||
beatmapSet.Beatmaps = createBeatmapDifficulties(beatmapSet.Files);
|
beatmapSet.Beatmaps = createBeatmapDifficulties(beatmapSet.Files);
|
||||||
@ -103,7 +103,19 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
validateOnlineIds(beatmapSet);
|
validateOnlineIds(beatmapSet);
|
||||||
|
|
||||||
return updateQueue.UpdateAsync(beatmapSet, cancellationToken);
|
bool hadOnlineBeatmapIDs = beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0);
|
||||||
|
|
||||||
|
await updateQueue.UpdateAsync(beatmapSet, cancellationToken);
|
||||||
|
|
||||||
|
// ensure at least one beatmap was able to retrieve or keep an online ID, else drop the set ID.
|
||||||
|
if (hadOnlineBeatmapIDs && !beatmapSet.Beatmaps.Any(b => b.OnlineBeatmapID > 0))
|
||||||
|
{
|
||||||
|
if (beatmapSet.OnlineBeatmapSetID != null)
|
||||||
|
{
|
||||||
|
beatmapSet.OnlineBeatmapSetID = null;
|
||||||
|
LogForModel(beatmapSet, "Disassociating beatmap set ID due to loss of all beatmap IDs");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PreImport(BeatmapSetInfo beatmapSet)
|
protected override void PreImport(BeatmapSetInfo beatmapSet)
|
||||||
@ -447,12 +459,15 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
var res = req.Result;
|
var res = req.Result;
|
||||||
|
|
||||||
beatmap.Status = res.Status;
|
if (res != null)
|
||||||
beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
|
{
|
||||||
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
|
beatmap.Status = res.Status;
|
||||||
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
|
beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
|
||||||
|
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
|
||||||
|
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
|
||||||
|
|
||||||
LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
|
LogForModel(set, $"Online retrieval mapped {beatmap} to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -41,6 +41,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
section = Section.None;
|
section = Section.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnBeginNewSection(section);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +58,14 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.AsSpan().TrimStart().StartsWith("//".AsSpan(), StringComparison.Ordinal);
|
protected virtual bool ShouldSkipLine(string line) => string.IsNullOrWhiteSpace(line) || line.AsSpan().TrimStart().StartsWith("//".AsSpan(), StringComparison.Ordinal);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when a new <see cref="Section"/> has been entered.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="section">The entered <see cref="Section"/>.</param>
|
||||||
|
protected virtual void OnBeginNewSection(Section section)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void ParseLine(T output, Section section, string line)
|
protected virtual void ParseLine(T output, Section section, string line)
|
||||||
{
|
{
|
||||||
line = StripComments(line);
|
line = StripComments(line);
|
||||||
@ -139,7 +148,8 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
Colours,
|
Colours,
|
||||||
HitObjects,
|
HitObjects,
|
||||||
Variables,
|
Variables,
|
||||||
Fonts
|
Fonts,
|
||||||
|
Mania
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class LegacyDifficultyControlPoint : DifficultyControlPoint
|
internal class LegacyDifficultyControlPoint : DifficultyControlPoint
|
||||||
|
@ -40,7 +40,7 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether player is in break time.
|
/// Whether player is in break time.
|
||||||
/// Must be bound to <see cref="BreakOverlay.IsBreakTime"/> to allow for dim adjustments in gameplay.
|
/// Must be bound to <see cref="BreakTracker.IsBreakTime"/> to allow for dim adjustments in gameplay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly IBindable<bool> IsBreakTime = new Bindable<bool>();
|
public readonly IBindable<bool> IsBreakTime = new Bindable<bool>();
|
||||||
|
|
||||||
|
@ -17,9 +17,9 @@ namespace osu.Game.IO
|
|||||||
private readonly StreamReader streamReader;
|
private readonly StreamReader streamReader;
|
||||||
private readonly Queue<string> lineBuffer;
|
private readonly Queue<string> lineBuffer;
|
||||||
|
|
||||||
public LineBufferedReader(Stream stream)
|
public LineBufferedReader(Stream stream, bool leaveOpen = false)
|
||||||
{
|
{
|
||||||
streamReader = new StreamReader(stream);
|
streamReader = new StreamReader(stream, Encoding.UTF8, true, 1024, leaveOpen);
|
||||||
lineBuffer = new Queue<string>();
|
lineBuffer = new Queue<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,11 +12,11 @@ namespace osu.Game.Online.API
|
|||||||
/// An API request with a well-defined response type.
|
/// An API request with a well-defined response type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
|
/// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
|
||||||
public abstract class APIRequest<T> : APIRequest
|
public abstract class APIRequest<T> : APIRequest where T : class
|
||||||
{
|
{
|
||||||
protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest<T>(Uri);
|
protected override WebRequest CreateWebRequest() => new OsuJsonWebRequest<T>(Uri);
|
||||||
|
|
||||||
public T Result => ((OsuJsonWebRequest<T>)WebRequest).ResponseObject;
|
public T Result => ((OsuJsonWebRequest<T>)WebRequest)?.ResponseObject;
|
||||||
|
|
||||||
protected APIRequest()
|
protected APIRequest()
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,7 @@ using osu.Game.Rulesets;
|
|||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
namespace osu.Game.Online.API.Requests
|
||||||
{
|
{
|
||||||
public abstract class GetRankingsRequest<TModel> : APIRequest<TModel>
|
public abstract class GetRankingsRequest<TModel> : APIRequest<TModel> where TModel : class
|
||||||
{
|
{
|
||||||
private readonly RulesetInfo ruleset;
|
private readonly RulesetInfo ruleset;
|
||||||
private readonly int page;
|
private readonly int page;
|
||||||
|
@ -6,7 +6,7 @@ using osu.Framework.IO.Network;
|
|||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
namespace osu.Game.Online.API.Requests
|
||||||
{
|
{
|
||||||
public abstract class PaginatedAPIRequest<T> : APIRequest<T>
|
public abstract class PaginatedAPIRequest<T> : APIRequest<T> where T : class
|
||||||
{
|
{
|
||||||
private readonly int page;
|
private readonly int page;
|
||||||
private readonly int itemsPerPage;
|
private readonly int itemsPerPage;
|
||||||
|
@ -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 osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Configuration;
|
using osu.Framework.Configuration;
|
||||||
@ -56,24 +57,32 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
rawInputToggle.ValueChanged += enabled =>
|
if (RuntimeInfo.OS != RuntimeInfo.Platform.Windows)
|
||||||
{
|
{
|
||||||
// this is temporary until we support per-handler settings.
|
rawInputToggle.Disabled = true;
|
||||||
const string raw_mouse_handler = @"OsuTKRawMouseHandler";
|
sensitivity.Bindable.Disabled = true;
|
||||||
const string standard_mouse_handler = @"OsuTKMouseHandler";
|
}
|
||||||
|
else
|
||||||
ignoredInputHandler.Value = enabled.NewValue ? standard_mouse_handler : raw_mouse_handler;
|
|
||||||
};
|
|
||||||
|
|
||||||
ignoredInputHandler = config.GetBindable<string>(FrameworkSetting.IgnoredInputHandlers);
|
|
||||||
ignoredInputHandler.ValueChanged += handler =>
|
|
||||||
{
|
{
|
||||||
bool raw = !handler.NewValue.Contains("Raw");
|
rawInputToggle.ValueChanged += enabled =>
|
||||||
rawInputToggle.Value = raw;
|
{
|
||||||
sensitivity.Bindable.Disabled = !raw;
|
// this is temporary until we support per-handler settings.
|
||||||
};
|
const string raw_mouse_handler = @"OsuTKRawMouseHandler";
|
||||||
|
const string standard_mouse_handler = @"OsuTKMouseHandler";
|
||||||
|
|
||||||
ignoredInputHandler.TriggerChange();
|
ignoredInputHandler.Value = enabled.NewValue ? standard_mouse_handler : raw_mouse_handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
ignoredInputHandler = config.GetBindable<string>(FrameworkSetting.IgnoredInputHandlers);
|
||||||
|
ignoredInputHandler.ValueChanged += handler =>
|
||||||
|
{
|
||||||
|
bool raw = !handler.NewValue.Contains("Raw");
|
||||||
|
rawInputToggle.Value = raw;
|
||||||
|
sensitivity.Bindable.Disabled = !raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
ignoredInputHandler.TriggerChange();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SensitivitySetting : SettingsSlider<double, SensitivitySlider>
|
private class SensitivitySetting : SettingsSlider<double, SensitivitySlider>
|
||||||
|
@ -2,9 +2,15 @@
|
|||||||
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.IO.Serialization;
|
using osu.Game.IO.Serialization;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
@ -42,6 +48,51 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public virtual string Description => string.Empty;
|
public virtual string Description => string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The tooltip to display for this mod when used in a <see cref="ModIcon"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Differs from <see cref="Name"/>, as the value of attributes (AR, CS, etc) changeable via the mod
|
||||||
|
/// are displayed in the tooltip.
|
||||||
|
/// </remarks>
|
||||||
|
[JsonIgnore]
|
||||||
|
public string IconTooltip
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string description = SettingDescription;
|
||||||
|
|
||||||
|
return string.IsNullOrEmpty(description) ? Name : $"{Name} ({description})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The description of editable settings of a mod to use in the <see cref="IconTooltip"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Parentheses are added to the tooltip, surrounding the value of this property. If this property is <c>string.Empty</c>,
|
||||||
|
/// the tooltip will not have parentheses.
|
||||||
|
/// </remarks>
|
||||||
|
public virtual string SettingDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var tooltipTexts = new List<string>();
|
||||||
|
|
||||||
|
foreach ((SettingSourceAttribute attr, PropertyInfo property) in this.GetOrderedSettingsSourceProperties())
|
||||||
|
{
|
||||||
|
object bindableObj = property.GetValue(this);
|
||||||
|
|
||||||
|
if ((bindableObj as IHasDefaultValue)?.IsDefault == true)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tooltipTexts.Add($"{attr.Label} {bindableObj}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Join(", ", tooltipTexts.Where(s => !string.IsNullOrEmpty(s)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The score multiplier of this mod.
|
/// The score multiplier of this mod.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
@ -52,6 +53,21 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
Value = 5,
|
Value = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public override string SettingDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string drainRate = DrainRate.IsDefault ? string.Empty : $"HP {DrainRate.Value:N1}";
|
||||||
|
string overallDifficulty = OverallDifficulty.IsDefault ? string.Empty : $"OD {OverallDifficulty.Value:N1}";
|
||||||
|
|
||||||
|
return string.Join(", ", new[]
|
||||||
|
{
|
||||||
|
drainRate,
|
||||||
|
overallDifficulty
|
||||||
|
}.Where(s => !string.IsNullOrEmpty(s)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private BeatmapDifficulty difficulty;
|
private BeatmapDifficulty difficulty;
|
||||||
|
|
||||||
public void ReadFromDifficulty(BeatmapDifficulty difficulty)
|
public void ReadFromDifficulty(BeatmapDifficulty difficulty)
|
||||||
@ -79,7 +95,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transfer a setting from <see cref="BeatmapDifficulty"/> to a configuration bindable.
|
/// Transfer a setting from <see cref="BeatmapDifficulty"/> to a configuration bindable.
|
||||||
/// Only performs the transfer if the user it not currently overriding..
|
/// Only performs the transfer if the user is not currently overriding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void TransferSetting<T>(BindableNumber<T> bindable, T beatmapDefault)
|
protected void TransferSetting<T>(BindableNumber<T> bindable, T beatmapDefault)
|
||||||
where T : struct, IComparable<T>, IConvertible, IEquatable<T>
|
where T : struct, IComparable<T>, IConvertible, IEquatable<T>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// 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 Humanizer;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -28,6 +29,8 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
MaxValue = 10
|
MaxValue = 10
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public override string SettingDescription => Retries.IsDefault ? string.Empty : $"{"lives".ToQuantity(Retries.Value)}";
|
||||||
|
|
||||||
private int retries;
|
private int retries;
|
||||||
|
|
||||||
private BindableNumber<double> health;
|
private BindableNumber<double> health;
|
||||||
|
@ -15,5 +15,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
{
|
{
|
||||||
track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange);
|
track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override string SettingDescription => SpeedChange.IsDefault ? string.Empty : $"{SpeedChange.Value:N2}x";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
[SettingSource("Final rate", "The final speed to ramp to")]
|
[SettingSource("Final rate", "The final speed to ramp to")]
|
||||||
public abstract BindableNumber<double> FinalRate { get; }
|
public abstract BindableNumber<double> FinalRate { get; }
|
||||||
|
|
||||||
|
public override string SettingDescription => $"{InitialRate.Value:N2}x to {FinalRate.Value:N2}x";
|
||||||
|
|
||||||
private double finalRateTime;
|
private double finalRateTime;
|
||||||
private double beginRampTime;
|
private double beginRampTime;
|
||||||
|
|
||||||
|
@ -38,6 +38,15 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
private readonly Lazy<List<DrawableHitObject>> nestedHitObjects = new Lazy<List<DrawableHitObject>>();
|
private readonly Lazy<List<DrawableHitObject>> nestedHitObjects = new Lazy<List<DrawableHitObject>>();
|
||||||
public IReadOnlyList<DrawableHitObject> NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : (IReadOnlyList<DrawableHitObject>)Array.Empty<DrawableHitObject>();
|
public IReadOnlyList<DrawableHitObject> NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : (IReadOnlyList<DrawableHitObject>)Array.Empty<DrawableHitObject>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this object should handle any user input events.
|
||||||
|
/// </summary>
|
||||||
|
public bool HandleUserInput { get; set; } = true;
|
||||||
|
|
||||||
|
public override bool PropagatePositionalInputSubTree => HandleUserInput;
|
||||||
|
|
||||||
|
public override bool PropagateNonPositionalInputSubTree => HandleUserInput;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when a <see cref="JudgementResult"/> has been applied by this <see cref="DrawableHitObject"/> or a nested <see cref="DrawableHitObject"/>.
|
/// Invoked when a <see cref="JudgementResult"/> has been applied by this <see cref="DrawableHitObject"/> or a nested <see cref="DrawableHitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -344,7 +353,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
/// Plays all the hit sounds for this <see cref="DrawableHitObject"/>.
|
/// Plays all the hit sounds for this <see cref="DrawableHitObject"/>.
|
||||||
/// This is invoked automatically when this <see cref="DrawableHitObject"/> is hit.
|
/// This is invoked automatically when this <see cref="DrawableHitObject"/> is hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void PlaySamples() => Samples?.Play();
|
public virtual void PlaySamples() => Samples?.Play();
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
|
@ -72,9 +72,9 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public override Playfield Playfield => playfield.Value;
|
public override Playfield Playfield => playfield.Value;
|
||||||
|
|
||||||
private Container overlays;
|
public override Container Overlays { get; } = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
public override Container Overlays => overlays;
|
public override Container FrameStableComponents { get; } = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
public override GameplayClock FrameStableClock => frameStabilityContainer.GameplayClock;
|
public override GameplayClock FrameStableClock => frameStabilityContainer.GameplayClock;
|
||||||
|
|
||||||
@ -187,11 +187,12 @@ namespace osu.Game.Rulesets.UI
|
|||||||
FrameStablePlayback = FrameStablePlayback,
|
FrameStablePlayback = FrameStablePlayback,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
FrameStableComponents,
|
||||||
KeyBindingInputManager
|
KeyBindingInputManager
|
||||||
.WithChild(CreatePlayfieldAdjustmentContainer()
|
.WithChild(CreatePlayfieldAdjustmentContainer()
|
||||||
.WithChild(Playfield)
|
.WithChild(Playfield)
|
||||||
),
|
),
|
||||||
overlays = new Container { RelativeSizeAxes = Axes.Both }
|
Overlays,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -406,10 +407,15 @@ namespace osu.Game.Rulesets.UI
|
|||||||
public abstract Playfield Playfield { get; }
|
public abstract Playfield Playfield { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Place to put drawables above hit objects but below UI.
|
/// Content to be placed above hitobjects. Will be affected by frame stability.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract Container Overlays { get; }
|
public abstract Container Overlays { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Components to be run potentially multiple times in line with frame-stable gameplay.
|
||||||
|
/// </summary>
|
||||||
|
public abstract Container FrameStableComponents { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The frame-stable clock which is being used for playfield display.
|
/// The frame-stable clock which is being used for playfield display.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A container which consumes a parent gameplay clock and standardises frame counts for children.
|
/// A container which consumes a parent gameplay clock and standardises frame counts for children.
|
||||||
/// Will ensure a minimum of 40 frames per clock second is maintained, regardless of any system lag or seeks.
|
/// Will ensure a minimum of 50 frames per clock second is maintained, regardless of any system lag or seeks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class FrameStabilityContainer : Container, IHasReplayHandler
|
public class FrameStabilityContainer : Container, IHasReplayHandler
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
private readonly ModType type;
|
private readonly ModType type;
|
||||||
|
|
||||||
public virtual string TooltipText { get; }
|
public virtual string TooltipText => mod.IconTooltip;
|
||||||
|
|
||||||
private Mod mod;
|
private Mod mod;
|
||||||
|
|
||||||
@ -48,8 +48,6 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
type = mod.Type;
|
type = mod.Type;
|
||||||
|
|
||||||
TooltipText = mod.Name;
|
|
||||||
|
|
||||||
Size = new Vector2(size);
|
Size = new Vector2(size);
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
|
@ -28,10 +28,11 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
{
|
{
|
||||||
var score = new Score
|
var score = new Score
|
||||||
{
|
{
|
||||||
ScoreInfo = new ScoreInfo(),
|
|
||||||
Replay = new Replay()
|
Replay = new Replay()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
WorkingBeatmap workingBeatmap;
|
||||||
|
|
||||||
using (SerializationReader sr = new SerializationReader(stream))
|
using (SerializationReader sr = new SerializationReader(stream))
|
||||||
{
|
{
|
||||||
currentRuleset = GetRuleset(sr.ReadByte());
|
currentRuleset = GetRuleset(sr.ReadByte());
|
||||||
@ -41,13 +42,10 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
|
|
||||||
var version = sr.ReadInt32();
|
var version = sr.ReadInt32();
|
||||||
|
|
||||||
var workingBeatmap = GetBeatmap(sr.ReadString());
|
workingBeatmap = GetBeatmap(sr.ReadString());
|
||||||
if (workingBeatmap is DummyWorkingBeatmap)
|
if (workingBeatmap is DummyWorkingBeatmap)
|
||||||
throw new BeatmapNotFoundException();
|
throw new BeatmapNotFoundException();
|
||||||
|
|
||||||
currentBeatmap = workingBeatmap.Beatmap;
|
|
||||||
scoreInfo.Beatmap = currentBeatmap.BeatmapInfo;
|
|
||||||
|
|
||||||
scoreInfo.User = new User { Username = sr.ReadString() };
|
scoreInfo.User = new User { Username = sr.ReadString() };
|
||||||
|
|
||||||
// MD5Hash
|
// MD5Hash
|
||||||
@ -68,6 +66,9 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
|
|
||||||
scoreInfo.Mods = currentRuleset.ConvertFromLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
|
scoreInfo.Mods = currentRuleset.ConvertFromLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
|
||||||
|
|
||||||
|
currentBeatmap = workingBeatmap.GetPlayableBeatmap(currentRuleset.RulesetInfo, scoreInfo.Mods);
|
||||||
|
scoreInfo.Beatmap = currentBeatmap.BeatmapInfo;
|
||||||
|
|
||||||
/* score.HpGraphString = */
|
/* score.HpGraphString = */
|
||||||
sr.ReadString();
|
sr.ReadString();
|
||||||
|
|
||||||
@ -113,6 +114,10 @@ namespace osu.Game.Scoring.Legacy
|
|||||||
|
|
||||||
CalculateAccuracy(score.ScoreInfo);
|
CalculateAccuracy(score.ScoreInfo);
|
||||||
|
|
||||||
|
// before returning for database import, we must restore the database-sourced BeatmapInfo.
|
||||||
|
// if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception.
|
||||||
|
score.ScoreInfo.Beatmap = workingBeatmap.BeatmapInfo;
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +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 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.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@ -16,8 +14,6 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
public class BreakOverlay : Container
|
public class BreakOverlay : Container
|
||||||
{
|
{
|
||||||
private readonly ScoreProcessor scoreProcessor;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The duration of the break overlay fading.
|
/// The duration of the break overlay fading.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -37,10 +33,6 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
breaks = value;
|
breaks = value;
|
||||||
|
|
||||||
// reset index in case the new breaks list is smaller than last one
|
|
||||||
isBreakTime.Value = false;
|
|
||||||
CurrentBreakIndex = 0;
|
|
||||||
|
|
||||||
if (IsLoaded)
|
if (IsLoaded)
|
||||||
initializeBreaks();
|
initializeBreaks();
|
||||||
}
|
}
|
||||||
@ -48,27 +40,17 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public override bool RemoveCompletedTransforms => false;
|
public override bool RemoveCompletedTransforms => false;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the gameplay is currently in a break.
|
|
||||||
/// </summary>
|
|
||||||
public IBindable<bool> IsBreakTime => isBreakTime;
|
|
||||||
|
|
||||||
protected int CurrentBreakIndex;
|
|
||||||
|
|
||||||
private readonly BindableBool isBreakTime = new BindableBool();
|
|
||||||
|
|
||||||
private readonly Container remainingTimeAdjustmentBox;
|
private readonly Container remainingTimeAdjustmentBox;
|
||||||
private readonly Container remainingTimeBox;
|
private readonly Container remainingTimeBox;
|
||||||
private readonly RemainingTimeCounter remainingTimeCounter;
|
private readonly RemainingTimeCounter remainingTimeCounter;
|
||||||
private readonly BreakInfo info;
|
|
||||||
private readonly BreakArrows breakArrows;
|
private readonly BreakArrows breakArrows;
|
||||||
private readonly double gameplayStartTime;
|
|
||||||
|
|
||||||
public BreakOverlay(bool letterboxing, double gameplayStartTime = 0, ScoreProcessor scoreProcessor = null)
|
public BreakOverlay(bool letterboxing, ScoreProcessor scoreProcessor)
|
||||||
{
|
{
|
||||||
this.gameplayStartTime = gameplayStartTime;
|
|
||||||
this.scoreProcessor = scoreProcessor;
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
BreakInfo info;
|
||||||
|
|
||||||
Child = fadeContainer = new Container
|
Child = fadeContainer = new Container
|
||||||
{
|
{
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
@ -119,13 +101,11 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (scoreProcessor != null) bindProcessor(scoreProcessor);
|
if (scoreProcessor != null)
|
||||||
}
|
{
|
||||||
|
info.AccuracyDisplay.Current.BindTo(scoreProcessor.Accuracy);
|
||||||
[BackgroundDependencyLoader(true)]
|
info.GradeDisplay.Current.BindTo(scoreProcessor.Rank);
|
||||||
private void load(GameplayClock clock)
|
}
|
||||||
{
|
|
||||||
if (clock != null) Clock = clock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -134,42 +114,6 @@ namespace osu.Game.Screens.Play
|
|||||||
initializeBreaks();
|
initializeBreaks();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
updateBreakTimeBindable();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateBreakTimeBindable() =>
|
|
||||||
isBreakTime.Value = getCurrentBreak()?.HasEffect == true
|
|
||||||
|| Clock.CurrentTime < gameplayStartTime
|
|
||||||
|| scoreProcessor?.HasCompleted == true;
|
|
||||||
|
|
||||||
private BreakPeriod getCurrentBreak()
|
|
||||||
{
|
|
||||||
if (breaks?.Count > 0)
|
|
||||||
{
|
|
||||||
var time = Clock.CurrentTime;
|
|
||||||
|
|
||||||
if (time > breaks[CurrentBreakIndex].EndTime)
|
|
||||||
{
|
|
||||||
while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1)
|
|
||||||
CurrentBreakIndex++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0)
|
|
||||||
CurrentBreakIndex--;
|
|
||||||
}
|
|
||||||
|
|
||||||
var closest = breaks[CurrentBreakIndex];
|
|
||||||
|
|
||||||
return closest.Contains(time) ? closest : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeBreaks()
|
private void initializeBreaks()
|
||||||
{
|
{
|
||||||
FinishTransforms(true);
|
FinishTransforms(true);
|
||||||
@ -207,11 +151,5 @@ namespace osu.Game.Screens.Play
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindProcessor(ScoreProcessor processor)
|
|
||||||
{
|
|
||||||
info.AccuracyDisplay.Current.BindTo(processor.Accuracy);
|
|
||||||
info.GradeDisplay.Current.BindTo(processor.Rank);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
82
osu.Game/Screens/Play/BreakTracker.cs
Normal file
82
osu.Game/Screens/Play/BreakTracker.cs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play
|
||||||
|
{
|
||||||
|
public class BreakTracker : Component
|
||||||
|
{
|
||||||
|
private readonly ScoreProcessor scoreProcessor;
|
||||||
|
|
||||||
|
private readonly double gameplayStartTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the gameplay is currently in a break.
|
||||||
|
/// </summary>
|
||||||
|
public IBindable<bool> IsBreakTime => isBreakTime;
|
||||||
|
|
||||||
|
protected int CurrentBreakIndex;
|
||||||
|
|
||||||
|
private readonly BindableBool isBreakTime = new BindableBool();
|
||||||
|
|
||||||
|
private IReadOnlyList<BreakPeriod> breaks;
|
||||||
|
|
||||||
|
public IReadOnlyList<BreakPeriod> Breaks
|
||||||
|
{
|
||||||
|
get => breaks;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
breaks = value;
|
||||||
|
|
||||||
|
// reset index in case the new breaks list is smaller than last one
|
||||||
|
isBreakTime.Value = false;
|
||||||
|
CurrentBreakIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BreakTracker(double gameplayStartTime = 0, ScoreProcessor scoreProcessor = null)
|
||||||
|
{
|
||||||
|
this.gameplayStartTime = gameplayStartTime;
|
||||||
|
this.scoreProcessor = scoreProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
isBreakTime.Value = getCurrentBreak()?.HasEffect == true
|
||||||
|
|| Clock.CurrentTime < gameplayStartTime
|
||||||
|
|| scoreProcessor?.HasCompleted == true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BreakPeriod getCurrentBreak()
|
||||||
|
{
|
||||||
|
if (breaks?.Count > 0)
|
||||||
|
{
|
||||||
|
var time = Clock.CurrentTime;
|
||||||
|
|
||||||
|
if (time > breaks[CurrentBreakIndex].EndTime)
|
||||||
|
{
|
||||||
|
while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1)
|
||||||
|
CurrentBreakIndex++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0)
|
||||||
|
CurrentBreakIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
var closest = breaks[CurrentBreakIndex];
|
||||||
|
|
||||||
|
return closest.Contains(time) ? closest : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -76,6 +76,8 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
public BreakOverlay BreakOverlay;
|
public BreakOverlay BreakOverlay;
|
||||||
|
|
||||||
|
private BreakTracker breakTracker;
|
||||||
|
|
||||||
protected ScoreProcessor ScoreProcessor { get; private set; }
|
protected ScoreProcessor ScoreProcessor { get; private set; }
|
||||||
|
|
||||||
protected HealthProcessor HealthProcessor { get; private set; }
|
protected HealthProcessor HealthProcessor { get; private set; }
|
||||||
@ -204,7 +206,7 @@ namespace osu.Game.Screens.Play
|
|||||||
foreach (var mod in Mods.Value.OfType<IApplicableToHealthProcessor>())
|
foreach (var mod in Mods.Value.OfType<IApplicableToHealthProcessor>())
|
||||||
mod.ApplyToHealthProcessor(HealthProcessor);
|
mod.ApplyToHealthProcessor(HealthProcessor);
|
||||||
|
|
||||||
BreakOverlay.IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
|
breakTracker.IsBreakTime.BindValueChanged(onBreakTimeChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addUnderlayComponents(Container target)
|
private void addUnderlayComponents(Container target)
|
||||||
@ -231,12 +233,30 @@ namespace osu.Game.Screens.Play
|
|||||||
DrawableRuleset,
|
DrawableRuleset,
|
||||||
new ComboEffects(ScoreProcessor)
|
new ComboEffects(ScoreProcessor)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
DrawableRuleset.FrameStableComponents.AddRange(new Drawable[]
|
||||||
|
{
|
||||||
|
ScoreProcessor,
|
||||||
|
HealthProcessor,
|
||||||
|
breakTracker = new BreakTracker(DrawableRuleset.GameplayStartTime, ScoreProcessor)
|
||||||
|
{
|
||||||
|
Breaks = working.Beatmap.Breaks
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
HealthProcessor.IsBreakTime.BindTo(breakTracker.IsBreakTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addOverlayComponents(Container target, WorkingBeatmap working)
|
private void addOverlayComponents(Container target, WorkingBeatmap working)
|
||||||
{
|
{
|
||||||
target.AddRange(new[]
|
target.AddRange(new[]
|
||||||
{
|
{
|
||||||
|
BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
|
||||||
|
{
|
||||||
|
Clock = DrawableRuleset.FrameStableClock,
|
||||||
|
ProcessCustomClock = false,
|
||||||
|
Breaks = working.Beatmap.Breaks
|
||||||
|
},
|
||||||
// display the cursor above some HUD elements.
|
// display the cursor above some HUD elements.
|
||||||
DrawableRuleset.Cursor?.CreateProxy() ?? new Container(),
|
DrawableRuleset.Cursor?.CreateProxy() ?? new Container(),
|
||||||
DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(),
|
DrawableRuleset.ResumeOverlay?.CreateProxy() ?? new Container(),
|
||||||
@ -293,20 +313,8 @@ namespace osu.Game.Screens.Play
|
|||||||
performImmediateExit();
|
performImmediateExit();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }
|
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, },
|
||||||
});
|
});
|
||||||
|
|
||||||
DrawableRuleset.Overlays.Add(BreakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, DrawableRuleset.GameplayStartTime, ScoreProcessor)
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
Breaks = working.Beatmap.Breaks
|
|
||||||
});
|
|
||||||
|
|
||||||
DrawableRuleset.Overlays.Add(ScoreProcessor);
|
|
||||||
DrawableRuleset.Overlays.Add(HealthProcessor);
|
|
||||||
|
|
||||||
HealthProcessor.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onBreakTimeChanged(ValueChangedEvent<bool> isBreakTime)
|
private void onBreakTimeChanged(ValueChangedEvent<bool> isBreakTime)
|
||||||
@ -318,7 +326,7 @@ namespace osu.Game.Screens.Play
|
|||||||
private void updatePauseOnFocusLostState() =>
|
private void updatePauseOnFocusLostState() =>
|
||||||
HUDOverlay.HoldToQuit.PauseOnFocusLost = PauseOnFocusLost
|
HUDOverlay.HoldToQuit.PauseOnFocusLost = PauseOnFocusLost
|
||||||
&& !DrawableRuleset.HasReplayLoaded.Value
|
&& !DrawableRuleset.HasReplayLoaded.Value
|
||||||
&& !BreakOverlay.IsBreakTime.Value;
|
&& !breakTracker.IsBreakTime.Value;
|
||||||
|
|
||||||
private IBeatmap loadPlayableBeatmap()
|
private IBeatmap loadPlayableBeatmap()
|
||||||
{
|
{
|
||||||
@ -540,7 +548,7 @@ namespace osu.Game.Screens.Play
|
|||||||
PauseOverlay.Hide();
|
PauseOverlay.Hide();
|
||||||
|
|
||||||
// breaks and time-based conditions may allow instant resume.
|
// breaks and time-based conditions may allow instant resume.
|
||||||
if (BreakOverlay.IsBreakTime.Value)
|
if (breakTracker.IsBreakTime.Value)
|
||||||
completeResume();
|
completeResume();
|
||||||
else
|
else
|
||||||
DrawableRuleset.RequestResume(completeResume);
|
DrawableRuleset.RequestResume(completeResume);
|
||||||
@ -574,8 +582,8 @@ namespace osu.Game.Screens.Play
|
|||||||
Background.BlurAmount.Value = 0;
|
Background.BlurAmount.Value = 0;
|
||||||
|
|
||||||
// bind component bindables.
|
// bind component bindables.
|
||||||
Background.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
|
Background.IsBreakTime.BindTo(breakTracker.IsBreakTime);
|
||||||
DimmableStoryboard.IsBreakTime.BindTo(BreakOverlay.IsBreakTime);
|
DimmableStoryboard.IsBreakTime.BindTo(breakTracker.IsBreakTime);
|
||||||
|
|
||||||
Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
|
Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
|
||||||
DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
|
DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
|
||||||
@ -629,6 +637,39 @@ namespace osu.Game.Screens.Play
|
|||||||
return base.OnExiting(next);
|
return base.OnExiting(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual void GotoRanking()
|
||||||
|
{
|
||||||
|
if (DrawableRuleset.ReplayScore != null)
|
||||||
|
{
|
||||||
|
// if a replay is present, we likely don't want to import into the local database.
|
||||||
|
this.Push(CreateResults(CreateScore()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LegacyByteArrayReader replayReader = null;
|
||||||
|
|
||||||
|
var score = new Score { ScoreInfo = CreateScore() };
|
||||||
|
|
||||||
|
if (recordingReplay?.Frames.Count > 0)
|
||||||
|
{
|
||||||
|
score.Replay = recordingReplay;
|
||||||
|
|
||||||
|
using (var stream = new MemoryStream())
|
||||||
|
{
|
||||||
|
new LegacyScoreEncoder(score, gameplayBeatmap.PlayableBeatmap).Encode(stream);
|
||||||
|
replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scoreManager.Import(score.ScoreInfo, replayReader)
|
||||||
|
.ContinueWith(imported => Schedule(() =>
|
||||||
|
{
|
||||||
|
// screen may be in the exiting transition phase.
|
||||||
|
if (this.IsCurrentScreen())
|
||||||
|
this.Push(CreateResults(imported.Result));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
private void fadeOut(bool instant = false)
|
private void fadeOut(bool instant = false)
|
||||||
{
|
{
|
||||||
float fadeOutDuration = instant ? 0 : 250;
|
float fadeOutDuration = instant ? 0 : 250;
|
||||||
@ -641,36 +682,7 @@ namespace osu.Game.Screens.Play
|
|||||||
private void scheduleGotoRanking()
|
private void scheduleGotoRanking()
|
||||||
{
|
{
|
||||||
completionProgressDelegate?.Cancel();
|
completionProgressDelegate?.Cancel();
|
||||||
completionProgressDelegate = Schedule(delegate
|
completionProgressDelegate = Schedule(GotoRanking);
|
||||||
{
|
|
||||||
if (DrawableRuleset.ReplayScore != null)
|
|
||||||
this.Push(CreateResults(DrawableRuleset.ReplayScore.ScoreInfo));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var score = new Score { ScoreInfo = CreateScore() };
|
|
||||||
|
|
||||||
LegacyByteArrayReader replayReader = null;
|
|
||||||
|
|
||||||
if (recordingReplay?.Frames.Count > 0)
|
|
||||||
{
|
|
||||||
score.Replay = recordingReplay;
|
|
||||||
|
|
||||||
using (var stream = new MemoryStream())
|
|
||||||
{
|
|
||||||
new LegacyScoreEncoder(score, gameplayBeatmap).Encode(stream);
|
|
||||||
replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scoreManager.Import(score.ScoreInfo, replayReader)
|
|
||||||
.ContinueWith(imported => Schedule(() =>
|
|
||||||
{
|
|
||||||
// screen may be in the exiting transition phase.
|
|
||||||
if (this.IsCurrentScreen())
|
|
||||||
this.Push(CreateResults(imported.Result));
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
// 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.Screens;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Ranking;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
@ -23,6 +25,13 @@ namespace osu.Game.Screens.Play
|
|||||||
DrawableRuleset?.SetReplayScore(score);
|
DrawableRuleset?.SetReplayScore(score);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void GotoRanking()
|
||||||
|
{
|
||||||
|
this.Push(CreateResults(DrawableRuleset.ReplayScore.ScoreInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ResultsScreen CreateResults(ScoreInfo score) => new ResultsScreen(score, false);
|
||||||
|
|
||||||
protected override ScoreInfo CreateScore() => score.ScoreInfo;
|
protected override ScoreInfo CreateScore() => score.ScoreInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,23 +31,28 @@ namespace osu.Game.Screens.Ranking
|
|||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private Player player { get; set; }
|
private Player player { get; set; }
|
||||||
|
|
||||||
private readonly ScoreInfo score;
|
public readonly ScoreInfo Score;
|
||||||
|
|
||||||
|
private readonly bool allowRetry;
|
||||||
|
|
||||||
private Drawable bottomPanel;
|
private Drawable bottomPanel;
|
||||||
|
|
||||||
public ResultsScreen(ScoreInfo score)
|
public ResultsScreen(ScoreInfo score, bool allowRetry = true)
|
||||||
{
|
{
|
||||||
this.score = score;
|
Score = score;
|
||||||
|
this.allowRetry = allowRetry;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
FillFlowContainer buttons;
|
||||||
|
|
||||||
InternalChildren = new[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
new ResultsScrollContainer
|
new ResultsScrollContainer
|
||||||
{
|
{
|
||||||
Child = new ScorePanel(score)
|
Child = new ScorePanel(Score)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -68,7 +73,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Color4Extensions.FromHex("#333")
|
Colour = Color4Extensions.FromHex("#333")
|
||||||
},
|
},
|
||||||
new FillFlowContainer
|
buttons = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
@ -77,16 +82,17 @@ namespace osu.Game.Screens.Ranking
|
|||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new ReplayDownloadButton(score) { Width = 300 },
|
new ReplayDownloadButton(Score) { Width = 300 },
|
||||||
new RetryButton { Width = 300 },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (player != null)
|
if (player != null && allowRetry)
|
||||||
{
|
{
|
||||||
|
buttons.Add(new RetryButton { Width = 300 });
|
||||||
|
|
||||||
AddInternal(new HotkeyRetryOverlay
|
AddInternal(new HotkeyRetryOverlay
|
||||||
{
|
{
|
||||||
Action = () =>
|
Action = () =>
|
||||||
|
@ -253,46 +253,37 @@ namespace osu.Game.Screens.Select
|
|||||||
/// <param name="skipDifficulties">Whether to skip individual difficulties and only increment over full groups.</param>
|
/// <param name="skipDifficulties">Whether to skip individual difficulties and only increment over full groups.</param>
|
||||||
public void SelectNext(int direction = 1, bool skipDifficulties = true)
|
public void SelectNext(int direction = 1, bool skipDifficulties = true)
|
||||||
{
|
{
|
||||||
var visibleItems = Items.Where(s => !s.Item.Filtered.Value).ToList();
|
if (beatmapSets.All(s => s.Filtered.Value))
|
||||||
|
|
||||||
if (!visibleItems.Any())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
DrawableCarouselItem drawable = null;
|
if (skipDifficulties)
|
||||||
|
selectNextSet(direction, true);
|
||||||
|
else
|
||||||
|
selectNextDifficulty(direction);
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedBeatmap != null && (drawable = selectedBeatmap.Drawables.FirstOrDefault()) == null)
|
private void selectNextSet(int direction, bool skipDifficulties)
|
||||||
// if the selected beatmap isn't present yet, we can't correctly change selection.
|
{
|
||||||
// we can fix this by changing this method to not reference drawables / Items in the first place.
|
var unfilteredSets = beatmapSets.Where(s => !s.Filtered.Value).ToList();
|
||||||
return;
|
|
||||||
|
|
||||||
int originalIndex = visibleItems.IndexOf(drawable);
|
var nextSet = unfilteredSets[(unfilteredSets.IndexOf(selectedBeatmapSet) + direction + unfilteredSets.Count) % unfilteredSets.Count];
|
||||||
int currentIndex = originalIndex;
|
|
||||||
|
|
||||||
// local function to increment the index in the required direction, wrapping over extremities.
|
if (skipDifficulties)
|
||||||
int incrementIndex() => currentIndex = (currentIndex + direction + visibleItems.Count) % visibleItems.Count;
|
select(nextSet);
|
||||||
|
else
|
||||||
|
select(direction > 0 ? nextSet.Beatmaps.First(b => !b.Filtered.Value) : nextSet.Beatmaps.Last(b => !b.Filtered.Value));
|
||||||
|
}
|
||||||
|
|
||||||
while (incrementIndex() != originalIndex)
|
private void selectNextDifficulty(int direction)
|
||||||
{
|
{
|
||||||
var item = visibleItems[currentIndex].Item;
|
var unfilteredDifficulties = selectedBeatmapSet.Children.Where(s => !s.Filtered.Value).ToList();
|
||||||
|
|
||||||
if (item.Filtered.Value || item.State.Value == CarouselItemState.Selected) continue;
|
int index = unfilteredDifficulties.IndexOf(selectedBeatmap);
|
||||||
|
|
||||||
switch (item)
|
if (index + direction < 0 || index + direction >= unfilteredDifficulties.Count)
|
||||||
{
|
selectNextSet(direction, false);
|
||||||
case CarouselBeatmap beatmap:
|
else
|
||||||
if (skipDifficulties) continue;
|
select(unfilteredDifficulties[index + direction]);
|
||||||
|
|
||||||
select(beatmap);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case CarouselBeatmapSet set:
|
|
||||||
if (skipDifficulties)
|
|
||||||
select(set);
|
|
||||||
else
|
|
||||||
select(direction > 0 ? set.Beatmaps.First(b => !b.Filtered.Value) : set.Beatmaps.Last(b => !b.Filtered.Value));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -9,6 +9,8 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
public class LegacyBeatmapSkin : LegacySkin
|
public class LegacyBeatmapSkin : LegacySkin
|
||||||
{
|
{
|
||||||
|
protected override bool AllowManiaSkin => false;
|
||||||
|
|
||||||
public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore<byte[]> storage, AudioManager audioManager)
|
public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore<byte[]> storage, AudioManager audioManager)
|
||||||
: base(createSkinInfo(beatmap), new LegacySkinResourceStore<BeatmapSetFileInfo>(beatmap.BeatmapSet, storage), audioManager, beatmap.Path)
|
: base(createSkinInfo(beatmap), new LegacySkinResourceStore<BeatmapSetFileInfo>(beatmap.BeatmapSet, storage), audioManager, beatmap.Path)
|
||||||
{
|
{
|
||||||
|
30
osu.Game/Skinning/LegacyManiaSkinConfiguration.cs
Normal file
30
osu.Game/Skinning/LegacyManiaSkinConfiguration.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
public class LegacyManiaSkinConfiguration
|
||||||
|
{
|
||||||
|
public readonly int Keys;
|
||||||
|
|
||||||
|
public readonly float[] ColumnLineWidth;
|
||||||
|
public readonly float[] ColumnSpacing;
|
||||||
|
public readonly float[] ColumnWidth;
|
||||||
|
|
||||||
|
public float HitPosition = 124.8f; // (480 - 402) * 1.6f
|
||||||
|
|
||||||
|
public LegacyManiaSkinConfiguration(int keys)
|
||||||
|
{
|
||||||
|
Keys = keys;
|
||||||
|
|
||||||
|
ColumnLineWidth = new float[keys + 1];
|
||||||
|
ColumnSpacing = new float[keys - 1];
|
||||||
|
ColumnWidth = new float[keys];
|
||||||
|
|
||||||
|
ColumnLineWidth.AsSpan().Fill(2);
|
||||||
|
ColumnWidth.AsSpan().Fill(48);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs
Normal file
23
osu.Game/Skinning/LegacyManiaSkinConfigurationLookup.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// 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.Skinning
|
||||||
|
{
|
||||||
|
public class LegacyManiaSkinConfigurationLookup
|
||||||
|
{
|
||||||
|
public readonly int Keys;
|
||||||
|
public readonly LegacyManiaSkinConfigurationLookups Lookup;
|
||||||
|
public readonly int? TargetColumn;
|
||||||
|
|
||||||
|
public LegacyManiaSkinConfigurationLookup(int keys, LegacyManiaSkinConfigurationLookups lookup, int? targetColumn = null)
|
||||||
|
{
|
||||||
|
Keys = keys;
|
||||||
|
Lookup = lookup;
|
||||||
|
TargetColumn = targetColumn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum LegacyManiaSkinConfigurationLookups
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
110
osu.Game/Skinning/LegacyManiaSkinDecoder.cs
Normal file
110
osu.Game/Skinning/LegacyManiaSkinDecoder.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
|
|
||||||
|
namespace osu.Game.Skinning
|
||||||
|
{
|
||||||
|
public class LegacyManiaSkinDecoder : LegacyDecoder<List<LegacyManiaSkinConfiguration>>
|
||||||
|
{
|
||||||
|
private const float size_scale_factor = 1.6f;
|
||||||
|
|
||||||
|
public LegacyManiaSkinDecoder()
|
||||||
|
: base(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<string> pendingLines = new List<string>();
|
||||||
|
private LegacyManiaSkinConfiguration currentConfig;
|
||||||
|
|
||||||
|
protected override void OnBeginNewSection(Section section)
|
||||||
|
{
|
||||||
|
base.OnBeginNewSection(section);
|
||||||
|
|
||||||
|
// If a new section is reached with pending lines remaining, they can all be discarded as there isn't a valid configuration to parse them into.
|
||||||
|
pendingLines.Clear();
|
||||||
|
currentConfig = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ParseLine(List<LegacyManiaSkinConfiguration> output, Section section, string line)
|
||||||
|
{
|
||||||
|
line = StripComments(line);
|
||||||
|
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case Section.Mania:
|
||||||
|
var pair = SplitKeyVal(line);
|
||||||
|
|
||||||
|
switch (pair.Key)
|
||||||
|
{
|
||||||
|
case "Keys":
|
||||||
|
currentConfig = new LegacyManiaSkinConfiguration(int.Parse(pair.Value, CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
|
// Silently ignore duplicate configurations.
|
||||||
|
if (output.All(c => c.Keys != currentConfig.Keys))
|
||||||
|
output.Add(currentConfig);
|
||||||
|
|
||||||
|
// All existing lines can be flushed now that we have a valid configuration.
|
||||||
|
flushPendingLines();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
pendingLines.Add(line);
|
||||||
|
|
||||||
|
// Hold all lines until a "Keys" item is found.
|
||||||
|
if (currentConfig != null)
|
||||||
|
flushPendingLines();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flushPendingLines()
|
||||||
|
{
|
||||||
|
Debug.Assert(currentConfig != null);
|
||||||
|
|
||||||
|
foreach (var line in pendingLines)
|
||||||
|
{
|
||||||
|
var pair = SplitKeyVal(line);
|
||||||
|
|
||||||
|
switch (pair.Key)
|
||||||
|
{
|
||||||
|
case "ColumnLineWidth":
|
||||||
|
parseArrayValue(pair.Value, currentConfig.ColumnLineWidth);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ColumnSpacing":
|
||||||
|
parseArrayValue(pair.Value, currentConfig.ColumnSpacing);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ColumnWidth":
|
||||||
|
parseArrayValue(pair.Value, currentConfig.ColumnWidth);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "HitPosition":
|
||||||
|
currentConfig.HitPosition = (480 - float.Parse(pair.Value, CultureInfo.InvariantCulture)) * size_scale_factor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseArrayValue(string value, float[] output)
|
||||||
|
{
|
||||||
|
string[] values = value.Split(',');
|
||||||
|
|
||||||
|
for (int i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
if (i >= output.Length)
|
||||||
|
break;
|
||||||
|
|
||||||
|
output[i] = float.Parse(values[i], CultureInfo.InvariantCulture) * size_scale_factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,12 +26,16 @@ namespace osu.Game.Skinning
|
|||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
protected IResourceStore<SampleChannel> Samples;
|
protected IResourceStore<SampleChannel> Samples;
|
||||||
|
|
||||||
|
protected virtual bool AllowManiaSkin => true;
|
||||||
|
|
||||||
public new LegacySkinConfiguration Configuration
|
public new LegacySkinConfiguration Configuration
|
||||||
{
|
{
|
||||||
get => base.Configuration as LegacySkinConfiguration;
|
get => base.Configuration as LegacySkinConfiguration;
|
||||||
set => base.Configuration = value;
|
set => base.Configuration = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly Dictionary<int, LegacyManiaSkinConfiguration> maniaConfigurations = new Dictionary<int, LegacyManiaSkinConfiguration>();
|
||||||
|
|
||||||
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
|
public LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager)
|
||||||
: this(skin, new LegacySkinResourceStore<SkinFileInfo>(skin, storage), audioManager, "skin.ini")
|
: this(skin, new LegacySkinResourceStore<SkinFileInfo>(skin, storage), audioManager, "skin.ini")
|
||||||
{
|
{
|
||||||
@ -40,15 +44,26 @@ namespace osu.Game.Skinning
|
|||||||
protected LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager, string filename)
|
protected LegacySkin(SkinInfo skin, IResourceStore<byte[]> storage, AudioManager audioManager, string filename)
|
||||||
: base(skin)
|
: base(skin)
|
||||||
{
|
{
|
||||||
Stream stream = storage?.GetStream(filename);
|
using (var stream = storage?.GetStream(filename))
|
||||||
|
|
||||||
if (stream != null)
|
|
||||||
{
|
{
|
||||||
using (LineBufferedReader reader = new LineBufferedReader(stream))
|
if (stream != null)
|
||||||
Configuration = new LegacySkinDecoder().Decode(reader);
|
{
|
||||||
|
using (LineBufferedReader reader = new LineBufferedReader(stream, true))
|
||||||
|
Configuration = new LegacySkinDecoder().Decode(reader);
|
||||||
|
|
||||||
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
using (LineBufferedReader reader = new LineBufferedReader(stream))
|
||||||
|
{
|
||||||
|
var maniaList = new LegacyManiaSkinDecoder().Decode(reader);
|
||||||
|
|
||||||
|
foreach (var config in maniaList)
|
||||||
|
maniaConfigurations[config.Keys] = config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Configuration = new LegacySkinConfiguration { LegacyVersion = LegacySkinConfiguration.LATEST_VERSION };
|
||||||
}
|
}
|
||||||
else
|
|
||||||
Configuration = new LegacySkinConfiguration { LegacyVersion = LegacySkinConfiguration.LATEST_VERSION };
|
|
||||||
|
|
||||||
if (storage != null)
|
if (storage != null)
|
||||||
{
|
{
|
||||||
@ -105,6 +120,15 @@ namespace osu.Game.Skinning
|
|||||||
case SkinCustomColourLookup customColour:
|
case SkinCustomColourLookup customColour:
|
||||||
return SkinUtils.As<TValue>(getCustomColour(customColour.Lookup.ToString()));
|
return SkinUtils.As<TValue>(getCustomColour(customColour.Lookup.ToString()));
|
||||||
|
|
||||||
|
case LegacyManiaSkinConfigurationLookup legacy:
|
||||||
|
if (!AllowManiaSkin)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!maniaConfigurations.TryGetValue(legacy.Keys, out _))
|
||||||
|
maniaConfigurations[legacy.Keys] = new LegacyManiaSkinConfiguration(legacy.Keys);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// handles lookups like GlobalSkinConfiguration
|
// handles lookups like GlobalSkinConfiguration
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
|
|
||||||
AddInternal(Content = new Container<DrawableStoryboardLayer>
|
AddInternal(Content = new Container<DrawableStoryboardLayer>
|
||||||
{
|
{
|
||||||
Size = new Vector2(640, 480),
|
RelativeSizeAxes = Axes.Both,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
});
|
});
|
||||||
|
@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
|
|
||||||
namespace osu.Game.Storyboards.Drawables
|
namespace osu.Game.Storyboards.Drawables
|
||||||
{
|
{
|
||||||
public class DrawableStoryboardLayer : LifetimeManagementContainer
|
public class DrawableStoryboardLayer : CompositeDrawable
|
||||||
{
|
{
|
||||||
public StoryboardLayer Layer { get; }
|
public StoryboardLayer Layer { get; }
|
||||||
public bool Enabled;
|
public bool Enabled;
|
||||||
@ -23,17 +23,34 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
Origin = Anchor.Centre;
|
Origin = Anchor.Centre;
|
||||||
Enabled = layer.VisibleWhenPassing;
|
Enabled = layer.VisibleWhenPassing;
|
||||||
Masking = layer.Masking;
|
Masking = layer.Masking;
|
||||||
|
|
||||||
|
InternalChild = new LayerElementContainer(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
private class LayerElementContainer : LifetimeManagementContainer
|
||||||
private void load(CancellationToken? cancellationToken)
|
|
||||||
{
|
{
|
||||||
foreach (var element in Layer.Elements)
|
private readonly StoryboardLayer storyboardLayer;
|
||||||
{
|
|
||||||
cancellationToken?.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
if (element.IsDrawable)
|
public LayerElementContainer(StoryboardLayer layer)
|
||||||
AddInternal(element.CreateDrawable());
|
{
|
||||||
|
storyboardLayer = layer;
|
||||||
|
|
||||||
|
Width = 640;
|
||||||
|
Height = 480;
|
||||||
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(CancellationToken? cancellationToken)
|
||||||
|
{
|
||||||
|
foreach (var element in storyboardLayer.Elements)
|
||||||
|
{
|
||||||
|
cancellationToken?.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (element.IsDrawable)
|
||||||
|
AddInternal(element.CreateDrawable());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,8 @@ namespace osu.Game.Storyboards.Drawables
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
|
if (videoSprite == null) return;
|
||||||
|
|
||||||
using (videoSprite.BeginAbsoluteSequence(0))
|
using (videoSprite.BeginAbsoluteSequence(0))
|
||||||
videoSprite.FadeIn(500);
|
videoSprite.FadeIn(500);
|
||||||
}
|
}
|
||||||
|
@ -26,16 +26,16 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
protected OsuManualInputManagerTestScene()
|
protected OsuManualInputManagerTestScene()
|
||||||
{
|
{
|
||||||
|
MenuCursorContainer cursorContainer;
|
||||||
|
|
||||||
base.Content.AddRange(new Drawable[]
|
base.Content.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
InputManager = new ManualInputManager
|
InputManager = new ManualInputManager
|
||||||
{
|
{
|
||||||
UseParentInput = true,
|
UseParentInput = true,
|
||||||
Child = new GlobalActionContainer(null)
|
Child = new GlobalActionContainer(null)
|
||||||
{
|
.WithChild((cursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both })
|
||||||
RelativeSizeAxes = Axes.Both,
|
.WithChild(content = new OsuTooltipContainer(cursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }))
|
||||||
Child = content = new MenuCursorContainer { RelativeSizeAxes = Axes.Both }
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user