1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-15 09:22:54 +08:00

Fix merge conflicts.

This commit is contained in:
Lucas A 2020-03-26 15:48:18 +01:00
commit 00c1ff993a
252 changed files with 6037 additions and 2696 deletions

View File

@ -2,6 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.osu.Desktop/.idea/.idea.osu.Desktop.iml" filepath="$PROJECT_DIR$/.idea/.idea.osu.Desktop/.idea/.idea.osu.Desktop.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.osu.Desktop/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.osu.Desktop/riderModule.iml" />
</modules>
</component>

View File

@ -93,7 +93,7 @@ JetBrains ReSharper InspectCode is also used for wider rule sets. You can run it
We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention of having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time to ensure no effort is wasted.
If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues) (especially those with the ["good first issue"](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) label).
If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues) (especially those with the ["good first issue"](https://github.com/ppy/osu/issues?q=is%3Aopen+label%3Agood-first-issue+sort%3Aupdated-desc) label).
Before starting, please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**.

View File

@ -97,8 +97,10 @@ platform :ios do
changelog.gsub!('$BUILD_ID', options[:build])
pilot(
wait_processing_interval: 1800,
wait_processing_interval: 900,
changelog: changelog,
groups: ['osu! supporters', 'public'],
distribute_external: true,
ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa'
)
end

View File

@ -52,6 +52,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.312.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.319.0" />
</ItemGroup>
</Project>

View File

@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch
new KeyBinding(InputKey.Shift, CatchAction.Dash),
};
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new CatchModNightcore();

View File

@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Catch.Replays
}
}
public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
Position = currentFrame.Position.X / CatchPlayfield.BASE_WIDTH;
Dashing = currentFrame.ButtonState == ReplayButtonState.Left1;
@ -56,5 +56,14 @@ namespace osu.Game.Rulesets.Catch.Replays
Actions.Add(CatchAction.MoveLeft);
}
}
public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
{
ReplayButtonState state = ReplayButtonState.None;
if (Actions.Contains(CatchAction.Dash)) state |= ReplayButtonState.Left1;
return new LegacyReplayFrame(Time, Position * CatchPlayfield.BASE_WIDTH, null, state);
}
}
}

View File

@ -0,0 +1,26 @@
// 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.Game.Replays;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.UI;
using osuTK;
namespace osu.Game.Rulesets.Catch.UI
{
public class CatchReplayRecorder : ReplayRecorder<CatchAction>
{
private readonly CatchPlayfield playfield;
public CatchReplayRecorder(Replay target, CatchPlayfield playfield)
: base(target)
{
this.playfield = playfield;
}
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<CatchAction> actions, ReplayFrame previousFrame)
=> new CatchReplayFrame(Time.Current, playfield.CatcherArea.MovableCatcher.X, actions.Contains(CatchAction.Dash), previousFrame as CatchReplayFrame);
}
}

View File

@ -7,6 +7,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
@ -55,14 +56,14 @@ namespace osu.Game.Rulesets.Catch.UI
}
/// <summary>
/// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
/// Activate or deactivate the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met.
/// </summary>
protected bool Trail
{
get => trail;
set
{
if (value == trail) return;
if (value == trail || AdditiveTarget == null) return;
trail = value;
@ -77,6 +78,8 @@ namespace osu.Game.Rulesets.Catch.UI
private CatcherSprite catcherKiai;
private CatcherSprite catcherFail;
private CatcherSprite currentCatcher;
private int currentDirection;
private bool dashing;
@ -236,10 +239,10 @@ namespace osu.Game.Rulesets.Catch.UI
this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint);
Trail = true;
var hyperDashEndGlow = createAdditiveSprite(true);
var hyperDashEndGlow = createAdditiveSprite();
hyperDashEndGlow.MoveToOffset(new Vector2(0, -20), 1200, Easing.In);
hyperDashEndGlow.ScaleTo(hyperDashEndGlow.Scale * 0.9f).ScaleTo(hyperDashEndGlow.Scale * 1.2f, 1200, Easing.In);
hyperDashEndGlow.MoveToOffset(new Vector2(0, -10), 1200, Easing.In);
hyperDashEndGlow.ScaleTo(hyperDashEndGlow.Scale * 0.95f).ScaleTo(hyperDashEndGlow.Scale * 1.2f, 1200, Easing.In);
hyperDashEndGlow.FadeOut(1200);
hyperDashEndGlow.Expire(true);
}
@ -358,39 +361,36 @@ namespace osu.Game.Rulesets.Catch.UI
private void updateCatcher()
{
catcherIdle.Hide();
catcherKiai.Hide();
catcherFail.Hide();
CatcherSprite current;
currentCatcher?.Hide();
switch (CurrentState)
{
default:
current = catcherIdle;
currentCatcher = catcherIdle;
break;
case CatcherAnimationState.Fail:
current = catcherFail;
currentCatcher = catcherFail;
break;
case CatcherAnimationState.Kiai:
current = catcherKiai;
currentCatcher = catcherKiai;
break;
}
current.Show();
(current.Drawable as IAnimation)?.GotoFrame(0);
currentCatcher.Show();
(currentCatcher.Drawable as IAnimation)?.GotoFrame(0);
}
private void beginTrail()
{
Trail &= dashing || HyperDashing;
Trail &= AdditiveTarget != null;
if (!dashing && !HyperDashing)
{
Trail = false;
return;
}
if (!Trail) return;
var additive = createAdditiveSprite(HyperDashing);
var additive = createAdditiveSprite();
additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint);
additive.Expire(true);
@ -398,27 +398,6 @@ namespace osu.Game.Rulesets.Catch.UI
Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50);
}
private Drawable createAdditiveSprite(bool hyperDash)
{
var additive = createCatcherSprite();
additive.Anchor = Anchor;
additive.Scale = Scale;
additive.Colour = hyperDash ? Color4.Red : Color4.White;
additive.Blending = BlendingParameters.Additive;
additive.RelativePositionAxes = RelativePositionAxes;
additive.Position = Position;
AdditiveTarget.Add(additive);
return additive;
}
private Drawable createCatcherSprite()
{
return new CatcherSprite(CurrentState);
}
private void updateState(CatcherAnimationState state)
{
if (CurrentState == state)
@ -428,6 +407,25 @@ namespace osu.Game.Rulesets.Catch.UI
updateCatcher();
}
private CatcherTrailSprite createAdditiveSprite()
{
var tex = (currentCatcher.Drawable as TextureAnimation)?.CurrentFrame ?? ((Sprite)currentCatcher.Drawable).Texture;
var sprite = new CatcherTrailSprite(tex)
{
Anchor = Anchor,
Scale = Scale,
Colour = HyperDashing ? Color4.Red : Color4.White,
Blending = BlendingParameters.Additive,
RelativePositionAxes = RelativePositionAxes,
Position = Position
};
AdditiveTarget?.Add(sprite);
return sprite;
}
private void removeFromPlateWithTransform(DrawableHitObject fruit, Action<DrawableHitObject> action)
{
if (ExplodingFruitTarget != null)

View File

@ -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.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osuTK;
namespace osu.Game.Rulesets.Catch.UI
{
public class CatcherTrailSprite : Sprite
{
public CatcherTrailSprite(Texture texture)
{
Texture = texture;
Size = new Vector2(CatcherArea.CATCHER_SIZE);
// Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling.
OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE;
}
}
}

View File

@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Catch.UI
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new CatchReplayRecorder(replay, (CatchPlayfield)Playfield);
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, CreateDrawableRepresentation);
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new CatchPlayfieldAdjustmentContainer();

View File

@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{
}
protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
protected override KeyBindingContainer<ManiaAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new LocalKeyBindingContainer(ruleset, variant, unique);
private class LocalKeyBindingContainer : RulesetKeyBindingContainer

View File

@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania
public override ISkin CreateLegacySkinProvider(ISkinSource source) => new ManiaLegacySkinTransformer(source);
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new ManiaModNightcore();
@ -118,6 +118,59 @@ namespace osu.Game.Rulesets.Mania
yield return new ManiaModRandom();
}
public override LegacyMods ConvertToLegacyMods(Mod[] mods)
{
var value = base.ConvertToLegacyMods(mods);
foreach (var mod in mods)
{
switch (mod)
{
case ManiaModKey1 _:
value |= LegacyMods.Key1;
break;
case ManiaModKey2 _:
value |= LegacyMods.Key2;
break;
case ManiaModKey3 _:
value |= LegacyMods.Key3;
break;
case ManiaModKey4 _:
value |= LegacyMods.Key4;
break;
case ManiaModKey5 _:
value |= LegacyMods.Key5;
break;
case ManiaModKey6 _:
value |= LegacyMods.Key6;
break;
case ManiaModKey7 _:
value |= LegacyMods.Key7;
break;
case ManiaModKey8 _:
value |= LegacyMods.Key8;
break;
case ManiaModKey9 _:
value |= LegacyMods.Key9;
break;
case ManiaModFadeIn _:
value |= LegacyMods.FadeIn;
break;
}
}
return value;
}
public override IEnumerable<Mod> GetModsFor(ModType type)
{
switch (type)

View File

@ -3,24 +3,17 @@
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods
{
public class ManiaModRandom : Mod, IApplicableToBeatmap
public class ManiaModRandom : ModRandom, IApplicableToBeatmap
{
public override string Name => "Random";
public override string Acronym => "RD";
public override ModType Type => ModType.Conversion;
public override IconUsage? Icon => OsuIcon.Dice;
public override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 1;
public void ApplyToBeatmap(IBeatmap beatmap)
{

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Replays
Actions.AddRange(actions);
}
public void ConvertFrom(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 converter = new ManiaBeatmapConverter(beatmap, new ManiaRuleset());
@ -56,5 +56,42 @@ namespace osu.Game.Rulesets.Mania.Replays
activeColumns >>= 1;
}
}
public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
{
int keys = 0;
var converter = new ManiaBeatmapConverter(beatmap, new ManiaRuleset());
var stage = new StageDefinition { Columns = converter.TargetColumns };
var specialColumns = new List<int>();
for (int i = 0; i < converter.TargetColumns; i++)
{
if (stage.IsSpecialColumn(i))
specialColumns.Add(i);
}
foreach (var action in Actions)
{
switch (action)
{
case ManiaAction.Special1:
keys |= 1 << specialColumns[0];
break;
case ManiaAction.Special2:
keys |= 1 << specialColumns[1];
break;
default:
keys |= 1 << (action - ManiaAction.Key1);
break;
}
}
return new LegacyReplayFrame(Time, keys, null, ReplayButtonState.None);
}
}
}

View File

@ -88,5 +88,7 @@ namespace osu.Game.Rulesets.Mania.UI
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new ManiaReplayRecorder(replay);
}
}

View 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.
using System.Collections.Generic;
using osu.Game.Replays;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.UI;
using osuTK;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaReplayRecorder : ReplayRecorder<ManiaAction>
{
public ManiaReplayRecorder(Replay replay)
: base(replay)
{
}
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<ManiaAction> actions, ReplayFrame previousFrame)
=> new ManiaReplayFrame(Time.Current, actions.ToArray());
}
}

View File

@ -4,48 +4,43 @@
using System;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestSceneHitCircleArea : ManualInputManagerTestScene
public class TestSceneHitCircleArea : OsuManualInputManagerTestScene
{
private HitCircle hitCircle;
private DrawableHitCircle drawableHitCircle;
private DrawableHitCircle.HitReceptor hitAreaReceptor => drawableHitCircle.HitArea;
[SetUp]
public new void SetUp()
public void SetUp() => Schedule(() =>
{
base.SetUp();
Schedule(() =>
hitCircle = new HitCircle
{
hitCircle = new HitCircle
{
Position = new Vector2(100, 100),
StartTime = Time.Current + 500
};
Position = new Vector2(100, 100),
StartTime = Time.Current + 500
};
hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
Child = new SkinProvidingContainer(new DefaultSkin())
Child = new SkinProvidingContainer(new DefaultSkin())
{
RelativeSizeAxes = Axes.Both,
Child = drawableHitCircle = new DrawableHitCircle(hitCircle)
{
RelativeSizeAxes = Axes.Both,
Child = drawableHitCircle = new DrawableHitCircle(hitCircle)
{
Size = new Vector2(100)
}
};
});
}
Size = new Vector2(100)
}
};
});
[Test]
public void TestCircleHitCentre()

