1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 04:07:25 +08:00

Merge branch 'master' into fix-autoplay-mod-revert

This commit is contained in:
Bartłomiej Dach 2023-07-29 16:12:11 +02:00
commit 579b0bc570
No known key found for this signature in database
8 changed files with 208 additions and 14 deletions

View File

@ -0,0 +1,147 @@
// 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.Extensions.TypeExtensions;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Replays;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
public partial class TestSceneSpinnerJudgement : RateAdjustedBeatmapTestScene
{
private const double time_spinner_start = 2000;
private const double time_spinner_end = 4000;
private List<JudgementResult> judgementResults = new List<JudgementResult>();
private ScoreAccessibleReplayPlayer currentPlayer = null!;
[Test]
public void TestHitNothing()
{
performTest(new List<ReplayFrame>());
AddAssert("all min judgements", () => judgementResults.All(result => result.Type == result.Judgement.MinResult));
}
[TestCase(1)]
[TestCase(2)]
[TestCase(5)]
public void TestNumberOfSpins(int spins)
{
performTest(generateReplay(spins));
for (int i = 0; i < spins; ++i)
assertResult<SpinnerTick>(i, HitResult.SmallBonus);
assertResult<SpinnerTick>(spins, HitResult.IgnoreMiss);
}
[Test]
public void TestHitEverything()
{
performTest(generateReplay(20));
AddAssert("all max judgements", () => judgementResults.All(result => result.Type == result.Judgement.MaxResult));
}
private static List<ReplayFrame> generateReplay(int spins)
{
var replayFrames = new List<ReplayFrame>();
const int frames_per_spin = 30;
for (int i = 0; i < spins * frames_per_spin; ++i)
{
float totalProgress = i / (float)(spins * frames_per_spin);
float spinProgress = (i % frames_per_spin) / (float)frames_per_spin;
double time = time_spinner_start + (time_spinner_end - time_spinner_start) * totalProgress;
float posX = MathF.Cos(2 * MathF.PI * spinProgress);
float posY = MathF.Sin(2 * MathF.PI * spinProgress);
Vector2 finalPos = OsuPlayfield.BASE_SIZE / 2 + new Vector2(posX, posY) * 50;
replayFrames.Add(new OsuReplayFrame(time, finalPos, OsuAction.LeftButton));
}
return replayFrames;
}
private void performTest(List<ReplayFrame> frames)
{
AddStep("load player", () =>
{
Beatmap.Value = CreateWorkingBeatmap(new Beatmap<OsuHitObject>
{
HitObjects =
{
new Spinner
{
StartTime = time_spinner_start,
EndTime = time_spinner_end,
Position = OsuPlayfield.BASE_SIZE / 2
}
},
BeatmapInfo =
{
Difficulty = new BeatmapDifficulty(),
Ruleset = new OsuRuleset().RulesetInfo
},
});
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
p.OnLoadComplete += _ =>
{
p.ScoreProcessor.NewJudgement += result =>
{
if (currentPlayer == p) judgementResults.Add(result);
};
};
LoadScreen(currentPlayer = p);
judgementResults = new List<JudgementResult>();
});
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
}
private void assertResult<T>(int index, HitResult expectedResult)
{
AddAssert($"{typeof(T).ReadableName()} ({index}) judged as {expectedResult}",
() => judgementResults.Where(j => j.HitObject is T).OrderBy(j => j.HitObject.StartTime).ElementAt(index).Type,
() => Is.EqualTo(expectedResult));
}
private partial class ScoreAccessibleReplayPlayer : ReplayPlayer
{
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
protected override bool PauseOnFocusLost => false;
public ScoreAccessibleReplayPlayer(Score score)
: base(score, new PlayerConfiguration
{
AllowPause = false,
ShowResults = false,
})
{
}
}
}
}

View File

@ -72,9 +72,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
lastAngle = thisAngle;
IsSpinning.Value = isSpinnableTime && Math.Abs(currentRotation / 2 - Rotation) > 5f;
IsSpinning.Value = isSpinnableTime && Math.Abs(currentRotation - Rotation) > 10f;
Rotation = (float)Interpolation.Damp(Rotation, currentRotation / 2, 0.99, Math.Abs(Time.Elapsed));
Rotation = (float)Interpolation.Damp(Rotation, currentRotation, 0.99, Math.Abs(Time.Elapsed));
}
/// <summary>

View File

