mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 20:22:55 +08:00
Merge branch 'master' into custom-data-directory
This commit is contained in:
commit
0b05a6d895
@ -10,10 +10,13 @@ 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.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Edit;
|
using osu.Game.Rulesets.Mania.Edit;
|
||||||
|
using osu.Game.Rulesets.Mania.Edit.Blueprints;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
@ -48,6 +51,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
DrawableHitObject lastObject = null;
|
DrawableHitObject lastObject = null;
|
||||||
Vector2 originalPosition = Vector2.Zero;
|
Vector2 originalPosition = Vector2.Zero;
|
||||||
|
|
||||||
|
setScrollStep(ScrollingDirection.Up);
|
||||||
|
|
||||||
AddStep("seek to last object", () =>
|
AddStep("seek to last object", () =>
|
||||||
{
|
{
|
||||||
lastObject = this.ChildrenOfType<DrawableHitObject>().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last());
|
lastObject = this.ChildrenOfType<DrawableHitObject>().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last());
|
||||||
@ -81,7 +86,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
DrawableHitObject lastObject = null;
|
DrawableHitObject lastObject = null;
|
||||||
Vector2 originalPosition = Vector2.Zero;
|
Vector2 originalPosition = Vector2.Zero;
|
||||||
|
|
||||||
AddStep("set down scroll", () => ((Bindable<ScrollingDirection>)composer.Composer.ScrollingInfo.Direction).Value = ScrollingDirection.Down);
|
setScrollStep(ScrollingDirection.Down);
|
||||||
|
|
||||||
AddStep("seek to last object", () =>
|
AddStep("seek to last object", () =>
|
||||||
{
|
{
|
||||||
@ -116,6 +121,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
DrawableHitObject lastObject = null;
|
DrawableHitObject lastObject = null;
|
||||||
Vector2 originalPosition = Vector2.Zero;
|
Vector2 originalPosition = Vector2.Zero;
|
||||||
|
|
||||||
|
setScrollStep(ScrollingDirection.Down);
|
||||||
|
|
||||||
AddStep("seek to last object", () =>
|
AddStep("seek to last object", () =>
|
||||||
{
|
{
|
||||||
lastObject = this.ChildrenOfType<DrawableHitObject>().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last());
|
lastObject = this.ChildrenOfType<DrawableHitObject>().Single(d => d.HitObject == composer.EditorBeatmap.HitObjects.Last());
|
||||||
@ -147,6 +154,46 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y <= DefaultNotePiece.NOTE_HEIGHT);
|
AddAssert("hitobjects not moved vertically", () => lastObject.DrawPosition.Y - originalPosition.Y <= DefaultNotePiece.NOTE_HEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestDragHoldNoteSelectionVertically()
|
||||||
|
{
|
||||||
|
setScrollStep(ScrollingDirection.Down);
|
||||||
|
|
||||||
|
AddStep("setup beatmap", () =>
|
||||||
|
{
|
||||||
|
composer.EditorBeatmap.Clear();
|
||||||
|
composer.EditorBeatmap.Add(new HoldNote
|
||||||
|
{
|
||||||
|
Column = 1,
|
||||||
|
EndTime = 200
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
DrawableHoldNote holdNote = null;
|
||||||
|
|
||||||
|
AddStep("grab hold note", () =>
|
||||||
|
{
|
||||||
|
holdNote = this.ChildrenOfType<DrawableHoldNote>().Single();
|
||||||
|
InputManager.MoveMouseTo(holdNote);
|
||||||
|
InputManager.PressButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("move drag upwards", () =>
|
||||||
|
{
|
||||||
|
InputManager.MoveMouseTo(holdNote, new Vector2(0, -100));
|
||||||
|
InputManager.ReleaseButton(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("head note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.BottomLeft, holdNote.Head.ScreenSpaceDrawQuad.BottomLeft));
|
||||||
|
AddAssert("tail note positioned correctly", () => Precision.AlmostEquals(holdNote.ScreenSpaceDrawQuad.TopLeft, holdNote.Tail.ScreenSpaceDrawQuad.BottomLeft));
|
||||||
|
|
||||||
|
AddAssert("head blueprint positioned correctly", () => this.ChildrenOfType<HoldNoteNoteSelectionBlueprint>().ElementAt(0).DrawPosition == holdNote.Head.DrawPosition);
|
||||||
|
AddAssert("tail blueprint positioned correctly", () => this.ChildrenOfType<HoldNoteNoteSelectionBlueprint>().ElementAt(1).DrawPosition == holdNote.Tail.DrawPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setScrollStep(ScrollingDirection direction)
|
||||||
|
=> AddStep($"set scroll direction = {direction}", () => ((Bindable<ScrollingDirection>)composer.Composer.ScrollingInfo.Direction).Value = direction);
|
||||||
|
|
||||||
private class TestComposer : CompositeDrawable
|
private class TestComposer : CompositeDrawable
|
||||||
{
|
{
|
||||||
[Cached(typeof(EditorBeatmap))]
|
[Cached(typeof(EditorBeatmap))]
|
||||||
|
@ -399,7 +399,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public TestSlider()
|
public TestSlider()
|
||||||
{
|
{
|
||||||
DefaultsApplied += () =>
|
DefaultsApplied += _ =>
|
||||||
{
|
{
|
||||||
HeadCircle.HitWindows = new TestHitWindows();
|
HeadCircle.HitWindows = new TestHitWindows();
|
||||||
TailCircle.HitWindows = new TestHitWindows();
|
TailCircle.HitWindows = new TestHitWindows();
|
||||||
|
@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
|||||||
private void bindEvents(DrawableOsuHitObject drawableObject)
|
private void bindEvents(DrawableOsuHitObject drawableObject)
|
||||||
{
|
{
|
||||||
drawableObject.HitObject.PositionBindable.BindValueChanged(_ => scheduleRefresh());
|
drawableObject.HitObject.PositionBindable.BindValueChanged(_ => scheduleRefresh());
|
||||||
drawableObject.HitObject.DefaultsApplied += scheduleRefresh;
|
drawableObject.HitObject.DefaultsApplied += _ => scheduleRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleRefresh()
|
private void scheduleRefresh()
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 183 KiB |
Binary file not shown.
After Width: | Height: | Size: 185 KiB |
Binary file not shown.
After Width: | Height: | Size: 100 KiB |
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
typeof(TaikoHitTarget),
|
typeof(TaikoHitTarget),
|
||||||
typeof(TaikoLegacyHitTarget),
|
typeof(TaikoLegacyHitTarget),
|
||||||
typeof(PlayfieldBackgroundRight),
|
typeof(PlayfieldBackgroundRight),
|
||||||
|
typeof(LegacyTaikoScroller),
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
[Cached(typeof(IScrollingInfo))]
|
[Cached(typeof(IScrollingInfo))]
|
||||||
@ -51,6 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
|
Height = 0.6f,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddRepeatStep("change height", () => this.ChildrenOfType<TaikoPlayfield>().ForEach(p => p.Height = Math.Max(0.2f, (p.Height + 0.2f) % 1f)), 50);
|
AddRepeatStep("change height", () => this.ChildrenOfType<TaikoPlayfield>().ForEach(p => p.Height = Math.Max(0.2f, (p.Height + 0.2f) % 1f)), 50);
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
// 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.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.Taiko.Skinning;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||||
|
{
|
||||||
|
public class TestSceneTaikoScroller : TaikoSkinnableTestScene
|
||||||
|
{
|
||||||
|
public TestSceneTaikoScroller()
|
||||||
|
{
|
||||||
|
AddStep("Load scroller", () => SetContents(() => new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty())));
|
||||||
|
AddToggleStep("Toggle passing", passing => this.ChildrenOfType<LegacyTaikoScroller>().ForEach(s => s.LastResult.Value =
|
||||||
|
new JudgementResult(null, new Judgement()) { Type = passing ? HitResult.Perfect : HitResult.Miss }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
149
osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs
Normal file
149
osu.Game.Rulesets.Taiko/Skinning/LegacyTaikoScroller.cs
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Skinning
|
||||||
|
{
|
||||||
|
public class LegacyTaikoScroller : CompositeDrawable
|
||||||
|
{
|
||||||
|
public LegacyTaikoScroller()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader(true)]
|
||||||
|
private void load(GameplayBeatmap gameplayBeatmap)
|
||||||
|
{
|
||||||
|
if (gameplayBeatmap != null)
|
||||||
|
((IBindable<JudgementResult>)LastResult).BindTo(gameplayBeatmap.LastJudgementResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool passing;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
LastResult.BindValueChanged(result =>
|
||||||
|
{
|
||||||
|
var r = result.NewValue;
|
||||||
|
|
||||||
|
// always ignore hitobjects that don't affect combo (drumroll ticks etc.)
|
||||||
|
if (r?.Judgement.AffectsCombo == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
passing = r == null || r.Type > HitResult.Miss;
|
||||||
|
|
||||||
|
foreach (var sprite in InternalChildren.OfType<ScrollerSprite>())
|
||||||
|
sprite.Passing = passing;
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bindable<JudgementResult> LastResult = new Bindable<JudgementResult>();
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
float? additiveX = null;
|
||||||
|
|
||||||
|
foreach (var sprite in InternalChildren)
|
||||||
|
{
|
||||||
|
// add the x coordinates and perform re-layout on all sprites as spacing may change with gameplay scale.
|
||||||
|
sprite.X = additiveX ??= sprite.X - (float)Time.Elapsed * 0.1f;
|
||||||
|
|
||||||
|
additiveX += sprite.DrawWidth - 1;
|
||||||
|
|
||||||
|
if (sprite.X + sprite.DrawWidth < 0)
|
||||||
|
sprite.Expire();
|
||||||
|
}
|
||||||
|
|
||||||
|
var last = InternalChildren.LastOrDefault();
|
||||||
|
|
||||||
|
// only break from this loop once we have saturated horizontal space completely.
|
||||||
|
if (last != null && last.ScreenSpaceDrawQuad.TopRight.X >= ScreenSpaceDrawQuad.TopRight.X)
|
||||||
|
break;
|
||||||
|
|
||||||
|
AddInternal(new ScrollerSprite
|
||||||
|
{
|
||||||
|
Passing = passing
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ScrollerSprite : CompositeDrawable
|
||||||
|
{
|
||||||
|
private Sprite passingSprite;
|
||||||
|
private Sprite failingSprite;
|
||||||
|
|
||||||
|
private bool passing = true;
|
||||||
|
|
||||||
|
public bool Passing
|
||||||
|
{
|
||||||
|
get => passing;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == passing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
passing = value;
|
||||||
|
|
||||||
|
if (IsLoaded)
|
||||||
|
updatePassing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X;
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
FillMode = FillMode.Fit;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
passingSprite = new Sprite { Texture = skin.GetTexture("taiko-slider") },
|
||||||
|
failingSprite = new Sprite { Texture = skin.GetTexture("taiko-slider-fail"), Alpha = 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
updatePassing();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
|
||||||
|
foreach (var c in InternalChildren)
|
||||||
|
c.Scale = new Vector2(DrawHeight / c.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePassing()
|
||||||
|
{
|
||||||
|
if (passing)
|
||||||
|
{
|
||||||
|
passingSprite.Show();
|
||||||
|
failingSprite.FadeOut(200);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
failingSprite.FadeIn(200);
|
||||||
|
passingSprite.Delay(200).FadeOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -85,6 +85,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning
|
|||||||
return new LegacyHitExplosion(sprite);
|
return new LegacyHitExplosion(sprite);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
case TaikoSkinComponents.TaikoScroller:
|
||||||
|
if (GetTexture("taiko-slider") != null)
|
||||||
|
return new LegacyTaikoScroller();
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return source.GetDrawableComponent(component);
|
return source.GetDrawableComponent(component);
|
||||||
|
@ -18,5 +18,6 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
TaikoExplosionMiss,
|
TaikoExplosionMiss,
|
||||||
TaikoExplosionGood,
|
TaikoExplosionGood,
|
||||||
TaikoExplosionGreat,
|
TaikoExplosionGreat,
|
||||||
|
TaikoScroller
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
@ -16,11 +17,15 @@ using osu.Game.Replays;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.UI
|
namespace osu.Game.Rulesets.Taiko.UI
|
||||||
{
|
{
|
||||||
public class DrawableTaikoRuleset : DrawableScrollingRuleset<TaikoHitObject>
|
public class DrawableTaikoRuleset : DrawableScrollingRuleset<TaikoHitObject>
|
||||||
{
|
{
|
||||||
|
private SkinnableDrawable scroller;
|
||||||
|
|
||||||
protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping;
|
protected override ScrollVisualisationMethod VisualisationMethod => ScrollVisualisationMethod.Overlapping;
|
||||||
|
|
||||||
protected override bool UserScrollSpeedAdjustment => false;
|
protected override bool UserScrollSpeedAdjustment => false;
|
||||||
@ -36,6 +41,20 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
|
new BarLineGenerator<BarLine>(Beatmap).BarLines.ForEach(bar => Playfield.Add(bar.Major ? new DrawableBarLineMajor(bar) : new DrawableBarLine(bar)));
|
||||||
|
|
||||||
|
AddInternal(scroller = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoScroller), _ => Empty())
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Depth = float.MaxValue
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateAfterChildren()
|
||||||
|
{
|
||||||
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
|
var playfieldScreen = Playfield.ScreenSpaceDrawQuad;
|
||||||
|
scroller.Height = ToLocalSpace(playfieldScreen.TopLeft + new Vector2(0, playfieldScreen.Height / 20)).Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer();
|
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new TaikoPlayfieldAdjustmentContainer();
|
||||||
|
@ -18,6 +18,7 @@ using osu.Game.Screens.Menu;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Development;
|
using osu.Framework.Development;
|
||||||
@ -97,6 +98,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
private MainMenu menuScreen;
|
private MainMenu menuScreen;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
private IntroScreen introScreen;
|
private IntroScreen introScreen;
|
||||||
|
|
||||||
private Bindable<int> configRuleset;
|
private Bindable<int> configRuleset;
|
||||||
@ -914,7 +916,7 @@ namespace osu.Game
|
|||||||
if (ScreenStack.CurrentScreen is Loader)
|
if (ScreenStack.CurrentScreen is Loader)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (introScreen.DidLoadMenu && !(ScreenStack.CurrentScreen is IntroScreen))
|
if (introScreen?.DidLoadMenu == true && !(ScreenStack.CurrentScreen is IntroScreen))
|
||||||
{
|
{
|
||||||
Scheduler.Add(introScreen.MakeCurrent);
|
Scheduler.Add(introScreen.MakeCurrent);
|
||||||
return true;
|
return true;
|
||||||
|
@ -50,13 +50,6 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
|||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(OsuGame game, BeatmapManager beatmaps, OsuConfigManager osuConfig)
|
private void load(OsuGame game, BeatmapManager beatmaps, OsuConfigManager osuConfig)
|
||||||
{
|
{
|
||||||
if (BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false)
|
|
||||||
{
|
|
||||||
button.Enabled.Value = false;
|
|
||||||
button.TooltipText = "this beatmap is currently not available for download.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
noVideoSetting = osuConfig.GetBindable<bool>(OsuSetting.PreferNoVideo);
|
noVideoSetting = osuConfig.GetBindable<bool>(OsuSetting.PreferNoVideo);
|
||||||
|
|
||||||
button.Action = () =>
|
button.Action = () =>
|
||||||
@ -81,6 +74,26 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
State.BindValueChanged(state =>
|
||||||
|
{
|
||||||
|
switch (state.NewValue)
|
||||||
|
{
|
||||||
|
case DownloadState.LocallyAvailable:
|
||||||
|
button.Enabled.Value = true;
|
||||||
|
button.TooltipText = string.Empty;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (BeatmapSet.Value?.OnlineInfo?.Availability?.DownloadDisabled ?? false)
|
||||||
|
{
|
||||||
|
button.Enabled.Value = false;
|
||||||
|
button.TooltipText = "this beatmap is currently not available for download.";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
{
|
{
|
||||||
if (BeatmapSet.Value == null) return;
|
if (BeatmapSet.Value == null) return;
|
||||||
|
|
||||||
if (BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false)
|
if ((BeatmapSet.Value.OnlineInfo.Availability?.DownloadDisabled ?? false) && State.Value != DownloadState.LocallyAvailable)
|
||||||
{
|
{
|
||||||
downloadButtonsContainer.Clear();
|
downloadButtonsContainer.Clear();
|
||||||
return;
|
return;
|
||||||
|
@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
[Cached(typeof(DrawableHitObject))]
|
[Cached(typeof(DrawableHitObject))]
|
||||||
public abstract class DrawableHitObject : SkinReloadableDrawable
|
public abstract class DrawableHitObject : SkinReloadableDrawable
|
||||||
{
|
{
|
||||||
|
public event Action<DrawableHitObject> DefaultsApplied;
|
||||||
|
|
||||||
public readonly HitObject HitObject;
|
public readonly HitObject HitObject;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -148,7 +150,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
samplesBindable.CollectionChanged += (_, __) => loadSamples();
|
samplesBindable.CollectionChanged += (_, __) => loadSamples();
|
||||||
|
|
||||||
updateState(ArmedState.Idle, true);
|
updateState(ArmedState.Idle, true);
|
||||||
onDefaultsApplied();
|
apply(HitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadSamples()
|
private void loadSamples()
|
||||||
@ -175,7 +177,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
AddInternal(Samples);
|
AddInternal(Samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onDefaultsApplied() => apply(HitObject);
|
private void onDefaultsApplied(HitObject hitObject)
|
||||||
|
{
|
||||||
|
apply(hitObject);
|
||||||
|
DefaultsApplied?.Invoke(this);
|
||||||
|
}
|
||||||
|
|
||||||
private void apply(HitObject hitObject)
|
private void apply(HitObject hitObject)
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked after <see cref="ApplyDefaults"/> has completed on this <see cref="HitObject"/>.
|
/// Invoked after <see cref="ApplyDefaults"/> has completed on this <see cref="HitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action DefaultsApplied;
|
public event Action<HitObject> DefaultsApplied;
|
||||||
|
|
||||||
public readonly Bindable<double> StartTimeBindable = new BindableDouble();
|
public readonly Bindable<double> StartTimeBindable = new BindableDouble();
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
foreach (var h in nestedHitObjects)
|
foreach (var h in nestedHitObjects)
|
||||||
h.ApplyDefaults(controlPointInfo, difficulty);
|
h.ApplyDefaults(controlPointInfo, difficulty);
|
||||||
|
|
||||||
DefaultsApplied?.Invoke();
|
DefaultsApplied?.Invoke(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||||
|
@ -487,6 +487,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
protected virtual ResumeOverlay CreateResumeOverlay() => null;
|
protected virtual ResumeOverlay CreateResumeOverlay() => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to display gameplay overlays, such as <see cref="HUDOverlay"/> and <see cref="BreakOverlay"/>.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool AllowGameplayOverlays => true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets a replay to be used, overriding local input.
|
/// Sets a replay to be used, overriding local input.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -16,17 +16,23 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
{
|
{
|
||||||
private readonly IBindable<double> timeRange = new BindableDouble();
|
private readonly IBindable<double> timeRange = new BindableDouble();
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
private readonly Dictionary<DrawableHitObject, Cached> hitObjectInitialStateCache = new Dictionary<DrawableHitObject, Cached>();
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private IScrollingInfo scrollingInfo { get; set; }
|
private IScrollingInfo scrollingInfo { get; set; }
|
||||||
|
|
||||||
private readonly LayoutValue initialStateCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo);
|
// Responds to changes in the layout. When the layout changes, all hit object states must be recomputed.
|
||||||
|
private readonly LayoutValue layoutCache = new LayoutValue(Invalidation.RequiredParentSizeToFit | Invalidation.DrawInfo);
|
||||||
|
|
||||||
|
// A combined cache across all hit object states to reduce per-update iterations.
|
||||||
|
// When invalidated, one or more (but not necessarily all) hitobject states must be re-validated.
|
||||||
|
private readonly Cached combinedObjCache = new Cached();
|
||||||
|
|
||||||
public ScrollingHitObjectContainer()
|
public ScrollingHitObjectContainer()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
AddLayout(initialStateCache);
|
AddLayout(layoutCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -35,13 +41,14 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
direction.BindTo(scrollingInfo.Direction);
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
timeRange.BindTo(scrollingInfo.TimeRange);
|
timeRange.BindTo(scrollingInfo.TimeRange);
|
||||||
|
|
||||||
direction.ValueChanged += _ => initialStateCache.Invalidate();
|
direction.ValueChanged += _ => layoutCache.Invalidate();
|
||||||
timeRange.ValueChanged += _ => initialStateCache.Invalidate();
|
timeRange.ValueChanged += _ => layoutCache.Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Add(DrawableHitObject hitObject)
|
public override void Add(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
initialStateCache.Invalidate();
|
combinedObjCache.Invalidate();
|
||||||
|
hitObject.DefaultsApplied += onDefaultsApplied;
|
||||||
base.Add(hitObject);
|
base.Add(hitObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,8 +58,10 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
initialStateCache.Invalidate();
|
combinedObjCache.Invalidate();
|
||||||
hitObjectInitialStateCache.Remove(hitObject);
|
hitObjectInitialStateCache.Remove(hitObject);
|
||||||
|
|
||||||
|
hitObject.DefaultsApplied -= onDefaultsApplied;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -60,23 +69,45 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
|
|
||||||
public override void Clear(bool disposeChildren = true)
|
public override void Clear(bool disposeChildren = true)
|
||||||
{
|
{
|
||||||
|
foreach (var h in Objects)
|
||||||
|
h.DefaultsApplied -= onDefaultsApplied;
|
||||||
|
|
||||||
base.Clear(disposeChildren);
|
base.Clear(disposeChildren);
|
||||||
|
|
||||||
initialStateCache.Invalidate();
|
combinedObjCache.Invalidate();
|
||||||
hitObjectInitialStateCache.Clear();
|
hitObjectInitialStateCache.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onDefaultsApplied(DrawableHitObject drawableObject)
|
||||||
|
{
|
||||||
|
// The cache may not exist if the hitobject state hasn't been computed yet (e.g. if the hitobject was added + defaults applied in the same frame).
|
||||||
|
// In such a case, combinedObjCache will take care of updating the hitobject.
|
||||||
|
if (hitObjectInitialStateCache.TryGetValue(drawableObject, out var objCache))
|
||||||
|
{
|
||||||
|
combinedObjCache.Invalidate();
|
||||||
|
objCache.Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private float scrollLength;
|
private float scrollLength;
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
if (!initialStateCache.IsValid)
|
if (!layoutCache.IsValid)
|
||||||
{
|
{
|
||||||
foreach (var cached in hitObjectInitialStateCache.Values)
|
foreach (var cached in hitObjectInitialStateCache.Values)
|
||||||
cached.Invalidate();
|
cached.Invalidate();
|
||||||
|
combinedObjCache.Invalidate();
|
||||||
|
|
||||||
|
scrollingInfo.Algorithm.Reset();
|
||||||
|
|
||||||
|
layoutCache.Validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!combinedObjCache.IsValid)
|
||||||
|
{
|
||||||
switch (direction.Value)
|
switch (direction.Value)
|
||||||
{
|
{
|
||||||
case ScrollingDirection.Up:
|
case ScrollingDirection.Up:
|
||||||
@ -89,15 +120,21 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollingInfo.Algorithm.Reset();
|
|
||||||
|
|
||||||
foreach (var obj in Objects)
|
foreach (var obj in Objects)
|
||||||
{
|
{
|
||||||
|
if (!hitObjectInitialStateCache.TryGetValue(obj, out var objCache))
|
||||||
|
objCache = hitObjectInitialStateCache[obj] = new Cached();
|
||||||
|
|
||||||
|
if (objCache.IsValid)
|
||||||
|
continue;
|
||||||
|
|
||||||
computeLifetimeStartRecursive(obj);
|
computeLifetimeStartRecursive(obj);
|
||||||
computeInitialStateRecursive(obj);
|
computeInitialStateRecursive(obj);
|
||||||
|
|
||||||
|
objCache.Validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
initialStateCache.Validate();
|
combinedObjCache.Validate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,8 +146,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
computeLifetimeStartRecursive(obj);
|
computeLifetimeStartRecursive(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly Dictionary<DrawableHitObject, Cached> hitObjectInitialStateCache = new Dictionary<DrawableHitObject, Cached>();
|
|
||||||
|
|
||||||
private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject)
|
private double computeOriginAdjustedLifetimeStart(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
float originAdjustment = 0.0f;
|
float originAdjustment = 0.0f;
|
||||||
@ -142,12 +177,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
// Cant use AddOnce() since the delegate is re-constructed every invocation
|
// Cant use AddOnce() since the delegate is re-constructed every invocation
|
||||||
private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() =>
|
private void computeInitialStateRecursive(DrawableHitObject hitObject) => hitObject.Schedule(() =>
|
||||||
{
|
{
|
||||||
if (!hitObjectInitialStateCache.TryGetValue(hitObject, out var cached))
|
|
||||||
cached = hitObjectInitialStateCache[hitObject] = new Cached();
|
|
||||||
|
|
||||||
if (cached.IsValid)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (hitObject.HitObject is IHasEndTime e)
|
if (hitObject.HitObject is IHasEndTime e)
|
||||||
{
|
{
|
||||||
switch (direction.Value)
|
switch (direction.Value)
|
||||||
@ -171,8 +200,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
// Nested hitobjects don't need to scroll, but they do need accurate positions
|
// Nested hitobjects don't need to scroll, but they do need accurate positions
|
||||||
updatePosition(obj, hitObject.HitObject.StartTime);
|
updatePosition(obj, hitObject.HitObject.StartTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
cached.Validate();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
protected override void UpdateAfterChildrenLife()
|
protected override void UpdateAfterChildrenLife()
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -201,6 +202,25 @@ namespace osu.Game.Screens.Edit
|
|||||||
updateHitObject(null, true);
|
updateHitObject(null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all <see cref="HitObjects"/> from this <see cref="EditorBeatmap"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
var removed = HitObjects.ToList();
|
||||||
|
|
||||||
|
mutableHitObjects.Clear();
|
||||||
|
|
||||||
|
foreach (var b in startTimeBindables)
|
||||||
|
b.Value.UnbindAll();
|
||||||
|
startTimeBindables.Clear();
|
||||||
|
|
||||||
|
foreach (var h in removed)
|
||||||
|
HitObjectRemoved?.Invoke(h);
|
||||||
|
|
||||||
|
updateHitObject(null, true);
|
||||||
|
}
|
||||||
|
|
||||||
private void trackStartTime(HitObject hitObject)
|
private void trackStartTime(HitObject hitObject)
|
||||||
{
|
{
|
||||||
startTimeBindables[hitObject] = hitObject.StartTimeBindable.GetBoundCopy();
|
startTimeBindables[hitObject] = hitObject.StartTimeBindable.GetBoundCopy();
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
@ -38,5 +40,11 @@ namespace osu.Game.Screens.Play
|
|||||||
public IEnumerable<BeatmapStatistic> GetStatistics() => PlayableBeatmap.GetStatistics();
|
public IEnumerable<BeatmapStatistic> GetStatistics() => PlayableBeatmap.GetStatistics();
|
||||||
|
|
||||||
public IBeatmap Clone() => PlayableBeatmap.Clone();
|
public IBeatmap Clone() => PlayableBeatmap.Clone();
|
||||||
|
|
||||||
|
private readonly Bindable<JudgementResult> lastJudgementResult = new Bindable<JudgementResult>();
|
||||||
|
|
||||||
|
public IBindable<JudgementResult> LastJudgementResult => lastJudgementResult;
|
||||||
|
|
||||||
|
public void ApplyResult(JudgementResult result) => lastJudgementResult.Value = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,6 +184,13 @@ namespace osu.Game.Screens.Play
|
|||||||
addGameplayComponents(GameplayClockContainer, Beatmap.Value, playableBeatmap);
|
addGameplayComponents(GameplayClockContainer, Beatmap.Value, playableBeatmap);
|
||||||
addOverlayComponents(GameplayClockContainer, Beatmap.Value);
|
addOverlayComponents(GameplayClockContainer, Beatmap.Value);
|
||||||
|
|
||||||
|
if (!DrawableRuleset.AllowGameplayOverlays)
|
||||||
|
{
|
||||||
|
HUDOverlay.ShowHud.Value = false;
|
||||||
|
HUDOverlay.ShowHud.Disabled = true;
|
||||||
|
BreakOverlay.Hide();
|
||||||
|
}
|
||||||
|
|
||||||
DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
||||||
|
|
||||||
// bind clock into components that require it
|
// bind clock into components that require it
|
||||||
@ -193,6 +200,7 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
HealthProcessor.ApplyResult(r);
|
HealthProcessor.ApplyResult(r);
|
||||||
ScoreProcessor.ApplyResult(r);
|
ScoreProcessor.ApplyResult(r);
|
||||||
|
gameplayBeatmap.ApplyResult(r);
|
||||||
};
|
};
|
||||||
|
|
||||||
DrawableRuleset.OnRevertResult += r =>
|
DrawableRuleset.OnRevertResult += r =>
|
||||||
|
Loading…
Reference in New Issue
Block a user