View File

@ -23,7 +23,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestSceneOsuDistanceSnapGrid : ManualInputManagerTestScene
public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene
{
private const double beat_length = 100;
private static readonly Vector2 grid_position = new Vector2(512, 384);

View File

@ -12,7 +12,7 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests
{
public class TestSceneResumeOverlay : ManualInputManagerTestScene
public class TestSceneResumeOverlay : OsuManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{

View File

@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Tests
typeof(DrawableSliderTick),
typeof(DrawableSliderTail),
typeof(DrawableSliderHead),
typeof(DrawableRepeatPoint),
typeof(DrawableSliderRepeat),
typeof(DrawableOsuHitObject)
};
@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("head samples updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle));
AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<SliderTick>().All(assertTickSamples));
AddAssert("repeat samples updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<RepeatPoint>().All(assertSamples));
AddAssert("repeat samples updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<SliderRepeat>().All(assertSamples));
AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("head samples not updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle));
AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<SliderTick>().All(assertTickSamples));
AddAssert("repeat samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<RepeatPoint>().All(assertSamples));
AddAssert("repeat samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<SliderRepeat>().All(assertSamples));
AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";

View File

@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests
typeof(SliderBall),
typeof(DrawableSlider),
typeof(DrawableSliderTick),
typeof(DrawableRepeatPoint),
typeof(DrawableSliderRepeat),
typeof(DrawableOsuHitObject),
typeof(DrawableSliderHead),
typeof(DrawableSliderTail),
@ -327,7 +327,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("Tracking dropped", assertMidSliderJudgementFail);
}
private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great;
private bool assertGreatJudge() => judgementResults.Any() && judgementResults.All(t => t.Type == HitResult.Great);
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;

View File

@ -0,0 +1,16 @@
// 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.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Judgements
{
public class OsuIgnoreJudgement : OsuJudgement
{
public override bool AffectsCombo => false;
protected override int NumericResultFor(HitResult result) => 0;
protected override double HealthIncreaseFor(HitResult result) => 0;
}
}

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
return;
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
slider.NestedHitObjects.OfType<RepeatPoint>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
foreach (var point in slider.Path.ControlPoints)
point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);

View File

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Mods
case DrawableSliderHead _:
case DrawableSliderTail _:
case DrawableSliderTick _:
case DrawableRepeatPoint _:
case DrawableSliderRepeat _:
return;
default:

View File

@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Mods
// Wiggle the repeat points with the slider instead of independently.
// Also fixes an issue with repeat points being positioned incorrectly.
if (osuObject is RepeatPoint)
if (osuObject is SliderRepeat)
return;
Random objRand = new Random((int)osuObject.StartTime);

View File

@ -88,8 +88,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
private void refresh()
{
ClearInternal();
OsuHitObject osuStart = Start.HitObject;
double startTime = osuStart.GetEndTime();
@ -116,6 +114,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
double? firstTransformStartTime = null;
double finalTransformEndTime = startTime;
int point = 0;
for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing)
{
float fraction = (float)d / distance;
@ -126,13 +126,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
FollowPoint fp;
AddInternal(fp = new FollowPoint
if (InternalChildren.Count > point)
{
Position = pointStartPosition,
Rotation = rotation,
Alpha = 0,
Scale = new Vector2(1.5f * osuEnd.Scale),
});
fp = (FollowPoint)InternalChildren[point];
fp.ClearTransforms();
}
else
AddInternal(fp = new FollowPoint());
fp.Position = pointStartPosition;
fp.Rotation = rotation;
fp.Alpha = 0;
fp.Scale = new Vector2(1.5f * osuEnd.Scale);
if (firstTransformStartTime == null)
firstTransformStartTime = fadeInTime;
@ -146,8 +151,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
finalTransformEndTime = fadeOutTime + osuEnd.TimeFadeIn;
}
point++;
}
int excessPoints = InternalChildren.Count - point;
for (int i = 0; i < excessPoints; i++)
RemoveInternal(InternalChildren[^1]);
// todo: use Expire() on FollowPoints and take lifetime from them when https://github.com/ppy/osu-framework/issues/3300 is fixed.
LifetimeStart = firstTransformStartTime ?? startTime;
LifetimeEnd = finalTransformEndTime;

View File

@ -6,13 +6,11 @@ using osuTK;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
using osu.Game.Skinning;
@ -26,12 +24,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public readonly SliderBall Ball;
public readonly SkinnableDrawable Body;
public override bool DisplayResult => false;
private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody;
private readonly Container<DrawableSliderHead> headContainer;
private readonly Container<DrawableSliderTail> tailContainer;
private readonly Container<DrawableSliderTick> tickContainer;
private readonly Container<DrawableRepeatPoint> repeatContainer;
private readonly Container<DrawableSliderRepeat> repeatContainer;
private readonly Slider slider;
@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling),
tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
repeatContainer = new Container<DrawableRepeatPoint> { RelativeSizeAxes = Axes.Both },
repeatContainer = new Container<DrawableSliderRepeat> { RelativeSizeAxes = Axes.Both },
Ball = new SliderBall(s, this)
{
GetInitialHitAction = () => HeadCircle.HitAction,
@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
tickContainer.Add(tick);
break;
case DrawableRepeatPoint repeat:
case DrawableSliderRepeat repeat:
repeatContainer.Add(repeat);
break;
}
@ -129,8 +129,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
case SliderTick tick:
return new DrawableSliderTick(tick) { Position = tick.Position - slider.Position };
case RepeatPoint repeat:
return new DrawableRepeatPoint(repeat, this) { Position = repeat.Position - slider.Position };
case SliderRepeat repeat:
return new DrawableSliderRepeat(repeat, this) { Position = repeat.Position - slider.Position };
}
return base.CreateNestedHitObject(hitObject);
@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.ApplySkin(skin, allowFallback);
bool allowBallTint = skin.GetConfig<OsuSkinConfiguration, bool>(OsuSkinConfiguration.AllowSliderBallTint)?.Value ?? false;
Ball.AccentColour = allowBallTint ? AccentColour.Value : Color4.White;
Ball.Colour = allowBallTint ? AccentColour.Value : Color4.White;
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
@ -193,22 +193,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (userTriggered || Time.Current < slider.EndTime)
return;
ApplyResult(r =>
{
var judgementsCount = NestedHitObjects.Count;
var judgementsHit = NestedHitObjects.Count(h => h.IsHit);
var hitFraction = (double)judgementsHit / judgementsCount;
if (hitFraction == 1 && HeadCircle.Result.Type == HitResult.Great)
r.Type = HitResult.Great;
else if (hitFraction >= 0.5 && HeadCircle.Result.Type >= HitResult.Good)
r.Type = HitResult.Good;
else if (hitFraction > 0)
r.Type = HitResult.Meh;
else
r.Type = HitResult.Miss;
});
ApplyResult(r => r.Type = r.Judgement.MaxResult);
}
protected override void UpdateStateTransforms(ArmedState state)

View File

@ -14,19 +14,19 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableRepeatPoint : DrawableOsuHitObject, ITrackSnaking
public class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking
{
private readonly RepeatPoint repeatPoint;
private readonly SliderRepeat sliderRepeat;
private readonly DrawableSlider drawableSlider;
private double animDuration;
private readonly Drawable scaleContainer;
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider)
: base(repeatPoint)
public DrawableSliderRepeat(SliderRepeat sliderRepeat, DrawableSlider drawableSlider)
: base(sliderRepeat)
{
this.repeatPoint = repeatPoint;
this.sliderRepeat = sliderRepeat;
this.drawableSlider = drawableSlider;
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
@ -48,13 +48,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (repeatPoint.StartTime <= Time.Current)
if (sliderRepeat.StartTime <= Time.Current)
ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? HitResult.Great : HitResult.Miss);
}
protected override void UpdateInitialTransforms()
{
animDuration = Math.Min(300, repeatPoint.SpanDuration);
animDuration = Math.Min(300, sliderRepeat.SpanDuration);
this.Animate(
d => d.FadeIn(animDuration),
@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
{
bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0;
bool isRepeatAtEnd = sliderRepeat.RepeatIndex % 2 == 0;
List<Vector2> curve = ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
Position = isRepeatAtEnd ? end : start;

View File

@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered && timeOffset >= 0)
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : HitResult.Miss);
}
private void updatePosition() => Position = HitObject.Position - slider.Position;

View File

@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : HitResult.Miss);
}
protected override void UpdateInitialTransforms()

View File

@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
.FadeTo(tracking_alpha, 250, Easing.OutQuint);
}
this.RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, Easing.OutExpo);
Rotation = (float)Interpolation.Lerp(Rotation, currentRotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1));
}
}
}

View File

@ -177,7 +177,7 @@ namespace osu.Game.Rulesets.Osu.Objects
break;
case SliderEventType.Repeat:
AddNested(new RepeatPoint
AddNested(new SliderRepeat
{
RepeatIndex = e.SpanIndex,
SpanDuration = SpanDuration,
@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Osu.Objects
foreach (var tick in NestedHitObjects.OfType<SliderTick>())
tick.Samples = sampleList;
foreach (var repeat in NestedHitObjects.OfType<RepeatPoint>())
foreach (var repeat in NestedHitObjects.OfType<SliderRepeat>())
repeat.Samples = getNodeSamples(repeat.RepeatIndex + 1);
if (HeadCircle != null)
@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Osu.Objects
private IList<HitSampleInfo> getNodeSamples(int nodeIndex) =>
nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples;
public override Judgement CreateJudgement() => new OsuJudgement();
public override Judgement CreateJudgement() => new OsuIgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}

View File

@ -10,7 +10,7 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects
{
public class RepeatPoint : OsuHitObject
public class SliderRepeat : OsuHitObject
{
public int RepeatIndex { get; set; }
public double SpanDuration { get; set; }
@ -28,8 +28,15 @@ namespace osu.Game.Rulesets.Osu.Objects
TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
}
public override Judgement CreateJudgement() => new OsuJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
public override Judgement CreateJudgement() => new SliderRepeatJudgement();
public class SliderRepeatJudgement : OsuJudgement
{
public override bool IsBonus => true;
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 30 : 0;
}
}
}

View File

@ -22,8 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects
pathVersion.BindValueChanged(_ => Position = slider.EndPosition);
}
public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
public override Judgement CreateJudgement() => new SliderRepeat.SliderRepeatJudgement();
}
}

View File

@ -30,8 +30,15 @@ namespace osu.Game.Rulesets.Osu.Objects
TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
}
public override Judgement CreateJudgement() => new OsuJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
public override Judgement CreateJudgement() => new SliderTickJudgement();
public class SliderTickJudgement : OsuJudgement
{
public override bool IsBonus => true;
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 10 : 0;
}
}
}

View File

@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu
/// </summary>
public bool AllowUserCursorMovement { get; set; } = true;
protected override RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
protected override KeyBindingContainer<OsuAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new OsuKeyBindingContainer(ruleset, variant, unique);
public OsuInputManager(RulesetInfo ruleset)

View File

@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
};
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new OsuModNightcore();

View File