@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Taiko.Edit
{
public partial class TaikoHitObjectComposer : HitObjectComposer<TaikoHitObject>
{
protected override bool ApplyHorizontalCentering => false;
public TaikoHitObjectComposer(TaikoRuleset ruleset)
: base(ruleset)
{

View File

@ -136,11 +136,11 @@ namespace osu.Game.Tournament.Components
if (match.NewValue != null)
match.NewValue.PicksBans.CollectionChanged += picksBansOnCollectionChanged;
updateState();
Scheduler.AddOnce(updateState);
}
private void picksBansOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
=> updateState();
=> Scheduler.AddOnce(updateState);
private BeatmapChoice? choice;

View File

@ -37,6 +37,8 @@ namespace osu.Game.Tournament.Screens.Editors
private WarningBox rightClickMessage;
private RectangularPositionSnapGrid grid;
[Resolved(canBeNull: true)]
[CanBeNull]
private IDialogOverlay dialogOverlay { get; set; }
@ -53,10 +55,12 @@ namespace osu.Game.Tournament.Screens.Editors
AddInternal(rightClickMessage = new WarningBox("Right click to place and link matches"));
ScrollContent.Add(new RectangularPositionSnapGrid(Vector2.Zero)
ScrollContent.Add(grid = new RectangularPositionSnapGrid(Vector2.Zero)
{
Spacing = new Vector2(GRID_SPACING),
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BypassAutoSizeAxes = Axes.Both,
Depth = float.MaxValue
});
@ -64,6 +68,22 @@ namespace osu.Game.Tournament.Screens.Editors
updateMessage();
}
protected override void Update()
{
base.Update();
// Expand grid with the content to allow going beyond the bounds of the screen.
grid.Size = ScrollContent.Size + new Vector2(GRID_SPACING * 2);
}
private Vector2 lastMatchesContainerMouseDownPosition;
protected override bool OnMouseDown(MouseDownEvent e)
{
lastMatchesContainerMouseDownPosition = MatchesContainer.ToLocalSpace(e.ScreenSpaceMouseDownPosition);
return base.OnMouseDown(e);
}
private void updateMessage()
{
rightClickMessage.Alpha = LadderInfo.Matches.Count > 0 ? 0 : 1;
@ -85,7 +105,8 @@ namespace osu.Game.Tournament.Screens.Editors
{
new OsuMenuItem("Create new match", MenuItemType.Highlighted, () =>
{
Vector2 pos = MatchesContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position);
Vector2 pos = MatchesContainer.Count == 0 ? Vector2.Zero : lastMatchesContainerMouseDownPosition;
TournamentMatch newMatch = new TournamentMatch { Position = { Value = new Point((int)pos.X, (int)pos.Y) } };
LadderInfo.Matches.Add(newMatch);

View File

@ -57,12 +57,15 @@ namespace osu.Game.Tournament.Screens.Ladder
},
ScrollContent = new LadderDragContainer
{
RelativeSizeAxes = Axes.Both,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
paths = new Container<Path> { RelativeSizeAxes = Axes.Both },
headings = new Container { RelativeSizeAxes = Axes.Both },
MatchesContainer = new Container<DrawableTournamentMatch> { RelativeSizeAxes = Axes.Both },
MatchesContainer = new Container<DrawableTournamentMatch>
{
AutoSizeAxes = Axes.Both
},
}
},
}

View File

@ -44,6 +44,11 @@ namespace osu.Game.Rulesets.Edit
public abstract partial class HitObjectComposer<TObject> : HitObjectComposer, IPlacementHandler
where TObject : HitObject
{
/// <summary>
/// Whether the playfield should be centered horizontally. Should be disabled for playfields which span the full horizontal width.
/// </summary>
protected virtual bool ApplyHorizontalCentering => true;
protected IRulesetConfigManager Config { get; private set; }
// Provides `Playfield`
@ -119,8 +124,6 @@ namespace osu.Game.Rulesets.Edit
{
Name = "Playfield content",
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
// layers below playfield
@ -241,8 +244,23 @@ namespace osu.Game.Rulesets.Edit
{
base.Update();
// Ensure that the playfield is always centered but also doesn't get cut off by toolboxes.
PlayfieldContentContainer.Width = Math.Max(1024, DrawWidth) - TOOLBOX_CONTRACTED_SIZE_RIGHT * 2;
if (ApplyHorizontalCentering)
{
PlayfieldContentContainer.Anchor = Anchor.Centre;
PlayfieldContentContainer.Origin = Anchor.Centre;
// Ensure that the playfield is always centered but also doesn't get cut off by toolboxes.
PlayfieldContentContainer.Width = Math.Max(1024, DrawWidth) - TOOLBOX_CONTRACTED_SIZE_RIGHT * 2;
PlayfieldContentContainer.X = 0;
}
else
{
PlayfieldContentContainer.Anchor = Anchor.CentreLeft;
PlayfieldContentContainer.Origin = Anchor.CentreLeft;
PlayfieldContentContainer.Width = Math.Max(1024, DrawWidth) - (TOOLBOX_CONTRACTED_SIZE_LEFT + TOOLBOX_CONTRACTED_SIZE_RIGHT);
PlayfieldContentContainer.X = TOOLBOX_CONTRACTED_SIZE_LEFT;
}
}
public override Playfield Playfield => drawableRulesetWrapper.Playfield;

View File

@ -54,7 +54,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (!gridCache.IsValid)
{
ClearInternal();
createContent();
if (DrawWidth > 0 && DrawHeight > 0)
createContent();
gridCache.Validate();
}
}