mirror of
https://github.com/ppy/osu.git
synced 2026-05-17 16:53:15 +08:00
Compare commits
690 Commits
2021.808.0
...
2021.820.0
@@ -4,7 +4,7 @@
|
||||
|
||||
# osu!
|
||||
|
||||
[](https://ci.appveyor.com/project/peppy/osu)
|
||||
[](https://github.com/ppy/osu/actions/workflows/ci.yml)
|
||||
[](https://github.com/ppy/osu/releases/latest)
|
||||
[](https://www.codefactor.io/repository/github/ppy/osu)
|
||||
[](https://discord.gg/ppy)
|
||||
|
||||
+2
-2
@@ -51,8 +51,8 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.808.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.807.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.820.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.819.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
|
||||
@@ -5,23 +5,23 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Desktop.Windows
|
||||
{
|
||||
public class GameplayWinKeyBlocker : Component
|
||||
{
|
||||
private Bindable<bool> disableWinKey;
|
||||
private Bindable<bool> localUserPlaying;
|
||||
private IBindable<bool> localUserPlaying;
|
||||
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGame game, OsuConfigManager config)
|
||||
private void load(ILocalUserPlayInfo localUserInfo, OsuConfigManager config)
|
||||
{
|
||||
localUserPlaying = game.LocalUserPlaying.GetBoundCopy();
|
||||
localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy();
|
||||
localUserPlaying.BindValueChanged(_ => updateBlocking());
|
||||
|
||||
disableWinKey = config.GetBindable<bool>(OsuSetting.GameplayDisableWinKey);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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 BenchmarkDotNet.Attributes;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Benchmarks
|
||||
{
|
||||
public class BenchmarkMod : BenchmarkTest
|
||||
{
|
||||
private OsuModDoubleTime mod;
|
||||
|
||||
[Params(1, 10, 100)]
|
||||
public int Times { get; set; }
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
mod = new OsuModDoubleTime();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int ModHashCode()
|
||||
{
|
||||
var hashCode = new HashCode();
|
||||
|
||||
for (int i = 0; i < Times; i++)
|
||||
hashCode.Add(mod);
|
||||
|
||||
return hashCode.ToHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Mods;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests.Mods
|
||||
{
|
||||
[TestFixture]
|
||||
public class CatchModMirrorTest
|
||||
{
|
||||
[Test]
|
||||
public void TestModMirror()
|
||||
{
|
||||
IBeatmap original = createBeatmap(false);
|
||||
IBeatmap mirrored = createBeatmap(true);
|
||||
|
||||
assertEffectivePositionsMirrored(original, mirrored);
|
||||
}
|
||||
|
||||
private static IBeatmap createBeatmap(bool withMirrorMod)
|
||||
{
|
||||
var beatmap = createRawBeatmap();
|
||||
var mirrorMod = new CatchModMirror();
|
||||
|
||||
var beatmapProcessor = new CatchBeatmapProcessor(beatmap);
|
||||
beatmapProcessor.PreProcess();
|
||||
|
||||
foreach (var hitObject in beatmap.HitObjects)
|
||||
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
beatmapProcessor.PostProcess();
|
||||
|
||||
if (withMirrorMod)
|
||||
mirrorMod.ApplyToBeatmap(beatmap);
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
private static IBeatmap createRawBeatmap() => new Beatmap
|
||||
{
|
||||
HitObjects = new List<HitObject>
|
||||
{
|
||||
new Fruit
|
||||
{
|
||||
OriginalX = 150,
|
||||
StartTime = 0
|
||||
},
|
||||
new Fruit
|
||||
{
|
||||
OriginalX = 450,
|
||||
StartTime = 500
|
||||
},
|
||||
new JuiceStream
|
||||
{
|
||||
OriginalX = 250,
|
||||
Path = new SliderPath
|
||||
{
|
||||
ControlPoints =
|
||||
{
|
||||
new PathControlPoint(new Vector2(-100, 1)),
|
||||
new PathControlPoint(new Vector2(0, 2)),
|
||||
new PathControlPoint(new Vector2(100, 3)),
|
||||
new PathControlPoint(new Vector2(0, 4))
|
||||
}
|
||||
},
|
||||
StartTime = 1000,
|
||||
},
|
||||
new BananaShower
|
||||
{
|
||||
StartTime = 5000,
|
||||
Duration = 5000
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static void assertEffectivePositionsMirrored(IBeatmap original, IBeatmap mirrored)
|
||||
{
|
||||
if (original.HitObjects.Count != mirrored.HitObjects.Count)
|
||||
Assert.Fail($"Top-level object count mismatch (original: {original.HitObjects.Count}, mirrored: {mirrored.HitObjects.Count})");
|
||||
|
||||
for (int i = 0; i < original.HitObjects.Count; ++i)
|
||||
{
|
||||
var originalObject = (CatchHitObject)original.HitObjects[i];
|
||||
var mirroredObject = (CatchHitObject)mirrored.HitObjects[i];
|
||||
|
||||
// banana showers themselves are exempt, as we only really care about their nested bananas' positions.
|
||||
if (!effectivePositionMirrored(originalObject, mirroredObject) && !(originalObject is BananaShower))
|
||||
Assert.Fail($"{originalObject.GetType().Name} at time {originalObject.StartTime} is not mirrored ({printEffectivePositions(originalObject, mirroredObject)})");
|
||||
|
||||
if (originalObject.NestedHitObjects.Count != mirroredObject.NestedHitObjects.Count)
|
||||
Assert.Fail($"{originalObject.GetType().Name} nested object count mismatch (original: {originalObject.NestedHitObjects.Count}, mirrored: {mirroredObject.NestedHitObjects.Count})");
|
||||
|
||||
for (int j = 0; j < originalObject.NestedHitObjects.Count; ++j)
|
||||
{
|
||||
var originalNested = (CatchHitObject)originalObject.NestedHitObjects[j];
|
||||
var mirroredNested = (CatchHitObject)mirroredObject.NestedHitObjects[j];
|
||||
|
||||
if (!effectivePositionMirrored(originalNested, mirroredNested))
|
||||
Assert.Fail($"{originalObject.GetType().Name}'s nested {originalNested.GetType().Name} at time {originalObject.StartTime} is not mirrored ({printEffectivePositions(originalNested, mirroredNested)})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string printEffectivePositions(CatchHitObject original, CatchHitObject mirrored)
|
||||
=> $"original X: {original.EffectiveX}, mirrored X is: {mirrored.EffectiveX}, mirrored X should be: {CatchPlayfield.WIDTH - original.EffectiveX}";
|
||||
|
||||
private static bool effectivePositionMirrored(CatchHitObject original, CatchHitObject mirrored)
|
||||
=> Precision.AlmostEquals(original.EffectiveX, CatchPlayfield.WIDTH - mirrored.EffectiveX);
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
AddSliderStep<float>("circle size", 0, 8, 5, createCatcher);
|
||||
AddToggleStep("hyper dash", t => this.ChildrenOfType<TestCatcherArea>().ForEach(area => area.ToggleHyperDash(t)));
|
||||
AddToggleStep("toggle hit lighting", lighting => config.SetValue(OsuSetting.HitLighting, lighting));
|
||||
|
||||
AddStep("catch centered fruit", () => attemptCatch(new Fruit()));
|
||||
AddStep("catch many random fruit", () =>
|
||||
|
||||
@@ -117,6 +117,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
{
|
||||
new CatchModDifficultyAdjust(),
|
||||
new CatchModClassic(),
|
||||
new CatchModMirror(),
|
||||
};
|
||||
|
||||
case ModType.Automation:
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
Banana,
|
||||
Droplet,
|
||||
Catcher,
|
||||
CatchComboCounter
|
||||
CatchComboCounter,
|
||||
HitExplosion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
{
|
||||
public class Movement : StrainSkill
|
||||
public class Movement : StrainDecaySkill
|
||||
{
|
||||
private const float absolute_player_positioning_error = 16f;
|
||||
private const float normalized_hitobject_radius = 41.0f;
|
||||
|
||||
@@ -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.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModMirror : ModMirror, IApplicableToBeatmap
|
||||
{
|
||||
public override string Description => "Fruits are flipped horizontally.";
|
||||
|
||||
/// <remarks>
|
||||
/// <see cref="IApplicableToBeatmap"/> is used instead of <see cref="IApplicableToHitObject"/>,
|
||||
/// as <see cref="CatchBeatmapProcessor"/> applies offsets in <see cref="CatchBeatmapProcessor.PostProcess"/>.
|
||||
/// <see cref="IApplicableToBeatmap"/> runs after post-processing, while <see cref="IApplicableToHitObject"/> runs before it.
|
||||
/// </remarks>
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
foreach (var hitObject in beatmap.HitObjects)
|
||||
applyToHitObject(hitObject);
|
||||
}
|
||||
|
||||
private void applyToHitObject(HitObject hitObject)
|
||||
{
|
||||
var catchObject = (CatchHitObject)hitObject;
|
||||
|
||||
switch (catchObject)
|
||||
{
|
||||
case Fruit fruit:
|
||||
mirrorEffectiveX(fruit);
|
||||
break;
|
||||
|
||||
case JuiceStream juiceStream:
|
||||
mirrorEffectiveX(juiceStream);
|
||||
mirrorJuiceStreamPath(juiceStream);
|
||||
break;
|
||||
|
||||
case BananaShower bananaShower:
|
||||
mirrorBananaShower(bananaShower);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors the effective X position of <paramref name="catchObject"/> and its nested hit objects.
|
||||
/// </summary>
|
||||
private static void mirrorEffectiveX(CatchHitObject catchObject)
|
||||
{
|
||||
catchObject.OriginalX = CatchPlayfield.WIDTH - catchObject.OriginalX;
|
||||
catchObject.XOffset = -catchObject.XOffset;
|
||||
|
||||
foreach (var nested in catchObject.NestedHitObjects.Cast<CatchHitObject>())
|
||||
{
|
||||
nested.OriginalX = CatchPlayfield.WIDTH - nested.OriginalX;
|
||||
nested.XOffset = -nested.XOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors the path of the <paramref name="juiceStream"/>.
|
||||
/// </summary>
|
||||
private static void mirrorJuiceStreamPath(JuiceStream juiceStream)
|
||||
{
|
||||
var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position.Value, p.Type.Value)).ToArray();
|
||||
foreach (var point in controlPoints)
|
||||
point.Position.Value = new Vector2(-point.Position.Value.X, point.Position.Value.Y);
|
||||
|
||||
juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors X positions of all bananas in the <paramref name="bananaShower"/>.
|
||||
/// </summary>
|
||||
private static void mirrorBananaShower(BananaShower bananaShower)
|
||||
{
|
||||
foreach (var banana in bananaShower.NestedHitObjects.OfType<Banana>())
|
||||
banana.XOffset = CatchPlayfield.WIDTH - banana.XOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
{
|
||||
public class DefaultHitExplosion : CompositeDrawable, IHitExplosion
|
||||
{
|
||||
private CircularContainer largeFaint;
|
||||
private CircularContainer smallFaint;
|
||||
private CircularContainer directionalGlow1;
|
||||
private CircularContainer directionalGlow2;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Size = new Vector2(20);
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
|
||||
// scale roughly in-line with visual appearance of notes
|
||||
const float initial_height = 10;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
largeFaint = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
smallFaint = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
directionalGlow1 = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
directionalGlow2 = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void Animate(HitExplosionEntry entry)
|
||||
{
|
||||
X = entry.Position;
|
||||
Scale = new Vector2(entry.HitObject.Scale);
|
||||
setColour(entry.ObjectColour);
|
||||
|
||||
using (BeginAbsoluteSequence(entry.LifetimeStart))
|
||||
applyTransforms(entry.HitObject.RandomSeed);
|
||||
}
|
||||
|
||||
private void applyTransforms(int randomSeed)
|
||||
{
|
||||
const double duration = 400;
|
||||
|
||||
// we want our size to be very small so the glow dominates it.
|
||||
largeFaint.Size = new Vector2(0.8f);
|
||||
largeFaint
|
||||
.ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
|
||||
.FadeOut(duration * 2);
|
||||
|
||||
const float angle_variangle = 15; // should be less than 45
|
||||
directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 4);
|
||||
directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 5);
|
||||
|
||||
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out);
|
||||
}
|
||||
|
||||
private void setColour(Color4 objectColour)
|
||||
{
|
||||
const float roundness = 100;
|
||||
|
||||
largeFaint.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f),
|
||||
Roundness = 160,
|
||||
Radius = 200,
|
||||
};
|
||||
|
||||
smallFaint.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1),
|
||||
Roundness = 20,
|
||||
Radius = 50,
|
||||
};
|
||||
|
||||
directionalGlow1.EdgeEffect = directionalGlow2.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1),
|
||||
Roundness = roundness,
|
||||
Radius = 40,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,13 +70,11 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
|
||||
if (version < 2.3m)
|
||||
{
|
||||
if (GetTexture(@"fruit-ryuuta") != null ||
|
||||
GetTexture(@"fruit-ryuuta-0") != null)
|
||||
if (hasOldStyleCatcherSprite())
|
||||
return new LegacyCatcherOld();
|
||||
}
|
||||
|
||||
if (GetTexture(@"fruit-catcher-idle") != null ||
|
||||
GetTexture(@"fruit-catcher-idle-0") != null)
|
||||
if (hasNewStyleCatcherSprite())
|
||||
return new LegacyCatcherNew();
|
||||
|
||||
return null;
|
||||
@@ -86,12 +84,26 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
return new LegacyCatchComboCounter(Skin);
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.HitExplosion:
|
||||
if (hasOldStyleCatcherSprite() || hasNewStyleCatcherSprite())
|
||||
return new LegacyHitExplosion();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetDrawableComponent(component);
|
||||
}
|
||||
|
||||
private bool hasOldStyleCatcherSprite() =>
|
||||
GetTexture(@"fruit-ryuuta") != null
|
||||
|| GetTexture(@"fruit-ryuuta-0") != null;
|
||||
|
||||
private bool hasNewStyleCatcherSprite() =>
|
||||
GetTexture(@"fruit-catcher-idle") != null
|
||||
|| GetTexture(@"fruit-catcher-idle-0") != null;
|
||||
|
||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
switch (lookup)
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
public class LegacyHitExplosion : CompositeDrawable, IHitExplosion
|
||||
{
|
||||
[Resolved]
|
||||
private Catcher catcher { get; set; }
|
||||
|
||||
private const float catch_margin = (1 - Catcher.ALLOWED_CATCH_RANGE) / 2;
|
||||
|
||||
private readonly Sprite explosion1;
|
||||
private readonly Sprite explosion2;
|
||||
|
||||
public LegacyHitExplosion()
|
||||
{
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Scale = new Vector2(0.5f);
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
explosion1 = new Sprite
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Alpha = 0,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Rotation = -90
|
||||
},
|
||||
explosion2 = new Sprite
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Alpha = 0,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Rotation = -90
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(SkinManager skins)
|
||||
{
|
||||
var defaultLegacySkin = skins.DefaultLegacySkin;
|
||||
|
||||
// sprite names intentionally swapped to match stable member naming / ease of cross-referencing
|
||||
explosion1.Texture = defaultLegacySkin.GetTexture("scoreboard-explosion-2");
|
||||
explosion2.Texture = defaultLegacySkin.GetTexture("scoreboard-explosion-1");
|
||||
}
|
||||
|
||||
public void Animate(HitExplosionEntry entry)
|
||||
{
|
||||
Colour = entry.ObjectColour;
|
||||
|
||||
using (BeginAbsoluteSequence(entry.LifetimeStart))
|
||||
{
|
||||
float halfCatchWidth = catcher.CatchWidth / 2;
|
||||
float explosionOffset = Math.Clamp(entry.Position, -halfCatchWidth + catch_margin * 3, halfCatchWidth - catch_margin * 3);
|
||||
|
||||
if (!(entry.HitObject is Droplet))
|
||||
{
|
||||
float scale = Math.Clamp(entry.JudgementResult.ComboAtJudgement / 200f, 0.35f, 1.125f);
|
||||
|
||||
explosion1.Scale = new Vector2(1, 0.9f);
|
||||
explosion1.Position = new Vector2(explosionOffset, 0);
|
||||
|
||||
explosion1.FadeOutFromOne(300);
|
||||
explosion1.ScaleTo(new Vector2(16 * scale, 1.1f), 160, Easing.Out);
|
||||
}
|
||||
|
||||
explosion2.Scale = new Vector2(0.9f, 1);
|
||||
explosion2.Position = new Vector2(explosionOffset, 0);
|
||||
|
||||
explosion2.FadeOutFromOne(700);
|
||||
explosion2.ScaleTo(new Vector2(0.9f, 1.3f), 500, Easing.Out);
|
||||
|
||||
this.Delay(700).FadeOutFromOne();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
[Cached]
|
||||
public class Catcher : SkinReloadableDrawable
|
||||
{
|
||||
/// <summary>
|
||||
@@ -106,7 +107,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// <summary>
|
||||
/// Width of the area that can be used to attempt catches during gameplay.
|
||||
/// </summary>
|
||||
private readonly float catchWidth;
|
||||
public readonly float CatchWidth;
|
||||
|
||||
private readonly SkinnableCatcher body;
|
||||
|
||||
@@ -133,7 +134,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
if (difficulty != null)
|
||||
Scale = calculateScale(difficulty);
|
||||
|
||||
catchWidth = CalculateCatchWidth(Scale);
|
||||
CatchWidth = CalculateCatchWidth(Scale);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
@@ -193,7 +194,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
if (!(hitObject is PalpableCatchHitObject fruit))
|
||||
return false;
|
||||
|
||||
float halfCatchWidth = catchWidth * 0.5f;
|
||||
float halfCatchWidth = CatchWidth * 0.5f;
|
||||
return fruit.EffectiveX >= X - halfCatchWidth &&
|
||||
fruit.EffectiveX <= X + halfCatchWidth;
|
||||
}
|
||||
@@ -216,7 +217,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
placeCaughtObject(palpableObject, positionInStack);
|
||||
|
||||
if (hitLighting.Value)
|
||||
addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value);
|
||||
addLighting(result, drawableObject.AccentColour.Value, positionInStack.X);
|
||||
}
|
||||
|
||||
// droplet doesn't affect the catcher state
|
||||
@@ -365,8 +366,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
return position;
|
||||
}
|
||||
|
||||
private void addLighting(CatchHitObject hitObject, float x, Color4 colour) =>
|
||||
hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, x, hitObject.Scale, colour, hitObject.RandomSeed));
|
||||
private void addLighting(JudgementResult judgementResult, Color4 colour, float x) =>
|
||||
hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, judgementResult, colour, x));
|
||||
|
||||
private CaughtObject getCaughtObject(PalpableCatchHitObject source)
|
||||
{
|
||||
|
||||
@@ -1,129 +1,56 @@
|
||||
// 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.Effects;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||
using osu.Game.Rulesets.Objects.Pooling;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class HitExplosion : PoolableDrawableWithLifetime<HitExplosionEntry>
|
||||
{
|
||||
private readonly CircularContainer largeFaint;
|
||||
private readonly CircularContainer smallFaint;
|
||||
private readonly CircularContainer directionalGlow1;
|
||||
private readonly CircularContainer directionalGlow2;
|
||||
private readonly SkinnableDrawable skinnableExplosion;
|
||||
|
||||
public HitExplosion()
|
||||
{
|
||||
Size = new Vector2(20);
|
||||
Anchor = Anchor.TopCentre;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
|
||||
// scale roughly in-line with visual appearance of notes
|
||||
const float initial_height = 10;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
InternalChild = skinnableExplosion = new SkinnableDrawable(new CatchSkinComponent(CatchSkinComponents.HitExplosion), _ => new DefaultHitExplosion())
|
||||
{
|
||||
largeFaint = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
smallFaint = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
directionalGlow1 = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
directionalGlow2 = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
}
|
||||
CentreComponent = false,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnApply(HitExplosionEntry entry)
|
||||
{
|
||||
X = entry.Position;
|
||||
Scale = new Vector2(entry.Scale);
|
||||
setColour(entry.ObjectColour);
|
||||
|
||||
using (BeginAbsoluteSequence(entry.LifetimeStart))
|
||||
applyTransforms(entry.RNGSeed);
|
||||
base.OnApply(entry);
|
||||
if (IsLoaded)
|
||||
apply(entry);
|
||||
}
|
||||
|
||||
private void applyTransforms(int randomSeed)
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
apply(Entry);
|
||||
}
|
||||
|
||||
private void apply(HitExplosionEntry? entry)
|
||||
{
|
||||
if (entry == null)
|
||||
return;
|
||||
|
||||
ApplyTransformsAt(double.MinValue, true);
|
||||
ClearTransforms(true);
|
||||
|
||||
const double duration = 400;
|
||||
|
||||
// we want our size to be very small so the glow dominates it.
|
||||
largeFaint.Size = new Vector2(0.8f);
|
||||
largeFaint
|
||||
.ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
|
||||
.FadeOut(duration * 2);
|
||||
|
||||
const float angle_variangle = 15; // should be less than 45
|
||||
directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 4);
|
||||
directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 5);
|
||||
|
||||
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out).Expire();
|
||||
}
|
||||
|
||||
private void setColour(Color4 objectColour)
|
||||
{
|
||||
const float roundness = 100;
|
||||
|
||||
largeFaint.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f),
|
||||
Roundness = 160,
|
||||
Radius = 200,
|
||||
};
|
||||
|
||||
smallFaint.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1),
|
||||
Roundness = 20,
|
||||
Radius = 50,
|
||||
};
|
||||
|
||||
directionalGlow1.EdgeEffect = directionalGlow2.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1),
|
||||
Roundness = roundness,
|
||||
Radius = 40,
|
||||
};
|
||||
(skinnableExplosion.Drawable as IHitExplosion)?.Animate(entry);
|
||||
LifetimeEnd = skinnableExplosion.Drawable.LatestTransformEndTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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.Pooling;
|
||||
using osu.Game.Rulesets.Objects.Pooling;
|
||||
|
||||
@@ -14,6 +15,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
public HitExplosionContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
AddInternal(pool = new DrawablePool<HitExplosion>(10));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,24 +2,42 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics.Performance;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osuTK.Graphics;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class HitExplosionEntry : LifetimeEntry
|
||||
{
|
||||
public readonly float Position;
|
||||
public readonly float Scale;
|
||||
public readonly Color4 ObjectColour;
|
||||
public readonly int RNGSeed;
|
||||
/// <summary>
|
||||
/// The judgement result that triggered this explosion.
|
||||
/// </summary>
|
||||
public JudgementResult JudgementResult { get; }
|
||||
|
||||
public HitExplosionEntry(double startTime, float position, float scale, Color4 objectColour, int rngSeed)
|
||||
/// <summary>
|
||||
/// The hitobject which triggered this explosion.
|
||||
/// </summary>
|
||||
public CatchHitObject HitObject => (CatchHitObject)JudgementResult.HitObject;
|
||||
|
||||
/// <summary>
|
||||
/// The accent colour of the object caught.
|
||||
/// </summary>
|
||||
public Color4 ObjectColour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The position at which the object was caught.
|
||||
/// </summary>
|
||||
public float Position { get; }
|
||||
|
||||
public HitExplosionEntry(double startTime, JudgementResult judgementResult, Color4 objectColour, float position)
|
||||
{
|
||||
LifetimeStart = startTime;
|
||||
Position = position;
|
||||
Scale = scale;
|
||||
JudgementResult = judgementResult;
|
||||
ObjectColour = objectColour;
|
||||
RNGSeed = rngSeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Common interface for all hit explosion skinnables.
|
||||
/// </summary>
|
||||
public interface IHitExplosion
|
||||
{
|
||||
/// <summary>
|
||||
/// Begins animating this <see cref="IHitExplosion"/>.
|
||||
/// </summary>
|
||||
void Animate(HitExplosionEntry entry);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
{
|
||||
public class Strain : StrainSkill
|
||||
public class Strain : StrainDecaySkill
|
||||
{
|
||||
private const double individual_decay_base = 0.125;
|
||||
private const double overall_decay_base = 0.30;
|
||||
@@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
return individualStrain + overallStrain - CurrentStrain;
|
||||
}
|
||||
|
||||
protected override double GetPeakStrain(double offset)
|
||||
protected override double CalculateInitialStrain(double offset)
|
||||
=> applyDecay(individualStrain, offset - Previous[0].StartTime, individual_decay_base)
|
||||
+ applyDecay(overallStrain, offset - Previous[0].StartTime, overall_decay_base);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ using osu.Framework.Utils;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
||||
{
|
||||
public abstract class OsuStrainSkill : StrainSkill
|
||||
public abstract class OsuStrainSkill : StrainDecaySkill
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of sections with the highest strains, which the peak strain reductions will apply to.
|
||||
|
||||
@@ -41,6 +41,11 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
protected override GameplayCursorContainer CreateCursor() => null;
|
||||
|
||||
public OsuEditorPlayfield()
|
||||
{
|
||||
HitPolicy = new AnyOrderHitPolicy();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
@@ -59,11 +64,14 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
if (hitObject is DrawableHitCircle circle)
|
||||
{
|
||||
circle.ApproachCircle
|
||||
.FadeOutFromOne(EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION * 4)
|
||||
.Expire();
|
||||
using (circle.BeginAbsoluteSequence(circle.HitStateUpdateTime))
|
||||
{
|
||||
circle.ApproachCircle
|
||||
.FadeOutFromOne(EDITOR_HIT_OBJECT_FADE_OUT_EXTENSION * 4)
|
||||
.Expire();
|
||||
|
||||
circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint);
|
||||
circle.ApproachCircle.ScaleTo(1.1f, 300, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
if (hitObject is IHasMainCirclePiece mainPieceContainer)
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
@@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||
{
|
||||
drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield.HitObjectContainer, drawableRuleset.Beatmap));
|
||||
drawableRuleset.Overlays.Add(blinds = new DrawableOsuBlinds(drawableRuleset.Playfield, drawableRuleset.Beatmap));
|
||||
}
|
||||
|
||||
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
||||
@@ -128,8 +129,21 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
float start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X;
|
||||
float end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X;
|
||||
float start, end;
|
||||
|
||||
if (Precision.AlmostEquals(restrictTo.Rotation, 0))
|
||||
{
|
||||
start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X;
|
||||
end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X;
|
||||
}
|
||||
else
|
||||
{
|
||||
float center = restrictTo.ToSpaceOfOtherDrawable(restrictTo.OriginPosition, Parent).X;
|
||||
float halfDiagonal = (restrictTo.DrawSize / 2).LengthFast;
|
||||
|
||||
start = center - halfDiagonal;
|
||||
end = center + halfDiagonal;
|
||||
}
|
||||
|
||||
float rawWidth = end - start;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using osu.Framework.Input.Events;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
@@ -21,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
private double lastTrailTime;
|
||||
private IBindable<float> cursorSize;
|
||||
|
||||
private Vector2? currentPosition;
|
||||
|
||||
public LegacyCursorTrail(ISkin skin)
|
||||
{
|
||||
this.skin = skin;
|
||||
@@ -54,22 +57,35 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
}
|
||||
|
||||
protected override double FadeDuration => disjointTrail ? 150 : 500;
|
||||
protected override float FadeExponent => 1;
|
||||
|
||||
protected override bool InterpolateMovements => !disjointTrail;
|
||||
|
||||
protected override float IntervalMultiplier => 1 / Math.Max(cursorSize.Value, 1);
|
||||
protected override bool AvoidDrawingNearCursor => !disjointTrail;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!disjointTrail || !currentPosition.HasValue)
|
||||
return;
|
||||
|
||||
if (Time.Current - lastTrailTime >= disjoint_trail_time_separation)
|
||||
{
|
||||
lastTrailTime = Time.Current;
|
||||
AddTrail(currentPosition.Value);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
if (!disjointTrail)
|
||||
return base.OnMouseMove(e);
|
||||
|
||||
if (Time.Current - lastTrailTime >= disjoint_trail_time_separation)
|
||||
{
|
||||
lastTrailTime = Time.Current;
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
currentPosition = e.ScreenSpaceMousePosition;
|
||||
|
||||
// Intentionally block the base call as we're adding the trails ourselves.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Utils;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
// Roughly matches osu!stable's slider border portions.
|
||||
=> base.CalculatedBorderPortion * 0.77f;
|
||||
|
||||
public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, base.AccentColour.A * 0.70f);
|
||||
public new Color4 AccentColour => new Color4(base.AccentColour.R, base.AccentColour.G, base.AccentColour.B, 0.7f);
|
||||
|
||||
protected override Color4 ColourAt(float position)
|
||||
{
|
||||
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
Color4 outerColour = AccentColour.Darken(0.1f);
|
||||
Color4 innerColour = lighten(AccentColour, 0.5f);
|
||||
|
||||
return Interpolation.ValueAt(position / realGradientPortion, outerColour, innerColour, 0, 1);
|
||||
return LegacyUtils.InterpolateNonLinear(position / realGradientPortion, outerColour, innerColour, 0, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IHitPolicy"/> which allows hitobjects to be hit in any order.
|
||||
/// </summary>
|
||||
public class AnyOrderHitPolicy : IHitPolicy
|
||||
{
|
||||
public IHitObjectContainer HitObjectContainer { get; set; }
|
||||
|
||||
public bool IsHittable(DrawableHitObject hitObject, double time) => true;
|
||||
|
||||
public void HandleHit(DrawableHitObject hitObject)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,11 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
{
|
||||
private const int max_sprites = 2048;
|
||||
|
||||
/// <summary>
|
||||
/// An exponentiating factor to ease the trail fade.
|
||||
/// </summary>
|
||||
protected virtual float FadeExponent => 1.7f;
|
||||
|
||||
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
||||
private int currentIndex;
|
||||
private IShader shader;
|
||||
@@ -133,6 +138,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
protected virtual bool InterpolateMovements => true;
|
||||
|
||||
protected virtual float IntervalMultiplier => 1.0f;
|
||||
protected virtual bool AvoidDrawingNearCursor => false;
|
||||
|
||||
private Vector2? lastPosition;
|
||||
private readonly InputResampler resampler = new InputResampler();
|
||||
@@ -141,43 +147,45 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
Vector2 pos = e.ScreenSpaceMousePosition;
|
||||
AddTrail(e.ScreenSpaceMousePosition);
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
if (lastPosition == null)
|
||||
protected void AddTrail(Vector2 position)
|
||||
{
|
||||
if (InterpolateMovements)
|
||||
{
|
||||
lastPosition = pos;
|
||||
resampler.AddPosition(lastPosition.Value);
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
foreach (Vector2 pos2 in resampler.AddPosition(pos))
|
||||
{
|
||||
Trace.Assert(lastPosition.HasValue);
|
||||
|
||||
if (InterpolateMovements)
|
||||
if (!lastPosition.HasValue)
|
||||
{
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
lastPosition = position;
|
||||
resampler.AddPosition(lastPosition.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Vector2 pos2 in resampler.AddPosition(position))
|
||||
{
|
||||
Trace.Assert(lastPosition.HasValue);
|
||||
|
||||
Vector2 pos1 = lastPosition.Value;
|
||||
Vector2 diff = pos2 - pos1;
|
||||
float distance = diff.Length;
|
||||
Vector2 direction = diff / distance;
|
||||
|
||||
float interval = partSize.X / 2.5f * IntervalMultiplier;
|
||||
float stopAt = distance - (AvoidDrawingNearCursor ? interval : 0);
|
||||
|
||||
for (float d = interval; d < distance; d += interval)
|
||||
for (float d = interval; d < stopAt; d += interval)
|
||||
{
|
||||
lastPosition = pos1 + direction * d;
|
||||
addPart(lastPosition.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lastPosition = pos2;
|
||||
addPart(lastPosition.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return base.OnMouseMove(e);
|
||||
else
|
||||
{
|
||||
lastPosition = position;
|
||||
addPart(lastPosition.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void addPart(Vector2 screenSpacePosition)
|
||||
@@ -206,10 +214,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
private Texture texture;
|
||||
|
||||
private float time;
|
||||
private float fadeExponent;
|
||||
|
||||
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
||||
private Vector2 size;
|
||||
|
||||
private Vector2 originPosition;
|
||||
|
||||
private readonly QuadBatch<TexturedTrailVertex> vertexBatch = new QuadBatch<TexturedTrailVertex>(max_sprites, 1);
|
||||
@@ -227,6 +235,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
texture = Source.texture;
|
||||
size = Source.partSize;
|
||||
time = Source.time;
|
||||
fadeExponent = Source.FadeExponent;
|
||||
|
||||
originPosition = Vector2.Zero;
|
||||
|
||||
@@ -249,6 +258,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
||||
|
||||
shader.Bind();
|
||||
shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time);
|
||||
shader.GetUniform<float>("g_FadeExponent").UpdateValue(ref fadeExponent);
|
||||
|
||||
texture.TextureGL.Bind();
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
/// <summary>
|
||||
/// Calculates the colour coefficient of taiko difficulty.
|
||||
/// </summary>
|
||||
public class Colour : StrainSkill
|
||||
public class Colour : StrainDecaySkill
|
||||
{
|
||||
protected override double SkillMultiplier => 1;
|
||||
protected override double StrainDecayBase => 0.4;
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
/// <summary>
|
||||
/// Calculates the rhythm coefficient of taiko difficulty.
|
||||
/// </summary>
|
||||
public class Rhythm : StrainSkill
|
||||
public class Rhythm : StrainDecaySkill
|
||||
{
|
||||
protected override double SkillMultiplier => 10;
|
||||
protected override double StrainDecayBase => 0;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||
/// <remarks>
|
||||
/// The reference play style chosen uses two hands, with full alternating (the hand changes after every hit).
|
||||
/// </remarks>
|
||||
public class Stamina : StrainSkill
|
||||
public class Stamina : StrainDecaySkill
|
||||
{
|
||||
protected override double SkillMultiplier => 1;
|
||||
protected override double StrainDecayBase => 0.4;
|
||||
|
||||
@@ -1,56 +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 NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
[TestFixture]
|
||||
public class BeatmapDifficultyCacheTest
|
||||
{
|
||||
[Test]
|
||||
public void TestKeyEqualsWithDifferentModInstances()
|
||||
{
|
||||
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
|
||||
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
|
||||
|
||||
Assert.That(key1, Is.EqualTo(key2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestKeyEqualsWithDifferentModOrder()
|
||||
{
|
||||
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
|
||||
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
|
||||
|
||||
Assert.That(key1, Is.EqualTo(key2));
|
||||
}
|
||||
|
||||
[TestCase(1.3, DifficultyRating.Easy)]
|
||||
[TestCase(1.993, DifficultyRating.Easy)]
|
||||
[TestCase(1.998, DifficultyRating.Normal)]
|
||||
[TestCase(2.4, DifficultyRating.Normal)]
|
||||
[TestCase(2.693, DifficultyRating.Normal)]
|
||||
[TestCase(2.698, DifficultyRating.Hard)]
|
||||
[TestCase(3.5, DifficultyRating.Hard)]
|
||||
[TestCase(3.993, DifficultyRating.Hard)]
|
||||
[TestCase(3.997, DifficultyRating.Insane)]
|
||||
[TestCase(5.0, DifficultyRating.Insane)]
|
||||
[TestCase(5.292, DifficultyRating.Insane)]
|
||||
[TestCase(5.297, DifficultyRating.Expert)]
|
||||
[TestCase(6.2, DifficultyRating.Expert)]
|
||||
[TestCase(6.493, DifficultyRating.Expert)]
|
||||
[TestCase(6.498, DifficultyRating.ExpertPlus)]
|
||||
[TestCase(8.3, DifficultyRating.ExpertPlus)]
|
||||
public void TestDifficultyRatingMapping(double starRating, DifficultyRating expectedBracket)
|
||||
{
|
||||
var actualBracket = BeatmapDifficultyCache.GetDifficultyRating(starRating);
|
||||
|
||||
Assert.AreEqual(expectedBracket, actualBracket);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,7 +169,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
|
||||
protected override Track GetBeatmapTrack() => throw new NotImplementedException();
|
||||
|
||||
protected override ISkin GetSkin() => throw new NotImplementedException();
|
||||
protected internal override ISkin GetSkin() => throw new NotImplementedException();
|
||||
|
||||
public override Stream GetStream(string storagePath) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
// 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.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneBeatmapDifficultyCache : OsuTestScene
|
||||
{
|
||||
public const double BASE_STARS = 5.55;
|
||||
|
||||
private BeatmapSetInfo importedSet;
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
|
||||
private TestBeatmapDifficultyCache difficultyCache;
|
||||
|
||||
private IBindable<StarDifficulty?> starDifficultyBindable;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase osu)
|
||||
{
|
||||
importedSet = ImportBeatmapTest.LoadQuickOszIntoOsu(osu).Result;
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("setup difficulty cache", () =>
|
||||
{
|
||||
SelectedMods.Value = Array.Empty<Mod>();
|
||||
|
||||
Child = difficultyCache = new TestBeatmapDifficultyCache();
|
||||
|
||||
starDifficultyBindable = difficultyCache.GetBindableDifficulty(importedSet.Beatmaps.First());
|
||||
});
|
||||
|
||||
AddUntilStep($"star difficulty -> {BASE_STARS}", () => starDifficultyBindable.Value?.Stars == BASE_STARS);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStarDifficultyChangesOnModSettings()
|
||||
{
|
||||
OsuModDoubleTime dt = null;
|
||||
|
||||
AddStep("change selected mod to DT", () => SelectedMods.Value = new[] { dt = new OsuModDoubleTime { SpeedChange = { Value = 1.5 } } });
|
||||
AddUntilStep($"star difficulty -> {BASE_STARS + 1.5}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.5);
|
||||
|
||||
AddStep("change DT speed to 1.25", () => dt.SpeedChange.Value = 1.25);
|
||||
AddUntilStep($"star difficulty -> {BASE_STARS + 1.25}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.25);
|
||||
|
||||
AddStep("change selected mod to NC", () => SelectedMods.Value = new[] { new OsuModNightcore { SpeedChange = { Value = 1.75 } } });
|
||||
AddUntilStep($"star difficulty -> {BASE_STARS + 1.75}", () => starDifficultyBindable.Value?.Stars == BASE_STARS + 1.75);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestKeyEqualsWithDifferentModInstances()
|
||||
{
|
||||
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
|
||||
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
|
||||
|
||||
Assert.That(key1, Is.EqualTo(key2));
|
||||
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestKeyEqualsWithDifferentModOrder()
|
||||
{
|
||||
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
|
||||
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
|
||||
|
||||
Assert.That(key1, Is.EqualTo(key2));
|
||||
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestKeyDoesntEqualWithDifferentModSettings()
|
||||
{
|
||||
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } });
|
||||
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.9 } } });
|
||||
|
||||
Assert.That(key1, Is.Not.EqualTo(key2));
|
||||
Assert.That(key1.GetHashCode(), Is.Not.EqualTo(key2.GetHashCode()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestKeyEqualWithMatchingModSettings()
|
||||
{
|
||||
var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
|
||||
var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(new BeatmapInfo { ID = 1234 }, new RulesetInfo { ID = 0 }, new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 1.25 } } });
|
||||
|
||||
Assert.That(key1, Is.EqualTo(key2));
|
||||
Assert.That(key1.GetHashCode(), Is.EqualTo(key2.GetHashCode()));
|
||||
}
|
||||
|
||||
[TestCase(1.3, DifficultyRating.Easy)]
|
||||
[TestCase(1.993, DifficultyRating.Easy)]
|
||||
[TestCase(1.998, DifficultyRating.Normal)]
|
||||
[TestCase(2.4, DifficultyRating.Normal)]
|
||||
[TestCase(2.693, DifficultyRating.Normal)]
|
||||
[TestCase(2.698, DifficultyRating.Hard)]
|
||||
[TestCase(3.5, DifficultyRating.Hard)]
|
||||
[TestCase(3.993, DifficultyRating.Hard)]
|
||||
[TestCase(3.997, DifficultyRating.Insane)]
|
||||
[TestCase(5.0, DifficultyRating.Insane)]
|
||||
[TestCase(5.292, DifficultyRating.Insane)]
|
||||
[TestCase(5.297, DifficultyRating.Expert)]
|
||||
[TestCase(6.2, DifficultyRating.Expert)]
|
||||
[TestCase(6.493, DifficultyRating.Expert)]
|
||||
[TestCase(6.498, DifficultyRating.ExpertPlus)]
|
||||
[TestCase(8.3, DifficultyRating.ExpertPlus)]
|
||||
public void TestDifficultyRatingMapping(double starRating, DifficultyRating expectedBracket)
|
||||
{
|
||||
var actualBracket = BeatmapDifficultyCache.GetDifficultyRating(starRating);
|
||||
|
||||
Assert.AreEqual(expectedBracket, actualBracket);
|
||||
}
|
||||
|
||||
private class TestBeatmapDifficultyCache : BeatmapDifficultyCache
|
||||
{
|
||||
protected override Task<StarDifficulty> ComputeValueAsync(DifficultyCacheLookup lookup, CancellationToken token = default)
|
||||
{
|
||||
var rateAdjust = lookup.OrderedMods.OfType<ModRateAdjust>().SingleOrDefault();
|
||||
if (rateAdjust != null)
|
||||
return Task.FromResult(new StarDifficulty(BASE_STARS + rateAdjust.SpeedChange.Value, 0));
|
||||
|
||||
return Task.FromResult(new StarDifficulty(BASE_STARS, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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.Allocation;
|
||||
@@ -19,6 +20,7 @@ namespace osu.Game.Tests.Chat
|
||||
{
|
||||
private ChannelManager channelManager;
|
||||
private int currentMessageId;
|
||||
private List<Message> sentMessages;
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
@@ -34,6 +36,7 @@ namespace osu.Game.Tests.Chat
|
||||
AddStep("register request handling", () =>
|
||||
{
|
||||
currentMessageId = 0;
|
||||
sentMessages = new List<Message>();
|
||||
|
||||
((DummyAPIAccess)API).HandleRequest = req =>
|
||||
{
|
||||
@@ -44,16 +47,11 @@ namespace osu.Game.Tests.Chat
|
||||
return true;
|
||||
|
||||
case PostMessageRequest postMessage:
|
||||
postMessage.TriggerSuccess(new Message(++currentMessageId)
|
||||
{
|
||||
IsAction = postMessage.Message.IsAction,
|
||||
ChannelId = postMessage.Message.ChannelId,
|
||||
Content = postMessage.Message.Content,
|
||||
Links = postMessage.Message.Links,
|
||||
Timestamp = postMessage.Message.Timestamp,
|
||||
Sender = postMessage.Message.Sender
|
||||
});
|
||||
handlePostMessageRequest(postMessage);
|
||||
return true;
|
||||
|
||||
case MarkChannelAsReadRequest markRead:
|
||||
handleMarkChannelAsReadRequest(markRead);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -83,12 +81,65 @@ namespace osu.Game.Tests.Chat
|
||||
AddAssert("/np command received by channel 2", () => channel2.Messages.Last().Content.Contains("is listening to"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMarkAsReadIgnoringLocalMessages()
|
||||
{
|
||||
Channel channel = null;
|
||||
|
||||
AddStep("join channel and select it", () =>
|
||||
{
|
||||
channelManager.JoinChannel(channel = createChannel(1, ChannelType.Public));
|
||||
channelManager.CurrentChannel.Value = channel;
|
||||
});
|
||||
|
||||
AddStep("post message", () => channelManager.PostMessage("Something interesting"));
|
||||
|
||||
AddStep("post /help command", () => channelManager.PostCommand("help", channel));
|
||||
AddStep("post /me command with no action", () => channelManager.PostCommand("me", channel));
|
||||
AddStep("post /join command with no channel", () => channelManager.PostCommand("join", channel));
|
||||
AddStep("post /join command with non-existent channel", () => channelManager.PostCommand("join i-dont-exist", channel));
|
||||
AddStep("post non-existent command", () => channelManager.PostCommand("non-existent-cmd arg", channel));
|
||||
|
||||
AddStep("mark channel as read", () => channelManager.MarkChannelAsRead(channel));
|
||||
AddAssert("channel's last read ID is set to the latest message", () => channel.LastReadId == sentMessages.Last().Id);
|
||||
}
|
||||
|
||||
private void handlePostMessageRequest(PostMessageRequest request)
|
||||
{
|
||||
var message = new Message(++currentMessageId)
|
||||
{
|
||||
IsAction = request.Message.IsAction,
|
||||
ChannelId = request.Message.ChannelId,
|
||||
Content = request.Message.Content,
|
||||
Links = request.Message.Links,
|
||||
Timestamp = request.Message.Timestamp,
|
||||
Sender = request.Message.Sender
|
||||
};
|
||||
|
||||
sentMessages.Add(message);
|
||||
request.TriggerSuccess(message);
|
||||
}
|
||||
|
||||
private void handleMarkChannelAsReadRequest(MarkChannelAsReadRequest request)
|
||||
{
|
||||
// only accept messages that were sent through the API
|
||||
if (sentMessages.Contains(request.Message))
|
||||
{
|
||||
request.TriggerSuccess();
|
||||
}
|
||||
else
|
||||
{
|
||||
request.TriggerFailure(new APIException("unknown message!", null));
|
||||
}
|
||||
}
|
||||
|
||||
private Channel createChannel(int id, ChannelType type) => new Channel(new User())
|
||||
{
|
||||
Id = id,
|
||||
Name = $"Channel {id}",
|
||||
Topic = $"Topic of channel {id} with type {type}",
|
||||
Type = type,
|
||||
LastMessageId = 0,
|
||||
};
|
||||
|
||||
private class ChannelManagerContainer : CompositeDrawable
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Tests.Collections.IO
|
||||
@@ -127,7 +128,7 @@ namespace osu.Game.Tests.Collections.IO
|
||||
[Test]
|
||||
public async Task TestSaveAndReload()
|
||||
{
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
|
||||
using (HeadlessGameHost host = new TestRunHeadlessGameHost("TestSaveAndReload", bypassCleanup: true))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -148,7 +149,7 @@ namespace osu.Game.Tests.Collections.IO
|
||||
}
|
||||
}
|
||||
|
||||
using (HeadlessGameHost host = new HeadlessGameHost("TestSaveAndReload"))
|
||||
using (HeadlessGameHost host = new TestRunHeadlessGameHost("TestSaveAndReload"))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Gameplay
|
||||
@@ -121,6 +123,18 @@ namespace osu.Game.Tests.Gameplay
|
||||
AddAssert("Drawable lifetime is restored", () => dho.LifetimeStart == 666 && dho.LifetimeEnd == 999);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStateChangeBeforeLoadComplete()
|
||||
{
|
||||
TestDrawableHitObject dho = null;
|
||||
AddStep("Add DHO and apply result", () =>
|
||||
{
|
||||
Child = dho = new TestDrawableHitObject(new HitObject { StartTime = Time.Current });
|
||||
dho.MissForcefully();
|
||||
});
|
||||
AddAssert("DHO state is correct", () => dho.State.Value == ArmedState.Miss);
|
||||
}
|
||||
|
||||
private class TestDrawableHitObject : DrawableHitObject
|
||||
{
|
||||
public const double INITIAL_LIFETIME_OFFSET = 100;
|
||||
@@ -141,6 +155,19 @@ namespace osu.Game.Tests.Gameplay
|
||||
if (SetLifetimeStartOnApply)
|
||||
LifetimeStart = LIFETIME_ON_APPLY;
|
||||
}
|
||||
|
||||
public void MissForcefully() => ApplyResult(r => r.Type = HitResult.Miss);
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
{
|
||||
if (state != ArmedState.Miss)
|
||||
{
|
||||
base.UpdateHitStateTransforms(state);
|
||||
return;
|
||||
}
|
||||
|
||||
this.FadeOut(1000);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestLifetimeEntry : HitObjectLifetimeEntry
|
||||
|
||||
@@ -204,7 +204,7 @@ namespace osu.Game.Tests.Gameplay
|
||||
this.resources = resources;
|
||||
}
|
||||
|
||||
protected override ISkin GetSkin() => new TestSkin("test-sample", resources);
|
||||
protected internal override ISkin GetSkin() => new TestSkin("test-sample", resources);
|
||||
}
|
||||
|
||||
private class TestDrawableStoryboardSample : DrawableStoryboardSample
|
||||
|
||||
@@ -8,7 +8,7 @@ using osu.Framework.Input;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Input;
|
||||
using osu.Game.Tests.Visual.Navigation;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Input
|
||||
{
|
||||
|
||||
@@ -6,10 +6,10 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.IO;
|
||||
|
||||
@@ -278,7 +278,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
|
||||
private static string getDefaultLocationFor(string testTypeName)
|
||||
{
|
||||
string path = Path.Combine(RuntimeInfo.StartupDirectory, "headless", testTypeName);
|
||||
string path = Path.Combine(TestRunHeadlessGameHost.TemporaryTestDirectory, testTypeName);
|
||||
|
||||
if (Directory.Exists(path))
|
||||
Directory.Delete(path, true);
|
||||
@@ -288,7 +288,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
|
||||
private string prepareCustomPath(string suffix = "")
|
||||
{
|
||||
string path = Path.Combine(RuntimeInfo.StartupDirectory, $"custom-path{suffix}");
|
||||
string path = Path.Combine(TestRunHeadlessGameHost.TemporaryTestDirectory, $"custom-path{suffix}");
|
||||
|
||||
if (Directory.Exists(path))
|
||||
Directory.Delete(path, true);
|
||||
@@ -308,6 +308,19 @@ namespace osu.Game.Tests.NonVisual
|
||||
InitialStorage = new DesktopStorage(defaultStorageLocation, this);
|
||||
InitialStorage.DeleteDirectory(string.Empty);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
try
|
||||
{
|
||||
// the storage may have changed from the initial location.
|
||||
// this handles cleanup of the initial location.
|
||||
InitialStorage.DeleteDirectory(string.Empty);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
AddRepeatStep("add some users", () => Client.AddUser(new User { Id = id++ }), 5);
|
||||
checkPlayingUserCount(0);
|
||||
|
||||
AddAssert("playlist item is available", () => Client.CurrentMatchPlayingItem.Value != null);
|
||||
|
||||
changeState(3, MultiplayerUserState.WaitingForLoad);
|
||||
checkPlayingUserCount(3);
|
||||
|
||||
@@ -41,6 +43,8 @@ namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
|
||||
AddStep("leave room", () => Client.LeaveRoom());
|
||||
checkPlayingUserCount(0);
|
||||
|
||||
AddAssert("playlist item is null", () => Client.CurrentMatchPlayingItem.Value == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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 MessagePack;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
|
||||
|
||||
namespace osu.Game.Tests.Online
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestMultiplayerMessagePackSerialization
|
||||
{
|
||||
[Test]
|
||||
public void TestSerialiseRoom()
|
||||
{
|
||||
var room = new MultiplayerRoom(1)
|
||||
{
|
||||
MatchState = new TeamVersusRoomState()
|
||||
};
|
||||
|
||||
var serialized = MessagePackSerializer.Serialize(room);
|
||||
|
||||
var deserialized = MessagePackSerializer.Deserialize<MultiplayerRoom>(serialized);
|
||||
|
||||
Assert.IsTrue(deserialized.MatchState is TeamVersusRoomState);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSerialiseUserStateExpected()
|
||||
{
|
||||
var state = new TeamVersusUserState();
|
||||
|
||||
var serialized = MessagePackSerializer.Serialize(typeof(MatchUserState), state);
|
||||
var deserialized = MessagePackSerializer.Deserialize<MatchUserState>(serialized);
|
||||
|
||||
Assert.IsTrue(deserialized is TeamVersusUserState);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSerialiseUnionFailsWithSingalR()
|
||||
{
|
||||
var state = new TeamVersusUserState();
|
||||
|
||||
// SignalR serialises using the actual type, rather than a base specification.
|
||||
var serialized = MessagePackSerializer.Serialize(typeof(TeamVersusUserState), state);
|
||||
|
||||
// works with explicit type specified.
|
||||
MessagePackSerializer.Deserialize<TeamVersusUserState>(serialized);
|
||||
|
||||
// fails with base (union) type.
|
||||
Assert.Throws<MessagePackSerializationException>(() => MessagePackSerializer.Deserialize<MatchUserState>(serialized));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSerialiseUnionSucceedsWithWorkaround()
|
||||
{
|
||||
var state = new TeamVersusUserState();
|
||||
|
||||
// SignalR serialises using the actual type, rather than a base specification.
|
||||
var serialized = MessagePackSerializer.Serialize(typeof(TeamVersusUserState), state, SignalRUnionWorkaroundResolver.OPTIONS);
|
||||
|
||||
// works with explicit type specified.
|
||||
MessagePackSerializer.Deserialize<TeamVersusUserState>(serialized);
|
||||
|
||||
// works with custom resolver.
|
||||
var deserialized = MessagePackSerializer.Deserialize<MatchUserState>(serialized, SignalRUnionWorkaroundResolver.OPTIONS);
|
||||
Assert.IsTrue(deserialized is TeamVersusUserState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,10 +50,10 @@ namespace osu.Game.Tests.Skins.IO
|
||||
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin", "skinner"), "skin2.osk"));
|
||||
|
||||
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().Count, Is.EqualTo(1));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Count, Is.EqualTo(1));
|
||||
|
||||
// the first should be overwritten by the second import.
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -76,10 +76,10 @@ namespace osu.Game.Tests.Skins.IO
|
||||
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk(string.Empty, string.Empty), "download.osk"));
|
||||
|
||||
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().Count, Is.EqualTo(2));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Count, Is.EqualTo(2));
|
||||
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -101,10 +101,10 @@ namespace osu.Game.Tests.Skins.IO
|
||||
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("test skin v2.1", "skinner"), "skin2.osk"));
|
||||
|
||||
Assert.That(imported2.ID, Is.Not.EqualTo(imported.ID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().Count, Is.EqualTo(2));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Count, Is.EqualTo(2));
|
||||
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins().Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).First().Files.First().FileInfoID, Is.EqualTo(imported.Files.First().FileInfoID));
|
||||
Assert.That(osu.Dependencies.Get<SkinManager>().GetAllUserSkins(true).Last().Files.First().FileInfoID, Is.EqualTo(imported2.Files.First().FileInfoID));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -133,11 +133,12 @@ namespace osu.Game.Tests.Skins
|
||||
[Test]
|
||||
public void TestEmptyComboColoursNoFallback()
|
||||
{
|
||||
AddStep("Add custom combo colours to user skin", () => userSource.Configuration.AddComboColours(
|
||||
AddStep("Add custom combo colours to user skin", () => userSource.Configuration.CustomComboColours = new List<Color4>
|
||||
{
|
||||
new Color4(100, 150, 200, 255),
|
||||
new Color4(55, 110, 166, 255),
|
||||
new Color4(75, 125, 175, 255)
|
||||
));
|
||||
});
|
||||
|
||||
AddStep("Disallow default colours fallback in beatmap skin", () => beatmapSource.Configuration.AllowDefaultComboColoursFallback = false);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Online;
|
||||
using osuTK;
|
||||
@@ -15,6 +16,7 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Components
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestScenePollingComponent : OsuTestScene
|
||||
{
|
||||
private Container pollBox;
|
||||
|
||||
@@ -78,6 +78,24 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("clock looped to start", () => Clock.IsRunning && Clock.CurrentTime < 500);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClampWhenSeekOutsideBeatmapBounds()
|
||||
{
|
||||
AddStep("stop clock", Clock.Stop);
|
||||
|
||||
AddStep("seek before start time", () => Clock.Seek(-1000));
|
||||
AddAssert("time is clamped to 0", () => Clock.CurrentTime == 0);
|
||||
|
||||
AddStep("seek beyond track length", () => Clock.Seek(Clock.TrackLength + 1000));
|
||||
AddAssert("time is clamped to track length", () => Clock.CurrentTime == Clock.TrackLength);
|
||||
|
||||
AddStep("seek smoothly before start time", () => Clock.SeekSmoothlyTo(-1000));
|
||||
AddAssert("time is clamped to 0", () => Clock.CurrentTime == 0);
|
||||
|
||||
AddStep("seek smoothly beyond track length", () => Clock.SeekSmoothlyTo(Clock.TrackLength + 1000));
|
||||
AddAssert("time is clamped to track length", () => Clock.CurrentTime == Clock.TrackLength);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
Beatmap.Disabled = false;
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
this.beatmapSkin = beatmapSkin;
|
||||
}
|
||||
|
||||
protected override ISkin GetSkin() => beatmapSkin;
|
||||
protected internal override ISkin GetSkin() => beatmapSkin;
|
||||
}
|
||||
|
||||
private class TestOsuRuleset : OsuRuleset
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
});
|
||||
|
||||
AddStep("add local player", () => createLeaderboardScore(playerScore, new User { Username = "You", Id = 3 }, true));
|
||||
AddStep("toggle expanded", () => leaderboard.Expanded.Value = !leaderboard.Expanded.Value);
|
||||
AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v);
|
||||
}
|
||||
|
||||
@@ -83,19 +84,38 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("add frenzibyte", () => createRandomScore(new User { Username = "frenzibyte", Id = 14210502 }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMaxHeight()
|
||||
{
|
||||
int playerNumber = 1;
|
||||
AddRepeatStep("add 3 other players", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 3);
|
||||
checkHeight(4);
|
||||
|
||||
AddRepeatStep("add 4 other players", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 4);
|
||||
checkHeight(8);
|
||||
|
||||
AddRepeatStep("add 4 other players", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 4);
|
||||
checkHeight(8);
|
||||
|
||||
void checkHeight(int panelCount)
|
||||
=> AddAssert($"leaderboard height is {panelCount} panels high", () => leaderboard.DrawHeight == (GameplayLeaderboardScore.PANEL_HEIGHT + leaderboard.Spacing) * panelCount);
|
||||
}
|
||||
|
||||
private void createRandomScore(User user) => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), user);
|
||||
|
||||
private void createLeaderboardScore(BindableDouble score, User user, bool isTracked = false)
|
||||
{
|
||||
var leaderboardScore = leaderboard.AddPlayer(user, isTracked);
|
||||
var leaderboardScore = leaderboard.Add(user, isTracked);
|
||||
leaderboardScore.TotalScore.BindTo(score);
|
||||
}
|
||||
|
||||
private class TestGameplayLeaderboard : GameplayLeaderboard
|
||||
{
|
||||
public float Spacing => Flow.Spacing.Y;
|
||||
|
||||
public bool CheckPositionByUsername(string username, int? expectedPosition)
|
||||
{
|
||||
var scoreItem = this.FirstOrDefault(i => i.User?.Username == username);
|
||||
var scoreItem = Flow.FirstOrDefault(i => i.User?.Username == username);
|
||||
|
||||
return scoreItem != null && scoreItem.ScorePosition == expectedPosition;
|
||||
}
|
||||
|
||||
@@ -12,12 +12,15 @@ using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneHUDOverlay : OsuManualInputManagerTestScene
|
||||
{
|
||||
private OsuConfigManager localConfig;
|
||||
|
||||
private HUDOverlay hudOverlay;
|
||||
|
||||
[Cached]
|
||||
@@ -30,8 +33,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private Drawable hideTarget => hudOverlay.KeyCounter;
|
||||
private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First();
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Dependencies.Cache(localConfig = new OsuConfigManager(LocalStorage));
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always));
|
||||
|
||||
[Test]
|
||||
public void TestComboCounterIncrementing()
|
||||
@@ -84,11 +93,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
createNew();
|
||||
|
||||
HUDVisibilityMode originalConfigValue = HUDVisibilityMode.HideDuringGameplay;
|
||||
|
||||
AddStep("get original config value", () => originalConfigValue = config.Get<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||
|
||||
AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
|
||||
AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
|
||||
|
||||
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
|
||||
|
||||
@@ -97,37 +102,28 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("stop trigering", () => InputManager.ReleaseKey(Key.ControlLeft));
|
||||
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
|
||||
|
||||
AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExternalHideDoesntAffectConfig()
|
||||
{
|
||||
HUDVisibilityMode originalConfigValue = HUDVisibilityMode.HideDuringGameplay;
|
||||
|
||||
createNew();
|
||||
|
||||
AddStep("get original config value", () => originalConfigValue = config.Get<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||
|
||||
AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false);
|
||||
AddAssert("config unchanged", () => originalConfigValue == config.Get<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||
AddAssert("config unchanged", () => localConfig.GetBindable<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode).IsDefault);
|
||||
|
||||
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
|
||||
AddAssert("config unchanged", () => originalConfigValue == config.Get<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||
AddAssert("config unchanged", () => localConfig.GetBindable<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode).IsDefault);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangeHUDVisibilityOnHiddenKeyCounter()
|
||||
{
|
||||
bool keyCounterVisibleValue = false;
|
||||
|
||||
createNew();
|
||||
AddStep("save keycounter visible value", () => keyCounterVisibleValue = config.Get<bool>(OsuSetting.KeyOverlay));
|
||||
|
||||
AddStep("set keycounter visible false", () =>
|
||||
AddStep("hide key overlay", () =>
|
||||
{
|
||||
config.SetValue(OsuSetting.KeyOverlay, false);
|
||||
localConfig.SetValue(OsuSetting.KeyOverlay, false);
|
||||
hudOverlay.KeyCounter.AlwaysVisible.Value = false;
|
||||
});
|
||||
|
||||
@@ -138,8 +134,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true);
|
||||
AddUntilStep("hidetarget is visible", () => hideTarget.IsPresent);
|
||||
AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent);
|
||||
}
|
||||
|
||||
AddStep("return value", () => config.SetValue(OsuSetting.KeyOverlay, keyCounterVisibleValue));
|
||||
[Test]
|
||||
public void TestHiddenHUDDoesntBlockSkinnableComponentsLoad()
|
||||
{
|
||||
AddStep("set hud to never show", () => localConfig.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
|
||||
|
||||
createNew();
|
||||
AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded);
|
||||
AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType<SkinnableTargetContainer>().Single().ComponentsLoaded);
|
||||
}
|
||||
|
||||
private void createNew(Action<HUDOverlay> action = null)
|
||||
@@ -158,5 +162,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Child = hudOverlay;
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
localConfig?.Dispose();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
@@ -66,7 +65,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
protected class OverlayTestPlayer : TestPlayer
|
||||
{
|
||||
public new OverlayActivation OverlayActivationMode => base.OverlayActivationMode.Value;
|
||||
public new Bindable<bool> LocalUserPlaying => base.LocalUserPlaying;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ using osu.Game.Database;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Tests.Visual.Navigation;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
public class TestSceneSideOverlays : OsuGameTestScene
|
||||
{
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddAssert("no screen offset applied", () => Game.ScreenOffsetContainer.X == 0f);
|
||||
AddUntilStep("wait for overlays", () => Game.Settings.IsLoaded && Game.Notifications.IsLoaded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScreenOffsettingOnSettingsOverlay()
|
||||
{
|
||||
AddStep("open settings", () => Game.Settings.Show());
|
||||
AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == SettingsPanel.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO);
|
||||
|
||||
AddStep("hide settings", () => Game.Settings.Hide());
|
||||
AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScreenOffsettingOnNotificationOverlay()
|
||||
{
|
||||
AddStep("open notifications", () => Game.Notifications.Show());
|
||||
AddUntilStep("right screen offset applied", () => Game.ScreenOffsetContainer.X == -NotificationOverlay.WIDTH * TestOsuGame.SIDE_OVERLAY_OFFSET_RATIO);
|
||||
|
||||
AddStep("hide notifications", () => Game.Notifications.Hide());
|
||||
AddUntilStep("screen offset removed", () => Game.ScreenOffsetContainer.X == 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Mods
|
||||
{
|
||||
public class TestSceneModFailCondition : ModTestScene
|
||||
{
|
||||
private bool restartRequested;
|
||||
|
||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||
|
||||
protected override TestPlayer CreateModPlayer(Ruleset ruleset)
|
||||
{
|
||||
var player = base.CreateModPlayer(ruleset);
|
||||
player.RestartRequested = () => restartRequested = true;
|
||||
return player;
|
||||
}
|
||||
|
||||
protected override bool AllowFail => true;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUp()
|
||||
{
|
||||
AddStep("reset flag", () => restartRequested = false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRestartOnFailDisabled() => CreateModTest(new ModTestData
|
||||
{
|
||||
Autoplay = false,
|
||||
Mod = new OsuModSuddenDeath(),
|
||||
PassCondition = () => !restartRequested && Player.ChildrenOfType<FailOverlay>().Single().State.Value == Visibility.Visible
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestRestartOnFailEnabled() => CreateModTest(new ModTestData
|
||||
{
|
||||
Autoplay = false,
|
||||
Mod = new OsuModSuddenDeath
|
||||
{
|
||||
Restart = { Value = true }
|
||||
},
|
||||
PassCondition = () => restartRequested && Player.ChildrenOfType<FailOverlay>().Single().State.Value == Visibility.Hidden
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Online.Rooms.RoomStatuses;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneDrawableRoom : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly Bindable<Room> selectedRoom = new Bindable<Room>();
|
||||
|
||||
[Cached]
|
||||
protected readonly OverlayColourProvider ColourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
|
||||
|
||||
[Test]
|
||||
public void TestMultipleStatuses()
|
||||
{
|
||||
AddStep("create rooms", () =>
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(0.9f),
|
||||
Spacing = new Vector2(10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
createDrawableRoom(new Room
|
||||
{
|
||||
Name = { Value = "Flyte's Trash Playlist" },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
EndDate = { Value = DateTimeOffset.Now.AddDays(1) },
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem
|
||||
{
|
||||
Beatmap =
|
||||
{
|
||||
Value = new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
StarDifficulty = 2.5
|
||||
}
|
||||
}.BeatmapInfo,
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
createDrawableRoom(new Room
|
||||
{
|
||||
Name = { Value = "Room 2" },
|
||||
Status = { Value = new RoomStatusPlaying() },
|
||||
EndDate = { Value = DateTimeOffset.Now.AddDays(1) },
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem
|
||||
{
|
||||
Beatmap =
|
||||
{
|
||||
Value = new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
StarDifficulty = 2.5
|
||||
}
|
||||
}.BeatmapInfo,
|
||||
}
|
||||
},
|
||||
new PlaylistItem
|
||||
{
|
||||
Beatmap =
|
||||
{
|
||||
Value = new TestBeatmap(new OsuRuleset().RulesetInfo)
|
||||
{
|
||||
BeatmapInfo =
|
||||
{
|
||||
StarDifficulty = 4.5
|
||||
}
|
||||
}.BeatmapInfo,
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
createDrawableRoom(new Room
|
||||
{
|
||||
Name = { Value = "Room 3" },
|
||||
Status = { Value = new RoomStatusEnded() },
|
||||
EndDate = { Value = DateTimeOffset.Now },
|
||||
}),
|
||||
createDrawableRoom(new Room
|
||||
{
|
||||
Name = { Value = "Room 4 (spotlight)" },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
Category = { Value = RoomCategory.Spotlight },
|
||||
}),
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnableAndDisablePassword()
|
||||
{
|
||||
DrawableRoom drawableRoom = null;
|
||||
Room room = null;
|
||||
|
||||
AddStep("create room", () => Child = drawableRoom = createDrawableRoom(room = new Room
|
||||
{
|
||||
Name = { Value = "Room with password" },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
Type = { Value = MatchType.HeadToHead },
|
||||
}));
|
||||
|
||||
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha));
|
||||
|
||||
AddStep("set password", () => room.Password.Value = "password");
|
||||
AddAssert("password icon visible", () => Precision.AlmostEquals(1, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha));
|
||||
|
||||
AddStep("unset password", () => room.Password.Value = string.Empty);
|
||||
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha));
|
||||
}
|
||||
|
||||
private DrawableRoom createDrawableRoom(Room room)
|
||||
{
|
||||
room.Host.Value ??= new User { Username = "peppy", Id = 2 };
|
||||
|
||||
if (room.RecentParticipants.Count == 0)
|
||||
{
|
||||
room.RecentParticipants.AddRange(Enumerable.Range(0, 20).Select(i => new User
|
||||
{
|
||||
Id = i,
|
||||
Username = $"User {i}"
|
||||
}));
|
||||
}
|
||||
|
||||
return new DrawableLoungeRoom(room) { MatchingFilter = true };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.BeatmapListing.Panels;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
@@ -215,7 +215,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
assertDownloadButtonVisible(false);
|
||||
|
||||
void assertDownloadButtonVisible(bool visible) => AddUntilStep($"download button {(visible ? "shown" : "hidden")}",
|
||||
() => playlist.ChildrenOfType<BeatmapDownloadTrackingComposite>().Single().Alpha == (visible ? 1 : 0));
|
||||
() => playlist.ChildrenOfType<BeatmapPanelDownloadButton>().Single().Alpha == (visible ? 1 : 0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -229,7 +229,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
createPlaylist(byOnlineId, byChecksum);
|
||||
|
||||
AddAssert("download buttons shown", () => playlist.ChildrenOfType<BeatmapDownloadTrackingComposite>().All(d => d.IsPresent));
|
||||
AddAssert("download buttons shown", () => playlist.ChildrenOfType<BeatmapPanelDownloadButton>().All(d => d.IsPresent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
// 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 Moq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneGameplayChatDisplay : MultiplayerTestScene
|
||||
{
|
||||
private GameplayChatDisplay chatDisplay;
|
||||
|
||||
[Cached(typeof(ILocalUserPlayInfo))]
|
||||
private ILocalUserPlayInfo localUserInfo;
|
||||
|
||||
private readonly Bindable<bool> localUserPlaying = new Bindable<bool>();
|
||||
|
||||
private TextBox textBox => chatDisplay.ChildrenOfType<TextBox>().First();
|
||||
|
||||
public TestSceneGameplayChatDisplay()
|
||||
{
|
||||
var mockLocalUserInfo = new Mock<ILocalUserPlayInfo>();
|
||||
mockLocalUserInfo.SetupGet(i => i.IsPlaying).Returns(localUserPlaying);
|
||||
|
||||
localUserInfo = mockLocalUserInfo.Object;
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("load chat display", () => Child = chatDisplay = new GameplayChatDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 0.5f,
|
||||
});
|
||||
|
||||
AddStep("expand", () => chatDisplay.Expanded.Value = true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCantClickWhenPlaying()
|
||||
{
|
||||
setLocalUserPlaying(true);
|
||||
|
||||
AddStep("attempt focus chat", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(textBox);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
assertChatFocused(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFocusDroppedWhenPlaying()
|
||||
{
|
||||
assertChatFocused(false);
|
||||
|
||||
AddStep("focus chat", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(textBox);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
setLocalUserPlaying(true);
|
||||
assertChatFocused(false);
|
||||
|
||||
// should still stay non-focused even after entering a new break section.
|
||||
setLocalUserPlaying(false);
|
||||
assertChatFocused(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFocusOnTabKeyWhenExpanded()
|
||||
{
|
||||
setLocalUserPlaying(true);
|
||||
|
||||
assertChatFocused(false);
|
||||
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||
assertChatFocused(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFocusOnTabKeyWhenNotExpanded()
|
||||
{
|
||||
AddStep("set not expanded", () => chatDisplay.Expanded.Value = false);
|
||||
AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
|
||||
|
||||
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||
assertChatFocused(true);
|
||||
AddUntilStep("is visible", () => chatDisplay.IsPresent);
|
||||
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
assertChatFocused(false);
|
||||
AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFocusToggleViaAction()
|
||||
{
|
||||
AddStep("set not expanded", () => chatDisplay.Expanded.Value = false);
|
||||
AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
|
||||
|
||||
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||
assertChatFocused(true);
|
||||
AddUntilStep("is visible", () => chatDisplay.IsPresent);
|
||||
|
||||
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||
assertChatFocused(false);
|
||||
AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
|
||||
}
|
||||
|
||||
private void assertChatFocused(bool isFocused) =>
|
||||
AddAssert($"chat {(isFocused ? "focused" : "not focused")}", () => textBox.HasFocus == isFocused);
|
||||
|
||||
private void setLocalUserPlaying(bool playing) =>
|
||||
AddStep($"local user {(playing ? "playing" : "not playing")}", () => localUserPlaying.Value = playing);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +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 NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Online.Rooms.RoomStatuses;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneLoungeRoomInfo : OnlinePlayTestScene
|
||||
{
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
SelectedRoom.Value = new Room();
|
||||
|
||||
Child = new RoomInfo
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 500
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestNonSelectedRoom()
|
||||
{
|
||||
AddStep("set null room", () => SelectedRoom.Value.RoomID.Value = null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOpenRoom()
|
||||
{
|
||||
AddStep("set open room", () =>
|
||||
{
|
||||
SelectedRoom.Value.RoomID.Value = 0;
|
||||
SelectedRoom.Value.Name.Value = "Room 0";
|
||||
SelectedRoom.Value.Host.Value = new User { Username = "peppy", Id = 2 };
|
||||
SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMonths(1);
|
||||
SelectedRoom.Value.Status.Value = new RoomStatusOpen();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ using osu.Framework.Testing;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osuTK.Input;
|
||||
@@ -17,7 +18,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneLoungeRoomsContainer : OnlinePlayTestScene
|
||||
{
|
||||
protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager;
|
||||
protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
|
||||
|
||||
private RoomsContainer container;
|
||||
|
||||
@@ -38,7 +39,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(3));
|
||||
|
||||
AddAssert("has 3 rooms", () => container.Rooms.Count == 3);
|
||||
AddStep("remove first room", () => RoomManager.Rooms.Remove(RoomManager.Rooms.FirstOrDefault()));
|
||||
AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.FirstOrDefault()));
|
||||
AddAssert("has 2 rooms", () => container.Rooms.Count == 2);
|
||||
AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0));
|
||||
|
||||
@@ -74,7 +75,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
var room = RoomManager.Rooms[1];
|
||||
|
||||
RoomManager.RemoveRoom(room);
|
||||
RoomManager.AddRoom(room);
|
||||
RoomManager.AddOrUpdateRoom(room);
|
||||
});
|
||||
|
||||
AddAssert("no selection", () => checkRoomSelected(null));
|
||||
@@ -115,11 +116,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddUntilStep("4 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 4);
|
||||
|
||||
AddStep("filter one room", () => container.Filter(new FilterCriteria { SearchString = "1" }));
|
||||
AddStep("filter one room", () => container.Filter.Value = new FilterCriteria { SearchString = "1" });
|
||||
|
||||
AddUntilStep("1 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 1);
|
||||
|
||||
AddStep("remove filter", () => container.Filter(null));
|
||||
AddStep("remove filter", () => container.Filter.Value = null);
|
||||
|
||||
AddUntilStep("4 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 4);
|
||||
}
|
||||
@@ -131,13 +132,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(3, new CatchRuleset().RulesetInfo));
|
||||
|
||||
// Todo: What even is this case...?
|
||||
AddStep("set empty filter criteria", () => container.Filter(null));
|
||||
AddStep("set empty filter criteria", () => container.Filter.Value = null);
|
||||
AddUntilStep("5 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 5);
|
||||
|
||||
AddStep("filter osu! rooms", () => container.Filter(new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo }));
|
||||
AddStep("filter osu! rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new OsuRuleset().RulesetInfo });
|
||||
AddUntilStep("2 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 2);
|
||||
|
||||
AddStep("filter catch rooms", () => container.Filter(new FilterCriteria { Ruleset = new CatchRuleset().RulesetInfo }));
|
||||
AddStep("filter catch rooms", () => container.Filter.Value = new FilterCriteria { Ruleset = new CatchRuleset().RulesetInfo });
|
||||
AddUntilStep("3 rooms visible", () => container.Rooms.Count(r => r.IsPresent) == 3);
|
||||
}
|
||||
|
||||
@@ -150,6 +151,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
private bool checkRoomSelected(Room room) => SelectedRoom.Value == room;
|
||||
|
||||
private Room getRoomInFlow(int index) =>
|
||||
(container.ChildrenOfType<FillFlowContainer<DrawableRoom>>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room;
|
||||
(container.ChildrenOfType<FillFlowContainer<DrawableLoungeRoom>>().First().FlowingChildren.ElementAt(index) as DrawableRoom)?.Room;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +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 NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMatchHeader : OnlinePlayTestScene
|
||||
{
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
SelectedRoom.Value = new Room
|
||||
{
|
||||
Name = { Value = "A very awesome room" },
|
||||
Host = { Value = new User { Id = 2, Username = "peppy" } },
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem
|
||||
{
|
||||
Beatmap =
|
||||
{
|
||||
Value = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata
|
||||
{
|
||||
Title = "Title",
|
||||
Artist = "Artist",
|
||||
AuthorString = "Author",
|
||||
},
|
||||
Version = "Version",
|
||||
Ruleset = new OsuRuleset().RulesetInfo
|
||||
}
|
||||
},
|
||||
RequiredMods =
|
||||
{
|
||||
new OsuModDoubleTime(),
|
||||
new OsuModNoFail(),
|
||||
new OsuModRelax(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Child = new Header();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,11 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Rulesets.Osu.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
@@ -31,7 +33,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
};
|
||||
|
||||
foreach (var (userId, _) in clocks)
|
||||
{
|
||||
SpectatorClient.StartPlay(userId, 0);
|
||||
OnlinePlayDependencies.Client.AddUser(new User { Id = userId });
|
||||
}
|
||||
});
|
||||
|
||||
AddStep("create leaderboard", () =>
|
||||
@@ -41,7 +46,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
var scoreProcessor = new OsuScoreProcessor();
|
||||
scoreProcessor.ApplyBeatmap(playable);
|
||||
|
||||
LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, clocks.Keys.ToArray()) { Expanded = { Value = true } }, Add);
|
||||
LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(scoreProcessor, clocks.Keys.Select(id => new MultiplayerRoomUser(id)).ToArray()) { Expanded = { Value = true } }, Add);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for load", () => leaderboard.IsLoaded);
|
||||
|
||||
@@ -6,12 +6,19 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Play.PlayerSettings;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
@@ -20,12 +27,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Resolved]
|
||||
private OsuGameBase game { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmapManager { get; set; }
|
||||
|
||||
private MultiSpectatorScreen spectatorScreen;
|
||||
|
||||
private readonly List<int> playingUserIds = new List<int>();
|
||||
private readonly List<MultiplayerRoomUser> playingUsers = new List<MultiplayerRoomUser>();
|
||||
|
||||
private BeatmapSetInfo importedSet;
|
||||
private BeatmapInfo importedBeatmap;
|
||||
@@ -40,17 +50,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() => playingUserIds.Clear());
|
||||
public new void Setup() => Schedule(() => playingUsers.Clear());
|
||||
|
||||
[Test]
|
||||
public void TestDelayedStart()
|
||||
{
|
||||
AddStep("start players silently", () =>
|
||||
{
|
||||
Client.CurrentMatchPlayingUserIds.Add(PLAYER_1_ID);
|
||||
Client.CurrentMatchPlayingUserIds.Add(PLAYER_2_ID);
|
||||
playingUserIds.Add(PLAYER_1_ID);
|
||||
playingUserIds.Add(PLAYER_2_ID);
|
||||
OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }, true);
|
||||
OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }, true);
|
||||
|
||||
playingUsers.Add(new MultiplayerRoomUser(PLAYER_1_ID));
|
||||
playingUsers.Add(new MultiplayerRoomUser(PLAYER_2_ID));
|
||||
});
|
||||
|
||||
loadSpectateScreen(false);
|
||||
@@ -76,6 +87,64 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddWaitStep("wait a bit", 20);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSpectatorPlayerInteractiveElementsHidden()
|
||||
{
|
||||
HUDVisibilityMode originalConfigValue = default;
|
||||
|
||||
AddStep("get original config hud visibility", () => originalConfigValue = config.Get<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||
AddStep("set config hud visibility to always", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always));
|
||||
|
||||
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
|
||||
loadSpectateScreen(false);
|
||||
|
||||
AddUntilStep("wait for player loaders", () => this.ChildrenOfType<PlayerLoader>().Count() == 2);
|
||||
AddAssert("all player loader settings hidden", () => this.ChildrenOfType<PlayerLoader>().All(l => !l.ChildrenOfType<FillFlowContainer<PlayerSettingsGroup>>().Any()));
|
||||
|
||||
AddUntilStep("wait for players to load", () => spectatorScreen.AllPlayersLoaded);
|
||||
|
||||
// components wrapped in skinnable target containers load asynchronously, potentially taking more than one frame to load.
|
||||
// therefore use until step rather than direct assert to account for that.
|
||||
AddUntilStep("all interactive elements removed", () => this.ChildrenOfType<Player>().All(p =>
|
||||
!p.ChildrenOfType<PlayerSettingsOverlay>().Any() &&
|
||||
!p.ChildrenOfType<HoldForMenuButton>().Any() &&
|
||||
p.ChildrenOfType<SongProgressBar>().SingleOrDefault()?.ShowHandle == false));
|
||||
|
||||
AddStep("restore config hud visibility", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTeamDisplay()
|
||||
{
|
||||
AddStep("start players", () =>
|
||||
{
|
||||
var player1 = OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_1_ID }, true);
|
||||
player1.MatchState = new TeamVersusUserState
|
||||
{
|
||||
TeamID = 0,
|
||||
};
|
||||
|
||||
var player2 = OnlinePlayDependencies.Client.AddUser(new User { Id = PLAYER_2_ID }, true);
|
||||
player2.MatchState = new TeamVersusUserState
|
||||
{
|
||||
TeamID = 1,
|
||||
};
|
||||
|
||||
SpectatorClient.StartPlay(player1.UserID, importedBeatmapId);
|
||||
SpectatorClient.StartPlay(player2.UserID, importedBeatmapId);
|
||||
|
||||
playingUsers.Add(player1);
|
||||
playingUsers.Add(player2);
|
||||
});
|
||||
|
||||
loadSpectateScreen();
|
||||
|
||||
sendFrames(PLAYER_1_ID, 1000);
|
||||
sendFrames(PLAYER_2_ID, 1000);
|
||||
|
||||
AddWaitStep("wait a bit", 20);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTimeDoesNotProgressWhileAllPlayersPaused()
|
||||
{
|
||||
@@ -252,7 +321,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(importedBeatmap);
|
||||
Ruleset.Value = importedBeatmap.Ruleset;
|
||||
|
||||
LoadScreen(spectatorScreen = new MultiSpectatorScreen(playingUserIds.ToArray()));
|
||||
LoadScreen(spectatorScreen = new MultiSpectatorScreen(playingUsers.ToArray()));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded && (!waitForPlayerLoad || spectatorScreen.AllPlayersLoaded));
|
||||
@@ -264,9 +333,10 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
foreach (int id in userIds)
|
||||
{
|
||||
Client.CurrentMatchPlayingUserIds.Add(id);
|
||||
OnlinePlayDependencies.Client.AddUser(new User { Id = id }, true);
|
||||
|
||||
SpectatorClient.StartPlay(id, beatmapId ?? importedBeatmapId);
|
||||
playingUserIds.Add(id);
|
||||
playingUsers.Add(new MultiplayerRoomUser(id));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ using osu.Game.Screens;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Match.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Match;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
using osu.Game.Tests.Resources;
|
||||
@@ -44,6 +44,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
private TestMultiplayer multiplayerScreen;
|
||||
private TestMultiplayerClient client;
|
||||
|
||||
private TestRequestHandlingMultiplayerRoomManager roomManager => multiplayerScreen.RoomManager;
|
||||
|
||||
[Cached(typeof(UserLookupCache))]
|
||||
private UserLookupCache lookupCache = new TestUserLookupCache();
|
||||
|
||||
@@ -68,7 +70,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddStep("load dependencies", () =>
|
||||
{
|
||||
client = new TestMultiplayerClient(multiplayerScreen.RoomManager);
|
||||
client = new TestMultiplayerClient(roomManager);
|
||||
|
||||
// The screen gets suspended so it stops receiving updates.
|
||||
Child = client;
|
||||
@@ -87,6 +89,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
public void TestEmpty()
|
||||
{
|
||||
// used to test the flow of multiplayer from visual tests.
|
||||
AddStep("empty step", () => { });
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -131,39 +134,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestExitMidJoin()
|
||||
{
|
||||
Room room = null;
|
||||
|
||||
AddStep("create room", () =>
|
||||
{
|
||||
room = new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo },
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria());
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("join room and immediately exit", () =>
|
||||
{
|
||||
multiplayerScreen.ChildrenOfType<LoungeSubScreen>().Single().Open(room);
|
||||
Schedule(() => Stack.CurrentScreen.Exit());
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestJoinRoomWithoutPassword()
|
||||
{
|
||||
AddStep("create room", () =>
|
||||
{
|
||||
multiplayerScreen.RoomManager.AddRoom(new Room
|
||||
roomManager.AddServerSideRoom(new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Playlist =
|
||||
@@ -177,7 +150,39 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria());
|
||||
AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter());
|
||||
AddUntilStep("wait for room", () => this.ChildrenOfType<DrawableRoom>().Any());
|
||||
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("join room and immediately exit select", () =>
|
||||
{
|
||||
InputManager.Key(Key.Enter);
|
||||
Schedule(() => Stack.CurrentScreen.Exit());
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestJoinRoomWithoutPassword()
|
||||
{
|
||||
AddStep("create room", () =>
|
||||
{
|
||||
roomManager.AddServerSideRoom(new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.RulesetID == 0)).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo },
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter());
|
||||
AddUntilStep("wait for room", () => this.ChildrenOfType<DrawableRoom>().Any());
|
||||
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("join room", () => InputManager.Key(Key.Enter));
|
||||
|
||||
@@ -210,7 +215,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddStep("create room", () =>
|
||||
{
|
||||
multiplayerScreen.RoomManager.AddRoom(new Room
|
||||
roomManager.AddServerSideRoom(new Room
|
||||
{
|
||||
Name = { Value = "Test Room" },
|
||||
Password = { Value = "password" },
|
||||
@@ -225,12 +230,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("refresh rooms", () => multiplayerScreen.RoomManager.Filter.Value = new FilterCriteria());
|
||||
AddStep("refresh rooms", () => this.ChildrenOfType<LoungeSubScreen>().Single().UpdateFilter());
|
||||
AddUntilStep("wait for room", () => this.ChildrenOfType<DrawableRoom>().Any());
|
||||
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("join room", () => InputManager.Key(Key.Enter));
|
||||
|
||||
DrawableRoom.PasswordEntryPopover passwordEntryPopover = null;
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password");
|
||||
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
|
||||
|
||||
@@ -312,6 +319,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for spectating user state", () => client.LocalUser?.State == MultiplayerUserState.Spectating);
|
||||
|
||||
AddStep("start match externally", () => client.StartMatch());
|
||||
|
||||
AddAssert("play not started", () => multiplayerScreen.IsCurrentScreen());
|
||||
@@ -348,6 +357,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for spectating user state", () => client.LocalUser?.State == MultiplayerUserState.Spectating);
|
||||
|
||||
AddStep("start match externally", () => client.StartMatch());
|
||||
|
||||
AddStep("restore beatmap", () =>
|
||||
@@ -396,7 +407,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
});
|
||||
|
||||
AddStep("open mod overlay", () => this.ChildrenOfType<PurpleTriangleButton>().ElementAt(2).TriggerClick());
|
||||
AddStep("open mod overlay", () => this.ChildrenOfType<RoomSubScreen.UserModSelectButton>().Single().TriggerClick());
|
||||
|
||||
AddStep("invoke on back button", () => multiplayerScreen.OnBackButton());
|
||||
|
||||
@@ -404,8 +415,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden);
|
||||
|
||||
testLeave("lounge tab item", () => this.ChildrenOfType<BreadcrumbControl<IScreen>.BreadcrumbTabItem>().First().TriggerClick());
|
||||
|
||||
testLeave("back button", () => multiplayerScreen.OnBackButton());
|
||||
|
||||
// mimics home button and OS window close
|
||||
@@ -423,10 +432,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void createRoom(Func<Room> room)
|
||||
{
|
||||
AddStep("open room", () =>
|
||||
{
|
||||
multiplayerScreen.OpenNewRoom(room());
|
||||
});
|
||||
AddUntilStep("wait for lounge", () => multiplayerScreen.ChildrenOfType<LoungeSubScreen>().SingleOrDefault()?.IsLoaded == true);
|
||||
AddStep("open room", () => multiplayerScreen.ChildrenOfType<LoungeSubScreen>().Single().Open(room()));
|
||||
|
||||
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
|
||||
AddWaitStep("wait for transition", 2);
|
||||
|
||||
@@ -12,6 +12,7 @@ using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Spectator;
|
||||
using osu.Game.Replays.Legacy;
|
||||
using osu.Game.Rulesets.Osu.Scoring;
|
||||
@@ -20,6 +21,7 @@ using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Tests.Visual.Spectator;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
@@ -50,22 +52,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
OsuScoreProcessor scoreProcessor;
|
||||
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
|
||||
|
||||
var playable = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value);
|
||||
var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value);
|
||||
var multiplayerUsers = new List<MultiplayerRoomUser>();
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0);
|
||||
|
||||
// Todo: This is REALLY bad.
|
||||
Client.CurrentMatchPlayingUserIds.AddRange(users);
|
||||
multiplayerUsers.Add(OnlinePlayDependencies.Client.AddUser(new User { Id = user }, true));
|
||||
}
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
scoreProcessor = new OsuScoreProcessor(),
|
||||
};
|
||||
|
||||
scoreProcessor.ApplyBeatmap(playable);
|
||||
scoreProcessor.ApplyBeatmap(playableBeatmap);
|
||||
|
||||
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, users.ToArray())
|
||||
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, multiplayerUsers.ToArray())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
// 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.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Osu.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Tests.Visual.Spectator;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerGameplayLeaderboardTeams : MultiplayerTestScene
|
||||
{
|
||||
private static IEnumerable<int> users => Enumerable.Range(0, 16);
|
||||
|
||||
public new TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient SpectatorClient =>
|
||||
(TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient)OnlinePlayDependencies?.SpectatorClient;
|
||||
|
||||
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
|
||||
|
||||
protected class TestDependencies : MultiplayerTestSceneDependencies
|
||||
{
|
||||
protected override TestSpectatorClient CreateSpectatorClient() => new TestSceneMultiplayerGameplayLeaderboard.TestMultiplayerSpectatorClient();
|
||||
}
|
||||
|
||||
private MultiplayerGameplayLeaderboard leaderboard;
|
||||
private GameplayMatchScoreDisplay gameplayScoreDisplay;
|
||||
|
||||
protected override Room CreateRoom()
|
||||
{
|
||||
var room = base.CreateRoom();
|
||||
room.Type.Value = MatchType.TeamVersus;
|
||||
return room;
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
AddStep("set local user", () => ((DummyAPIAccess)API).LocalUser.Value = LookupCache.GetUserAsync(1).Result);
|
||||
|
||||
AddStep("create leaderboard", () =>
|
||||
{
|
||||
leaderboard?.Expire();
|
||||
|
||||
OsuScoreProcessor scoreProcessor;
|
||||
Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value);
|
||||
|
||||
var playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Ruleset.Value);
|
||||
var multiplayerUsers = new List<MultiplayerRoomUser>();
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
SpectatorClient.StartPlay(user, Beatmap.Value.BeatmapInfo.OnlineBeatmapID ?? 0);
|
||||
var roomUser = OnlinePlayDependencies.Client.AddUser(new User { Id = user }, true);
|
||||
|
||||
roomUser.MatchState = new TeamVersusUserState
|
||||
{
|
||||
TeamID = RNG.Next(0, 2)
|
||||
};
|
||||
|
||||
multiplayerUsers.Add(roomUser);
|
||||
}
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
scoreProcessor = new OsuScoreProcessor(),
|
||||
};
|
||||
|
||||
scoreProcessor.ApplyBeatmap(playableBeatmap);
|
||||
|
||||
LoadComponentAsync(leaderboard = new MultiplayerGameplayLeaderboard(scoreProcessor, multiplayerUsers.ToArray())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}, gameplayLeaderboard =>
|
||||
{
|
||||
LoadComponentAsync(new MatchScoreDisplay
|
||||
{
|
||||
Team1Score = { BindTarget = leaderboard.TeamScores[0] },
|
||||
Team2Score = { BindTarget = leaderboard.TeamScores[1] }
|
||||
}, Add);
|
||||
|
||||
LoadComponentAsync(gameplayScoreDisplay = new GameplayMatchScoreDisplay
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Team1Score = { BindTarget = leaderboard.TeamScores[0] },
|
||||
Team2Score = { BindTarget = leaderboard.TeamScores[1] }
|
||||
}, Add);
|
||||
|
||||
Add(gameplayLeaderboard);
|
||||
});
|
||||
});
|
||||
|
||||
AddUntilStep("wait for load", () => leaderboard.IsLoaded);
|
||||
AddUntilStep("wait for user population", () => Client.CurrentMatchPlayingUserIds.Count > 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScoreUpdates()
|
||||
{
|
||||
AddRepeatStep("update state", () => SpectatorClient.RandomlyUpdateState(), 100);
|
||||
AddToggleStep("switch compact mode", expanded =>
|
||||
{
|
||||
leaderboard.Expanded.Value = expanded;
|
||||
gameplayScoreDisplay.Expanded.Value = expanded;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osuTK.Input;
|
||||
@@ -18,7 +17,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerLoungeSubScreen : OnlinePlayTestScene
|
||||
{
|
||||
protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager;
|
||||
protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
|
||||
|
||||
private LoungeSubScreen loungeScreen;
|
||||
|
||||
@@ -59,20 +58,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType<DrawableRoom.PasswordEntryPopover>().Any());
|
||||
AddUntilStep("password prompt appeared", () => InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().Any());
|
||||
AddStep("exit screen", () => Stack.Exit());
|
||||
AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType<DrawableRoom.PasswordEntryPopover>().Any());
|
||||
AddUntilStep("password prompt hidden", () => !InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestJoinRoomWithPassword()
|
||||
{
|
||||
DrawableRoom.PasswordEntryPopover passwordEntryPopover = null;
|
||||
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
|
||||
|
||||
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password");
|
||||
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
|
||||
|
||||
@@ -83,12 +82,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
[Test]
|
||||
public void TestJoinRoomWithPasswordViaKeyboardOnly()
|
||||
{
|
||||
DrawableRoom.PasswordEntryPopover passwordEntryPopover = null;
|
||||
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
|
||||
|
||||
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
|
||||
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "password");
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
|
||||
|
||||
@@ -59,23 +59,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for load", () => screen.IsCurrentScreen());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSettingValidity()
|
||||
{
|
||||
AddAssert("create button not enabled", () => !this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("set playlist", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo },
|
||||
});
|
||||
});
|
||||
|
||||
AddAssert("create button enabled", () => this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCreatedRoom()
|
||||
{
|
||||
@@ -97,6 +80,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for join", () => Client.Room != null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSettingValidity()
|
||||
{
|
||||
AddAssert("create button not enabled", () => !this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("set playlist", () =>
|
||||
{
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
|
||||
Ruleset = { Value = new OsuRuleset().RulesetInfo },
|
||||
});
|
||||
});
|
||||
|
||||
AddAssert("create button enabled", () => this.ChildrenOfType<MultiplayerMatchSettingsOverlay.CreateOrUpdateButton>().Single().Enabled.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStartMatchWhileSpectating()
|
||||
{
|
||||
@@ -129,6 +129,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for spectating user state", () => Client.LocalUser?.State == MultiplayerUserState.Spectating);
|
||||
|
||||
AddUntilStep("wait for ready button to be enabled", () => this.ChildrenOfType<MultiplayerReadyButton>().Single().ChildrenOfType<ReadyButton>().Single().Enabled.Value);
|
||||
|
||||
AddStep("click ready button", () =>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Testing;
|
||||
@@ -48,9 +49,15 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
AddAssert("one unique panel", () => this.ChildrenOfType<ParticipantPanel>().Select(p => p.User).Distinct().Count() == 1);
|
||||
|
||||
AddStep("add non-resolvable user", () => Client.AddNullUser(-3));
|
||||
AddStep("add non-resolvable user", () => Client.AddNullUser());
|
||||
AddAssert("null user added", () => Client.Room.AsNonNull().Users.Count(u => u.User == null) == 1);
|
||||
|
||||
AddUntilStep("two unique panels", () => this.ChildrenOfType<ParticipantPanel>().Select(p => p.User).Distinct().Count() == 2);
|
||||
|
||||
AddStep("kick null user", () => this.ChildrenOfType<ParticipantPanel>().Single(p => p.User.User == null)
|
||||
.ChildrenOfType<ParticipantPanel.KickButton>().Single().TriggerClick());
|
||||
|
||||
AddAssert("null user kicked", () => Client.Room.AsNonNull().Users.Count == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -155,6 +162,42 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("second user crown visible", () => this.ChildrenOfType<ParticipantPanel>().ElementAt(1).ChildrenOfType<SpriteIcon>().First().Alpha == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestKickButtonOnlyPresentWhenHost()
|
||||
{
|
||||
AddStep("add user", () => Client.AddUser(new User
|
||||
{
|
||||
Id = 3,
|
||||
Username = "Second",
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||
}));
|
||||
|
||||
AddUntilStep("kick buttons visible", () => this.ChildrenOfType<ParticipantPanel.KickButton>().Count(d => d.IsPresent) == 1);
|
||||
|
||||
AddStep("make second user host", () => Client.TransferHost(3));
|
||||
|
||||
AddUntilStep("kick buttons not visible", () => this.ChildrenOfType<ParticipantPanel.KickButton>().Count(d => d.IsPresent) == 0);
|
||||
|
||||
AddStep("make local user host again", () => Client.TransferHost(API.LocalUser.Value.Id));
|
||||
|
||||
AddUntilStep("kick buttons visible", () => this.ChildrenOfType<ParticipantPanel.KickButton>().Count(d => d.IsPresent) == 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestKickButtonKicks()
|
||||
{
|
||||
AddStep("add user", () => Client.AddUser(new User
|
||||
{
|
||||
Id = 3,
|
||||
Username = "Second",
|
||||
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||
}));
|
||||
|
||||
AddStep("kick second user", () => this.ChildrenOfType<ParticipantPanel.KickButton>().Single(d => d.IsPresent).TriggerClick());
|
||||
|
||||
AddAssert("second user kicked", () => Client.Room?.Users.Single().UserID == API.LocalUser.Value.Id);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestManyUsers()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerPlayer : MultiplayerTestScene
|
||||
{
|
||||
private MultiplayerPlayer player;
|
||||
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("set beatmap", () =>
|
||||
{
|
||||
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
});
|
||||
|
||||
AddStep("initialise gameplay", () =>
|
||||
{
|
||||
Stack.Push(player = new MultiplayerPlayer(Client.CurrentMatchPlayingItem.Value, Client.Room?.Users.ToArray()));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGameplay()
|
||||
{
|
||||
AddUntilStep("wait for gameplay start", () => player.LocalUserPlaying.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerResults : ScreenTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestDisplayResults()
|
||||
{
|
||||
MultiplayerResultsScreen screen = null;
|
||||
|
||||
AddStep("show results screen", () =>
|
||||
{
|
||||
var rulesetInfo = new OsuRuleset().RulesetInfo;
|
||||
var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo;
|
||||
|
||||
var score = new ScoreInfo
|
||||
{
|
||||
Rank = ScoreRank.B,
|
||||
TotalScore = 987654,
|
||||
Accuracy = 0.8,
|
||||
MaxCombo = 500,
|
||||
Combo = 250,
|
||||
Beatmap = beatmapInfo,
|
||||
User = new User { Username = "Test user" },
|
||||
Date = DateTimeOffset.Now,
|
||||
OnlineScoreID = 12345,
|
||||
Ruleset = rulesetInfo,
|
||||
};
|
||||
|
||||
PlaylistItem playlistItem = new PlaylistItem
|
||||
{
|
||||
BeatmapID = beatmapInfo.ID,
|
||||
};
|
||||
|
||||
Stack.Push(screen = new MultiplayerResultsScreen(score, 1, playlistItem));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,157 +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 NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneMultiplayerRoomManager : MultiplayerTestScene
|
||||
{
|
||||
protected override OnlinePlayTestSceneDependencies CreateOnlinePlayDependencies() => new TestDependencies();
|
||||
|
||||
public TestSceneMultiplayerRoomManager()
|
||||
: base(false)
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPollsInitially()
|
||||
{
|
||||
AddStep("create room manager with a few rooms", () =>
|
||||
{
|
||||
RoomManager.CreateRoom(createRoom(r => r.Name.Value = "1"));
|
||||
RoomManager.PartRoom();
|
||||
RoomManager.CreateRoom(createRoom(r => r.Name.Value = "2"));
|
||||
RoomManager.PartRoom();
|
||||
RoomManager.ClearRooms();
|
||||
});
|
||||
|
||||
AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2);
|
||||
AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRoomsClearedOnDisconnection()
|
||||
{
|
||||
AddStep("create room manager with a few rooms", () =>
|
||||
{
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.PartRoom();
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.PartRoom();
|
||||
});
|
||||
|
||||
AddStep("disconnect", () => Client.Disconnect());
|
||||
|
||||
AddAssert("rooms cleared", () => ((RoomManager)RoomManager).Rooms.Count == 0);
|
||||
AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRoomsPolledOnReconnect()
|
||||
{
|
||||
AddStep("create room manager with a few rooms", () =>
|
||||
{
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.PartRoom();
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.PartRoom();
|
||||
});
|
||||
|
||||
AddStep("disconnect", () => Client.Disconnect());
|
||||
AddStep("connect", () => Client.Connect());
|
||||
|
||||
AddAssert("manager polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 2);
|
||||
AddAssert("initial rooms received", () => RoomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRoomsNotPolledWhenJoined()
|
||||
{
|
||||
AddStep("create room manager with a room", () =>
|
||||
{
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.ClearRooms();
|
||||
});
|
||||
|
||||
AddAssert("manager not polled for rooms", () => ((RoomManager)RoomManager).Rooms.Count == 0);
|
||||
AddAssert("initial rooms not received", () => !RoomManager.InitialRoomsReceived.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultiplayerRoomJoinedWhenCreated()
|
||||
{
|
||||
AddStep("create room manager with a room", () =>
|
||||
{
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
});
|
||||
|
||||
AddUntilStep("multiplayer room joined", () => Client.Room != null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultiplayerRoomPartedWhenAPIRoomParted()
|
||||
{
|
||||
AddStep("create room manager with a room", () =>
|
||||
{
|
||||
RoomManager.CreateRoom(createRoom());
|
||||
RoomManager.PartRoom();
|
||||
});
|
||||
|
||||
AddAssert("multiplayer room parted", () => Client.Room == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultiplayerRoomJoinedWhenAPIRoomJoined()
|
||||
{
|
||||
AddStep("create room manager with a room", () =>
|
||||
{
|
||||
var r = createRoom();
|
||||
RoomManager.CreateRoom(r);
|
||||
RoomManager.PartRoom();
|
||||
RoomManager.JoinRoom(r);
|
||||
});
|
||||
|
||||
AddUntilStep("multiplayer room joined", () => Client.Room != null);
|
||||
}
|
||||
|
||||
private Room createRoom(Action<Room> initFunc = null)
|
||||
{
|
||||
var room = new Room
|
||||
{
|
||||
Name =
|
||||
{
|
||||
Value = "test room"
|
||||
},
|
||||
Playlist =
|
||||
{
|
||||
new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = new TestBeatmap(Ruleset.Value).BeatmapInfo },
|
||||
Ruleset = { Value = Ruleset.Value }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
initFunc?.Invoke(room);
|
||||
return room;
|
||||
}
|
||||
|
||||
private class TestDependencies : MultiplayerTestSceneDependencies
|
||||
{
|
||||
public TestDependencies()
|
||||
{
|
||||
// Need to set these values as early as possible.
|
||||
RoomManager.TimeBetweenListingPolls.Value = 1;
|
||||
RoomManager.TimeBetweenSelectionPolls.Value = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// 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.Bindables;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneMultiplayerTeamResults : ScreenTestScene
|
||||
{
|
||||
[TestCase(7483253, 1048576)]
|
||||
[TestCase(1048576, 7483253)]
|
||||
[TestCase(1048576, 1048576)]
|
||||
public void TestDisplayTeamResults(int team1Score, int team2Score)
|
||||
{
|
||||
MultiplayerResultsScreen screen = null;
|
||||
|
||||
AddStep("show results screen", () =>
|
||||
{
|
||||
var rulesetInfo = new OsuRuleset().RulesetInfo;
|
||||
var beatmapInfo = CreateBeatmap(rulesetInfo).BeatmapInfo;
|
||||
|
||||
var score = new ScoreInfo
|
||||
{
|
||||
Rank = ScoreRank.B,
|
||||
TotalScore = 987654,
|
||||
Accuracy = 0.8,
|
||||
MaxCombo = 500,
|
||||
Combo = 250,
|
||||
Beatmap = beatmapInfo,
|
||||
User = new User { Username = "Test user" },
|
||||
Date = DateTimeOffset.Now,
|
||||
OnlineScoreID = 12345,
|
||||
Ruleset = rulesetInfo,
|
||||
};
|
||||
|
||||
PlaylistItem playlistItem = new PlaylistItem
|
||||
{
|
||||
BeatmapID = beatmapInfo.ID,
|
||||
};
|
||||
|
||||
SortedDictionary<int, BindableInt> teamScores = new SortedDictionary<int, BindableInt>
|
||||
{
|
||||
{ 0, new BindableInt(team1Score) },
|
||||
{ 1, new BindableInt(team2Score) }
|
||||
};
|
||||
|
||||
Stack.Push(screen = new MultiplayerTeamResultsScreen(score, 1, playlistItem, teamScores));
|
||||
});
|
||||
|
||||
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneRankRangePill : MultiplayerTestScene
|
||||
{
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
Child = new RankRangePill
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestSingleUser()
|
||||
{
|
||||
AddStep("add user", () =>
|
||||
{
|
||||
Client.AddUser(new User
|
||||
{
|
||||
Id = 2,
|
||||
Statistics = { GlobalRank = 1234 }
|
||||
});
|
||||
|
||||
// Remove the local user so only the one above is displayed.
|
||||
Client.RemoveUser(API.LocalUser.Value);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultipleUsers()
|
||||
{
|
||||
AddStep("add users", () =>
|
||||
{
|
||||
Client.AddUser(new User
|
||||
{
|
||||
Id = 2,
|
||||
Statistics = { GlobalRank = 1234 }
|
||||
});
|
||||
|
||||
Client.AddUser(new User
|
||||
{
|
||||
Id = 3,
|
||||
Statistics = { GlobalRank = 3333 }
|
||||
});
|
||||
|
||||
Client.AddUser(new User
|
||||
{
|
||||
Id = 4,
|
||||
Statistics = { GlobalRank = 4321 }
|
||||
});
|
||||
|
||||
// Remove the local user so only the ones above are displayed.
|
||||
Client.RemoveUser(API.LocalUser.Value);
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1, 10)]
|
||||
[TestCase(10, 100)]
|
||||
[TestCase(100, 1000)]
|
||||
[TestCase(1000, 10000)]
|
||||
[TestCase(10000, 100000)]
|
||||
[TestCase(100000, 1000000)]
|
||||
[TestCase(1000000, 10000000)]
|
||||
public void TestRange(int min, int max)
|
||||
{
|
||||
AddStep("add users", () =>
|
||||
{
|
||||
Client.AddUser(new User
|
||||
{
|
||||
Id = 2,
|
||||
Statistics = { GlobalRank = min }
|
||||
});
|
||||
|
||||
Client.AddUser(new User
|
||||
{
|
||||
Id = 3,
|
||||
Statistics = { GlobalRank = max }
|
||||
});
|
||||
|
||||
// Remove the local user so only the ones above are displayed.
|
||||
Client.RemoveUser(API.LocalUser.Value);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Users.Drawables;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneRecentParticipantsList : OnlinePlayTestScene
|
||||
{
|
||||
private RecentParticipantsList list;
|
||||
|
||||
[SetUp]
|
||||
public new void Setup() => Schedule(() =>
|
||||
{
|
||||
SelectedRoom.Value = new Room { Name = { Value = "test room" } };
|
||||
|
||||
Child = list = new RecentParticipantsList
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
NumberOfCircles = 4
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestCircleCountNearLimit()
|
||||
{
|
||||
AddStep("add 8 users", () =>
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
addUser(i);
|
||||
});
|
||||
|
||||
AddStep("set 8 circles", () => list.NumberOfCircles = 8);
|
||||
AddAssert("0 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 0);
|
||||
|
||||
AddStep("add one more user", () => addUser(9));
|
||||
AddAssert("2 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 2);
|
||||
|
||||
AddStep("remove first user", () => removeUserAt(0));
|
||||
AddAssert("0 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 0);
|
||||
|
||||
AddStep("add one more user", () => addUser(9));
|
||||
AddAssert("2 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 2);
|
||||
|
||||
AddStep("remove last user", () => removeUserAt(8));
|
||||
AddAssert("0 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHiddenUsersBecomeDisplayed()
|
||||
{
|
||||
AddStep("add 8 users", () =>
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
addUser(i);
|
||||
});
|
||||
|
||||
AddStep("set 3 circles", () => list.NumberOfCircles = 3);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
AddStep("remove user", () => removeUserAt(0));
|
||||
int remainingUsers = 7 - i;
|
||||
|
||||
int displayedUsers = remainingUsers > 3 ? 2 : remainingUsers;
|
||||
AddAssert($"{displayedUsers} avatars displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == displayedUsers);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCircleCount()
|
||||
{
|
||||
AddStep("add 50 users", () =>
|
||||
{
|
||||
for (int i = 0; i < 50; i++)
|
||||
addUser(i);
|
||||
});
|
||||
|
||||
AddStep("set 3 circles", () => list.NumberOfCircles = 3);
|
||||
AddAssert("2 users displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 2);
|
||||
AddAssert("48 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 48);
|
||||
|
||||
AddStep("set 10 circles", () => list.NumberOfCircles = 10);
|
||||
AddAssert("9 users displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 9);
|
||||
AddAssert("41 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 41);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAddAndRemoveUsers()
|
||||
{
|
||||
AddStep("add 50 users", () =>
|
||||
{
|
||||
for (int i = 0; i < 50; i++)
|
||||
addUser(i);
|
||||
});
|
||||
|
||||
AddStep("remove from start", () => removeUserAt(0));
|
||||
AddAssert("3 circles displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 3);
|
||||
AddAssert("46 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 46);
|
||||
|
||||
AddStep("remove from end", () => removeUserAt(SelectedRoom.Value.RecentParticipants.Count - 1));
|
||||
AddAssert("3 circles displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 3);
|
||||
AddAssert("45 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 45);
|
||||
|
||||
AddRepeatStep("remove 45 users", () => removeUserAt(0), 45);
|
||||
AddAssert("3 circles displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 3);
|
||||
AddAssert("0 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 0);
|
||||
AddAssert("hidden users bubble hidden", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Alpha < 0.5f);
|
||||
|
||||
AddStep("remove another user", () => removeUserAt(0));
|
||||
AddAssert("2 circles displayed", () => list.ChildrenOfType<UpdateableAvatar>().Count() == 2);
|
||||
AddAssert("0 hidden users", () => list.ChildrenOfType<RecentParticipantsList.HiddenUserCount>().Single().Count == 0);
|
||||
|
||||
AddRepeatStep("remove the remaining two users", () => removeUserAt(0), 2);
|
||||
AddAssert("0 circles displayed", () => !list.ChildrenOfType<UpdateableAvatar>().Any());
|
||||
}
|
||||
|
||||
private void addUser(int id)
|
||||
{
|
||||
SelectedRoom.Value.RecentParticipants.Add(new User
|
||||
{
|
||||
Id = id,
|
||||
Username = $"User {id}"
|
||||
});
|
||||
SelectedRoom.Value.ParticipantCount.Value++;
|
||||
}
|
||||
|
||||
private void removeUserAt(int index)
|
||||
{
|
||||
SelectedRoom.Value.RecentParticipants.RemoveAt(index);
|
||||
SelectedRoom.Value.ParticipantCount.Value--;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Online.Rooms.RoomStatuses;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
public class TestSceneRoomStatus : OsuTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestMultipleStatuses()
|
||||
{
|
||||
AddStep("create rooms", () =>
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 0.5f,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new DrawableRoom(new Room
|
||||
{
|
||||
Name = { Value = "Open - ending in 1 day" },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
EndDate = { Value = DateTimeOffset.Now.AddDays(1) }
|
||||
}) { MatchingFilter = true },
|
||||
new DrawableRoom(new Room
|
||||
{
|
||||
Name = { Value = "Playing - ending in 1 day" },
|
||||
Status = { Value = new RoomStatusPlaying() },
|
||||
EndDate = { Value = DateTimeOffset.Now.AddDays(1) }
|
||||
}) { MatchingFilter = true },
|
||||
new DrawableRoom(new Room
|
||||
{
|
||||
Name = { Value = "Ended" },
|
||||
Status = { Value = new RoomStatusEnded() },
|
||||
EndDate = { Value = DateTimeOffset.Now }
|
||||
}) { MatchingFilter = true },
|
||||
new DrawableRoom(new Room
|
||||
{
|
||||
Name = { Value = "Open" },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
Category = { Value = RoomCategory.Realtime }
|
||||
}) { MatchingFilter = true },
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnableAndDisablePassword()
|
||||
{
|
||||
DrawableRoom drawableRoom = null;
|
||||
Room room = null;
|
||||
|
||||
AddStep("create room", () => Child = drawableRoom = new DrawableRoom(room = new Room
|
||||
{
|
||||
Name = { Value = "Room with password" },
|
||||
Status = { Value = new RoomStatusOpen() },
|
||||
Category = { Value = RoomCategory.Realtime },
|
||||
}) { MatchingFilter = true });
|
||||
|
||||
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha));
|
||||
|
||||
AddStep("set password", () => room.Password.Value = "password");
|
||||
AddAssert("password icon visible", () => Precision.AlmostEquals(1, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha));
|
||||
|
||||
AddStep("unset password", () => room.Password.Value = string.Empty);
|
||||
AddAssert("password icon hidden", () => Precision.AlmostEquals(0, drawableRoom.ChildrenOfType<DrawableRoom.PasswordProtectedIcon>().Single().Alpha));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Participants;
|
||||
@@ -150,10 +151,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void createRoom(Func<Room> room)
|
||||
{
|
||||
AddStep("open room", () =>
|
||||
{
|
||||
multiplayerScreen.OpenNewRoom(room());
|
||||
});
|
||||
AddStep("open room", () => multiplayerScreen.ChildrenOfType<LoungeSubScreen>().Single().Open(room()));
|
||||
|
||||
AddUntilStep("wait for room open", () => this.ChildrenOfType<MultiplayerMatchSubScreen>().FirstOrDefault()?.IsLoaded == true);
|
||||
AddWaitStep("wait for transition", 2);
|
||||
|
||||
+19
-14
@@ -30,10 +30,9 @@ using osu.Game.Skinning;
|
||||
using osu.Game.Utils;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Tests.Visual
|
||||
namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
[TestFixture]
|
||||
[HeadlessTest]
|
||||
public class TestSceneOsuGame : OsuTestScene
|
||||
{
|
||||
private IReadOnlyList<Type> requiredGameDependencies => new[]
|
||||
@@ -90,21 +89,27 @@ namespace osu.Game.Tests.Visual
|
||||
[Resolved]
|
||||
private OsuGameBase gameBase { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host)
|
||||
{
|
||||
game = new OsuGame();
|
||||
game.SetHost(host);
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
|
||||
Children = new Drawable[]
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("create game", () =>
|
||||
{
|
||||
new Box
|
||||
game = new OsuGame();
|
||||
game.SetHost(host);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
game
|
||||
};
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
game
|
||||
};
|
||||
});
|
||||
|
||||
AddUntilStep("wait for load", () => game.IsLoaded);
|
||||
}
|
||||
@@ -16,6 +16,7 @@ using osu.Game.Overlays.Toolbar;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Screens.OnlinePlay.Components;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Screens.Select;
|
||||
@@ -316,7 +317,8 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
|
||||
PushAndConfirm(() => multiplayer = new TestMultiplayer());
|
||||
|
||||
AddStep("open room", () => multiplayer.OpenNewRoom());
|
||||
AddUntilStep("wait for lounge", () => multiplayer.ChildrenOfType<LoungeSubScreen>().SingleOrDefault()?.IsLoaded == true);
|
||||
AddStep("open room", () => multiplayer.ChildrenOfType<LoungeSubScreen>().Single().Open());
|
||||
AddStep("press back button", () => Game.ChildrenOfType<BackButton>().First().Action());
|
||||
AddWaitStep("wait two frames", 2);
|
||||
}
|
||||
|
||||
@@ -9,9 +9,12 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
{
|
||||
public class TestSettingsMigration : OsuGameTestScene
|
||||
{
|
||||
public override void RecycleLocalStorage()
|
||||
public override void RecycleLocalStorage(bool isDisposing)
|
||||
{
|
||||
base.RecycleLocalStorage();
|
||||
base.RecycleLocalStorage(isDisposing);
|
||||
|
||||
if (isDisposing)
|
||||
return;
|
||||
|
||||
using (var config = new OsuConfigManager(LocalStorage))
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Humanizer;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
@@ -95,9 +96,11 @@ namespace osu.Game.Tests.Visual.Online
|
||||
AddAssert(@"no stream selected", () => changelog.Header.Streams.Current.Value == null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ShowWithBuild()
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void ShowWithBuild(bool isSupporter)
|
||||
{
|
||||
AddStep(@"set supporter", () => dummyAPI.LocalUser.Value.IsSupporter = isSupporter);
|
||||
showBuild(() => new APIChangelogBuild
|
||||
{
|
||||
Version = "2018.712.0",
|
||||
@@ -155,6 +158,8 @@ namespace osu.Game.Tests.Visual.Online
|
||||
AddUntilStep(@"wait for streams", () => changelog.Streams?.Count > 0);
|
||||
AddAssert(@"correct build displayed", () => changelog.Current.Value.Version == "2018.712.0");
|
||||
AddAssert(@"correct stream selected", () => changelog.Header.Streams.Current.Value.Id == 5);
|
||||
AddUntilStep(@"wait for content load", () => changelog.ChildrenOfType<ChangelogSupporterPromo>().Any());
|
||||
AddAssert(@"supporter promo showed", () => changelog.ChildrenOfType<ChangelogSupporterPromo>().First().Alpha == (isSupporter ? 0 : 1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -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.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Changelog;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public class TestSceneChangelogSupporterPromo : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||
|
||||
public TestSceneChangelogSupporterPromo()
|
||||
{
|
||||
Child = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = colourProvider.Background4,
|
||||
},
|
||||
new ChangelogSupporterPromo(),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
-60
@@ -3,84 +3,52 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Overlays.Comments;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Users;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics;
|
||||
using osuTK;
|
||||
using JetBrains.Annotations;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Online
|
||||
{
|
||||
public class TestSceneCommentsPage : OsuTestScene
|
||||
public class TestSceneOfflineCommentsContainer : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
private readonly BindableBool showDeleted = new BindableBool();
|
||||
private readonly Container content;
|
||||
private TestCommentsContainer comments;
|
||||
|
||||
private TestCommentsPage commentsPage;
|
||||
|
||||
public TestSceneCommentsPage()
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
Add(new FillFlowContainer
|
||||
Clear();
|
||||
Add(new BasicScrollContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(0, 10),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 200,
|
||||
Child = new OsuCheckbox
|
||||
{
|
||||
Current = showDeleted,
|
||||
LabelText = @"Show Deleted"
|
||||
}
|
||||
},
|
||||
content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
}
|
||||
}
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = comments = new TestCommentsContainer()
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestAppendDuplicatedComment()
|
||||
{
|
||||
AddStep("Create page", () => createPage(getCommentBundle()));
|
||||
AddAssert("Dictionary length is 10", () => commentsPage?.DictionaryLength == 10);
|
||||
AddStep("Append existing comment", () => commentsPage?.AppendComments(getCommentSubBundle()));
|
||||
AddAssert("Dictionary length is 10", () => commentsPage?.DictionaryLength == 10);
|
||||
AddStep("Add comment bundle", () => comments.ShowComments(getCommentBundle()));
|
||||
AddUntilStep("Dictionary length is 10", () => comments.DictionaryLength == 10);
|
||||
AddStep("Append existing comment", () => comments.AppendComments(getCommentSubBundle()));
|
||||
AddAssert("Dictionary length is 10", () => comments.DictionaryLength == 10);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmptyBundle()
|
||||
public void TestLocalCommentBundle()
|
||||
{
|
||||
AddStep("Create page", () => createPage(getEmptyCommentBundle()));
|
||||
AddAssert("Dictionary length is 0", () => commentsPage?.DictionaryLength == 0);
|
||||
}
|
||||
|
||||
private void createPage(CommentBundle commentBundle)
|
||||
{
|
||||
commentsPage = null;
|
||||
content.Clear();
|
||||
content.Add(commentsPage = new TestCommentsPage(commentBundle)
|
||||
{
|
||||
ShowDeleted = { BindTarget = showDeleted }
|
||||
});
|
||||
AddStep("Add comment bundle", () => comments.ShowComments(getCommentBundle()));
|
||||
AddStep("Add empty comment bundle", () => comments.ShowComments(getEmptyCommentBundle()));
|
||||
}
|
||||
|
||||
private CommentBundle getEmptyCommentBundle() => new CommentBundle
|
||||
@@ -193,6 +161,7 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Username = "Good_Admin"
|
||||
}
|
||||
},
|
||||
Total = 10
|
||||
};
|
||||
|
||||
private CommentBundle getCommentSubBundle() => new CommentBundle
|
||||
@@ -211,16 +180,18 @@ namespace osu.Game.Tests.Visual.Online
|
||||
IncludedComments = new List<Comment>(),
|
||||
};
|
||||
|
||||
private class TestCommentsPage : CommentsPage
|
||||
private class TestCommentsContainer : CommentsContainer
|
||||
{
|
||||
public TestCommentsPage(CommentBundle commentBundle)
|
||||
: base(commentBundle)
|
||||
{
|
||||
}
|
||||
|
||||
public new void AppendComments([NotNull] CommentBundle bundle) => base.AppendComments(bundle);
|
||||
|
||||
public int DictionaryLength => CommentDictionary.Count;
|
||||
|
||||
public void ShowComments(CommentBundle bundle)
|
||||
{
|
||||
this.ChildrenOfType<TotalCommentsCounter>().Single().Current.Value = 0;
|
||||
ClearComments();
|
||||
OnSuccess(bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +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 osu.Framework.Graphics;
|
||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
public class TestScenePlaylistsFilterControl : OsuTestScene
|
||||
{
|
||||
public TestScenePlaylistsFilterControl()
|
||||
{
|
||||
Child = new PlaylistsFilterControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.7f,
|
||||
Height = 80,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
public class TestScenePlaylistsLoungeSubScreen : OnlinePlayTestScene
|
||||
{
|
||||
protected new BasicTestRoomManager RoomManager => (BasicTestRoomManager)base.RoomManager;
|
||||
protected new TestRequestHandlingRoomManager RoomManager => (TestRequestHandlingRoomManager)base.RoomManager;
|
||||
|
||||
private LoungeSubScreen loungeScreen;
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddStep("reset mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(30));
|
||||
AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
|
||||
|
||||
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
|
||||
|
||||
@@ -53,6 +54,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
public void TestScrollSelectedIntoView()
|
||||
{
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(30));
|
||||
AddUntilStep("wait for rooms", () => roomsContainer.Rooms.Count == 30);
|
||||
|
||||
AddUntilStep("first room is not masked", () => checkRoomVisible(roomsContainer.Rooms[0]));
|
||||
|
||||
@@ -62,6 +64,24 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms[^1]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnteringRoomTakesLeaseOnSelection()
|
||||
{
|
||||
AddStep("add rooms", () => RoomManager.AddRooms(1));
|
||||
|
||||
AddAssert("selected room is not disabled", () => !OnlinePlayDependencies.SelectedRoom.Disabled);
|
||||
|
||||
AddStep("select room", () => roomsContainer.Rooms[0].TriggerClick());
|
||||
AddAssert("selected room is non-null", () => OnlinePlayDependencies.SelectedRoom.Value != null);
|
||||
|
||||
AddStep("enter room", () => roomsContainer.Rooms[0].TriggerClick());
|
||||
|
||||
AddUntilStep("wait for match load", () => Stack.CurrentScreen is PlaylistsRoomSubScreen);
|
||||
|
||||
AddAssert("selected room is non-null", () => OnlinePlayDependencies.SelectedRoom.Value != null);
|
||||
AddAssert("selected room is disabled", () => OnlinePlayDependencies.SelectedRoom.Disabled);
|
||||
}
|
||||
|
||||
private bool checkRoomVisible(DrawableRoom room) =>
|
||||
loungeScreen.ChildrenOfType<OsuScrollContainer>().First().ScreenSpaceDrawQuad
|
||||
.Contains(room.ScreenSpaceDrawQuad.Centre);
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddUntilStep("error not displayed", () => !settings.ErrorText.IsPresent);
|
||||
}
|
||||
|
||||
private class TestRoomSettings : PlaylistsMatchSettingsOverlay
|
||||
private class TestRoomSettings : PlaylistsRoomSettingsOverlay
|
||||
{
|
||||
public TriangleButton ApplyButton => ((MatchSettings)Settings).ApplyButton;
|
||||
|
||||
@@ -141,6 +141,12 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
public IBindableList<Room> Rooms => null;
|
||||
|
||||
public void AddOrUpdateRoom(Room room) => throw new NotImplementedException();
|
||||
|
||||
public void RemoveRoom(Room room) => throw new NotImplementedException();
|
||||
|
||||
public void ClearRooms() => throw new NotImplementedException();
|
||||
|
||||
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||
{
|
||||
if (CreateRequested == null)
|
||||
|
||||
@@ -11,7 +11,6 @@ using osu.Framework.Platform;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
@@ -19,7 +18,6 @@ using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Tests.Visual.OnlinePlay;
|
||||
using osu.Game.Users;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Playlists
|
||||
@@ -36,18 +34,6 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, Resources, host, Beatmap.Default));
|
||||
|
||||
((DummyAPIAccess)API).HandleRequest = req =>
|
||||
{
|
||||
switch (req)
|
||||
{
|
||||
case CreateRoomScoreRequest createRoomScoreRequest:
|
||||
createRoomScoreRequest.TriggerSuccess(new APIScoreToken { ID = 1 });
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
@@ -66,7 +52,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
{
|
||||
SelectedRoom.Value.RoomID.Value = 1;
|
||||
SelectedRoom.Value.Name.Value = "my awesome room";
|
||||
SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
|
||||
SelectedRoom.Value.Host.Value = API.LocalUser.Value;
|
||||
SelectedRoom.Value.RecentParticipants.Add(SelectedRoom.Value.Host.Value);
|
||||
SelectedRoom.Value.EndDate.Value = DateTimeOffset.Now.AddMinutes(5);
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
@@ -86,7 +72,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddStep("set room properties", () =>
|
||||
{
|
||||
SelectedRoom.Value.Name.Value = "my awesome room";
|
||||
SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
|
||||
SelectedRoom.Value.Host.Value = API.LocalUser.Value;
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
|
||||
@@ -96,7 +82,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
AddStep("move mouse to create button", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<PlaylistsMatchSettingsOverlay.CreateRoomButton>().Single());
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<PlaylistsRoomSettingsOverlay.CreateRoomButton>().Single());
|
||||
});
|
||||
|
||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||
@@ -137,7 +123,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
AddStep("load room", () =>
|
||||
{
|
||||
SelectedRoom.Value.Name.Value = "my awesome room";
|
||||
SelectedRoom.Value.Host.Value = new User { Id = 2, Username = "peppy" };
|
||||
SelectedRoom.Value.Host.Value = API.LocalUser.Value;
|
||||
SelectedRoom.Value.Playlist.Add(new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = importedSet.Beatmaps[0] },
|
||||
@@ -147,7 +133,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
||||
|
||||
AddStep("create room", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(match.ChildrenOfType<PlaylistsMatchSettingsOverlay.CreateRoomButton>().Single());
|
||||
InputManager.MoveMouseTo(match.ChildrenOfType<PlaylistsRoomSettingsOverlay.CreateRoomButton>().Single());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,65 +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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Screens.Ranking.Expanded;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Ranking
|
||||
{
|
||||
public class TestSceneStarRatingDisplay : OsuTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestDisplay()
|
||||
{
|
||||
AddStep("load displays", () => Child = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
ChildrenEnumerable = new[]
|
||||
{
|
||||
1.23,
|
||||
2.34,
|
||||
3.45,
|
||||
4.56,
|
||||
5.67,
|
||||
6.78,
|
||||
10.11,
|
||||
}.Select(starRating => new StarRatingDisplay(new StarDifficulty(starRating, 0))
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangingStarRatingDisplay()
|
||||
{
|
||||
StarRatingDisplay starRating = null;
|
||||
|
||||
AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1))
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(3f),
|
||||
});
|
||||
|
||||
AddRepeatStep("set random value", () =>
|
||||
{
|
||||
starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1);
|
||||
}, 10);
|
||||
|
||||
AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d =>
|
||||
{
|
||||
if (starRating != null)
|
||||
starRating.Current.Value = new StarDifficulty(d, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddUntilStep("wait for load", () => panel.ChildrenOfType<GlobalKeyBindingsSection>().Any());
|
||||
AddStep("Scroll to top", () => panel.ChildrenOfType<SettingsPanel.SettingsSectionsContainer>().First().ScrollToTop());
|
||||
AddWaitStep("wait for scroll", 5);
|
||||
}
|
||||
|
||||
@@ -76,5 +76,23 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
AddStep("restore default", () => sliderBar.Current.SetDefault());
|
||||
AddUntilStep("restore button hidden", () => restoreDefaultValueButton.Alpha == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWarningTextVisibility()
|
||||
{
|
||||
SettingsNumberBox numberBox = null;
|
||||
|
||||
AddStep("create settings item", () => Child = numberBox = new SettingsNumberBox());
|
||||
AddAssert("warning text not created", () => !numberBox.ChildrenOfType<SettingsNoticeText>().Any());
|
||||
|
||||
AddStep("set warning text", () => numberBox.WarningText = "this is a warning!");
|
||||
AddAssert("warning text created", () => numberBox.ChildrenOfType<SettingsNoticeText>().Single().Alpha == 1);
|
||||
|
||||
AddStep("unset warning text", () => numberBox.WarningText = default);
|
||||
AddAssert("warning text hidden", () => numberBox.ChildrenOfType<SettingsNoticeText>().Single().Alpha == 0);
|
||||
|
||||
AddStep("set warning text again", () => numberBox.WarningText = "another warning!");
|
||||
AddAssert("warning text shown again", () => numberBox.ChildrenOfType<SettingsNoticeText>().Single().Alpha == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Settings
|
||||
@@ -11,27 +12,39 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
[TestFixture]
|
||||
public class TestSceneSettingsPanel : OsuTestScene
|
||||
{
|
||||
private readonly SettingsPanel settings;
|
||||
private readonly DialogOverlay dialogOverlay;
|
||||
private SettingsPanel settings;
|
||||
private DialogOverlay dialogOverlay;
|
||||
|
||||
public TestSceneSettingsPanel()
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
settings = new SettingsOverlay
|
||||
AddStep("create settings", () =>
|
||||
{
|
||||
State = { Value = Visibility.Visible }
|
||||
};
|
||||
Add(dialogOverlay = new DialogOverlay
|
||||
{
|
||||
Depth = -1
|
||||
settings?.Expire();
|
||||
|
||||
Add(settings = new SettingsOverlay
|
||||
{
|
||||
State = { Value = Visibility.Visible }
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ToggleVisibility()
|
||||
{
|
||||
AddWaitStep("wait some", 5);
|
||||
AddToggleStep("toggle editor visibility", visible => settings.ToggleVisibility());
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Dependencies.Cache(dialogOverlay);
|
||||
Add(dialogOverlay = new DialogOverlay
|
||||
{
|
||||
Depth = -1
|
||||
});
|
||||
|
||||
Add(settings);
|
||||
Dependencies.Cache(dialogOverlay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Settings
|
||||
new TabletSettings(tabletHandler)
|
||||
{
|
||||
RelativeSizeAxes = Axes.None,
|
||||
Width = SettingsPanel.WIDTH,
|
||||
Width = SettingsPanel.PANEL_WIDTH,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@@ -65,6 +66,12 @@ namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
AddStep("show", () => { infoWedge.Show(); });
|
||||
|
||||
AddSliderStep("change star difficulty", 0, 11.9, 5.55, v =>
|
||||
{
|
||||
foreach (var hasCurrentValue in infoWedge.Info.ChildrenOfType<IHasCurrentValue<StarDifficulty>>())
|
||||
hasCurrentValue.Current.Value = new StarDifficulty(v, 0);
|
||||
});
|
||||
|
||||
foreach (var rulesetInfo in rulesets.AvailableRulesets)
|
||||
{
|
||||
var instance = rulesetInfo.CreateInstance();
|
||||
|
||||
@@ -14,7 +14,6 @@ using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Tests.Visual.Navigation;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.Visual.SongSelect
|
||||
|
||||
@@ -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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -68,13 +70,40 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
);
|
||||
}
|
||||
|
||||
private class MyContextMenuContainer : Container, IHasContextMenu
|
||||
private static MenuItem[] makeMenu()
|
||||
{
|
||||
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||
return new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem(@"Some option"),
|
||||
new OsuMenuItem(@"Highlighted option", MenuItemType.Highlighted),
|
||||
new OsuMenuItem(@"Another option"),
|
||||
new OsuMenuItem(@"Nested option >")
|
||||
{
|
||||
Items = new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem(@"Sub-One"),
|
||||
new OsuMenuItem(@"Sub-Two"),
|
||||
new OsuMenuItem(@"Sub-Three"),
|
||||
new OsuMenuItem(@"Sub-Nested option >")
|
||||
{
|
||||
Items = new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem(@"Double Sub-One"),
|
||||
new OsuMenuItem(@"Double Sub-Two"),
|
||||
new OsuMenuItem(@"Double Sub-Three"),
|
||||
new OsuMenuItem(@"Sub-Sub-Nested option >")
|
||||
{
|
||||
Items = new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem(@"Too Deep One"),
|
||||
new OsuMenuItem(@"Too Deep Two"),
|
||||
new OsuMenuItem(@"Too Deep Three"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new OsuMenuItem(@"Choose me please"),
|
||||
new OsuMenuItem(@"And me too"),
|
||||
new OsuMenuItem(@"Trying to fill"),
|
||||
@@ -82,17 +111,29 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
};
|
||||
}
|
||||
|
||||
private class MyContextMenuContainer : Container, IHasContextMenu
|
||||
{
|
||||
public MenuItem[] ContextMenuItems => makeMenu();
|
||||
}
|
||||
|
||||
private class AnotherContextMenuContainer : Container, IHasContextMenu
|
||||
{
|
||||
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||
public MenuItem[] ContextMenuItems
|
||||
{
|
||||
new OsuMenuItem(@"Simple option"),
|
||||
new OsuMenuItem(@"Simple very very long option"),
|
||||
new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)),
|
||||
new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)),
|
||||
new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)),
|
||||
new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)),
|
||||
};
|
||||
get
|
||||
{
|
||||
List<MenuItem> items = makeMenu().ToList();
|
||||
items.AddRange(new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem(@"Change width", MenuItemType.Highlighted, () => this.ResizeWidthTo(Width * 2, 100, Easing.OutQuint)),
|
||||
new OsuMenuItem(@"Change height", MenuItemType.Highlighted, () => this.ResizeHeightTo(Height * 2, 100, Easing.OutQuint)),
|
||||
new OsuMenuItem(@"Change width back", MenuItemType.Destructive, () => this.ResizeWidthTo(Width / 2, 100, Easing.OutQuint)),
|
||||
new OsuMenuItem(@"Change height back", MenuItemType.Destructive, () => this.ResizeHeightTo(Height / 2, 100, Easing.OutQuint)),
|
||||
});
|
||||
|
||||
return items.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// 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.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@@ -174,6 +176,60 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
checkBindableAtValue("Circle Size", null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestModSettingChangeTracker()
|
||||
{
|
||||
ModSettingChangeTracker tracker = null;
|
||||
Queue<Mod> settingsChangedQueue = null;
|
||||
|
||||
setBeatmapWithDifficultyParameters(5);
|
||||
|
||||
AddStep("add mod settings change tracker", () =>
|
||||
{
|
||||
settingsChangedQueue = new Queue<Mod>();
|
||||
|
||||
tracker = new ModSettingChangeTracker(modDifficultyAdjust.Yield())
|
||||
{
|
||||
SettingChanged = settingsChangedQueue.Enqueue
|
||||
};
|
||||
});
|
||||
|
||||
AddAssert("no settings changed", () => settingsChangedQueue.Count == 0);
|
||||
|
||||
setSliderValue("Circle Size", 3);
|
||||
|
||||
settingsChangedFired();
|
||||
|
||||
setSliderValue("Circle Size", 5);
|
||||
checkBindableAtValue("Circle Size", 5);
|
||||
|
||||
settingsChangedFired();
|
||||
|
||||
AddStep("reset mod settings", () => modDifficultyAdjust.CircleSize.SetDefault());
|
||||
checkBindableAtValue("Circle Size", null);
|
||||
|
||||
settingsChangedFired();
|
||||
|
||||
setExtendedLimits(true);
|
||||
|
||||
settingsChangedFired();
|
||||
|
||||
AddStep("dispose tracker", () =>
|
||||
{
|
||||
tracker.Dispose();
|
||||
tracker = null;
|
||||
});
|
||||
|
||||
void settingsChangedFired()
|
||||
{
|
||||
AddAssert("setting changed event fired", () =>
|
||||
{
|
||||
settingsChangedQueue.Dequeue();
|
||||
return settingsChangedQueue.Count == 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void resetToDefault(string name)
|
||||
{
|
||||
AddStep($"Reset {name} to default", () =>
|
||||
|
||||
@@ -6,6 +6,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@@ -61,10 +62,12 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
));
|
||||
AddStep("scroll up", () => triggerUserScroll(1));
|
||||
AddStep("scroll down", () => triggerUserScroll(-1));
|
||||
AddStep("scroll up a bit", () => triggerUserScroll(0.1f));
|
||||
AddStep("scroll down a bit", () => triggerUserScroll(-0.1f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCorrectSectionSelected()
|
||||
public void TestCorrectSelectionAndVisibleTop()
|
||||
{
|
||||
const int sections_count = 11;
|
||||
float[] alternating = { 0.07f, 0.33f, 0.16f, 0.33f };
|
||||
@@ -79,6 +82,12 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
AddStep($"scroll to section {scrollIndex + 1}", () => container.ScrollTo(container.Children[scrollIndex]));
|
||||
AddUntilStep("correct section selected", () => container.SelectedSection.Value == container.Children[scrollIndex]);
|
||||
AddUntilStep("section top is visible", () =>
|
||||
{
|
||||
float scrollPosition = container.ChildrenOfType<UserTrackingScrollContainer>().First().Current;
|
||||
float sectionTop = container.Children[scrollIndex].BoundingBox.Top;
|
||||
return scrollPosition < sectionTop;
|
||||
});
|
||||
}
|
||||
|
||||
for (int i = 1; i < sections_count; i++)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public class TestSceneStarRatingDisplay : OsuTestScene
|
||||
{
|
||||
[TestCase(StarRatingDisplaySize.Regular)]
|
||||
[TestCase(StarRatingDisplaySize.Small)]
|
||||
public void TestDisplay(StarRatingDisplaySize size)
|
||||
{
|
||||
AddStep("load displays", () =>
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(2f),
|
||||
Direction = FillDirection.Horizontal,
|
||||
ChildrenEnumerable = Enumerable.Range(0, 15).Select(i => new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Spacing = new Vector2(2f),
|
||||
Direction = FillDirection.Vertical,
|
||||
ChildrenEnumerable = Enumerable.Range(0, 10).Select(j => new StarRatingDisplay(new StarDifficulty(i * (i >= 11 ? 25f : 1f) + j * 0.1f, 0), size)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}),
|
||||
})
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSpectrum()
|
||||
{
|
||||
StarRatingDisplay starRating = null;
|
||||
|
||||
AddStep("load display", () => Child = starRating = new StarRatingDisplay(new StarDifficulty(5.55, 1), animated: true)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(3f),
|
||||
});
|
||||
|
||||
AddRepeatStep("set random value", () =>
|
||||
{
|
||||
starRating.Current.Value = new StarDifficulty(RNG.NextDouble(0.0, 11.0), 1);
|
||||
}, 10);
|
||||
|
||||
AddSliderStep("set exact stars", 0.0, 11.0, 5.55, d =>
|
||||
{
|
||||
if (starRating != null)
|
||||
starRating.Current.Value = new StarDifficulty(d, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ namespace osu.Game.Tests
|
||||
|
||||
protected override Waveform GetWaveform() => new Waveform(trackStore.GetStream(firstAudioFile));
|
||||
|
||||
protected override ISkin GetSkin() => null;
|
||||
protected internal override ISkin GetSkin() => null;
|
||||
|
||||
public override Stream GetStream(string storagePath) => null;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace osu.Game.Tournament.Tests.Components
|
||||
|
||||
public TestSceneMatchScoreDisplay()
|
||||
{
|
||||
Add(new MatchScoreDisplay
|
||||
Add(new TournamentMatchScoreDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
||||
+3
-2
@@ -16,7 +16,8 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
{
|
||||
public class MatchScoreDisplay : CompositeDrawable
|
||||
// TODO: Update to derive from osu-side class?
|
||||
public class TournamentMatchScoreDisplay : CompositeDrawable
|
||||
{
|
||||
private const float bar_height = 18;
|
||||
|
||||
@@ -29,7 +30,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
private readonly Drawable score1Bar;
|
||||
private readonly Drawable score2Bar;
|
||||
|
||||
public MatchScoreDisplay()
|
||||
public TournamentMatchScoreDisplay()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
@@ -86,7 +86,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
||||
},
|
||||
}
|
||||
},
|
||||
scoreDisplay = new MatchScoreDisplay
|
||||
scoreDisplay = new TournamentMatchScoreDisplay
|
||||
{
|
||||
Y = -147,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
@@ -148,7 +148,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
||||
}
|
||||
|
||||
private ScheduledDelegate scheduledOperation;
|
||||
private MatchScoreDisplay scoreDisplay;
|
||||
private TournamentMatchScoreDisplay scoreDisplay;
|
||||
|
||||
private TourneyState lastState;
|
||||
private MatchHeader header;
|
||||
|
||||
@@ -26,8 +26,8 @@ namespace osu.Game.Tournament
|
||||
{
|
||||
public static ColourInfo GetTeamColour(TeamColour teamColour) => teamColour == TeamColour.Red ? COLOUR_RED : COLOUR_BLUE;
|
||||
|
||||
public static readonly Color4 COLOUR_RED = Color4Extensions.FromHex("#AA1414");
|
||||
public static readonly Color4 COLOUR_BLUE = Color4Extensions.FromHex("#1462AA");
|
||||
public static readonly Color4 COLOUR_RED = new OsuColour().TeamColourRed;
|
||||
public static readonly Color4 COLOUR_BLUE = new OsuColour().TeamColourBlue;
|
||||
|
||||
public static readonly Color4 ELEMENT_BACKGROUND_COLOUR = Color4Extensions.FromHex("#fff");
|
||||
public static readonly Color4 ELEMENT_FOREGROUND_COLOUR = Color4Extensions.FromHex("#000");
|
||||
|
||||
@@ -14,6 +14,7 @@ using osu.Framework.Lists;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
@@ -56,12 +57,28 @@ namespace osu.Game.Beatmaps
|
||||
[Resolved]
|
||||
private Bindable<IReadOnlyList<Mod>> currentMods { get; set; }
|
||||
|
||||
private ModSettingChangeTracker modSettingChangeTracker;
|
||||
private ScheduledDelegate debouncedModSettingsChange;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
currentRuleset.BindValueChanged(_ => updateTrackedBindables());
|
||||
currentMods.BindValueChanged(_ => updateTrackedBindables(), true);
|
||||
|
||||
currentMods.BindValueChanged(mods =>
|
||||
{
|
||||
modSettingChangeTracker?.Dispose();
|
||||
|
||||
updateTrackedBindables();
|
||||
|
||||
modSettingChangeTracker = new ModSettingChangeTracker(mods.NewValue);
|
||||
modSettingChangeTracker.SettingChanged += _ =>
|
||||
{
|
||||
debouncedModSettingsChange?.Cancel();
|
||||
debouncedModSettingsChange = Scheduler.AddDelayed(updateTrackedBindables, 100);
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,7 +101,7 @@ namespace osu.Game.Beatmaps
|
||||
/// Retrieves a bindable containing the star difficulty of a <see cref="BeatmapInfo"/> with a given <see cref="RulesetInfo"/> and <see cref="Mod"/> combination.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The bindable will not update to follow the currently-selected ruleset and mods.
|
||||
/// The bindable will not update to follow the currently-selected ruleset and mods or its settings.
|
||||
/// </remarks>
|
||||
/// <param name="beatmapInfo">The <see cref="BeatmapInfo"/> to get the difficulty of.</param>
|
||||
/// <param name="rulesetInfo">The <see cref="RulesetInfo"/> to get the difficulty with. If <c>null</c>, the <paramref name="beatmapInfo"/>'s ruleset is used.</param>
|
||||
@@ -275,6 +292,8 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
modSettingChangeTracker?.Dispose();
|
||||
|
||||
cancelTrackedBindableUpdate();
|
||||
updateScheduler?.Dispose();
|
||||
}
|
||||
@@ -297,7 +316,7 @@ namespace osu.Game.Beatmaps
|
||||
public bool Equals(DifficultyCacheLookup other)
|
||||
=> Beatmap.ID == other.Beatmap.ID
|
||||
&& Ruleset.ID == other.Ruleset.ID
|
||||
&& OrderedMods.Select(m => m.Acronym).SequenceEqual(other.OrderedMods.Select(m => m.Acronym));
|
||||
&& OrderedMods.SequenceEqual(other.OrderedMods);
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
@@ -307,7 +326,7 @@ namespace osu.Game.Beatmaps
|
||||
hashCode.Add(Ruleset.ID);
|
||||
|
||||
foreach (var mod in OrderedMods)
|
||||
hashCode.Add(mod.Acronym);
|
||||
hashCode.Add(mod);
|
||||
|
||||
return hashCode.ToHashCode();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user