@ -26,11 +26,23 @@ namespace osu.Game.Rulesets.Osu.Replays
Actions.AddRange(actions);
}
public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
Position = currentFrame.Position;
if (currentFrame.MouseLeft) Actions.Add(OsuAction.LeftButton);
if (currentFrame.MouseRight) Actions.Add(OsuAction.RightButton);
}
public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
{
ReplayButtonState state = ReplayButtonState.None;
if (Actions.Contains(OsuAction.LeftButton))
state |= ReplayButtonState.Left1;
if (Actions.Contains(OsuAction.RightButton))
state |= ReplayButtonState.Right1;
return new LegacyReplayFrame(Time, Position.X, Position.Y, state);
}
}
}

View File

@ -66,6 +66,8 @@ namespace osu.Game.Rulesets.Osu.UI
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new OsuFramedReplayInputHandler(replay);
protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new OsuReplayRecorder(replay);
public override double GameplayStartTime
{
get

View 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.
using System.Collections.Generic;
using osu.Game.Replays;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.UI;
using osuTK;
namespace osu.Game.Rulesets.Osu.UI
{
public class OsuReplayRecorder : ReplayRecorder<OsuAction>
{
public OsuReplayRecorder(Replay replay)
: base(replay)
{
}
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<OsuAction> actions, ReplayFrame previousFrame)
=> new OsuReplayFrame(Time.Current, mousePosition, actions.ToArray());
}
}

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods
[TestCase(false)]
[TestCase(true)]
public void TestHit(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new CentreHit { StartTime = 1000 }), shouldMiss);
public void TestHit(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new Hit { StartTime = 1000, Type = HitType.Centre }), shouldMiss);
[TestCase(false)]
[TestCase(true)]

View File

@ -27,8 +27,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
StartTime = hitObject.StartTime,
EndTime = hitObject.GetEndTime(),
IsRim = hitObject is RimHit,
IsCentre = hitObject is CentreHit,
IsRim = (hitObject as Hit)?.Type == HitType.Rim,
IsCentre = (hitObject as Hit)?.Type == HitType.Centre,
IsDrumRoll = hitObject is DrumRoll,
IsSwell = hitObject is Swell,
IsStrong = ((TaikoHitObject)hitObject).IsStrong

View File

@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
{
HitObjects = new List<HitObject> { new CentreHit() },
HitObjects = new List<HitObject> { new Hit { Type = HitType.Centre } },
BeatmapInfo = new BeatmapInfo
{
BaseDifficulty = new BeatmapDifficulty(),

View File

@ -124,24 +124,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
if (isRim)
yield return new Hit
{
yield return new RimHit
{
StartTime = j,
Samples = currentSamples,
IsStrong = strong
};
}
else
{
yield return new CentreHit
{
StartTime = j,
Samples = currentSamples,
IsStrong = strong
};
}
StartTime = j,
Type = isRim ? HitType.Rim : HitType.Centre,
Samples = currentSamples,
IsStrong = strong
};
i = (i + 1) % allSamples.Count;
}
@ -180,24 +169,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
{
bool isRim = samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
if (isRim)
yield return new Hit
{
yield return new RimHit
{
StartTime = obj.StartTime,
Samples = obj.Samples,
IsStrong = strong
};
}
else
{
yield return new CentreHit
{
StartTime = obj.StartTime,
Samples = obj.Samples,
IsStrong = strong
};
}
StartTime = obj.StartTime,
Type = isRim ? HitType.Rim : HitType.Centre,
Samples = obj.Samples,
IsStrong = strong
};
break;
}

View File

@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate)
: base(hitObject, lastObject, clockRate)
{
HasTypeChange = lastObject is RimHit != hitObject is RimHit;
HasTypeChange = (lastObject as Hit)?.Type != (hitObject as Hit)?.Type;
}
}
}

View File

@ -0,0 +1,27 @@
// 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.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.Mods
{
public class TaikoModRandom : ModRandom, IApplicableToBeatmap
{
public override string Description => @"Shuffle around the colours!";
public void ApplyToBeatmap(IBeatmap beatmap)
{
var taikoBeatmap = (TaikoBeatmap)beatmap;
foreach (var obj in taikoBeatmap.HitObjects)
{
if (obj is Hit hit)
hit.Type = RNG.Next(2) == 0 ? HitType.Centre : HitType.Rim;
}
}
}
}

View File

@ -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.Taiko.Objects
{
public class CentreHit : Hit
{
}
}

View File

@ -5,5 +5,9 @@ namespace osu.Game.Rulesets.Taiko.Objects
{
public class Hit : TaikoHitObject
{
/// <summary>
/// The <see cref="HitType"/> that actuates this <see cref="Hit"/>.
/// </summary>
public HitType Type { get; set; }
}
}

View File

@ -0,0 +1,21 @@
// 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.Taiko.Objects
{
/// <summary>
/// The type of a <see cref="Hit"/>.
/// </summary>
public enum HitType
{
/// <summary>
/// A <see cref="Hit"/> that can be hit by the centre portion of the drum.
/// </summary>
Centre,
/// <summary>
/// A <see cref="Hit"/> that can be hit by the rim portion of the drum.
/// </summary>
Rim
}
}

View File

@ -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.Taiko.Objects
{
public class RimHit : Hit
{
}
}

View File

@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
{
TaikoAction[] actions;
if (hit is CentreHit)
if (hit.Type == HitType.Centre)
{
actions = h.IsStrong
? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre }

View File

@ -23,12 +23,24 @@ namespace osu.Game.Rulesets.Taiko.Replays
Actions.AddRange(actions);
}
public void ConvertFrom(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
public void FromLegacy(LegacyReplayFrame currentFrame, IBeatmap beatmap, ReplayFrame lastFrame = null)
{
if (currentFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim);
if (currentFrame.MouseRight2) Actions.Add(TaikoAction.RightRim);
if (currentFrame.MouseLeft1) Actions.Add(TaikoAction.LeftCentre);
if (currentFrame.MouseLeft2) Actions.Add(TaikoAction.RightCentre);
}
public LegacyReplayFrame ToLegacy(IBeatmap beatmap)
{
ReplayButtonState state = ReplayButtonState.None;
if (Actions.Contains(TaikoAction.LeftRim)) state |= ReplayButtonState.Right1;
if (Actions.Contains(TaikoAction.RightRim)) state |= ReplayButtonState.Right2;
if (Actions.Contains(TaikoAction.LeftCentre)) state |= ReplayButtonState.Left1;
if (Actions.Contains(TaikoAction.RightCentre)) state |= ReplayButtonState.Left2;
return new LegacyReplayFrame(Time, null, null, state);
}
}
}

View File

@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Taiko
new KeyBinding(InputKey.K, TaikoAction.RightRim),
};
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
yield return new TaikoModNightcore();
@ -114,6 +114,7 @@ namespace osu.Game.Rulesets.Taiko
case ModType.Conversion:
return new Mod[]
{
new TaikoModRandom(),
new TaikoModDifficultyAdjust(),
};

View File

@ -48,11 +48,11 @@ namespace osu.Game.Rulesets.Taiko.UI
{
switch (h)
{
case CentreHit centreHit:
return new DrawableCentreHit(centreHit);
case RimHit rimHit:
return new DrawableRimHit(rimHit);
case Hit hit:
if (hit.Type == HitType.Centre)
return new DrawableCentreHit(hit);
else
return new DrawableRimHit(hit);
case DrumRoll drumRoll:
return new DrawableDrumRoll(drumRoll);
@ -65,5 +65,7 @@ namespace osu.Game.Rulesets.Taiko.UI
}
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new TaikoFramedReplayInputHandler(replay);
protected override ReplayRecorder CreateReplayRecorder(Replay replay) => new TaikoReplayRecorder(replay);
}
}

View File

@ -14,9 +14,9 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
using osuTK;
using osuTK.Graphics;
@ -245,7 +245,7 @@ namespace osu.Game.Rulesets.Taiko.UI
if (!result.IsHit)
break;
bool isRim = judgedObject.HitObject is RimHit;
bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));

View 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.
using System.Collections.Generic;
using osu.Game.Replays;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Taiko.Replays;
using osu.Game.Rulesets.UI;
using osuTK;
namespace osu.Game.Rulesets.Taiko.UI
{
public class TaikoReplayRecorder : ReplayRecorder<TaikoAction>
{
public TaikoReplayRecorder(Replay replay)
: base(replay)
{
}
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<TaikoAction> actions, ReplayFrame previousFrame) =>
new TaikoReplayFrame(Time.Current, actions.ToArray());
}
}

View File

@ -26,34 +26,34 @@ namespace osu.Game.Tests.Beatmaps.Formats
var storyboard = decoder.Decode(stream);
Assert.IsTrue(storyboard.HasDrawable);
Assert.AreEqual(4, storyboard.Layers.Count());
Assert.AreEqual(5, storyboard.Layers.Count());
StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3);
Assert.IsNotNull(background);
Assert.AreEqual(16, background.Elements.Count);
Assert.IsTrue(background.EnabledWhenFailing);
Assert.IsTrue(background.EnabledWhenPassing);
Assert.IsTrue(background.VisibleWhenFailing);
Assert.IsTrue(background.VisibleWhenPassing);
Assert.AreEqual("Background", background.Name);
StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2);
Assert.IsNotNull(fail);
Assert.AreEqual(0, fail.Elements.Count);
Assert.IsTrue(fail.EnabledWhenFailing);
Assert.IsFalse(fail.EnabledWhenPassing);
Assert.IsTrue(fail.VisibleWhenFailing);
Assert.IsFalse(fail.VisibleWhenPassing);
Assert.AreEqual("Fail", fail.Name);
StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1);
Assert.IsNotNull(pass);
Assert.AreEqual(0, pass.Elements.Count);
Assert.IsFalse(pass.EnabledWhenFailing);
Assert.IsTrue(pass.EnabledWhenPassing);
Assert.IsFalse(pass.VisibleWhenFailing);
Assert.IsTrue(pass.VisibleWhenPassing);
Assert.AreEqual("Pass", pass.Name);
StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0);
Assert.IsNotNull(foreground);
Assert.AreEqual(151, foreground.Elements.Count);
Assert.IsTrue(foreground.EnabledWhenFailing);
Assert.IsTrue(foreground.EnabledWhenPassing);
Assert.IsTrue(foreground.VisibleWhenFailing);
Assert.IsTrue(foreground.VisibleWhenPassing);
Assert.AreEqual("Foreground", foreground.Name);
int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite));

View File

