mirror of
https://github.com/ppy/osu
synced 2024-12-29 02:12:43 +00:00
Merge branch 'master' into mod-overlay-triangles
This commit is contained in:
commit
fa300b5ba0
@ -4,6 +4,7 @@
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
switch (targetComponent.Lookup)
|
||||
{
|
||||
case GlobalSkinComponentLookup.LookupType.MainHUDComponents:
|
||||
var components = base.GetDrawableComponent(lookup) as SkinnableTargetComponentsContainer;
|
||||
var components = base.GetDrawableComponent(lookup) as Container;
|
||||
|
||||
if (providesComboCounter && components != null)
|
||||
{
|
||||
|
@ -69,8 +69,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
/// </summary>
|
||||
private double? releaseTime;
|
||||
|
||||
public override double MaximumJudgementOffset => Tail.MaximumJudgementOffset;
|
||||
|
||||
public DrawableHoldNote()
|
||||
: this(null)
|
||||
{
|
||||
|
@ -15,13 +15,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
/// </summary>
|
||||
public partial class DrawableHoldNoteTail : DrawableNote
|
||||
{
|
||||
/// <summary>
|
||||
/// Lenience of release hit windows. This is to make cases where the hold note release
|
||||
/// is timed alongside presses of other hit objects less awkward.
|
||||
/// Todo: This shouldn't exist for non-LegacyBeatmapDecoder beatmaps
|
||||
/// </summary>
|
||||
private const double release_window_lenience = 1.5;
|
||||
|
||||
protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteTail;
|
||||
|
||||
protected DrawableHoldNote HoldNote => (DrawableHoldNote)ParentHitObject;
|
||||
@ -40,14 +33,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
public void UpdateResult() => base.UpdateResult(true);
|
||||
|
||||
public override double MaximumJudgementOffset => base.MaximumJudgementOffset * release_window_lenience;
|
||||
|
||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||
{
|
||||
Debug.Assert(HitObject.HitWindows != null);
|
||||
|
||||
// Factor in the release lenience
|
||||
timeOffset /= release_window_lenience;
|
||||
timeOffset /= TailNote.RELEASE_WINDOW_LENIENCE;
|
||||
|
||||
if (!userTriggered)
|
||||
{
|
||||
|
@ -81,6 +81,8 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// </summary>
|
||||
public TailNote Tail { get; private set; }
|
||||
|
||||
public override double MaximumJudgementOffset => Tail.MaximumJudgementOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The time between ticks of this hold.
|
||||
/// </summary>
|
||||
|
@ -10,6 +10,15 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
public class TailNote : Note
|
||||
{
|
||||
/// <summary>
|
||||
/// Lenience of release hit windows. This is to make cases where the hold note release
|
||||
/// is timed alongside presses of other hit objects less awkward.
|
||||
/// Todo: This shouldn't exist for non-LegacyBeatmapDecoder beatmaps
|
||||
/// </summary>
|
||||
public const double RELEASE_WINDOW_LENIENCE = 1.5;
|
||||
|
||||
public override Judgement CreateJudgement() => new ManiaJudgement();
|
||||
|
||||
public override double MaximumJudgementOffset => base.MaximumJudgementOffset * RELEASE_WINDOW_LENIENCE;
|
||||
}
|
||||
}
|
||||
|
31
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutopilot.cs
Normal file
31
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModAutopilot.cs
Normal file
@ -0,0 +1,31 @@
|
||||
// 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.Rulesets.Osu.Mods;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||
{
|
||||
public partial class TestSceneOsuModAutopilot : OsuModTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestInstantResume()
|
||||
{
|
||||
CreateModTest(new ModTestData
|
||||
{
|
||||
Mod = new OsuModAutopilot(),
|
||||
PassCondition = () => true,
|
||||
Autoplay = false,
|
||||
});
|
||||
|
||||
AddUntilStep("wait for gameplay start", () => Player.LocalUserPlaying.Value);
|
||||
AddStep("press pause", () => InputManager.PressKey(Key.Escape));
|
||||
AddUntilStep("wait until paused", () => Player.GameplayClockContainer.IsPaused.Value);
|
||||
AddStep("release pause", () => InputManager.ReleaseKey(Key.Escape));
|
||||
AddStep("press resume", () => InputManager.PressKey(Key.Escape));
|
||||
AddUntilStep("wait for resume", () => !Player.IsResuming);
|
||||
AddAssert("resumed", () => !Player.GameplayClockContainer.IsPaused.Value);
|
||||
}
|
||||
}
|
||||
}
|
@ -60,6 +60,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
// Generate the replay frames the cursor should follow
|
||||
replayFrames = new OsuAutoGenerator(drawableRuleset.Beatmap, drawableRuleset.Mods).Generate().Frames.Cast<OsuReplayFrame>().ToList();
|
||||
|
||||
drawableRuleset.UseResumeOverlay = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
public override double MaximumJudgementOffset => DrawableSpinner.HitObject.Duration;
|
||||
|
||||
/// <summary>
|
||||
/// Apply a judgement result.
|
||||
/// </summary>
|
||||
|
@ -71,8 +71,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
double startTime = StartTime + (float)(i + 1) / totalSpins * Duration;
|
||||
|
||||
AddNested(i < SpinsRequired
|
||||
? new SpinnerTick { StartTime = startTime }
|
||||
: new SpinnerBonusTick { StartTime = startTime });
|
||||
? new SpinnerTick { StartTime = startTime, SpinnerDuration = Duration }
|
||||
: new SpinnerBonusTick { StartTime = startTime, SpinnerDuration = Duration });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,10 +11,17 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
public class SpinnerTick : OsuHitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Duration of the <see cref="Spinner"/> containing this spinner tick.
|
||||
/// </summary>
|
||||
public double SpinnerDuration { get; set; }
|
||||
|
||||
public override Judgement CreateJudgement() => new OsuSpinnerTickJudgement();
|
||||
|
||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||
|
||||
public override double MaximumJudgementOffset => SpinnerDuration;
|
||||
|
||||
public class OsuSpinnerTickJudgement : OsuJudgement
|
||||
{
|
||||
public override HitResult MaxResult => HitResult.SmallBonus;
|
||||
|
@ -37,8 +37,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
|
||||
protected override SkinnableDrawable CreateMainPiece() => new SkinnableDrawable(new TaikoSkinComponentLookup(TaikoSkinComponents.DrumRollTick), _ => new TickPiece());
|
||||
|
||||
public override double MaximumJudgementOffset => HitObject.HitWindow;
|
||||
|
||||
protected override void OnApply()
|
||||
{
|
||||
base.OnApply();
|
||||
|
@ -31,6 +31,8 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
|
||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||
|
||||
public override double MaximumJudgementOffset => HitWindow;
|
||||
|
||||
protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime };
|
||||
|
||||
public class StrongNestedHit : StrongNestedHitObject
|
||||
|
@ -1,13 +1,12 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
@ -28,10 +27,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public partial class TestSceneBeatmapSkinFallbacks : OsuPlayerTestScene
|
||||
{
|
||||
private ISkin currentBeatmapSkin;
|
||||
private ISkin currentBeatmapSkin = null!;
|
||||
|
||||
[Resolved]
|
||||
private SkinManager skinManager { get; set; }
|
||||
private SkinManager skinManager { get; set; } = null!;
|
||||
|
||||
protected override bool HasCustomSteps => true;
|
||||
|
||||
@ -57,15 +56,15 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
protected bool AssertComponentsFromExpectedSource(GlobalSkinComponentLookup.LookupType target, ISkin expectedSource)
|
||||
{
|
||||
var actualComponentsContainer = Player.ChildrenOfType<SkinnableTargetContainer>().First(s => s.Target == target)
|
||||
.ChildrenOfType<SkinnableTargetComponentsContainer>().SingleOrDefault();
|
||||
var targetContainer = Player.ChildrenOfType<SkinnableTargetContainer>().First(s => s.Target == target);
|
||||
var actualComponentsContainer = targetContainer.ChildrenOfType<Container>().SingleOrDefault(c => c.Parent == targetContainer);
|
||||
|
||||
if (actualComponentsContainer == null)
|
||||
return false;
|
||||
|
||||
var actualInfo = actualComponentsContainer.CreateSkinnableInfo();
|
||||
|
||||
var expectedComponentsContainer = (SkinnableTargetComponentsContainer)expectedSource.GetDrawableComponent(new GlobalSkinComponentLookup(target));
|
||||
var expectedComponentsContainer = expectedSource.GetDrawableComponent(new GlobalSkinComponentLookup(target)) as Container;
|
||||
if (expectedComponentsContainer == null)
|
||||
return false;
|
||||
|
||||
@ -92,7 +91,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
return almostEqual(actualInfo, expectedInfo);
|
||||
}
|
||||
|
||||
private static bool almostEqual(SkinnableInfo info, SkinnableInfo other) =>
|
||||
private static bool almostEqual(SkinnableInfo info, SkinnableInfo? other) =>
|
||||
other != null
|
||||
&& info.Type == other.Type
|
||||
&& info.Anchor == other.Anchor
|
||||
@ -102,7 +101,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
&& Precision.AlmostEquals(info.Rotation, other.Rotation)
|
||||
&& info.Children.SequenceEqual(other.Children, new FuncEqualityComparer<SkinnableInfo>(almostEqual));
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null)
|
||||
=> new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin);
|
||||
|
||||
protected override Ruleset CreatePlayerRuleset() => new TestOsuRuleset();
|
||||
@ -111,7 +110,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
private readonly ISkin beatmapSkin;
|
||||
|
||||
public CustomSkinWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard, IFrameBasedClock referenceClock, AudioManager audio, ISkin beatmapSkin)
|
||||
public CustomSkinWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard, IFrameBasedClock referenceClock, AudioManager audio, ISkin beatmapSkin)
|
||||
: base(beatmap, storyboard, referenceClock, audio)
|
||||
{
|
||||
this.beatmapSkin = beatmapSkin;
|
||||
|
@ -1,10 +1,12 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@ -14,16 +16,23 @@ using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Storyboards;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public partial class TestSceneGameplaySampleTriggerSource : PlayerTestScene
|
||||
{
|
||||
private TestGameplaySampleTriggerSource sampleTriggerSource;
|
||||
private TestGameplaySampleTriggerSource sampleTriggerSource = null!;
|
||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||
|
||||
private Beatmap beatmap;
|
||||
private Beatmap beatmap = null!;
|
||||
|
||||
[Resolved]
|
||||
private AudioManager audio { get; set; } = null!;
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard? storyboard = null)
|
||||
=> new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audio);
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
@ -39,12 +48,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
const double start_offset = 8000;
|
||||
const double spacing = 2000;
|
||||
|
||||
// intentionally start objects a bit late so we can test the case of no alive objects.
|
||||
double t = start_offset;
|
||||
|
||||
beatmap.HitObjects.AddRange(new[]
|
||||
{
|
||||
new HitCircle
|
||||
{
|
||||
// intentionally start objects a bit late so we can test the case of no alive objects.
|
||||
StartTime = t += spacing,
|
||||
Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }
|
||||
},
|
||||
@ -80,42 +90,66 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestCorrectHitObject()
|
||||
{
|
||||
HitObjectLifetimeEntry nextObjectEntry = null;
|
||||
waitForAliveObjectIndex(null);
|
||||
checkValidObjectIndex(0);
|
||||
|
||||
AddAssert("no alive objects", () => getNextAliveObject() == null);
|
||||
seekBeforeIndex(0);
|
||||
waitForAliveObjectIndex(0);
|
||||
checkValidObjectIndex(0);
|
||||
|
||||
AddAssert("check initially correct object", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[0]);
|
||||
AddAssert("first object not hit", () => getNextAliveObject()?.Entry?.Result?.HasResult != true);
|
||||
|
||||
AddUntilStep("get next object", () =>
|
||||
AddStep("hit first object", () =>
|
||||
{
|
||||
var nextDrawableObject = getNextAliveObject();
|
||||
var next = getNextAliveObject();
|
||||
|
||||
if (nextDrawableObject != null)
|
||||
if (next != null)
|
||||
{
|
||||
nextObjectEntry = nextDrawableObject.Entry;
|
||||
InputManager.MoveMouseTo(nextDrawableObject.ScreenSpaceDrawQuad.Centre);
|
||||
return true;
|
||||
Debug.Assert(next.Entry?.Result?.HasResult != true);
|
||||
|
||||
InputManager.MoveMouseTo(next.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
AddUntilStep("hit first hitobject", () =>
|
||||
{
|
||||
InputManager.Click(MouseButton.Left);
|
||||
return nextObjectEntry.Result?.HasResult == true;
|
||||
});
|
||||
AddAssert("first object hit", () => getNextAliveObject()?.Entry?.Result?.HasResult == true);
|
||||
|
||||
AddAssert("check correct object after hit", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[1]);
|
||||
checkValidObjectIndex(1);
|
||||
|
||||
AddUntilStep("check correct object after miss", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[2]);
|
||||
AddUntilStep("check correct object after miss", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[3]);
|
||||
// Still object 1 as it's not hit yet.
|
||||
seekBeforeIndex(1);
|
||||
waitForAliveObjectIndex(1);
|
||||
checkValidObjectIndex(1);
|
||||
|
||||
AddUntilStep("no alive objects", () => getNextAliveObject() == null);
|
||||
AddAssert("check correct object after none alive", () => sampleTriggerSource.GetMostValidObject() == beatmap.HitObjects[3]);
|
||||
seekBeforeIndex(2);
|
||||
waitForAliveObjectIndex(2);
|
||||
checkValidObjectIndex(2);
|
||||
|
||||
seekBeforeIndex(3);
|
||||
waitForAliveObjectIndex(3);
|
||||
checkValidObjectIndex(3);
|
||||
|
||||
AddStep("Seek into future", () => Beatmap.Value.Track.Seek(beatmap.HitObjects.Last().GetEndTime() + 10000));
|
||||
|
||||
waitForAliveObjectIndex(null);
|
||||
checkValidObjectIndex(3);
|
||||
}
|
||||
|
||||
private DrawableHitObject getNextAliveObject() =>
|
||||
private void seekBeforeIndex(int index) =>
|
||||
AddStep($"seek to just before object {index}", () => Beatmap.Value.Track.Seek(beatmap.HitObjects[index].StartTime - 100));
|
||||
|
||||
private void waitForAliveObjectIndex(int? index)
|
||||
{
|
||||
if (index == null)
|
||||
AddUntilStep("wait for no alive objects", getNextAliveObject, () => Is.Null);
|
||||
else
|
||||
AddUntilStep($"wait for next alive to be {index}", () => getNextAliveObject()?.HitObject, () => Is.EqualTo(beatmap.HitObjects[index.Value]));
|
||||
}
|
||||
|
||||
private void checkValidObjectIndex(int index) =>
|
||||
AddAssert($"check valid object is {index}", () => sampleTriggerSource.GetMostValidObject(), () => Is.EqualTo(beatmap.HitObjects[index]));
|
||||
|
||||
private DrawableHitObject? getNextAliveObject() =>
|
||||
Player.DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.FirstOrDefault();
|
||||
|
||||
[Test]
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Graphics.Containers
|
||||
/// </summary>
|
||||
public const float BREAK_LIGHTEN_AMOUNT = 0.3f;
|
||||
|
||||
protected const double BACKGROUND_FADE_DURATION = 800;
|
||||
public const double BACKGROUND_FADE_DURATION = 800;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not user-configured settings relating to brightness of elements should be ignored
|
||||
|
@ -650,18 +650,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
UpdateResult(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The maximum offset from the end time of <see cref="HitObject"/> at which this <see cref="DrawableHitObject"/> can be judged.
|
||||
/// The time offset of <see cref="Result"/> will be clamped to this value during <see cref="ApplyResult"/>.
|
||||
/// <para>
|
||||
/// Defaults to the miss window of <see cref="HitObject"/>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This does not affect the time offset provided to invocations of <see cref="CheckForResult"/>.
|
||||
/// </remarks>
|
||||
public virtual double MaximumJudgementOffset => HitObject.HitWindows?.WindowFor(HitResult.Miss) ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// Applies the <see cref="Result"/> of this <see cref="DrawableHitObject"/>, notifying responders such as
|
||||
/// the <see cref="ScoreProcessor"/> of the <see cref="JudgementResult"/>.
|
||||
@ -683,7 +671,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
$"{GetType().ReadableName()} applied an invalid hit result (was: {Result.Type}, expected: [{Result.Judgement.MinResult} ... {Result.Judgement.MaxResult}]).");
|
||||
}
|
||||
|
||||
Result.TimeOffset = Math.Min(MaximumJudgementOffset, Time.Current - HitObject.GetEndTime());
|
||||
Result.TimeOffset = Math.Min(HitObject.MaximumJudgementOffset, Time.Current - HitObject.GetEndTime());
|
||||
|
||||
if (Result.HasResult)
|
||||
updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss);
|
||||
|
@ -200,6 +200,14 @@ namespace osu.Game.Rulesets.Objects
|
||||
[NotNull]
|
||||
protected virtual HitWindows CreateHitWindows() => new HitWindows();
|
||||
|
||||
/// <summary>
|
||||
/// The maximum offset from the end time of <see cref="HitObject"/> at which this <see cref="HitObject"/> can be judged.
|
||||
/// <para>
|
||||
/// Defaults to the miss window.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public virtual double MaximumJudgementOffset => HitWindows?.WindowFor(HitResult.Miss) ?? 0;
|
||||
|
||||
public IList<HitSampleInfo> CreateSlidingSamples()
|
||||
{
|
||||
var slidingSamples = new List<HitSampleInfo>();
|
||||
|
@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
public override void RequestResume(Action continueResume)
|
||||
{
|
||||
if (ResumeOverlay != null && (Cursor == null || (Cursor.LastFrameState == Visibility.Visible && Contains(Cursor.ActiveCursor.ScreenSpaceDrawQuad.Centre))))
|
||||
if (ResumeOverlay != null && UseResumeOverlay && (Cursor == null || (Cursor.LastFrameState == Visibility.Visible && Contains(Cursor.ActiveCursor.ScreenSpaceDrawQuad.Centre))))
|
||||
{
|
||||
ResumeOverlay.GameplayCursor = Cursor;
|
||||
ResumeOverlay.ResumeAction = continueResume;
|
||||
@ -507,6 +507,15 @@ namespace osu.Game.Rulesets.UI
|
||||
/// </summary>
|
||||
public ResumeOverlay ResumeOverlay { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the <see cref="ResumeOverlay"/> should be used to return the user's cursor position to its previous location after a pause.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Defaults to <c>true</c>.
|
||||
/// Even if <c>true</c>, will not have any effect if the ruleset does not have a resume overlay (see <see cref="CreateResumeOverlay"/>).
|
||||
/// </remarks>
|
||||
public bool UseResumeOverlay { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Returns first available <see cref="HitWindows"/> provided by a <see cref="HitObject"/>.
|
||||
/// </summary>
|
||||
@ -531,6 +540,11 @@ namespace osu.Game.Rulesets.UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an optional resume overlay, which is displayed when a player requests to resume gameplay during non-break time.
|
||||
/// This can be used to force the player to return their hands / cursor to the position they left off, to avoid players
|
||||
/// using pauses as a means of adjusting their inputs (aka "pause buffering").
|
||||
/// </summary>
|
||||
protected virtual ResumeOverlay CreateResumeOverlay() => null;
|
||||
|
||||
/// <summary>
|
||||
|
@ -232,8 +232,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
||||
double computedStartTime = computeDisplayStartTime(entry);
|
||||
|
||||
// always load the hitobject before its first judgement offset
|
||||
double judgementOffset = entry.HitObject.HitWindows?.WindowFor(Scoring.HitResult.Miss) ?? 0;
|
||||
entry.LifetimeStart = Math.Min(entry.HitObject.StartTime - judgementOffset, computedStartTime);
|
||||
entry.LifetimeStart = Math.Min(entry.HitObject.StartTime - entry.HitObject.MaximumJudgementOffset, computedStartTime);
|
||||
}
|
||||
|
||||
private void updateLayoutRecursive(DrawableHitObject hitObject, double? parentHitObjectStartTime = null)
|
||||
|
@ -417,7 +417,7 @@ namespace osu.Game.Screens.Play
|
||||
lowPassFilter.CutoffTo(1000, 650, Easing.OutQuint);
|
||||
highPassFilter.CutoffTo(300).Then().CutoffTo(0, 1250); // 1250 is to line up with the appearance of MetadataInfo (750 delay + 500 fade-in)
|
||||
|
||||
ApplyToBackground(b => b?.FadeColour(Color4.White, 800, Easing.OutQuint));
|
||||
ApplyToBackground(b => b.FadeColour(Color4.White, 800, Easing.OutQuint));
|
||||
}
|
||||
|
||||
protected virtual void ContentOut()
|
||||
|
@ -1,9 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
@ -12,6 +12,11 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
|
||||
|
||||
public void ApplyToBackground(Action<BackgroundScreenBeatmap> action) => base.ApplyToBackground(b => action.Invoke((BackgroundScreenBeatmap)b));
|
||||
public void ApplyToBackground(Action<BackgroundScreenBeatmap> action)
|
||||
{
|
||||
Debug.Assert(this.IsCurrentScreen());
|
||||
|
||||
base.ApplyToBackground(b => action.Invoke((BackgroundScreenBeatmap)b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -148,7 +149,7 @@ namespace osu.Game.Screens.Select
|
||||
if (!this.IsCurrentScreen())
|
||||
return;
|
||||
|
||||
ApplyToBackground(b => b.BlurAmount.Value = e.NewValue ? BACKGROUND_BLUR : 0);
|
||||
ApplyToBackground(applyBlurToBackground);
|
||||
});
|
||||
|
||||
LoadComponentAsync(Carousel = new BeatmapCarousel
|
||||
@ -194,6 +195,7 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
ParallaxAmount = 0.005f,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Child = new WedgeBackground
|
||||
@ -763,12 +765,18 @@ namespace osu.Game.Screens.Select
|
||||
/// <param name="beatmap">The working beatmap.</param>
|
||||
private void updateComponentFromBeatmap(WorkingBeatmap beatmap)
|
||||
{
|
||||
ApplyToBackground(backgroundModeBeatmap =>
|
||||
// If not the current screen, this will be applied in OnResuming.
|
||||
if (this.IsCurrentScreen())
|
||||
{
|
||||
backgroundModeBeatmap.Beatmap = beatmap;
|
||||
backgroundModeBeatmap.BlurAmount.Value = configBackgroundBlur.Value ? BACKGROUND_BLUR : 0f;
|
||||
backgroundModeBeatmap.FadeColour(Color4.White, 250);
|
||||
});
|
||||
ApplyToBackground(backgroundModeBeatmap =>
|
||||
{
|
||||
backgroundModeBeatmap.Beatmap = beatmap;
|
||||
backgroundModeBeatmap.IgnoreUserSettings.Value = true;
|
||||
backgroundModeBeatmap.FadeColour(Color4.White, 250);
|
||||
|
||||
applyBlurToBackground(backgroundModeBeatmap);
|
||||
});
|
||||
}
|
||||
|
||||
beatmapInfoWedge.Beatmap = beatmap;
|
||||
|
||||
@ -785,6 +793,14 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
}
|
||||
|
||||
private void applyBlurToBackground(BackgroundScreenBeatmap backgroundModeBeatmap)
|
||||
{
|
||||
backgroundModeBeatmap.BlurAmount.Value = configBackgroundBlur.Value ? BACKGROUND_BLUR : 0f;
|
||||
backgroundModeBeatmap.DimWhenUserSettingsIgnored.Value = configBackgroundBlur.Value ? 0 : 0.4f;
|
||||
|
||||
wedgeBackground.FadeTo(configBackgroundBlur.Value ? 0.5f : 0.2f, UserDimContainer.BACKGROUND_FADE_DURATION, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private readonly WeakReference<ITrack?> lastTrack = new WeakReference<ITrack?>(null);
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,14 +1,11 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
@ -22,7 +19,7 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(1, 0.5f),
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
Colour = Color4.Black,
|
||||
Shear = new Vector2(0.15f, 0),
|
||||
EdgeSmoothness = new Vector2(2, 0),
|
||||
},
|
||||
@ -32,7 +29,7 @@ namespace osu.Game.Screens.Select
|
||||
RelativePositionAxes = Axes.Y,
|
||||
Size = new Vector2(1, -0.5f),
|
||||
Position = new Vector2(0, 1),
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
Colour = Color4.Black,
|
||||
Shear = new Vector2(-0.15f, 0),
|
||||
EdgeSmoothness = new Vector2(2, 0),
|
||||
},
|
||||
|
@ -94,7 +94,7 @@ namespace osu.Game.Skinning
|
||||
switch (globalLookup.Lookup)
|
||||
{
|
||||
case GlobalSkinComponentLookup.LookupType.SongSelect:
|
||||
var songSelectComponents = new SkinnableTargetComponentsContainer(_ =>
|
||||
var songSelectComponents = new DefaultSkinComponentsContainer(_ =>
|
||||
{
|
||||
// do stuff when we need to.
|
||||
});
|
||||
@ -102,7 +102,7 @@ namespace osu.Game.Skinning
|
||||
return songSelectComponents;
|
||||
|
||||
case GlobalSkinComponentLookup.LookupType.MainHUDComponents:
|
||||
var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container =>
|
||||
var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container =>
|
||||
{
|
||||
var score = container.OfType<DefaultScoreCounter>().FirstOrDefault();
|
||||
var accuracy = container.OfType<DefaultAccuracyCounter>().FirstOrDefault();
|
||||
|
@ -2,39 +2,28 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
/// <summary>
|
||||
/// A container which groups the components of a <see cref="SkinnableTargetContainer"/> into a single object.
|
||||
/// Optionally also applies a default layout to the components.
|
||||
/// A container which can be used to specify default skin components layouts.
|
||||
/// Handles applying a default layout to the components.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public partial class SkinnableTargetComponentsContainer : Container, ISkinnableDrawable
|
||||
public partial class DefaultSkinComponentsContainer : Container
|
||||
{
|
||||
public bool IsEditable => false;
|
||||
|
||||
public bool UsesFixedAnchor { get; set; }
|
||||
|
||||
private readonly Action<Container>? applyDefaults;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a wrapper with defaults that should be applied once.
|
||||
/// </summary>
|
||||
/// <param name="applyDefaults">A function to apply the default layout.</param>
|
||||
public SkinnableTargetComponentsContainer(Action<Container> applyDefaults)
|
||||
: this()
|
||||
{
|
||||
this.applyDefaults = applyDefaults;
|
||||
}
|
||||
|
||||
[JsonConstructor]
|
||||
public SkinnableTargetComponentsContainer()
|
||||
public DefaultSkinComponentsContainer(Action<Container> applyDefaults)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
this.applyDefaults = applyDefaults;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
@ -347,7 +347,7 @@ namespace osu.Game.Skinning
|
||||
switch (target.Lookup)
|
||||
{
|
||||
case GlobalSkinComponentLookup.LookupType.MainHUDComponents:
|
||||
var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container =>
|
||||
var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container =>
|
||||
{
|
||||
var score = container.OfType<LegacyScoreCounter>().FirstOrDefault();
|
||||
var accuracy = container.OfType<GameplayAccuracyCounter>().FirstOrDefault();
|
||||
|
@ -11,6 +11,7 @@ using Newtonsoft.Json;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Logging;
|
||||
@ -174,8 +175,9 @@ namespace osu.Game.Skinning
|
||||
foreach (var i in skinnableInfo)
|
||||
components.Add(i.CreateInstance());
|
||||
|
||||
return new SkinnableTargetComponentsContainer
|
||||
return new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = components,
|
||||
};
|
||||
}
|
||||
|
@ -7,13 +7,14 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public partial class SkinnableTargetContainer : SkinReloadableDrawable, ISkinnableTarget
|
||||
{
|
||||
private SkinnableTargetComponentsContainer? content;
|
||||
private Container? content;
|
||||
|
||||
public GlobalSkinComponentLookup.LookupType Target { get; }
|
||||
|
||||
@ -39,15 +40,16 @@ namespace osu.Game.Skinning
|
||||
foreach (var i in skinnableInfo)
|
||||
drawables.Add(i.CreateInstance());
|
||||
|
||||
Reload(new SkinnableTargetComponentsContainer
|
||||
Reload(new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = drawables,
|
||||
});
|
||||
}
|
||||
|
||||
public void Reload() => Reload(CurrentSkin.GetDrawableComponent(new GlobalSkinComponentLookup(Target)) as SkinnableTargetComponentsContainer);
|
||||
public void Reload() => Reload(CurrentSkin.GetDrawableComponent(new GlobalSkinComponentLookup(Target)) as Container);
|
||||
|
||||
public void Reload(SkinnableTargetComponentsContainer? componentsContainer)
|
||||
public void Reload(Container? componentsContainer)
|
||||
{
|
||||
ClearInternal();
|
||||
components.Clear();
|
||||
|
@ -72,7 +72,7 @@ namespace osu.Game.Skinning
|
||||
switch (target.Lookup)
|
||||
{
|
||||
case GlobalSkinComponentLookup.LookupType.SongSelect:
|
||||
var songSelectComponents = new SkinnableTargetComponentsContainer(_ =>
|
||||
var songSelectComponents = new DefaultSkinComponentsContainer(_ =>
|
||||
{
|
||||
// do stuff when we need to.
|
||||
});
|
||||
@ -80,7 +80,7 @@ namespace osu.Game.Skinning
|
||||
return songSelectComponents;
|
||||
|
||||
case GlobalSkinComponentLookup.LookupType.MainHUDComponents:
|
||||
var skinnableTargetWrapper = new SkinnableTargetComponentsContainer(container =>
|
||||
var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container =>
|
||||
{
|
||||
var score = container.OfType<DefaultScoreCounter>().FirstOrDefault();
|
||||
var accuracy = container.OfType<DefaultAccuracyCounter>().FirstOrDefault();
|
||||
|
@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual
|
||||
protected override bool CheckModsAllowFailure() => allowFail;
|
||||
|
||||
public ModTestPlayer(ModTestData data, bool allowFail)
|
||||
: base(false, false)
|
||||
: base(true, false)
|
||||
{
|
||||
this.allowFail = allowFail;
|
||||
currentTestData = data;
|
||||
|
Loading…
Reference in New Issue
Block a user