@ -417,7 +417,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportWithDuplicateHashes()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportNestedStructure)))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithDuplicateHashes)))
{
try
{

View File

@ -0,0 +1,282 @@
// 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.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Input.StateChanges;
using osu.Framework.Testing;
using osu.Framework.Threading;
using osu.Game.Graphics.Sprites;
using osu.Game.Replays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.UI;
using osu.Game.Tests.Visual;
using osu.Game.Tests.Visual.UserInterface;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;
namespace osu.Game.Tests.Gameplay
{
public class TestSceneReplayRecorder : OsuManualInputManagerTestScene
{
private TestRulesetInputManager playbackManager;
private TestRulesetInputManager recordingManager;
private Replay replay;
private TestReplayRecorder recorder;
[SetUp]
public void SetUp() => Schedule(() =>
{
replay = new Replay();
Add(new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
Recorder = recorder = new TestReplayRecorder(replay)
{
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Brown,
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
{
Text = "Recording",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestInputConsumer()
}
},
}
},
new Drawable[]
{
playbackManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
ReplayInputHandler = new TestFramedReplayInputHandler(replay)
{
GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos),
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
Colour = Color4.DarkBlue,
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
{
Text = "Playback",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestInputConsumer()
}
},
}
}
}
});
});
[Test]
public void TestBasic()
{
AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
AddUntilStep("one frame recorded", () => replay.Frames.Count == 1);
AddAssert("position matches", () => playbackManager.ChildrenOfType<Box>().First().Position == recordingManager.ChildrenOfType<Box>().First().Position);
}
[Test]
public void TestHighFrameRate()
{
ScheduledDelegate moveFunction = null;
AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
AddStep("much move", () => moveFunction = Scheduler.AddDelayed(() =>
InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0)), 10, true));
AddWaitStep("move", 10);
AddStep("stop move", () => moveFunction.Cancel());
AddAssert("at least 60 frames recorded", () => replay.Frames.Count > 60);
}
[Test]
public void TestLimitedFrameRate()
{
ScheduledDelegate moveFunction = null;
AddStep("lower rate", () => recorder.RecordFrameRate = 2);
AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
AddStep("much move", () => moveFunction = Scheduler.AddDelayed(() =>
InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0)), 10, true));
AddWaitStep("move", 10);
AddStep("stop move", () => moveFunction.Cancel());
AddAssert("less than 10 frames recorded", () => replay.Frames.Count < 10);
}
[Test]
public void TestLimitedFrameRateWithImportantFrames()
{
ScheduledDelegate moveFunction = null;
AddStep("lower rate", () => recorder.RecordFrameRate = 2);
AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
AddStep("much move with press", () => moveFunction = Scheduler.AddDelayed(() =>
{
InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0));
InputManager.PressButton(MouseButton.Left);
InputManager.ReleaseButton(MouseButton.Left);
}, 10, true));
AddWaitStep("move", 10);
AddStep("stop move", () => moveFunction.Cancel());
AddAssert("at least 60 frames recorded", () => replay.Frames.Count > 60);
}
protected override void Update()
{
base.Update();
playbackManager?.ReplayInputHandler.SetFrameFromTime(Time.Current - 100);
}
public class TestFramedReplayInputHandler : FramedReplayInputHandler<TestReplayFrame>
{
public TestFramedReplayInputHandler(Replay replay)
: base(replay)
{
}
public override List<IInput> GetPendingInputs()
{
return new List<IInput>
{
new MousePositionAbsoluteInput
{
Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero)
},
new ReplayState<TestAction>
{
PressedActions = CurrentFrame?.Actions ?? new List<TestAction>()
}
};
}
}
public class TestInputConsumer : CompositeDrawable, IKeyBindingHandler<TestAction>
{
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent.ReceivePositionalInputAt(screenSpacePos);
private readonly Box box;
public TestInputConsumer()
{
Size = new Vector2(30);
Origin = Anchor.Centre;
InternalChildren = new Drawable[]
{
box = new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
},
};
}
protected override bool OnMouseMove(MouseMoveEvent e)
{
Position = e.MousePosition;
return base.OnMouseMove(e);
}
public bool OnPressed(TestAction action)
{
box.Colour = Color4.White;
return true;
}
public void OnReleased(TestAction action)
{
box.Colour = Color4.Black;
}
}
public class TestRulesetInputManager : RulesetInputManager<TestAction>
{
public TestRulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
: base(ruleset, variant, unique)
{
}
protected override KeyBindingContainer<TestAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new TestKeyBindingContainer();
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
{
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
{
new KeyBinding(InputKey.MouseLeft, TestAction.Down),
};
}
}
public class TestReplayFrame : ReplayFrame
{
public Vector2 Position;
public List<TestAction> Actions = new List<TestAction>();
public TestReplayFrame(double time, Vector2 position, params TestAction[] actions)
: base(time)
{
Position = position;
Actions.AddRange(actions);
}
}
public enum TestAction
{
Down,
}
internal class TestReplayRecorder : ReplayRecorder<TestAction>
{
public TestReplayRecorder(Replay target)
: base(target)
{
}
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<TestAction> actions, ReplayFrame previousFrame)
=> new TestReplayFrame(Time.Current, mousePosition, actions.ToArray());
}
}
}

View File

@ -0,0 +1,221 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Input.StateChanges;
using osu.Game.Graphics.Sprites;
using osu.Game.Replays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.UI;
using osu.Game.Tests.Visual;
using osu.Game.Tests.Visual.UserInterface;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Gameplay
{
public class TestSceneReplayRecording : OsuTestScene
{
private readonly TestRulesetInputManager playbackManager;
private readonly TestRulesetInputManager recordingManager;
public TestSceneReplayRecording()
{
Replay replay = new Replay();
Add(new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
Recorder = new TestReplayRecorder(replay)
{
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos)
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
Colour = Color4.Brown,
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
{
Text = "Recording",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestConsumer()
}
},
}
},
new Drawable[]
{
playbackManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
{
ReplayInputHandler = new TestFramedReplayInputHandler(replay)
{
GamefieldToScreenSpace = pos => playbackManager.ToScreenSpace(pos),
},
Child = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
Colour = Color4.DarkBlue,
RelativeSizeAxes = Axes.Both,
},
new OsuSpriteText
{
Text = "Playback",
Scale = new Vector2(3),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new TestConsumer()
}
},
}
}
}
});
}
protected override void Update()
{
base.Update();
playbackManager.ReplayInputHandler.SetFrameFromTime(Time.Current - 500);
}
}
public class TestFramedReplayInputHandler : FramedReplayInputHandler<TestReplayFrame>
{
public TestFramedReplayInputHandler(Replay replay)
: base(replay)
{
}
public override List<IInput> GetPendingInputs()
{
return new List<IInput>
{
new MousePositionAbsoluteInput
{
Position = GamefieldToScreenSpace(CurrentFrame?.Position ?? Vector2.Zero)
},
new ReplayState<TestAction>
{
PressedActions = CurrentFrame?.Actions ?? new List<TestAction>()
}
};
}
}
public class TestConsumer : CompositeDrawable, IKeyBindingHandler<TestAction>
{
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parent.ReceivePositionalInputAt(screenSpacePos);
private readonly Box box;
public TestConsumer()
{
Size = new Vector2(30);
Origin = Anchor.Centre;
InternalChildren = new Drawable[]
{
box = new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
},
};
}
protected override bool OnMouseMove(MouseMoveEvent e)
{
Position = e.MousePosition;
return base.OnMouseMove(e);
}
public bool OnPressed(TestAction action)
{
box.Colour = Color4.White;
return true;
}
public void OnReleased(TestAction action)
{
box.Colour = Color4.Black;
}
}
public class TestRulesetInputManager : RulesetInputManager<TestAction>
{
public TestRulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
: base(ruleset, variant, unique)
{
}
protected override KeyBindingContainer<TestAction> CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new TestKeyBindingContainer();
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
{
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
{
new KeyBinding(InputKey.MouseLeft, TestAction.Down),
};
}
}
public class TestReplayFrame : ReplayFrame
{
public Vector2 Position;
public List<TestAction> Actions = new List<TestAction>();
public TestReplayFrame(double time, Vector2 position, params TestAction[] actions)
: base(time)
{
Position = position;
Actions.AddRange(actions);
}
}
public enum TestAction
{
Down,
}
internal class TestReplayRecorder : ReplayRecorder<TestAction>
{
public TestReplayRecorder(Replay target)
: base(target)
{
}
protected override ReplayFrame HandleFrame(Vector2 mousePosition, List<TestAction> actions, ReplayFrame previousFrame) =>
new TestReplayFrame(Time.Current, mousePosition, actions.ToArray());
}
}

View File

@ -236,8 +236,6 @@ namespace osu.Game.Tests.Scores.IO
}
public override IEnumerable<string> Filenames => new[] { "test_file.osr" };
public override Stream GetUnderlyingStream() => new MemoryStream();
}
}
}

View File

@ -27,6 +27,7 @@ using osu.Game.Screens;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Tests.Resources;
using osu.Game.Users;
@ -36,7 +37,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Background
{
[TestFixture]
public class TestSceneUserDimBackgrounds : ManualInputManagerTestScene
public class TestSceneUserDimBackgrounds : OsuManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -203,7 +204,7 @@ namespace osu.Game.Tests.Visual.Background
}
/// <summary>
/// Check if the visual settings container removes user dim when suspending <see cref="Player"/> for <see cref="SoloResults"/>
/// Check if the visual settings container removes user dim when suspending <see cref="Player"/> for <see cref="ResultsScreen"/>
/// </summary>
[Test]
public void TransitionTest()
@ -277,6 +278,7 @@ namespace osu.Game.Tests.Visual.Background
private void setupUserSettings()
{
AddUntilStep("Song select is current", () => songSelect.IsCurrentScreen());
AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmap != null);
AddStep("Set default user settings", () =>
{
@ -335,7 +337,7 @@ namespace osu.Game.Tests.Visual.Background
public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen();
}
private class FadeAccessibleResults : SoloResults
private class FadeAccessibleResults : ResultsScreen
{
public FadeAccessibleResults(ScoreInfo score)
: base(score)

View File

@ -12,7 +12,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Components
{
[TestFixture]
public class TestSceneIdleTracker : ManualInputManagerTestScene
public class TestSceneIdleTracker : OsuManualInputManagerTestScene
{
private IdleTrackingBox box1;
private IdleTrackingBox box2;

View File

@ -3,27 +3,83 @@
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Editor
{
public class TestSceneBeatDivisorControl : OsuTestScene
public class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BindableBeatDivisor) };
private BeatDivisorControl beatDivisorControl;
private BindableBeatDivisor bindableBeatDivisor;
[BackgroundDependencyLoader]
private void load()
private SliderBar<int> tickSliderBar;
private EquilateralTriangle tickMarkerHead;
[SetUp]
public void SetUp() => Schedule(() =>
{
Child = new BeatDivisorControl(new BindableBeatDivisor())
Child = beatDivisorControl = new BeatDivisorControl(bindableBeatDivisor = new BindableBeatDivisor(16))
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(90, 90)
};
tickSliderBar = beatDivisorControl.ChildrenOfType<SliderBar<int>>().Single();
tickMarkerHead = tickSliderBar.ChildrenOfType<EquilateralTriangle>().Single();
});
[Test]
public void TestBindableBeatDivisor()
{
AddRepeatStep("move previous", () => bindableBeatDivisor.Previous(), 4);
AddAssert("divisor is 4", () => bindableBeatDivisor.Value == 4);
AddRepeatStep("move next", () => bindableBeatDivisor.Next(), 3);
AddAssert("divisor is 12", () => bindableBeatDivisor.Value == 12);
}
[Test]
public void TestMouseInput()
{
AddStep("hold marker", () =>
{
InputManager.MoveMouseTo(tickMarkerHead.ScreenSpaceDrawQuad.Centre);
InputManager.PressButton(MouseButton.Left);
});
AddStep("move to 8 and release", () =>
{
InputManager.MoveMouseTo(tickSliderBar.ScreenSpaceDrawQuad.Centre);
InputManager.ReleaseButton(MouseButton.Left);
});
AddAssert("divisor is 8", () => bindableBeatDivisor.Value == 8);
AddStep("hold marker", () => InputManager.PressButton(MouseButton.Left));
AddStep("move to 16", () => InputManager.MoveMouseTo(getPositionForDivisor(16)));
AddStep("move to ~10 and release", () =>
{
InputManager.MoveMouseTo(getPositionForDivisor(10));
InputManager.ReleaseButton(MouseButton.Left);
});
AddAssert("divisor clamped to 8", () => bindableBeatDivisor.Value == 8);
}
private Vector2 getPositionForDivisor(int divisor)
{
var relativePosition = (float)Math.Clamp(divisor, 0, 16) / 16;
var sliderDrawQuad = tickSliderBar.ScreenSpaceDrawQuad;
return new Vector2(
sliderDrawQuad.TopLeft.X + sliderDrawQuad.Width * relativePosition,
sliderDrawQuad.Centre.Y
);
}
}
}

View File

@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
{
public class TestSceneZoomableScrollContainer : ManualInputManagerTestScene
public class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene
{
private ZoomableScrollContainer scrollContainer;
private Drawable innerBox;

View File

@ -18,7 +18,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
[Description("player pause/fail screens")]
public class TestSceneGameplayMenuOverlay : ManualInputManagerTestScene
public class TestSceneGameplayMenuOverlay : OsuManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) };

View File

@ -15,7 +15,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneHUDOverlay : ManualInputManagerTestScene
public class TestSceneHUDOverlay : OsuManualInputManagerTestScene
{
private HUDOverlay hudOverlay;

View File

@ -13,7 +13,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
[Description("'Hold to Quit' UI element")]
public class TestSceneHoldForMenuButton : ManualInputManagerTestScene
public class TestSceneHoldForMenuButton : OsuManualInputManagerTestScene
{
private bool exitAction;

View File

@ -13,7 +13,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
[TestFixture]
public class TestSceneKeyCounter : ManualInputManagerTestScene
public class TestSceneKeyCounter : OsuManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{

View File

@ -29,7 +29,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
public class TestScenePlayerLoader : ManualInputManagerTestScene
public class TestScenePlayerLoader : OsuManualInputManagerTestScene
{
private TestPlayerLoader loader;
private TestPlayerLoaderContainer container;

View File

@ -11,7 +11,7 @@ using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets;
using osu.Game.Screens.Ranking.Pages;
using osu.Game.Screens.Ranking;
namespace osu.Game.Tests.Visual.Gameplay
{

View File

@ -14,7 +14,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay
{
[TestFixture]
public class TestSceneSkipOverlay : ManualInputManagerTestScene
public class TestSceneSkipOverlay : OsuManualInputManagerTestScene
{
private SkipOverlay skip;
private int requestCount;

View File

@ -64,6 +64,8 @@ namespace osu.Game.Tests.Visual.Menus
introStack.Push(CreateScreen());
});
AddUntilStep("wait for menu", () => introStack.CurrentScreen is MainMenu);
}
protected abstract IScreen CreateScreen();

View File

@ -1,12 +1,15 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using System.Threading;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osuTK.Graphics;
@ -14,14 +17,14 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Menus
{
[TestFixture]
public class TestSceneLoaderAnimation : ScreenTestScene
public class TestSceneLoader : ScreenTestScene
{
private TestLoader loader;
[Cached]
private OsuLogo logo;
public TestSceneLoaderAnimation()
public TestSceneLoader()
{
Child = logo = new OsuLogo
{
@ -42,33 +45,33 @@ namespace osu.Game.Tests.Visual.Menus
LoadScreen(loader);
});
AddAssert("spinner did not display", () => loader.LoadingSpinner?.Alpha == 0);
AddUntilStep("loaded", () => loader.ScreenLoaded);
AddUntilStep("not current", () => !loader.IsCurrentScreen());
}
[Test]
public void TestDelayedLoad()
{
AddStep("begin loading", () => LoadScreen(loader = new TestLoader()));
AddUntilStep("wait for logo visible", () => loader.Logo?.Alpha > 0);
AddUntilStep("wait for spinner visible", () => loader.LoadingSpinner?.Alpha > 0);
AddStep("finish loading", () => loader.AllowLoad.Set());
AddUntilStep("loaded", () => loader.Logo != null && loader.ScreenLoaded);
AddUntilStep("logo gone", () => loader.Logo?.Alpha == 0);
AddUntilStep("spinner gone", () => loader.LoadingSpinner?.Alpha == 0);
AddUntilStep("loaded", () => loader.ScreenLoaded);
AddUntilStep("not current", () => !loader.IsCurrentScreen());
}
private class TestLoader : Loader
{
public readonly ManualResetEventSlim AllowLoad = new ManualResetEventSlim();
public OsuLogo Logo;
public LoadingSpinner LoadingSpinner => this.ChildrenOfType<LoadingSpinner>().FirstOrDefault();
private TestScreen screen;
public bool ScreenLoaded => screen.IsCurrentScreen();
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
Logo = logo;
base.LogoArriving(logo, resuming);
}
protected override OsuScreen CreateLoadableScreen() => screen = new TestScreen();
protected override ShaderPrecompiler CreateShaderPrecompiler() => new TestShaderPrecompiler(AllowLoad);

View File

@ -20,7 +20,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneDrawableRoomPlaylist : ManualInputManagerTestScene
public class TestSceneDrawableRoomPlaylist : OsuManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{

View File

@ -1,106 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Scoring;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Screens.Multi.Ranking;
using osu.Game.Screens.Multi.Ranking.Pages;
using osu.Game.Screens.Multi.Ranking.Types;
using osu.Game.Screens.Ranking;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMatchResults : MultiplayerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(MatchResults),
typeof(RoomLeaderboardPageInfo),
typeof(RoomLeaderboardPage)
};
[Resolved]
private BeatmapManager beatmaps { get; set; }
[BackgroundDependencyLoader]
private void load()
{
var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0);
if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
Room.RoomID.Value = 1;
Room.Name.Value = "an awesome room";
LoadScreen(new TestMatchResults(new ScoreInfo
{
User = new User { Id = 10 },
}));
}
private class TestMatchResults : MatchResults
{
public TestMatchResults(ScoreInfo score)
: base(score)
{
}
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap.Value) };
}
private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo
{
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
this.score = score;
this.beatmap = beatmap;
}
public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap);
}
private class TestRoomLeaderboardPage : RoomLeaderboardPage
{
public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
}
protected override MatchLeaderboard CreateLeaderboard() => new TestMatchLeaderboard();
}
private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard
{
protected override APIRequest FetchScores(Action<IEnumerable<APIUserScoreAggregate>> scoresCallback)
{
var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray();
scoresCallback?.Invoke(scores);
ScoresLoaded?.Invoke(scores);
return null;
}
private APIUserScoreAggregate createRoomScore(int id) => new APIUserScoreAggregate
{
User = new User { Id = id, Username = $"User {id}" },
Accuracy = 0.98,
TotalScore = 987654,
TotalAttempts = 13,
CompletedBeatmaps = 5
};
}
}
}

View File

@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Navigation
/// <summary>
/// A scene which tests full game flow.
/// </summary>
public abstract class OsuGameTestScene : ManualInputManagerTestScene
public abstract class OsuGameTestScene : OsuManualInputManagerTestScene
{
private GameHost host;

View File

@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual.Online
API.Logout();
localUser = API.LocalUser.GetBoundCopy();
localUser.BindValueChanged(user => { userPanelArea.Child = new UserPanel(user.NewValue) { Width = 200 }; }, true);
localUser.BindValueChanged(user => { userPanelArea.Child = new UserGridPanel(user.NewValue) { Width = 200 }; }, true);
AddStep("logout", API.Logout);
}

View File

@ -22,7 +22,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneChatOverlay : ManualInputManagerTestScene
public class TestSceneChatOverlay : OsuManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
@ -38,8 +38,13 @@ namespace osu.Game.Tests.Visual.Online
private TestChatOverlay chatOverlay;
private ChannelManager channelManager;
private IEnumerable<Channel> visibleChannels => chatOverlay.ChannelTabControl.VisibleItems.Where(channel => channel.Name != "+");
private IEnumerable<Channel> joinedChannels => chatOverlay.ChannelTabControl.Items.Where(channel => channel.Name != "+");
private readonly List<Channel> channels;
private Channel currentChannel => channelManager.CurrentChannel.Value;
private Channel nextChannel => joinedChannels.ElementAt(joinedChannels.ToList().IndexOf(currentChannel) + 1);
private Channel previousChannel => joinedChannels.ElementAt(joinedChannels.ToList().IndexOf(currentChannel) - 1);
private Channel channel1 => channels[0];
private Channel channel2 => channels[1];
@ -49,7 +54,8 @@ namespace osu.Game.Tests.Visual.Online
.Select(index => new Channel(new User())
{
Name = $"Channel no. {index}",
Topic = index == 3 ? null : $"We talk about the number {index} here"
Topic = index == 3 ? null : $"We talk about the number {index} here",
Type = index % 2 == 0 ? ChannelType.PM : ChannelType.Temporary
})
.ToList();
}
@ -91,32 +97,15 @@ namespace osu.Game.Tests.Visual.Online
AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
AddAssert("Current channel is channel 1", () => currentChannel == channel1);
AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
}
[Test]
public void TestCloseChannelWhileSelectorClosed()
{
AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
AddStep("Join channel 2", () => channelManager.JoinChannel(channel2));
AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2]));
AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child));
AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child));
AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
}
[Test]
public void TestSearchInSelector()
{
AddStep("search for 'no. 2'", () => chatOverlay.ChildrenOfType<SearchTextBox>().First().Text = "no. 2");
AddUntilStep("only channel 2 visible", () =>
AddStep("Search for 'no. 2'", () => chatOverlay.ChildrenOfType<SearchTextBox>().First().Text = "no. 2");
AddUntilStep("Only channel 2 visible", () =>
{
var listItems = chatOverlay.ChildrenOfType<ChannelListItem>().Where(c => c.IsPresent);
return listItems.Count() == 1 && listItems.Single().Channel == channel2;
@ -126,24 +115,116 @@ namespace osu.Game.Tests.Visual.Online
[Test]
public void TestChannelShortcutKeys()
{
AddStep("join 10 channels", () => channels.ForEach(channel => channelManager.JoinChannel(channel)));
AddStep("close channel selector", () =>
AddStep("Join channels", () => channels.ForEach(channel => channelManager.JoinChannel(channel)));
AddStep("Close channel selector", () =>
{
InputManager.PressKey(Key.Escape);
InputManager.ReleaseKey(Key.Escape);
});
AddUntilStep("wait for close", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
AddUntilStep("Wait for close", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
for (int zeroBasedIndex = 0; zeroBasedIndex < 10; ++zeroBasedIndex)
{
var oneBasedIndex = zeroBasedIndex + 1;
var targetNumberKey = oneBasedIndex % 10;
var targetChannel = channels[zeroBasedIndex];
AddStep($"press Alt+{targetNumberKey}", () => pressChannelHotkey(targetNumberKey));
AddAssert($"channel #{oneBasedIndex} is selected", () => channelManager.CurrentChannel.Value == targetChannel);
AddStep($"Press Alt+{targetNumberKey}", () => pressChannelHotkey(targetNumberKey));
AddAssert($"Channel #{oneBasedIndex} is selected", () => currentChannel == targetChannel);
}
}
private Channel expectedChannel;
[Test]
public void TestCloseChannelBehaviour()
{
AddUntilStep("Join until dropdown has channels", () =>
{
if (visibleChannels.Count() < joinedChannels.Count())
return true;
// Using temporary channels because they don't hide their names when not active
channelManager.JoinChannel(new Channel
{
Name = $"Channel no. {joinedChannels.Count() + 11}",
Type = ChannelType.Temporary
});
return false;
});
AddStep("Switch to last tab", () => clickDrawable(chatOverlay.TabMap[visibleChannels.Last()]));
AddAssert("Last visible selected", () => currentChannel == visibleChannels.Last());
// Closing the last channel before dropdown
AddStep("Close current channel", () =>
{
expectedChannel = nextChannel;
chatOverlay.ChannelTabControl.RemoveChannel(currentChannel);
});
AddAssert("Next channel selected", () => currentChannel == expectedChannel);
AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
// Depending on the window size, one more channel might need to be closed for the selectorTab to appear
AddUntilStep("Close channels until selector visible", () =>
{
if (chatOverlay.ChannelTabControl.VisibleItems.Last().Name == "+")
return true;
chatOverlay.ChannelTabControl.RemoveChannel(visibleChannels.Last());
return false;
});
AddAssert("Last visible selected", () => currentChannel == visibleChannels.Last());
// Closing the last channel with dropdown no longer present
AddStep("Close last when selector next", () =>
{
expectedChannel = previousChannel;
chatOverlay.ChannelTabControl.RemoveChannel(currentChannel);
});
AddAssert("Previous channel selected", () => currentChannel == expectedChannel);
// Standard channel closing
AddStep("Switch to previous channel", () => chatOverlay.ChannelTabControl.SwitchTab(-1));
AddStep("Close current channel", () =>
{
expectedChannel = nextChannel;
chatOverlay.ChannelTabControl.RemoveChannel(currentChannel);
});
AddAssert("Next channel selected", () => currentChannel == expectedChannel);
// Selector reappearing after all channels closed
AddUntilStep("Close all channels", () =>
{
if (!joinedChannels.Any())
return true;
chatOverlay.ChannelTabControl.RemoveChannel(joinedChannels.Last());
return false;
});
AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
}
[Test]
public void TestChannelCloseButton()
{
AddStep("Join 2 channels", () =>
{
channelManager.JoinChannel(channel1);
channelManager.JoinChannel(channel2);
});
// PM channel close button only appears when active
AddStep("Select PM channel", () => clickDrawable(chatOverlay.TabMap[channel2]));
AddStep("Click PM close button", () => clickDrawable(((TestPrivateChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child));
AddAssert("PM channel closed", () => !channelManager.JoinedChannels.Contains(channel2));
// Non-PM chat channel close button only appears when hovered
AddStep("Hover normal channel tab", () => InputManager.MoveMouseTo(chatOverlay.TabMap[channel1]));
AddStep("Click normal close button", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child));
AddAssert("All channels closed", () => !channelManager.JoinedChannels.Any());
}
private void pressChannelHotkey(int number)
{
var channelKey = Key.Number0 + number;
@ -187,6 +268,8 @@ namespace osu.Game.Tests.Visual.Online
{
public Visibility SelectionOverlayState => ChannelSelectionOverlay.State.Value;
public new ChannelTabControl ChannelTabControl => base.ChannelTabControl;
public new ChannelSelectionOverlay ChannelSelectionOverlay => base.ChannelSelectionOverlay;
protected override ChannelTabControl CreateChannelTabControl() => new TestTabControl();
@ -196,12 +279,22 @@ namespace osu.Game.Tests.Visual.Online
private class TestTabControl : ChannelTabControl
{
protected override TabItem<Channel> CreateTabItem(Channel value) => new TestChannelTabItem(value);
protected override TabItem<Channel> CreateTabItem(Channel value)
{
switch (value.Type)
{
case ChannelType.PM:
return new TestPrivateChannelTabItem(value);
default:
return new TestChannelTabItem(value);
}
}
public new IReadOnlyDictionary<Channel, TabItem<Channel>> TabMap => base.TabMap;
}
private class TestChannelTabItem : PrivateChannelTabItem
private class TestChannelTabItem : ChannelTabItem
{
public TestChannelTabItem(Channel channel)
: base(channel)
@ -210,5 +303,15 @@ namespace osu.Game.Tests.Visual.Online
public new ClickableContainer CloseButton => base.CloseButton;
}
private class TestPrivateChannelTabItem : PrivateChannelTabItem
{
public TestPrivateChannelTabItem(Channel channel)
: base(channel)
{
}
public new ClickableContainer CloseButton => base.CloseButton;
}
}
}

View 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 System;
using System.Collections.Generic;
using osu.Framework.Graphics.Containers;
using osu.Game.Overlays.Dashboard.Friends;
using osu.Framework.Graphics;
using osu.Game.Users;
using osu.Game.Overlays;
using osu.Framework.Allocation;
using NUnit.Framework;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneFriendDisplay : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(FriendDisplay),
typeof(FriendOnlineStreamControl),
typeof(UserListToolbar)
};
protected override bool UseOnlineAPI => true;
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
private FriendDisplay display;
[SetUp]
public void Setup() => Schedule(() =>
{
Child = new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = display = new FriendDisplay()
};
});
[Test]
public void TestOffline()
{
AddStep("Populate", () => display.Users = getUsers());
}
[Test]
public void TestOnline()
{
AddStep("Fetch online", () => display?.Fetch());
}
private List<User> getUsers() => new List<User>
{
new User
{
Username = "flyte",
Id = 3103765,
IsOnline = true,
CurrentModeRank = 1111,
Country = new Country { FlagName = "JP" },
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
},
new User
{
Username = "peppy",
Id = 2,
IsOnline = false,
CurrentModeRank = 2222,
Country = new Country { FlagName = "AU" },
CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
IsSupporter = true,
SupportLevel = 3,
},
new User
{
Username = "Evast",
Id = 8195163,
Country = new Country { FlagName = "BY" },
CoverUrl = "https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
IsOnline = false,
LastVisit = DateTimeOffset.Now
}
};
}
}

View File

@ -18,10 +18,9 @@ namespace osu.Game.Tests.Visual.Online
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(UserPanel),
typeof(SocialPanel),
typeof(FilterControl),
typeof(SocialGridPanel),
typeof(SocialListPanel)
typeof(UserGridPanel),
typeof(UserListPanel)
};
public TestSceneSocialOverlay()

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -15,9 +17,18 @@ namespace osu.Game.Tests.Visual.Online
[TestFixture]
public class TestSceneUserPanel : OsuTestScene
{
private readonly Bindable<UserActivity> activity = new Bindable<UserActivity>();
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(UserPanel),
typeof(UserListPanel),
typeof(UserGridPanel),
};
private UserPanel peppy;
private readonly Bindable<UserActivity> activity = new Bindable<UserActivity>();
private readonly Bindable<UserStatus> status = new Bindable<UserStatus>();
private UserGridPanel peppy;
private TestUserListPanel evast;
[Resolved]
private RulesetStore rulesetStore { get; set; }
@ -25,24 +36,28 @@ namespace osu.Game.Tests.Visual.Online
[SetUp]
public void SetUp() => Schedule(() =>
{
UserPanel flyte;
UserGridPanel flyte;
activity.Value = null;
status.Value = null;
Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Spacing = new Vector2(10f),
Children = new[]
Children = new Drawable[]
{
flyte = new UserPanel(new User
flyte = new UserGridPanel(new User
{
Username = @"flyte",
Id = 3103765,
Country = new Country { FlagName = @"JP" },
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg"
}) { Width = 300 },
peppy = new UserPanel(new User
peppy = new UserGridPanel(new User
{
Username = @"peppy",
Id = 2,
@ -51,27 +66,40 @@ namespace osu.Game.Tests.Visual.Online
IsSupporter = true,
SupportLevel = 3,
}) { Width = 300 },
evast = new TestUserListPanel(new User
{
Username = @"Evast",
Id = 8195163,
Country = new Country { FlagName = @"BY" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg",
IsOnline = false,
LastVisit = DateTimeOffset.Now
})
},
};
flyte.Status.Value = new UserStatusOnline();
peppy.Status.Value = null;
peppy.Status.BindTo(status);
peppy.Activity.BindTo(activity);
evast.Status.BindTo(status);
evast.Activity.BindTo(activity);
});
[Test]
public void TestUserStatus()
{
AddStep("online", () => peppy.Status.Value = new UserStatusOnline());
AddStep("do not disturb", () => peppy.Status.Value = new UserStatusDoNotDisturb());
AddStep("offline", () => peppy.Status.Value = new UserStatusOffline());
AddStep("null status", () => peppy.Status.Value = null);
AddStep("online", () => status.Value = new UserStatusOnline());
AddStep("do not disturb", () => status.Value = new UserStatusDoNotDisturb());
AddStep("offline", () => status.Value = new UserStatusOffline());
AddStep("null status", () => status.Value = null);
}
[Test]
public void TestUserActivity()
{
AddStep("set online status", () => peppy.Status.Value = new UserStatusOnline());
AddStep("set online status", () => status.Value = new UserStatusOnline());
AddStep("idle", () => activity.Value = null);
AddStep("spectating", () => activity.Value = new UserActivity.Spectating());
@ -84,6 +112,29 @@ namespace osu.Game.Tests.Visual.Online
AddStep("modding", () => activity.Value = new UserActivity.Modding());
}
[Test]
public void TestUserActivityChange()
{
AddAssert("visit message is visible", () => evast.LastVisitMessage.IsPresent);
AddStep("set online status", () => status.Value = new UserStatusOnline());
AddAssert("visit message is not visible", () => !evast.LastVisitMessage.IsPresent);
AddStep("set choosing activity", () => activity.Value = new UserActivity.ChoosingBeatmap());
AddStep("set offline status", () => status.Value = new UserStatusOffline());
AddAssert("visit message is visible", () => evast.LastVisitMessage.IsPresent);
AddStep("set online status", () => status.Value = new UserStatusOnline());
AddAssert("visit message is not visible", () => !evast.LastVisitMessage.IsPresent);
}
private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.SoloGame(null, rulesetStore.GetRuleset(rulesetId));
private class TestUserListPanel : UserListPanel
{
public TestUserListPanel(User user)
: base(user)
{
}
public new TextFlowContainer LastVisitMessage => base.LastVisitMessage;
}
}
}

View File

@ -0,0 +1,165 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneAccuracyCircle : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(AccuracyCircle),
typeof(RankBadge),
typeof(RankNotch),
typeof(RankText),
typeof(SmoothCircularProgress)
};
[Test]
public void TestLowDRank()
{
var score = createScore();
score.Accuracy = 0.2;
score.Rank = ScoreRank.D;
addCircleStep(score);
}
[Test]
public void TestDRank()
{
var score = createScore();
score.Accuracy = 0.5;
score.Rank = ScoreRank.D;
addCircleStep(score);
}
[Test]
public void TestCRank()
{
var score = createScore();
score.Accuracy = 0.75;
score.Rank = ScoreRank.C;
addCircleStep(score);
}
[Test]
public void TestBRank()
{
var score = createScore();
score.Accuracy = 0.85;
score.Rank = ScoreRank.B;
addCircleStep(score);
}
[Test]
public void TestARank()
{
var score = createScore();
score.Accuracy = 0.925;
score.Rank = ScoreRank.A;
addCircleStep(score);
}
[Test]
public void TestSRank()
{
var score = createScore();
score.Accuracy = 0.975;
score.Rank = ScoreRank.S;
addCircleStep(score);
}
[Test]
public void TestAlmostSSRank()
{
var score = createScore();
score.Accuracy = 0.9999;
score.Rank = ScoreRank.S;
addCircleStep(score);
}
[Test]
public void TestSSRank()
{
var score = createScore();
score.Accuracy = 1;
score.Rank = ScoreRank.X;
addCircleStep(score);
}
private void addCircleStep(ScoreInfo score) => AddStep("add panel", () =>
{
Children = new Drawable[]
{
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500, 700),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#555"), Color4Extensions.FromHex("#333"))
}
}
},
new AccuracyCircle(score)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(230)
}
};
});
private ScoreInfo createScore() => new ScoreInfo
{
User = new User
{
Id = 2,
Username = "peppy",
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
TotalScore = 2845370,
Accuracy = 0.95,
MaxCombo = 999,
Rank = ScoreRank.S,
Date = DateTimeOffset.Now,
Statistics =
{
{ HitResult.Miss, 1 },
{ HitResult.Meh, 50 },
{ HitResult.Good, 100 },
{ HitResult.Great, 300 },
}
};
}
}

View File

@ -0,0 +1,130 @@
// 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.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Expanded;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osu.Game.Screens.Ranking.Expanded.Statistics;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneExpandedPanelMiddleContent : OsuTestScene
{
[Resolved]
private RulesetStore rulesetStore { get; set; }
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ExpandedPanelMiddleContent),
typeof(AccuracyCircle),
typeof(AccuracyStatistic),
typeof(ComboStatistic),
typeof(CounterStatistic),
typeof(StarRatingDisplay),
typeof(StatisticDisplay),
typeof(TotalScoreCounter)
};
[Test]
public void TestMapWithKnownMapper()
{
var author = new User { Username = "mapper_name" };
AddStep("show example score", () => showPanel(createTestBeatmap(author), createTestScore()));
AddAssert("mapper name present", () => this.ChildrenOfType<OsuSpriteText>().Any(spriteText => spriteText.Text == "mapper_name"));
}
[Test]
public void TestMapWithUnknownMapper()
{
AddStep("show example score", () => showPanel(createTestBeatmap(null), createTestScore()));
AddAssert("mapped by text not present", () =>
this.ChildrenOfType<OsuSpriteText>().All(spriteText => !containsAny(spriteText.Text, "mapped", "by")));
}
private void showPanel(WorkingBeatmap workingBeatmap, ScoreInfo score)
{
Child = new ExpandedPanelMiddleContentContainer(workingBeatmap, score);
}
private WorkingBeatmap createTestBeatmap(User author)
{
var beatmap = new TestBeatmap(rulesetStore.GetRuleset(0));
beatmap.Metadata.Author = author;
return new TestWorkingBeatmap(beatmap);
}
private ScoreInfo createTestScore() => new ScoreInfo
{
User = new User
{
Id = 2,
Username = "peppy",
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
TotalScore = 999999,
Accuracy = 0.95,
MaxCombo = 999,
Rank = ScoreRank.S,
Date = DateTimeOffset.Now,
Statistics =
{
{ HitResult.Miss, 1 },
{ HitResult.Meh, 50 },
{ HitResult.Good, 100 },
{ HitResult.Great, 300 },
}
};
private bool containsAny(string text, params string[] stringsToMatch) => stringsToMatch.Any(text.Contains);
private class ExpandedPanelMiddleContentContainer : Container
{
[Cached]
private Bindable<WorkingBeatmap> workingBeatmap { get; set; }
public ExpandedPanelMiddleContentContainer(WorkingBeatmap beatmap, ScoreInfo score)
{
workingBeatmap = new Bindable<WorkingBeatmap>(beatmap);
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Size = new Vector2(500, 700);
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#444"),
},
new ExpandedPanelMiddleContent(score)
};
}
}
}
}

View File

@ -0,0 +1,35 @@
// 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.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Screens.Ranking.Expanded;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneExpandedPanelTopContent : OsuTestScene
{
public TestSceneExpandedPanelTopContent()
{
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500, 200),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#444"),
},
new ExpandedPanelTopContent(new User { Id = 2, Username = "peppy" }),
}
};
}
}
}

View File

@ -10,29 +10,27 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Pages;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Gameplay
namespace osu.Game.Tests.Visual.Ranking
{
[TestFixture]
public class TestSceneResults : ScreenTestScene
public class TestSceneResultsScreen : ScreenTestScene
{
private BeatmapManager beatmaps;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Results),
typeof(ResultsPage),
typeof(ScoreResultsPage),
typeof(ResultsScreen),
typeof(RetryButton),
typeof(ReplayDownloadButton),
typeof(LocalLeaderboardPage),
typeof(TestPlayer)
};
@ -65,6 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ HitResult.Meh, 50 },
{ HitResult.Miss, 1 }
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
User = new User
{
Username = "peppy",
@ -119,7 +118,7 @@ namespace osu.Game.Tests.Visual.Gameplay
}
}
private class TestSoloResults : SoloResults
private class TestSoloResults : ResultsScreen
{
public HotkeyRetryOverlay RetryOverlay;

View File

@ -0,0 +1,143 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Expanded;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneScorePanel : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ScorePanel),
typeof(PanelState),
typeof(ExpandedPanelMiddleContent),
typeof(ExpandedPanelTopContent),
};
[Test]
public void TestDRank()
{
var score = createScore();
score.Accuracy = 0.5;
score.Rank = ScoreRank.D;
addPanelStep(score);
}
[Test]
public void TestCRank()
{
var score = createScore();
score.Accuracy = 0.75;
score.Rank = ScoreRank.C;
addPanelStep(score);
}
[Test]
public void TestBRank()
{
var score = createScore();
score.Accuracy = 0.85;
score.Rank = ScoreRank.B;
addPanelStep(score);
}
[Test]
public void TestARank()
{
var score = createScore();
score.Accuracy = 0.925;
score.Rank = ScoreRank.A;
addPanelStep(score);
}
[Test]
public void TestSRank()
{
var score = createScore();
score.Accuracy = 0.975;
score.Rank = ScoreRank.S;
addPanelStep(score);
}
[Test]
public void TestAlmostSSRank()
{
var score = createScore();
score.Accuracy = 0.9999;
score.Rank = ScoreRank.S;
addPanelStep(score);
}
[Test]
public void TestSSRank()
{
var score = createScore();
score.Accuracy = 1;
score.Rank = ScoreRank.X;
addPanelStep(score);
}
[Test]
public void TestAllHitResults()
{
var score = createScore();
score.Statistics[HitResult.Perfect] = 350;
score.Statistics[HitResult.Ok] = 200;
addPanelStep(score);
}
private void addPanelStep(ScoreInfo score) => AddStep("add panel", () =>
{
Child = new ScorePanel(score)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
State = PanelState.Expanded
};
});
private ScoreInfo createScore() => new ScoreInfo
{
User = new User
{
Id = 2,
Username = "peppy",
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
TotalScore = 2845370,
Accuracy = 0.95,
MaxCombo = 999,
Rank = ScoreRank.S,
Date = DateTimeOffset.Now,
Statistics =
{
{ HitResult.Miss, 1 },
{ HitResult.Meh, 50 },
{ HitResult.Good, 100 },
{ HitResult.Great, 300 },
}
};
}
}

View File

@ -0,0 +1,32 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Screens.Ranking.Expanded;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneStarRatingDisplay : OsuTestScene
{
public TestSceneStarRatingDisplay()
{
Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 1.23 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 2.34 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 3.45 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 4.56 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 5.67 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 6.78 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 10.11 }),
}
};
}
}
}

View File

@ -227,6 +227,34 @@ namespace osu.Game.Tests.Visual.SongSelect
waitForSelection(set_count);
}
[Test]
public void TestSelectionEnteringFromEmptyRuleset()
{
var sets = new List<BeatmapSetInfo>();
AddStep("Create beatmaps for taiko only", () =>
{
sets.Clear();
var rulesetBeatmapSet = createTestBeatmapSet(1);
var taikoRuleset = rulesets.AvailableRulesets.ElementAt(1);
rulesetBeatmapSet.Beatmaps.ForEach(b =>
{
b.Ruleset = taikoRuleset;
b.RulesetID = 1;
});
sets.Add(rulesetBeatmapSet);
});
loadBeatmaps(sets, () => new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) });
AddStep("Set non-empty mode filter", () =>
carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(1) }, false));
AddAssert("Something is selected", () => carousel.SelectedBeatmap != null);
}
/// <summary>
/// Test sorting
/// </summary>
@ -399,27 +427,32 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("filter to ruleset 0", () =>
carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false));
AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false));
AddAssert("unfiltered beatmap selected", () => carousel.SelectedBeatmap.Equals(testMixed.Beatmaps[0]));
AddAssert("unfiltered beatmap not selected", () => carousel.SelectedBeatmap.RulesetID == 0);
AddStep("remove mixed set", () =>
{
carousel.RemoveBeatmapSet(testMixed);
testMixed = null;
});
var testSingle = createTestBeatmapSet(set_count + 2);
testSingle.Beatmaps.ForEach(b =>
BeatmapSetInfo testSingle = null;
AddStep("add single ruleset beatmapset", () =>
{
b.Ruleset = rulesets.AvailableRulesets.ElementAt(1);
b.RulesetID = b.Ruleset.ID ?? 1;
testSingle = createTestBeatmapSet(set_count + 2);
testSingle.Beatmaps.ForEach(b =>
{
b.Ruleset = rulesets.AvailableRulesets.ElementAt(1);
b.RulesetID = b.Ruleset.ID ?? 1;
});
carousel.UpdateBeatmapSet(testSingle);
});
AddStep("add single ruleset beatmapset", () => carousel.UpdateBeatmapSet(testSingle));
AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testSingle.Beatmaps[0], false));
checkNoSelection();
AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle));
}
[Test]
public void TestCarouselRootIsRandom()
public void TestCarouselRemembersSelection()
{
List<BeatmapSetInfo> manySets = new List<BeatmapSetInfo>();
@ -429,12 +462,74 @@ namespace osu.Game.Tests.Visual.SongSelect
loadBeatmaps(manySets);
advanceSelection(direction: 1, diff: false);
checkNonmatchingFilter();
checkNonmatchingFilter();
checkNonmatchingFilter();
checkNonmatchingFilter();
checkNonmatchingFilter();
AddAssert("Selection was random", () => eagerSelectedIDs.Count > 1);
for (int i = 0; i < 5; i++)
{
AddStep("Toggle non-matching filter", () =>
{
carousel.Filter(new FilterCriteria { SearchText = Guid.NewGuid().ToString() }, false);
});
AddStep("Restore no filter", () =>
{
carousel.Filter(new FilterCriteria(), false);
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
});
}
// always returns to same selection as long as it's available.
AddAssert("Selection was remembered", () => eagerSelectedIDs.Count == 1);
}
[Test]
public void TestRandomFallbackOnNonMatchingPrevious()
{
List<BeatmapSetInfo> manySets = new List<BeatmapSetInfo>();
AddStep("populate maps", () =>
{
for (int i = 0; i < 10; i++)
{
var set = createTestBeatmapSet(i);
foreach (var b in set.Beatmaps)
{
// all taiko except for first
int ruleset = i > 0 ? 1 : 0;
b.Ruleset = rulesets.GetRuleset(ruleset);
b.RulesetID = ruleset;
}
manySets.Add(set);
}
});
loadBeatmaps(manySets);
for (int i = 0; i < 10; i++)
{
AddStep("Reset filter", () => carousel.Filter(new FilterCriteria(), false));
AddStep("select first beatmap", () => carousel.SelectBeatmap(manySets.First().Beatmaps.First()));
AddStep("Toggle non-matching filter", () =>
{
carousel.Filter(new FilterCriteria { SearchText = Guid.NewGuid().ToString() }, false);
});
AddAssert("selection lost", () => carousel.SelectedBeatmap == null);
AddStep("Restore different ruleset filter", () =>
{
carousel.Filter(new FilterCriteria { Ruleset = rulesets.GetRuleset(1) }, false);
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
});
AddAssert("selection changed", () => carousel.SelectedBeatmap != manySets.First().Beatmaps.First());
}
AddAssert("Selection was random", () => eagerSelectedIDs.Count > 2);
}
[Test]
@ -484,7 +579,7 @@ namespace osu.Game.Tests.Visual.SongSelect
checkVisibleItemCount(true, 15);
}
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null)
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null, Func<FilterCriteria> initialCriteria = null)
{
createCarousel();
@ -499,7 +594,7 @@ namespace osu.Game.Tests.Visual.SongSelect
bool changed = false;
AddStep($"Load {(beatmapSets.Count > 0 ? beatmapSets.Count.ToString() : "some")} beatmaps", () =>
{
carousel.Filter(new FilterCriteria());
carousel.Filter(initialCriteria?.Invoke() ?? new FilterCriteria());
carousel.BeatmapSetsChanged = () => changed = true;
carousel.BeatmapSets = beatmapSets;
});
@ -593,16 +688,6 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("Selection is visible", selectedBeatmapVisible);
}
private void checkNonmatchingFilter()
{
AddStep("Toggle non-matching filter", () =>
{
carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false);
carousel.Filter(new FilterCriteria(), false);
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
});
}
private BeatmapSetInfo createTestBeatmapSet(int id)
{
return new BeatmapSetInfo

View File

@ -283,7 +283,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("import multi-ruleset map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
manager.Import(createTestBeatmapSet(0, usableRulesets)).Wait();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
});
}
else
@ -436,6 +436,9 @@ namespace osu.Game.Tests.Visual.SongSelect
changeRuleset(0);
// used for filter check below
AddStep("allow convert display", () => config.Set(OsuSetting.ShowConvertedBeatmaps, true));
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
AddStep("set filter text", () => songSelect.FilterControl.ChildrenOfType<SearchTextBox>().First().Text = "nonono");
@ -446,16 +449,28 @@ namespace osu.Game.Tests.Visual.SongSelect
BeatmapInfo target = null;
int targetRuleset = differentRuleset ? 1 : 0;
AddStep("select beatmap externally", () =>
{
target = manager.GetAllUsableBeatmapSets().Where(b => b.Beatmaps.Any(bi => bi.RulesetID == (differentRuleset ? 1 : 0)))
.ElementAt(5).Beatmaps.First();
target = manager.GetAllUsableBeatmapSets()
.Where(b => b.Beatmaps.Any(bi => bi.RulesetID == targetRuleset))
.ElementAt(5).Beatmaps.First(bi => bi.RulesetID == targetRuleset);
Beatmap.Value = manager.GetWorkingBeatmap(target);
});
AddUntilStep("has selection", () => songSelect.Carousel.SelectedBeatmap != null);
AddAssert("selected only shows expected ruleset (plus converts)", () =>
{
var selectedPanel = songSelect.Carousel.ChildrenOfType<DrawableCarouselBeatmapSet>().First(s => s.Item.State.Value == CarouselItemState.Selected);
// special case for converts checked here.
return selectedPanel.ChildrenOfType<DrawableCarouselBeatmapSet.FilterableDifficultyIcon>().All(i =>
i.IsFiltered || i.Item.Beatmap.Ruleset.ID == targetRuleset || i.Item.Beatmap.Ruleset.ID == 0);
});
AddUntilStep("carousel has correct", () => songSelect.Carousel.SelectedBeatmap?.OnlineBeatmapID == target.OnlineBeatmapID);
AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.OnlineBeatmapID == target.OnlineBeatmapID);
@ -557,6 +572,7 @@ namespace osu.Game.Tests.Visual.SongSelect
difficultyIcon = set.ChildrenOfType<DrawableCarouselBeatmapSet.FilterableDifficultyIcon>()
.First(icon => getDifficultyIconIndex(set, icon) != getCurrentBeatmapIndex());
});
AddStep("Click on a difficulty", () =>
{
InputManager.MoveMouseTo(difficultyIcon);
@ -564,6 +580,7 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.PressButton(MouseButton.Left);
InputManager.ReleaseButton(MouseButton.Left);
});
AddAssert("Selected beatmap correct", () => getCurrentBeatmapIndex() == getDifficultyIconIndex(set, difficultyIcon));
double? maxBPM = null;
@ -576,16 +593,16 @@ namespace osu.Game.Tests.Visual.SongSelect
}
}));
BeatmapInfo filteredBeatmap = null;
DrawableCarouselBeatmapSet.FilterableDifficultyIcon filteredIcon = null;
AddStep("Get filtered icon", () =>
{
var filteredBeatmap = songSelect.Carousel.SelectedBeatmapSet.Beatmaps.Find(b => b.BPM < maxBPM);
filteredBeatmap = songSelect.Carousel.SelectedBeatmapSet.Beatmaps.First(b => b.BPM < maxBPM);
int filteredBeatmapIndex = getBeatmapIndex(filteredBeatmap.BeatmapSet, filteredBeatmap);
filteredIcon = set.ChildrenOfType<DrawableCarouselBeatmapSet.FilterableDifficultyIcon>().ElementAt(filteredBeatmapIndex);
});
int? previousID = null;
AddStep("Store current ID", () => previousID = songSelect.Carousel.SelectedBeatmap.ID);
AddStep("Click on a filtered difficulty", () =>
{
InputManager.MoveMouseTo(filteredIcon);
@ -593,7 +610,101 @@ namespace osu.Game.Tests.Visual.SongSelect
InputManager.PressButton(MouseButton.Left);
InputManager.ReleaseButton(MouseButton.Left);
});
AddAssert("Selected beatmap has not changed", () => songSelect.Carousel.SelectedBeatmap.ID == previousID);
AddAssert("Selected beatmap correct", () => songSelect.Carousel.SelectedBeatmap == filteredBeatmap);
}
[Test]
public void TestDifficultyIconSelectingForDifferentRuleset()
{
changeRuleset(0);
createSongSelect();
AddStep("import multi-ruleset map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
});
DrawableCarouselBeatmapSet set = null;
AddUntilStep("Find the DrawableCarouselBeatmapSet", () =>
{
set = songSelect.Carousel.ChildrenOfType<DrawableCarouselBeatmapSet>().FirstOrDefault();
return set != null;
});
DrawableCarouselBeatmapSet.FilterableDifficultyIcon difficultyIcon = null;
AddStep("Find an icon for different ruleset", () =>
{
difficultyIcon = set.ChildrenOfType<DrawableCarouselBeatmapSet.FilterableDifficultyIcon>()
.First(icon => icon.Item.Beatmap.Ruleset.ID == 3);
});
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
int previousSetID = 0;
AddStep("record set ID", () => previousSetID = Beatmap.Value.BeatmapSetInfo.ID);
AddStep("Click on a difficulty", () =>
{
InputManager.MoveMouseTo(difficultyIcon);
InputManager.PressButton(MouseButton.Left);
InputManager.ReleaseButton(MouseButton.Left);
});
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
AddAssert("Selected beatmap still same set", () => songSelect.Carousel.SelectedBeatmap.BeatmapSet.ID == previousSetID);
AddAssert("Selected beatmap is mania", () => Beatmap.Value.BeatmapInfo.Ruleset.ID == 3);
}
[Test]
public void TestGroupedDifficultyIconSelecting()
{
changeRuleset(0);
createSongSelect();
BeatmapSetInfo imported = null;
AddStep("import huge difficulty count map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
imported = manager.Import(createTestBeatmapSet(usableRulesets, 50)).Result;
});
AddStep("select the first beatmap of import", () => Beatmap.Value = manager.GetWorkingBeatmap(imported.Beatmaps.First()));
DrawableCarouselBeatmapSet set = null;
AddUntilStep("Find the DrawableCarouselBeatmapSet", () =>
{
set = songSelect.Carousel.ChildrenOfType<DrawableCarouselBeatmapSet>().FirstOrDefault();
return set != null;
});
DrawableCarouselBeatmapSet.FilterableGroupedDifficultyIcon groupIcon = null;
AddStep("Find group icon for different ruleset", () =>
{
groupIcon = set.ChildrenOfType<DrawableCarouselBeatmapSet.FilterableGroupedDifficultyIcon>()
.First(icon => icon.Items.First().Beatmap.Ruleset.ID == 3);
});
AddAssert("Check ruleset is osu!", () => Ruleset.Value.ID == 0);
AddStep("Click on group", () =>
{
InputManager.MoveMouseTo(groupIcon);
InputManager.PressButton(MouseButton.Left);
InputManager.ReleaseButton(MouseButton.Left);
});
AddUntilStep("Check ruleset changed to mania", () => Ruleset.Value.ID == 3);
AddAssert("Check first item in group selected", () => Beatmap.Value.BeatmapInfo == groupIcon.Items.First().Beatmap);
}
private int getBeatmapIndex(BeatmapSetInfo set, BeatmapInfo info) => set.Beatmaps.FindIndex(b => b == info);
@ -607,7 +718,7 @@ namespace osu.Game.Tests.Visual.SongSelect
private void addRulesetImportStep(int id) => AddStep($"import test map for ruleset {id}", () => importForRuleset(id));
private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait();
private void importForRuleset(int id) => manager.Import(createTestBeatmapSet(rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())).Wait();
private static int importId;
@ -633,20 +744,22 @@ namespace osu.Game.Tests.Visual.SongSelect
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
for (int i = 0; i < 100; i += 10)
manager.Import(createTestBeatmapSet(i, usableRulesets)).Wait();
manager.Import(createTestBeatmapSet(usableRulesets)).Wait();
});
}
private BeatmapSetInfo createTestBeatmapSet(int setId, RulesetInfo[] rulesets)
private BeatmapSetInfo createTestBeatmapSet(RulesetInfo[] rulesets, int countPerRuleset = 6)
{
int j = 0;
RulesetInfo getRuleset() => rulesets[j++ % rulesets.Length];
int setId = getImportId();
var beatmaps = new List<BeatmapInfo>();
for (int i = 0; i < 6; i++)
for (int i = 0; i < countPerRuleset; i++)
{
int beatmapId = setId * 10 + i;
int beatmapId = setId * 1000 + i;
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);

View File

@ -15,7 +15,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneCommentEditor : ManualInputManagerTestScene
public class TestSceneCommentEditor : OsuManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{

View File

@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneCursors : ManualInputManagerTestScene
public class TestSceneCursors : OsuManualInputManagerTestScene
{
private readonly MenuCursorContainer menuCursorContainer;
private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6];

View File

@ -27,7 +27,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneDeleteLocalScore : ManualInputManagerTestScene
public class TestSceneDeleteLocalScore : OsuManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{

View File

@ -8,7 +8,7 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Home.Friends;
using osu.Game.Overlays.Dashboard.Friends;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.UserInterface
@ -17,20 +17,20 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(FriendsOnlineStatusControl),
typeof(FriendOnlineStreamControl),
typeof(FriendsOnlineStatusItem),
typeof(OverlayStreamControl<>),
typeof(OverlayStreamItem<>),
typeof(FriendsBundle)
typeof(FriendStream)
};
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private FriendsOnlineStatusControl control;
private FriendOnlineStreamControl control;
[SetUp]
public void SetUp() => Schedule(() => Child = control = new FriendsOnlineStatusControl
public void SetUp() => Schedule(() => Child = control = new FriendOnlineStreamControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -55,9 +55,9 @@ namespace osu.Game.Tests.Visual.UserInterface
}
}));
AddAssert("3 users", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.All)?.Count == 3);
AddAssert("1 online user", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.Online)?.Count == 1);
AddAssert("2 offline users", () => control.Items.FirstOrDefault(item => item.Status == FriendsOnlineStatus.Offline)?.Count == 2);
AddAssert("3 users", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.All)?.Count == 3);
AddAssert("1 online user", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.Online)?.Count == 1);
AddAssert("2 offline users", () => control.Items.FirstOrDefault(item => item.Status == OnlineStatus.Offline)?.Count == 2);
}
}
}

View File

@ -12,7 +12,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneOsuHoverContainer : ManualInputManagerTestScene
public class TestSceneOsuHoverContainer : OsuManualInputManagerTestScene
{
private OsuHoverTestContainer hoverContainer;
private Box colourContainer;

View File

@ -12,7 +12,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneStatefulMenuItem : ManualInputManagerTestScene
public class TestSceneStatefulMenuItem : OsuManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{

View File

@ -9,7 +9,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneSwitchButton : ManualInputManagerTestScene
public class TestSceneSwitchButton : OsuManualInputManagerTestScene
{
private SwitchButton switchButton;

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays;
using osu.Game.Overlays.Home.Friends;
using osu.Game.Overlays.Dashboard.Friends;
using osuTK;
namespace osu.Game.Tests.Visual.UserInterface

View File

@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using osu.Game.IO;
@ -51,8 +50,6 @@ namespace osu.Game.Tests
protected override Texture GetBackground() => null;
protected override VideoSprite GetVideo() => null;
protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile));
protected override Track GetTrack() => trackStore.Get(firstAudioFile);

View File

@ -0,0 +1,40 @@
// 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 osu.Framework.Graphics;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
namespace osu.Game.Tournament.Tests.Components
{
public class TestSceneRoundDisplay : TournamentTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(DrawableTournamentHeaderText),
typeof(DrawableTournamentHeaderLogo),
};
public TestSceneRoundDisplay()
{
Children = new Drawable[]
{
new RoundDisplay(new TournamentMatch
{
Round =
{
Value = new TournamentRound
{
Name = { Value = "Test Round" }
}
}
})
{
Margin = new MarginPadding(20)
}
};
}
}
}

Some files were not shown because too many files have changed in this diff Show More