mirror of
https://github.com/ppy/osu.git
synced 2025-01-29 01:22:54 +08:00
Merge branch 'master' into open-tablet-driver-faq
This commit is contained in:
commit
4d31d07c51
@ -51,7 +51,7 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.810.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.813.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.813.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.813.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
|
@ -40,6 +40,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
{
|
{
|
||||||
AddSliderStep<float>("circle size", 0, 8, 5, createCatcher);
|
AddSliderStep<float>("circle size", 0, 8, 5, createCatcher);
|
||||||
AddToggleStep("hyper dash", t => this.ChildrenOfType<TestCatcherArea>().ForEach(area => area.ToggleHyperDash(t)));
|
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 centered fruit", () => attemptCatch(new Fruit()));
|
||||||
AddStep("catch many random fruit", () =>
|
AddStep("catch many random fruit", () =>
|
||||||
|
@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
Banana,
|
Banana,
|
||||||
Droplet,
|
Droplet,
|
||||||
Catcher,
|
Catcher,
|
||||||
CatchComboCounter
|
CatchComboCounter,
|
||||||
|
HitExplosion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
129
osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs
Normal file
129
osu.Game.Rulesets.Catch/Skinning/Default/DefaultHitExplosion.cs
Normal file
@ -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 (version < 2.3m)
|
||||||
{
|
{
|
||||||
if (GetTexture(@"fruit-ryuuta") != null ||
|
if (hasOldStyleCatcherSprite())
|
||||||
GetTexture(@"fruit-ryuuta-0") != null)
|
|
||||||
return new LegacyCatcherOld();
|
return new LegacyCatcherOld();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetTexture(@"fruit-catcher-idle") != null ||
|
if (hasNewStyleCatcherSprite())
|
||||||
GetTexture(@"fruit-catcher-idle-0") != null)
|
|
||||||
return new LegacyCatcherNew();
|
return new LegacyCatcherNew();
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -86,12 +84,26 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
|||||||
return new LegacyCatchComboCounter(Skin);
|
return new LegacyCatchComboCounter(Skin);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
case CatchSkinComponents.HitExplosion:
|
||||||
|
if (hasOldStyleCatcherSprite() || hasNewStyleCatcherSprite())
|
||||||
|
return new LegacyHitExplosion();
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.GetDrawableComponent(component);
|
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)
|
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
{
|
{
|
||||||
switch (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
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
public class Catcher : SkinReloadableDrawable
|
public class Catcher : SkinReloadableDrawable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -106,7 +107,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Width of the area that can be used to attempt catches during gameplay.
|
/// Width of the area that can be used to attempt catches during gameplay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly float catchWidth;
|
public readonly float CatchWidth;
|
||||||
|
|
||||||
private readonly SkinnableCatcher body;
|
private readonly SkinnableCatcher body;
|
||||||
|
|
||||||
@ -133,7 +134,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
if (difficulty != null)
|
if (difficulty != null)
|
||||||
Scale = calculateScale(difficulty);
|
Scale = calculateScale(difficulty);
|
||||||
|
|
||||||
catchWidth = CalculateCatchWidth(Scale);
|
CatchWidth = CalculateCatchWidth(Scale);
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -193,7 +194,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
if (!(hitObject is PalpableCatchHitObject fruit))
|
if (!(hitObject is PalpableCatchHitObject fruit))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
float halfCatchWidth = catchWidth * 0.5f;
|
float halfCatchWidth = CatchWidth * 0.5f;
|
||||||
return fruit.EffectiveX >= X - halfCatchWidth &&
|
return fruit.EffectiveX >= X - halfCatchWidth &&
|
||||||
fruit.EffectiveX <= X + halfCatchWidth;
|
fruit.EffectiveX <= X + halfCatchWidth;
|
||||||
}
|
}
|
||||||
@ -216,7 +217,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
placeCaughtObject(palpableObject, positionInStack);
|
placeCaughtObject(palpableObject, positionInStack);
|
||||||
|
|
||||||
if (hitLighting.Value)
|
if (hitLighting.Value)
|
||||||
addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value);
|
addLighting(result, drawableObject.AccentColour.Value, positionInStack.X);
|
||||||
}
|
}
|
||||||
|
|
||||||
// droplet doesn't affect the catcher state
|
// droplet doesn't affect the catcher state
|
||||||
@ -365,8 +366,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addLighting(CatchHitObject hitObject, float x, Color4 colour) =>
|
private void addLighting(JudgementResult judgementResult, Color4 colour, float x) =>
|
||||||
hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, x, hitObject.Scale, colour, hitObject.RandomSeed));
|
hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, judgementResult, colour, x));
|
||||||
|
|
||||||
private CaughtObject getCaughtObject(PalpableCatchHitObject source)
|
private CaughtObject getCaughtObject(PalpableCatchHitObject source)
|
||||||
{
|
{
|
||||||
|
@ -1,129 +1,56 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// 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;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||||
using osu.Framework.Graphics.Effects;
|
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Rulesets.Objects.Pooling;
|
using osu.Game.Rulesets.Objects.Pooling;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
|
||||||
using osuTK.Graphics;
|
#nullable enable
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
public class HitExplosion : PoolableDrawableWithLifetime<HitExplosionEntry>
|
public class HitExplosion : PoolableDrawableWithLifetime<HitExplosionEntry>
|
||||||
{
|
{
|
||||||
private readonly CircularContainer largeFaint;
|
private readonly SkinnableDrawable skinnableExplosion;
|
||||||
private readonly CircularContainer smallFaint;
|
|
||||||
private readonly CircularContainer directionalGlow1;
|
|
||||||
private readonly CircularContainer directionalGlow2;
|
|
||||||
|
|
||||||
public HitExplosion()
|
public HitExplosion()
|
||||||
{
|
{
|
||||||
Size = new Vector2(20);
|
RelativeSizeAxes = Axes.Both;
|
||||||
Anchor = Anchor.TopCentre;
|
Anchor = Anchor.BottomCentre;
|
||||||
Origin = Anchor.BottomCentre;
|
Origin = Anchor.BottomCentre;
|
||||||
|
|
||||||
// scale roughly in-line with visual appearance of notes
|
InternalChild = skinnableExplosion = new SkinnableDrawable(new CatchSkinComponent(CatchSkinComponents.HitExplosion), _ => new DefaultHitExplosion())
|
||||||
const float initial_height = 10;
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
|
||||||
{
|
{
|
||||||
largeFaint = new CircularContainer
|
CentreComponent = false,
|
||||||
{
|
Anchor = Anchor.BottomCentre,
|
||||||
Anchor = Anchor.Centre,
|
Origin = Anchor.BottomCentre
|
||||||
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,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnApply(HitExplosionEntry entry)
|
protected override void OnApply(HitExplosionEntry entry)
|
||||||
{
|
{
|
||||||
X = entry.Position;
|
base.OnApply(entry);
|
||||||
Scale = new Vector2(entry.Scale);
|
if (IsLoaded)
|
||||||
setColour(entry.ObjectColour);
|
apply(entry);
|
||||||
|
|
||||||
using (BeginAbsoluteSequence(entry.LifetimeStart))
|
|
||||||
applyTransforms(entry.RNGSeed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
ClearTransforms(true);
|
||||||
|
|
||||||
const double duration = 400;
|
(skinnableExplosion.Drawable as IHitExplosion)?.Animate(entry);
|
||||||
|
LifetimeEnd = skinnableExplosion.Drawable.LatestTransformEndTime;
|
||||||
// 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,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Pooling;
|
using osu.Framework.Graphics.Pooling;
|
||||||
using osu.Game.Rulesets.Objects.Pooling;
|
using osu.Game.Rulesets.Objects.Pooling;
|
||||||
|
|
||||||
@ -14,6 +15,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public HitExplosionContainer()
|
public HitExplosionContainer()
|
||||||
{
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
AddInternal(pool = new DrawablePool<HitExplosion>(10));
|
AddInternal(pool = new DrawablePool<HitExplosion>(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,24 +2,42 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics.Performance;
|
using osu.Framework.Graphics.Performance;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
public class HitExplosionEntry : LifetimeEntry
|
public class HitExplosionEntry : LifetimeEntry
|
||||||
{
|
{
|
||||||
public readonly float Position;
|
/// <summary>
|
||||||
public readonly float Scale;
|
/// The judgement result that triggered this explosion.
|
||||||
public readonly Color4 ObjectColour;
|
/// </summary>
|
||||||
public readonly int RNGSeed;
|
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;
|
LifetimeStart = startTime;
|
||||||
Position = position;
|
Position = position;
|
||||||
Scale = scale;
|
JudgementResult = judgementResult;
|
||||||
ObjectColour = objectColour;
|
ObjectColour = objectColour;
|
||||||
RNGSeed = rngSeed;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
osu.Game.Rulesets.Catch/UI/IHitExplosion.cs
Normal file
18
osu.Game.Rulesets.Catch/UI/IHitExplosion.cs
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -32,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
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)
|
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
||||||
@ -128,8 +129,21 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
float start = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopLeft).X;
|
float start, end;
|
||||||
float end = Parent.ToLocalSpace(restrictTo.ScreenSpaceDrawQuad.TopRight).X;
|
|
||||||
|
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;
|
float rawWidth = end - start;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Input.Events;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||||
{
|
{
|
||||||
@ -21,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
private double lastTrailTime;
|
private double lastTrailTime;
|
||||||
private IBindable<float> cursorSize;
|
private IBindable<float> cursorSize;
|
||||||
|
|
||||||
|
private Vector2? currentPosition;
|
||||||
|
|
||||||
public LegacyCursorTrail(ISkin skin)
|
public LegacyCursorTrail(ISkin skin)
|
||||||
{
|
{
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
@ -54,22 +57,34 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override double FadeDuration => disjointTrail ? 150 : 500;
|
protected override double FadeDuration => disjointTrail ? 150 : 500;
|
||||||
|
protected override float FadeExponent => 1;
|
||||||
|
|
||||||
protected override bool InterpolateMovements => !disjointTrail;
|
protected override bool InterpolateMovements => !disjointTrail;
|
||||||
|
|
||||||
protected override float IntervalMultiplier => 1 / Math.Max(cursorSize.Value, 1);
|
protected override float IntervalMultiplier => 1 / Math.Max(cursorSize.Value, 1);
|
||||||
|
|
||||||
|
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)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
{
|
{
|
||||||
if (!disjointTrail)
|
if (!disjointTrail)
|
||||||
return base.OnMouseMove(e);
|
return base.OnMouseMove(e);
|
||||||
|
|
||||||
if (Time.Current - lastTrailTime >= disjoint_trail_time_separation)
|
currentPosition = e.ScreenSpaceMousePosition;
|
||||||
{
|
|
||||||
lastTrailTime = Time.Current;
|
|
||||||
return base.OnMouseMove(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Intentionally block the base call as we're adding the trails ourselves.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,11 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
{
|
{
|
||||||
private const int max_sprites = 2048;
|
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 readonly TrailPart[] parts = new TrailPart[max_sprites];
|
||||||
private int currentIndex;
|
private int currentIndex;
|
||||||
private IShader shader;
|
private IShader shader;
|
||||||
@ -141,22 +146,25 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||||
{
|
{
|
||||||
Vector2 pos = e.ScreenSpaceMousePosition;
|
AddTrail(e.ScreenSpaceMousePosition);
|
||||||
|
|
||||||
if (lastPosition == null)
|
|
||||||
{
|
|
||||||
lastPosition = pos;
|
|
||||||
resampler.AddPosition(lastPosition.Value);
|
|
||||||
return base.OnMouseMove(e);
|
return base.OnMouseMove(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Vector2 pos2 in resampler.AddPosition(pos))
|
protected void AddTrail(Vector2 position)
|
||||||
|
{
|
||||||
|
if (InterpolateMovements)
|
||||||
|
{
|
||||||
|
if (!lastPosition.HasValue)
|
||||||
|
{
|
||||||
|
lastPosition = position;
|
||||||
|
resampler.AddPosition(lastPosition.Value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Vector2 pos2 in resampler.AddPosition(position))
|
||||||
{
|
{
|
||||||
Trace.Assert(lastPosition.HasValue);
|
Trace.Assert(lastPosition.HasValue);
|
||||||
|
|
||||||
if (InterpolateMovements)
|
|
||||||
{
|
|
||||||
// ReSharper disable once PossibleInvalidOperationException
|
|
||||||
Vector2 pos1 = lastPosition.Value;
|
Vector2 pos1 = lastPosition.Value;
|
||||||
Vector2 diff = pos2 - pos1;
|
Vector2 diff = pos2 - pos1;
|
||||||
float distance = diff.Length;
|
float distance = diff.Length;
|
||||||
@ -170,16 +178,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
addPart(lastPosition.Value);
|
addPart(lastPosition.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lastPosition = pos2;
|
lastPosition = position;
|
||||||
addPart(lastPosition.Value);
|
addPart(lastPosition.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnMouseMove(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addPart(Vector2 screenSpacePosition)
|
private void addPart(Vector2 screenSpacePosition)
|
||||||
{
|
{
|
||||||
parts[currentIndex].Position = screenSpacePosition;
|
parts[currentIndex].Position = screenSpacePosition;
|
||||||
@ -206,10 +212,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
private Texture texture;
|
private Texture texture;
|
||||||
|
|
||||||
private float time;
|
private float time;
|
||||||
|
private float fadeExponent;
|
||||||
|
|
||||||
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
private readonly TrailPart[] parts = new TrailPart[max_sprites];
|
||||||
private Vector2 size;
|
private Vector2 size;
|
||||||
|
|
||||||
private Vector2 originPosition;
|
private Vector2 originPosition;
|
||||||
|
|
||||||
private readonly QuadBatch<TexturedTrailVertex> vertexBatch = new QuadBatch<TexturedTrailVertex>(max_sprites, 1);
|
private readonly QuadBatch<TexturedTrailVertex> vertexBatch = new QuadBatch<TexturedTrailVertex>(max_sprites, 1);
|
||||||
@ -227,6 +233,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
texture = Source.texture;
|
texture = Source.texture;
|
||||||
size = Source.partSize;
|
size = Source.partSize;
|
||||||
time = Source.time;
|
time = Source.time;
|
||||||
|
fadeExponent = Source.FadeExponent;
|
||||||
|
|
||||||
originPosition = Vector2.Zero;
|
originPosition = Vector2.Zero;
|
||||||
|
|
||||||
@ -249,6 +256,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
|
|||||||
|
|
||||||
shader.Bind();
|
shader.Bind();
|
||||||
shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time);
|
shader.GetUniform<float>("g_FadeClock").UpdateValue(ref time);
|
||||||
|
shader.GetUniform<float>("g_FadeExponent").UpdateValue(ref fadeExponent);
|
||||||
|
|
||||||
texture.TextureGL.Bind();
|
texture.TextureGL.Bind();
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -19,6 +20,7 @@ namespace osu.Game.Tests.Chat
|
|||||||
{
|
{
|
||||||
private ChannelManager channelManager;
|
private ChannelManager channelManager;
|
||||||
private int currentMessageId;
|
private int currentMessageId;
|
||||||
|
private List<Message> sentMessages;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup() => Schedule(() =>
|
public void Setup() => Schedule(() =>
|
||||||
@ -34,6 +36,7 @@ namespace osu.Game.Tests.Chat
|
|||||||
AddStep("register request handling", () =>
|
AddStep("register request handling", () =>
|
||||||
{
|
{
|
||||||
currentMessageId = 0;
|
currentMessageId = 0;
|
||||||
|
sentMessages = new List<Message>();
|
||||||
|
|
||||||
((DummyAPIAccess)API).HandleRequest = req =>
|
((DummyAPIAccess)API).HandleRequest = req =>
|
||||||
{
|
{
|
||||||
@ -44,16 +47,11 @@ namespace osu.Game.Tests.Chat
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case PostMessageRequest postMessage:
|
case PostMessageRequest postMessage:
|
||||||
postMessage.TriggerSuccess(new Message(++currentMessageId)
|
handlePostMessageRequest(postMessage);
|
||||||
{
|
return true;
|
||||||
IsAction = postMessage.Message.IsAction,
|
|
||||||
ChannelId = postMessage.Message.ChannelId,
|
|
||||||
Content = postMessage.Message.Content,
|
|
||||||
Links = postMessage.Message.Links,
|
|
||||||
Timestamp = postMessage.Message.Timestamp,
|
|
||||||
Sender = postMessage.Message.Sender
|
|
||||||
});
|
|
||||||
|
|
||||||
|
case MarkChannelAsReadRequest markRead:
|
||||||
|
handleMarkChannelAsReadRequest(markRead);
|
||||||
return true;
|
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"));
|
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())
|
private Channel createChannel(int id, ChannelType type) => new Channel(new User())
|
||||||
{
|
{
|
||||||
Id = id,
|
Id = id,
|
||||||
Name = $"Channel {id}",
|
Name = $"Channel {id}",
|
||||||
Topic = $"Topic of channel {id} with type {type}",
|
Topic = $"Topic of channel {id} with type {type}",
|
||||||
Type = type,
|
Type = type,
|
||||||
|
LastMessageId = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
private class ChannelManagerContainer : CompositeDrawable
|
private class ChannelManagerContainer : CompositeDrawable
|
||||||
|
@ -40,6 +40,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
});
|
});
|
||||||
|
|
||||||
AddStep("add local player", () => createLeaderboardScore(playerScore, new User { Username = "You", Id = 3 }, true));
|
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);
|
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 }));
|
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 createRandomScore(User user) => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), user);
|
||||||
|
|
||||||
private void createLeaderboardScore(BindableDouble score, User user, bool isTracked = false)
|
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);
|
leaderboardScore.TotalScore.BindTo(score);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestGameplayLeaderboard : GameplayLeaderboard
|
private class TestGameplayLeaderboard : GameplayLeaderboard
|
||||||
{
|
{
|
||||||
|
public float Spacing => Flow.Spacing.Y;
|
||||||
|
|
||||||
public bool CheckPositionByUsername(string username, int? expectedPosition)
|
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;
|
return scoreItem != null && scoreItem.ScorePosition == expectedPosition;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Skinning;
|
||||||
using osuTK.Input;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
@ -142,6 +143,22 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
AddStep("return value", () => config.SetValue(OsuSetting.KeyOverlay, keyCounterVisibleValue));
|
AddStep("return value", () => config.SetValue(OsuSetting.KeyOverlay, keyCounterVisibleValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHiddenHUDDoesntBlockSkinnableComponentsLoad()
|
||||||
|
{
|
||||||
|
HUDVisibilityMode originalConfigValue = default;
|
||||||
|
|
||||||
|
AddStep("get original config value", () => originalConfigValue = config.Get<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||||
|
|
||||||
|
AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
|
||||||
|
|
||||||
|
createNew();
|
||||||
|
AddUntilStep("wait for hud load", () => hudOverlay.IsLoaded);
|
||||||
|
AddUntilStep("skinnable components loaded", () => hudOverlay.ChildrenOfType<SkinnableTargetContainer>().Single().ComponentsLoaded);
|
||||||
|
|
||||||
|
AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
|
||||||
|
}
|
||||||
|
|
||||||
private void createNew(Action<HUDOverlay> action = null)
|
private void createNew(Action<HUDOverlay> action = null)
|
||||||
{
|
{
|
||||||
AddStep("create overlay", () =>
|
AddStep("create overlay", () =>
|
||||||
|
42
osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs
Normal file
42
osu.Game.Tests/Visual/Menus/TestSceneSideOverlays.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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;
|
||||||
|
using osu.Game.Tests.Visual.Navigation;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
osu.Game.Tests/Visual/Mods/TestSceneModFailCondition.cs
Normal file
55
osu.Game.Tests/Visual/Mods/TestSceneModFailCondition.cs
Normal file
@ -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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -87,6 +87,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
public void TestEmpty()
|
public void TestEmpty()
|
||||||
{
|
{
|
||||||
// used to test the flow of multiplayer from visual tests.
|
// used to test the flow of multiplayer from visual tests.
|
||||||
|
AddStep("empty step", () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -408,8 +409,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
|
|
||||||
AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden);
|
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());
|
testLeave("back button", () => multiplayerScreen.OnBackButton());
|
||||||
|
|
||||||
// mimics home button and OS window close
|
// mimics home button and OS window close
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
@ -95,6 +96,8 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
|
|
||||||
public class TestOsuGame : OsuGame
|
public class TestOsuGame : OsuGame
|
||||||
{
|
{
|
||||||
|
public new const float SIDE_OVERLAY_OFFSET_RATIO = OsuGame.SIDE_OVERLAY_OFFSET_RATIO;
|
||||||
|
|
||||||
public new ScreenStack ScreenStack => base.ScreenStack;
|
public new ScreenStack ScreenStack => base.ScreenStack;
|
||||||
|
|
||||||
public new BackButton BackButton => base.BackButton;
|
public new BackButton BackButton => base.BackButton;
|
||||||
@ -103,7 +106,11 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
|
|
||||||
public new ScoreManager ScoreManager => base.ScoreManager;
|
public new ScoreManager ScoreManager => base.ScoreManager;
|
||||||
|
|
||||||
public new SettingsPanel Settings => base.Settings;
|
public new Container ScreenOffsetContainer => base.ScreenOffsetContainer;
|
||||||
|
|
||||||
|
public new SettingsOverlay Settings => base.Settings;
|
||||||
|
|
||||||
|
public new NotificationOverlay Notifications => base.Notifications;
|
||||||
|
|
||||||
public new MusicController MusicController => base.MusicController;
|
public new MusicController MusicController => base.MusicController;
|
||||||
|
|
||||||
|
@ -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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -62,6 +62,24 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
AddUntilStep("last room is not masked", () => checkRoomVisible(roomsContainer.Rooms[^1]));
|
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) =>
|
private bool checkRoomVisible(DrawableRoom room) =>
|
||||||
loungeScreen.ChildrenOfType<OsuScrollContainer>().First().ScreenSpaceDrawQuad
|
loungeScreen.ChildrenOfType<OsuScrollContainer>().First().ScreenSpaceDrawQuad
|
||||||
.Contains(room.ScreenSpaceDrawQuad.Centre);
|
.Contains(room.ScreenSpaceDrawQuad.Centre);
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Settings
|
|||||||
new TabletSettings(tabletHandler)
|
new TabletSettings(tabletHandler)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.None,
|
RelativeSizeAxes = Axes.None,
|
||||||
Width = SettingsPanel.WIDTH,
|
Width = SettingsPanel.PANEL_WIDTH,
|
||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ namespace osu.Game.Configuration
|
|||||||
SetDefault(OsuSetting.HitLighting, true);
|
SetDefault(OsuSetting.HitLighting, true);
|
||||||
|
|
||||||
SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always);
|
SetDefault(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Always);
|
||||||
SetDefault(OsuSetting.ShowProgressGraph, true);
|
SetDefault(OsuSetting.ShowDifficultyGraph, true);
|
||||||
SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true);
|
SetDefault(OsuSetting.ShowHealthDisplayWhenCantFail, true);
|
||||||
SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true);
|
SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true);
|
||||||
SetDefault(OsuSetting.KeyOverlay, false);
|
SetDefault(OsuSetting.KeyOverlay, false);
|
||||||
@ -217,7 +217,7 @@ namespace osu.Game.Configuration
|
|||||||
AlwaysPlayFirstComboBreak,
|
AlwaysPlayFirstComboBreak,
|
||||||
FloatingComments,
|
FloatingComments,
|
||||||
HUDVisibilityMode,
|
HUDVisibilityMode,
|
||||||
ShowProgressGraph,
|
ShowDifficultyGraph,
|
||||||
ShowHealthDisplayWhenCantFail,
|
ShowHealthDisplayWhenCantFail,
|
||||||
FadePlayfieldWhenHealthLow,
|
FadePlayfieldWhenHealthLow,
|
||||||
MouseDisableButtons,
|
MouseDisableButtons,
|
||||||
|
24
osu.Game/Localisation/MultiplayerTeamResultsScreenStrings.cs
Normal file
24
osu.Game/Localisation/MultiplayerTeamResultsScreenStrings.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// 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.Localisation;
|
||||||
|
|
||||||
|
namespace osu.Game.Localisation
|
||||||
|
{
|
||||||
|
public static class MultiplayerTeamResultsScreenStrings
|
||||||
|
{
|
||||||
|
private const string prefix = @"osu.Game.Resources.Localisation.MultiplayerTeamResultsScreen";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "Team {0} wins!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString TeamWins(string winner) => new TranslatableString(getKey(@"team_wins"), @"Team {0} wins!", winner);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// "The teams are tied!"
|
||||||
|
/// </summary>
|
||||||
|
public static LocalisableString TheTeamsAreTied => new TranslatableString(getKey(@"the_teams_are_tied"), @"The teams are tied!");
|
||||||
|
|
||||||
|
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||||
|
}
|
||||||
|
}
|
@ -9,16 +9,16 @@ namespace osu.Game.Online.API.Requests
|
|||||||
{
|
{
|
||||||
public class MarkChannelAsReadRequest : APIRequest
|
public class MarkChannelAsReadRequest : APIRequest
|
||||||
{
|
{
|
||||||
private readonly Channel channel;
|
public readonly Channel Channel;
|
||||||
private readonly Message message;
|
public readonly Message Message;
|
||||||
|
|
||||||
public MarkChannelAsReadRequest(Channel channel, Message message)
|
public MarkChannelAsReadRequest(Channel channel, Message message)
|
||||||
{
|
{
|
||||||
this.channel = channel;
|
Channel = channel;
|
||||||
this.message = message;
|
Message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string Target => $"chat/channels/{channel.Id}/mark-as-read/{message.Id}";
|
protected override string Target => $"chat/channels/{Channel.Id}/mark-as-read/{Message.Id}";
|
||||||
|
|
||||||
protected override WebRequest CreateWebRequest()
|
protected override WebRequest CreateWebRequest()
|
||||||
{
|
{
|
||||||
|
@ -553,7 +553,7 @@ namespace osu.Game.Online.Chat
|
|||||||
if (channel.LastMessageId == channel.LastReadId)
|
if (channel.LastMessageId == channel.LastReadId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var message = channel.Messages.LastOrDefault();
|
var message = channel.Messages.FindLast(msg => !(msg is LocalMessage));
|
||||||
|
|
||||||
if (message == null)
|
if (message == null)
|
||||||
return;
|
return;
|
||||||
|
@ -31,6 +31,15 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
Task UserLeft(MultiplayerRoomUser user);
|
Task UserLeft(MultiplayerRoomUser user);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signals that a user has been kicked from the room.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This will also be sent to the user that was kicked.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="user">The user.</param>
|
||||||
|
Task UserKicked(MultiplayerRoomUser user);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signal that the host of the room has changed.
|
/// Signal that the host of the room has changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -389,6 +389,18 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task IMultiplayerClient.UserKicked(MultiplayerRoomUser user)
|
||||||
|
{
|
||||||
|
if (LocalUser == null)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
if (user.Equals(LocalUser))
|
||||||
|
LeaveRoom();
|
||||||
|
|
||||||
|
// TODO: also inform users of the kick operation.
|
||||||
|
return ((IMultiplayerClient)this).UserLeft(user);
|
||||||
|
}
|
||||||
|
|
||||||
Task IMultiplayerClient.HostChanged(int userId)
|
Task IMultiplayerClient.HostChanged(int userId)
|
||||||
{
|
{
|
||||||
if (Room == null)
|
if (Room == null)
|
||||||
|
@ -50,6 +50,7 @@ namespace osu.Game.Online.Multiplayer
|
|||||||
connection.On<MultiplayerRoomState>(nameof(IMultiplayerClient.RoomStateChanged), ((IMultiplayerClient)this).RoomStateChanged);
|
connection.On<MultiplayerRoomState>(nameof(IMultiplayerClient.RoomStateChanged), ((IMultiplayerClient)this).RoomStateChanged);
|
||||||
connection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserJoined), ((IMultiplayerClient)this).UserJoined);
|
connection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserJoined), ((IMultiplayerClient)this).UserJoined);
|
||||||
connection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserLeft), ((IMultiplayerClient)this).UserLeft);
|
connection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserLeft), ((IMultiplayerClient)this).UserLeft);
|
||||||
|
connection.On<MultiplayerRoomUser>(nameof(IMultiplayerClient.UserKicked), ((IMultiplayerClient)this).UserKicked);
|
||||||
connection.On<int>(nameof(IMultiplayerClient.HostChanged), ((IMultiplayerClient)this).HostChanged);
|
connection.On<int>(nameof(IMultiplayerClient.HostChanged), ((IMultiplayerClient)this).HostChanged);
|
||||||
connection.On<MultiplayerRoomSettings>(nameof(IMultiplayerClient.SettingsChanged), ((IMultiplayerClient)this).SettingsChanged);
|
connection.On<MultiplayerRoomSettings>(nameof(IMultiplayerClient.SettingsChanged), ((IMultiplayerClient)this).SettingsChanged);
|
||||||
connection.On<int, MultiplayerUserState>(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged);
|
connection.On<int, MultiplayerUserState>(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged);
|
||||||
|
@ -64,6 +64,11 @@ namespace osu.Game
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>
|
public class OsuGame : OsuGameBase, IKeyBindingHandler<GlobalAction>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications).
|
||||||
|
/// </summary>
|
||||||
|
protected const float SIDE_OVERLAY_OFFSET_RATIO = 0.05f;
|
||||||
|
|
||||||
public Toolbar Toolbar;
|
public Toolbar Toolbar;
|
||||||
|
|
||||||
private ChatOverlay chatOverlay;
|
private ChatOverlay chatOverlay;
|
||||||
@ -71,7 +76,7 @@ namespace osu.Game
|
|||||||
private ChannelManager channelManager;
|
private ChannelManager channelManager;
|
||||||
|
|
||||||
[NotNull]
|
[NotNull]
|
||||||
private readonly NotificationOverlay notifications = new NotificationOverlay();
|
protected readonly NotificationOverlay Notifications = new NotificationOverlay();
|
||||||
|
|
||||||
private BeatmapListingOverlay beatmapListing;
|
private BeatmapListingOverlay beatmapListing;
|
||||||
|
|
||||||
@ -97,7 +102,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
private ScalingContainer screenContainer;
|
private ScalingContainer screenContainer;
|
||||||
|
|
||||||
private Container screenOffsetContainer;
|
protected Container ScreenOffsetContainer { get; private set; }
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private FrameworkConfigManager frameworkConfig { get; set; }
|
private FrameworkConfigManager frameworkConfig { get; set; }
|
||||||
@ -312,7 +317,7 @@ namespace osu.Game
|
|||||||
case LinkAction.OpenEditorTimestamp:
|
case LinkAction.OpenEditorTimestamp:
|
||||||
case LinkAction.JoinMultiplayerMatch:
|
case LinkAction.JoinMultiplayerMatch:
|
||||||
case LinkAction.Spectate:
|
case LinkAction.Spectate:
|
||||||
waitForReady(() => notifications, _ => notifications.Post(new SimpleNotification
|
waitForReady(() => Notifications, _ => Notifications.Post(new SimpleNotification
|
||||||
{
|
{
|
||||||
Text = @"This link type is not yet supported!",
|
Text = @"This link type is not yet supported!",
|
||||||
Icon = FontAwesome.Solid.LifeRing,
|
Icon = FontAwesome.Solid.LifeRing,
|
||||||
@ -611,12 +616,12 @@ namespace osu.Game
|
|||||||
MenuCursorContainer.CanShowCursor = menuScreen?.CursorVisible ?? false;
|
MenuCursorContainer.CanShowCursor = menuScreen?.CursorVisible ?? false;
|
||||||
|
|
||||||
// todo: all archive managers should be able to be looped here.
|
// todo: all archive managers should be able to be looped here.
|
||||||
SkinManager.PostNotification = n => notifications.Post(n);
|
SkinManager.PostNotification = n => Notifications.Post(n);
|
||||||
|
|
||||||
BeatmapManager.PostNotification = n => notifications.Post(n);
|
BeatmapManager.PostNotification = n => Notifications.Post(n);
|
||||||
BeatmapManager.PresentImport = items => PresentBeatmap(items.First());
|
BeatmapManager.PresentImport = items => PresentBeatmap(items.First());
|
||||||
|
|
||||||
ScoreManager.PostNotification = n => notifications.Post(n);
|
ScoreManager.PostNotification = n => Notifications.Post(n);
|
||||||
ScoreManager.PresentImport = items => PresentScore(items.First());
|
ScoreManager.PresentImport = items => PresentScore(items.First());
|
||||||
|
|
||||||
// make config aware of how to lookup skins for on-screen display purposes.
|
// make config aware of how to lookup skins for on-screen display purposes.
|
||||||
@ -655,7 +660,7 @@ namespace osu.Game
|
|||||||
ActionRequested = action => volume.Adjust(action),
|
ActionRequested = action => volume.Adjust(action),
|
||||||
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
|
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
|
||||||
},
|
},
|
||||||
screenOffsetContainer = new Container
|
ScreenOffsetContainer = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -724,7 +729,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
loadComponentSingleFile(onScreenDisplay, Add, true);
|
loadComponentSingleFile(onScreenDisplay, Add, true);
|
||||||
|
|
||||||
loadComponentSingleFile(notifications.With(d =>
|
loadComponentSingleFile(Notifications.With(d =>
|
||||||
{
|
{
|
||||||
d.GetToolbarHeight = () => ToolbarOffset;
|
d.GetToolbarHeight = () => ToolbarOffset;
|
||||||
d.Anchor = Anchor.TopRight;
|
d.Anchor = Anchor.TopRight;
|
||||||
@ -733,7 +738,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
loadComponentSingleFile(new CollectionManager(Storage)
|
loadComponentSingleFile(new CollectionManager(Storage)
|
||||||
{
|
{
|
||||||
PostNotification = n => notifications.Post(n),
|
PostNotification = n => Notifications.Post(n),
|
||||||
}, Add, true);
|
}, Add, true);
|
||||||
|
|
||||||
loadComponentSingleFile(stableImportManager, Add);
|
loadComponentSingleFile(stableImportManager, Add);
|
||||||
@ -785,7 +790,7 @@ namespace osu.Game
|
|||||||
Add(new MusicKeyBindingHandler());
|
Add(new MusicKeyBindingHandler());
|
||||||
|
|
||||||
// side overlays which cancel each other.
|
// side overlays which cancel each other.
|
||||||
var singleDisplaySideOverlays = new OverlayContainer[] { Settings, notifications };
|
var singleDisplaySideOverlays = new OverlayContainer[] { Settings, Notifications };
|
||||||
|
|
||||||
foreach (var overlay in singleDisplaySideOverlays)
|
foreach (var overlay in singleDisplaySideOverlays)
|
||||||
{
|
{
|
||||||
@ -828,21 +833,6 @@ namespace osu.Game
|
|||||||
{
|
{
|
||||||
if (mode.NewValue != OverlayActivation.All) CloseAllOverlays();
|
if (mode.NewValue != OverlayActivation.All) CloseAllOverlays();
|
||||||
};
|
};
|
||||||
|
|
||||||
void updateScreenOffset()
|
|
||||||
{
|
|
||||||
float offset = 0;
|
|
||||||
|
|
||||||
if (Settings.State.Value == Visibility.Visible)
|
|
||||||
offset += Toolbar.HEIGHT / 2;
|
|
||||||
if (notifications.State.Value == Visibility.Visible)
|
|
||||||
offset -= Toolbar.HEIGHT / 2;
|
|
||||||
|
|
||||||
screenOffsetContainer.MoveToX(offset, SettingsPanel.TRANSITION_LENGTH, Easing.OutQuint);
|
|
||||||
}
|
|
||||||
|
|
||||||
Settings.State.ValueChanged += _ => updateScreenOffset();
|
|
||||||
notifications.State.ValueChanged += _ => updateScreenOffset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showOverlayAboveOthers(OverlayContainer overlay, OverlayContainer[] otherOverlays)
|
private void showOverlayAboveOthers(OverlayContainer overlay, OverlayContainer[] otherOverlays)
|
||||||
@ -874,7 +864,7 @@ namespace osu.Game
|
|||||||
|
|
||||||
if (recentLogCount < short_term_display_limit)
|
if (recentLogCount < short_term_display_limit)
|
||||||
{
|
{
|
||||||
Schedule(() => notifications.Post(new SimpleErrorNotification
|
Schedule(() => Notifications.Post(new SimpleErrorNotification
|
||||||
{
|
{
|
||||||
Icon = entry.Level == LogLevel.Important ? FontAwesome.Solid.ExclamationCircle : FontAwesome.Solid.Bomb,
|
Icon = entry.Level == LogLevel.Important ? FontAwesome.Solid.ExclamationCircle : FontAwesome.Solid.Bomb,
|
||||||
Text = entry.Message.Truncate(256) + (entry.Exception != null && IsDeployedBuild ? "\n\nThis error has been automatically reported to the devs." : string.Empty),
|
Text = entry.Message.Truncate(256) + (entry.Exception != null && IsDeployedBuild ? "\n\nThis error has been automatically reported to the devs." : string.Empty),
|
||||||
@ -882,7 +872,7 @@ namespace osu.Game
|
|||||||
}
|
}
|
||||||
else if (recentLogCount == short_term_display_limit)
|
else if (recentLogCount == short_term_display_limit)
|
||||||
{
|
{
|
||||||
Schedule(() => notifications.Post(new SimpleNotification
|
Schedule(() => Notifications.Post(new SimpleNotification
|
||||||
{
|
{
|
||||||
Icon = FontAwesome.Solid.EllipsisH,
|
Icon = FontAwesome.Solid.EllipsisH,
|
||||||
Text = "Subsequent messages have been logged. Click to view log files.",
|
Text = "Subsequent messages have been logged. Click to view log files.",
|
||||||
@ -1023,9 +1013,18 @@ namespace osu.Game
|
|||||||
{
|
{
|
||||||
base.UpdateAfterChildren();
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
screenOffsetContainer.Padding = new MarginPadding { Top = ToolbarOffset };
|
ScreenOffsetContainer.Padding = new MarginPadding { Top = ToolbarOffset };
|
||||||
overlayContent.Padding = new MarginPadding { Top = ToolbarOffset };
|
overlayContent.Padding = new MarginPadding { Top = ToolbarOffset };
|
||||||
|
|
||||||
|
var horizontalOffset = 0f;
|
||||||
|
|
||||||
|
if (Settings.IsLoaded && Settings.IsPresent)
|
||||||
|
horizontalOffset += ToLocalSpace(Settings.ScreenSpaceDrawQuad.TopRight).X * SIDE_OVERLAY_OFFSET_RATIO;
|
||||||
|
if (Notifications.IsLoaded && Notifications.IsPresent)
|
||||||
|
horizontalOffset += (ToLocalSpace(Notifications.ScreenSpaceDrawQuad.TopLeft).X - DrawWidth) * SIDE_OVERLAY_OFFSET_RATIO;
|
||||||
|
|
||||||
|
ScreenOffsetContainer.X = horizontalOffset;
|
||||||
|
|
||||||
MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
|
MenuCursorContainer.CanShowCursor = (ScreenStack.CurrentScreen as IOsuScreen)?.CursorVisible ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,10 +78,10 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Children = new[]
|
Children = new[]
|
||||||
{
|
{
|
||||||
length = new Statistic(FontAwesome.Regular.Clock, "Length") { Width = 0.25f },
|
length = new Statistic(BeatmapStatisticsIconType.Length, "Length") { Width = 0.25f },
|
||||||
bpm = new Statistic(FontAwesome.Regular.Circle, "BPM") { Width = 0.25f },
|
bpm = new Statistic(BeatmapStatisticsIconType.Bpm, "BPM") { Width = 0.25f },
|
||||||
circleCount = new Statistic(FontAwesome.Regular.Circle, "Circle Count") { Width = 0.25f },
|
circleCount = new Statistic(BeatmapStatisticsIconType.Circles, "Circle Count") { Width = 0.25f },
|
||||||
sliderCount = new Statistic(FontAwesome.Regular.Circle, "Slider Count") { Width = 0.25f },
|
sliderCount = new Statistic(BeatmapStatisticsIconType.Sliders, "Slider Count") { Width = 0.25f },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
set => this.value.Text = value;
|
set => this.value.Text = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Statistic(IconUsage icon, string name)
|
public Statistic(BeatmapStatisticsIconType icon, string name)
|
||||||
{
|
{
|
||||||
TooltipText = name;
|
TooltipText = name;
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
@ -133,8 +133,16 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Icon = icon,
|
Icon = FontAwesome.Regular.Circle,
|
||||||
Size = new Vector2(12),
|
Size = new Vector2(10),
|
||||||
|
Rotation = 0,
|
||||||
|
Colour = Color4Extensions.FromHex(@"f7dd55"),
|
||||||
|
},
|
||||||
|
new BeatmapStatisticIcon(icon)
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(10),
|
||||||
Colour = Color4Extensions.FromHex(@"f7dd55"),
|
Colour = Color4Extensions.FromHex(@"f7dd55"),
|
||||||
Scale = new Vector2(0.8f),
|
Scale = new Vector2(0.8f),
|
||||||
},
|
},
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Overlays
|
|||||||
public LocalisableString Title => NotificationsStrings.HeaderTitle;
|
public LocalisableString Title => NotificationsStrings.HeaderTitle;
|
||||||
public LocalisableString Description => NotificationsStrings.HeaderDescription;
|
public LocalisableString Description => NotificationsStrings.HeaderDescription;
|
||||||
|
|
||||||
private const float width = 320;
|
public const float WIDTH = 320;
|
||||||
|
|
||||||
public const float TRANSITION_LENGTH = 600;
|
public const float TRANSITION_LENGTH = 600;
|
||||||
|
|
||||||
@ -38,7 +38,8 @@ namespace osu.Game.Overlays
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
Width = width;
|
X = WIDTH;
|
||||||
|
Width = WIDTH;
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
@ -152,7 +153,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
markAllRead();
|
markAllRead();
|
||||||
|
|
||||||
this.MoveToX(width, TRANSITION_LENGTH, Easing.OutQuint);
|
this.MoveToX(WIDTH, TRANSITION_LENGTH, Easing.OutQuint);
|
||||||
this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint);
|
this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
|
|||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Show difficulty graph on progress bar",
|
LabelText = "Show difficulty graph on progress bar",
|
||||||
Current = config.GetBindable<bool>(OsuSetting.ShowProgressGraph)
|
Current = config.GetBindable<bool>(OsuSetting.ShowDifficultyGraph)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,6 @@ using osu.Game.Overlays.Settings.Sections;
|
|||||||
using osu.Game.Overlays.Settings.Sections.Input;
|
using osu.Game.Overlays.Settings.Sections.Input;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
@ -38,6 +37,8 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private readonly List<SettingsSubPanel> subPanels = new List<SettingsSubPanel>();
|
private readonly List<SettingsSubPanel> subPanels = new List<SettingsSubPanel>();
|
||||||
|
|
||||||
|
private SettingsSubPanel lastOpenedSubPanel;
|
||||||
|
|
||||||
protected override Drawable CreateHeader() => new SettingsHeader(Title, Description);
|
protected override Drawable CreateHeader() => new SettingsHeader(Title, Description);
|
||||||
protected override Drawable CreateFooter() => new SettingsFooter();
|
protected override Drawable CreateFooter() => new SettingsFooter();
|
||||||
|
|
||||||
@ -46,21 +47,21 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool AcceptsFocus => subPanels.All(s => s.State.Value != Visibility.Visible);
|
public override bool AcceptsFocus => lastOpenedSubPanel == null || lastOpenedSubPanel.State.Value == Visibility.Hidden;
|
||||||
|
|
||||||
private T createSubPanel<T>(T subPanel)
|
private T createSubPanel<T>(T subPanel)
|
||||||
where T : SettingsSubPanel
|
where T : SettingsSubPanel
|
||||||
{
|
{
|
||||||
subPanel.Depth = 1;
|
subPanel.Depth = 1;
|
||||||
subPanel.Anchor = Anchor.TopRight;
|
subPanel.Anchor = Anchor.TopRight;
|
||||||
subPanel.State.ValueChanged += subPanelStateChanged;
|
subPanel.State.ValueChanged += e => subPanelStateChanged(subPanel, e);
|
||||||
|
|
||||||
subPanels.Add(subPanel);
|
subPanels.Add(subPanel);
|
||||||
|
|
||||||
return subPanel;
|
return subPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void subPanelStateChanged(ValueChangedEvent<Visibility> state)
|
private void subPanelStateChanged(SettingsSubPanel panel, ValueChangedEvent<Visibility> state)
|
||||||
{
|
{
|
||||||
switch (state.NewValue)
|
switch (state.NewValue)
|
||||||
{
|
{
|
||||||
@ -68,7 +69,9 @@ namespace osu.Game.Overlays
|
|||||||
Sidebar?.FadeColour(Color4.DarkGray, 300, Easing.OutQuint);
|
Sidebar?.FadeColour(Color4.DarkGray, 300, Easing.OutQuint);
|
||||||
|
|
||||||
SectionsContainer.FadeOut(300, Easing.OutQuint);
|
SectionsContainer.FadeOut(300, Easing.OutQuint);
|
||||||
ContentContainer.MoveToX(-WIDTH, 500, Easing.OutQuint);
|
ContentContainer.MoveToX(-PANEL_WIDTH, 500, Easing.OutQuint);
|
||||||
|
|
||||||
|
lastOpenedSubPanel = panel;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Visibility.Hidden:
|
case Visibility.Hidden:
|
||||||
@ -80,7 +83,7 @@ namespace osu.Game.Overlays
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override float ExpandedPosition => subPanels.Any(s => s.State.Value == Visibility.Visible) ? -WIDTH : base.ExpandedPosition;
|
protected override float ExpandedPosition => lastOpenedSubPanel?.State.Value == Visibility.Visible ? -PANEL_WIDTH : base.ExpandedPosition;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
|
@ -28,7 +28,15 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
private const float sidebar_width = Sidebar.DEFAULT_WIDTH;
|
private const float sidebar_width = Sidebar.DEFAULT_WIDTH;
|
||||||
|
|
||||||
public const float WIDTH = 400;
|
/// <summary>
|
||||||
|
/// The width of the settings panel content, excluding the sidebar.
|
||||||
|
/// </summary>
|
||||||
|
public const float PANEL_WIDTH = 400;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The full width of the settings panel, including the sidebar.
|
||||||
|
/// </summary>
|
||||||
|
public const float WIDTH = sidebar_width + PANEL_WIDTH;
|
||||||
|
|
||||||
protected Container<Drawable> ContentContainer;
|
protected Container<Drawable> ContentContainer;
|
||||||
|
|
||||||
@ -64,7 +72,8 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
InternalChild = ContentContainer = new NonMaskedContent
|
InternalChild = ContentContainer = new NonMaskedContent
|
||||||
{
|
{
|
||||||
Width = WIDTH,
|
X = -WIDTH + ExpandedPosition,
|
||||||
|
Width = PANEL_WIDTH,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
@ -11,9 +13,12 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
{
|
{
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };
|
||||||
|
|
||||||
|
[SettingSource("Restart on fail", "Automatically restarts when failed.")]
|
||||||
|
public BindableBool Restart { get; } = new BindableBool();
|
||||||
|
|
||||||
public virtual bool PerformFail() => true;
|
public virtual bool PerformFail() => true;
|
||||||
|
|
||||||
public virtual bool RestartOnFail => true;
|
public virtual bool RestartOnFail => Restart.Value;
|
||||||
|
|
||||||
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
||||||
{
|
{
|
||||||
|
@ -21,6 +21,11 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModSuddenDeath)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModSuddenDeath)).ToArray();
|
||||||
|
|
||||||
|
protected ModPerfect()
|
||||||
|
{
|
||||||
|
Restart.Value = Restart.Default = true;
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
||||||
=> result.Type.AffectsAccuracy()
|
=> result.Type.AffectsAccuracy()
|
||||||
&& result.Type != result.Judgement.MaxResult;
|
&& result.Type != result.Judgement.MaxResult;
|
||||||
|
@ -10,12 +10,12 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
{
|
{
|
||||||
public class OnlinePlayBackgroundSprite : OnlinePlayComposite
|
public class OnlinePlayBackgroundSprite : OnlinePlayComposite
|
||||||
{
|
{
|
||||||
private readonly BeatmapSetCoverType beatmapSetCoverType;
|
protected readonly BeatmapSetCoverType BeatmapSetCoverType;
|
||||||
private UpdateableBeatmapBackgroundSprite sprite;
|
private UpdateableBeatmapBackgroundSprite sprite;
|
||||||
|
|
||||||
public OnlinePlayBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover)
|
public OnlinePlayBackgroundSprite(BeatmapSetCoverType beatmapSetCoverType = BeatmapSetCoverType.Cover)
|
||||||
{
|
{
|
||||||
this.beatmapSetCoverType = beatmapSetCoverType;
|
BeatmapSetCoverType = beatmapSetCoverType;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -33,6 +33,6 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
sprite.Beatmap.Value = Playlist.FirstOrDefault()?.Beatmap.Value;
|
sprite.Beatmap.Value = Playlist.FirstOrDefault()?.Beatmap.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(beatmapSetCoverType) { RelativeSizeAxes = Axes.Both };
|
protected virtual UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new UpdateableBeatmapBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,15 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Graphics.UserInterface;
|
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay
|
namespace osu.Game.Screens.OnlinePlay
|
||||||
{
|
{
|
||||||
@ -22,52 +18,30 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
{
|
{
|
||||||
public const float HEIGHT = 80;
|
public const float HEIGHT = 80;
|
||||||
|
|
||||||
|
private readonly ScreenStack stack;
|
||||||
|
private readonly MultiHeaderTitle title;
|
||||||
|
|
||||||
public Header(string mainTitle, ScreenStack stack)
|
public Header(string mainTitle, ScreenStack stack)
|
||||||
{
|
{
|
||||||
|
this.stack = stack;
|
||||||
|
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = HEIGHT;
|
Height = HEIGHT;
|
||||||
|
Padding = new MarginPadding { Left = WaveOverlayContainer.WIDTH_PADDING };
|
||||||
|
|
||||||
HeaderBreadcrumbControl breadcrumbs;
|
Child = title = new MultiHeaderTitle(mainTitle)
|
||||||
MultiHeaderTitle title;
|
|
||||||
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4Extensions.FromHex(@"#1f1921"),
|
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreLeft,
|
Anchor = Anchor.CentreLeft,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Padding = new MarginPadding { Left = WaveOverlayContainer.WIDTH_PADDING + OsuScreen.HORIZONTAL_OVERFLOW_PADDING },
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
title = new MultiHeaderTitle(mainTitle)
|
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.BottomLeft,
|
|
||||||
},
|
|
||||||
breadcrumbs = new HeaderBreadcrumbControl(stack)
|
|
||||||
{
|
|
||||||
Anchor = Anchor.BottomLeft,
|
|
||||||
Origin = Anchor.BottomLeft
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
breadcrumbs.Current.ValueChanged += screen =>
|
// unnecessary to unbind these as this header has the same lifetime as the screen stack we are attaching to.
|
||||||
{
|
stack.ScreenPushed += (_, __) => updateSubScreenTitle();
|
||||||
if (screen.NewValue is IOnlinePlaySubScreen onlineSubScreen)
|
stack.ScreenExited += (_, __) => updateSubScreenTitle();
|
||||||
title.Screen = onlineSubScreen;
|
|
||||||
};
|
|
||||||
|
|
||||||
breadcrumbs.Current.TriggerChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateSubScreenTitle() => title.Screen = stack.CurrentScreen as IOnlinePlaySubScreen;
|
||||||
|
|
||||||
private class MultiHeaderTitle : CompositeDrawable
|
private class MultiHeaderTitle : CompositeDrawable
|
||||||
{
|
{
|
||||||
private const float spacing = 6;
|
private const float spacing = 6;
|
||||||
@ -75,9 +49,10 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
private readonly OsuSpriteText dot;
|
private readonly OsuSpriteText dot;
|
||||||
private readonly OsuSpriteText pageTitle;
|
private readonly OsuSpriteText pageTitle;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
public IOnlinePlaySubScreen Screen
|
public IOnlinePlaySubScreen Screen
|
||||||
{
|
{
|
||||||
set => pageTitle.Text = value.ShortTitle.Titleize();
|
set => pageTitle.Text = value?.ShortTitle.Titleize() ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultiHeaderTitle(string mainTitle)
|
public MultiHeaderTitle(string mainTitle)
|
||||||
@ -125,35 +100,5 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
pageTitle.Colour = dot.Colour = colours.Yellow;
|
pageTitle.Colour = dot.Colour = colours.Yellow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class HeaderBreadcrumbControl : ScreenBreadcrumbControl
|
|
||||||
{
|
|
||||||
public HeaderBreadcrumbControl(ScreenStack stack)
|
|
||||||
: base(stack)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
StripColour = Color4.Transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
AccentColour = Color4Extensions.FromHex("#e35c99");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override TabItem<IScreen> CreateTabItem(IScreen value) => new HeaderBreadcrumbTabItem(value)
|
|
||||||
{
|
|
||||||
AccentColour = AccentColour
|
|
||||||
};
|
|
||||||
|
|
||||||
private class HeaderBreadcrumbTabItem : BreadcrumbTabItem
|
|
||||||
{
|
|
||||||
public HeaderBreadcrumbTabItem(IScreen value)
|
|
||||||
: base(value)
|
|
||||||
{
|
|
||||||
Bar.Colour = Color4.Transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,11 +158,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
// This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites.
|
// This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites.
|
||||||
new BufferedContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -172,8 +167,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
}
|
|
||||||
},
|
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = @"Room content",
|
Name = @"Room content",
|
||||||
@ -186,12 +179,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|||||||
Masking = true,
|
Masking = true,
|
||||||
CornerRadius = corner_radius,
|
CornerRadius = corner_radius,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
|
||||||
// This resolves internal 1px gaps due to applying the (parenting) corner radius and masking across multiple filling background sprites.
|
|
||||||
new BufferedContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
new GridContainer
|
new GridContainer
|
||||||
{
|
{
|
||||||
@ -217,8 +204,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
|
||||||
},
|
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
Name = @"Left details",
|
Name = @"Left details",
|
||||||
|
@ -1,125 +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.Allocation;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Graphics.UserInterface;
|
|
||||||
using osu.Framework.Threading;
|
|
||||||
using osu.Game.Graphics;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osuTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|
||||||
{
|
|
||||||
public abstract class FilterControl : CompositeDrawable
|
|
||||||
{
|
|
||||||
protected readonly FillFlowContainer Filters;
|
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
|
||||||
private Bindable<FilterCriteria> filter { get; set; }
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private IBindable<RulesetInfo> ruleset { get; set; }
|
|
||||||
|
|
||||||
private readonly SearchTextBox search;
|
|
||||||
private readonly Dropdown<RoomStatusFilter> statusDropdown;
|
|
||||||
|
|
||||||
protected FilterControl()
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X;
|
|
||||||
Height = 70;
|
|
||||||
|
|
||||||
InternalChild = new FillFlowContainer
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Direction = FillDirection.Vertical,
|
|
||||||
Spacing = new Vector2(10),
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
search = new FilterSearchTextBox
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopRight,
|
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Width = 0.6f,
|
|
||||||
},
|
|
||||||
Filters = new FillFlowContainer
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopRight,
|
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
AutoSizeAxes = Axes.Both,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Spacing = new Vector2(10),
|
|
||||||
Child = statusDropdown = new SlimEnumDropdown<RoomStatusFilter>
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopRight,
|
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
RelativeSizeAxes = Axes.None,
|
|
||||||
Width = 160,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
filter ??= new Bindable<FilterCriteria>();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
search.Current.BindValueChanged(_ => updateFilterDebounced());
|
|
||||||
ruleset.BindValueChanged(_ => UpdateFilter());
|
|
||||||
statusDropdown.Current.BindValueChanged(_ => UpdateFilter(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ScheduledDelegate scheduledFilterUpdate;
|
|
||||||
|
|
||||||
private void updateFilterDebounced()
|
|
||||||
{
|
|
||||||
scheduledFilterUpdate?.Cancel();
|
|
||||||
scheduledFilterUpdate = Scheduler.AddDelayed(UpdateFilter, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void UpdateFilter() => Scheduler.AddOnce(updateFilter);
|
|
||||||
|
|
||||||
private void updateFilter()
|
|
||||||
{
|
|
||||||
scheduledFilterUpdate?.Cancel();
|
|
||||||
|
|
||||||
var criteria = CreateCriteria();
|
|
||||||
criteria.SearchString = search.Current.Value;
|
|
||||||
criteria.Status = statusDropdown.Current.Value;
|
|
||||||
criteria.Ruleset = ruleset.Value;
|
|
||||||
|
|
||||||
filter.Value = criteria;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual FilterCriteria CreateCriteria() => new FilterCriteria();
|
|
||||||
|
|
||||||
public bool HoldFocus
|
|
||||||
{
|
|
||||||
get => search.HoldFocus;
|
|
||||||
set => search.HoldFocus = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TakeFocus() => search.TakeFocus();
|
|
||||||
|
|
||||||
private class FilterSearchTextBox : SearchTextBox
|
|
||||||
{
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load()
|
|
||||||
{
|
|
||||||
BackgroundUnfocused = OsuColour.Gray(0.06f);
|
|
||||||
BackgroundFocused = OsuColour.Gray(0.12f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +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.Framework.Graphics.UserInterface;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|
||||||
{
|
|
||||||
public class PlaylistsFilterControl : FilterControl
|
|
||||||
{
|
|
||||||
private readonly Dropdown<PlaylistsCategory> categoryDropdown;
|
|
||||||
|
|
||||||
public PlaylistsFilterControl()
|
|
||||||
{
|
|
||||||
Filters.Add(categoryDropdown = new SlimEnumDropdown<PlaylistsCategory>
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopRight,
|
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
RelativeSizeAxes = Axes.None,
|
|
||||||
Width = 160,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
|
||||||
{
|
|
||||||
base.LoadComplete();
|
|
||||||
|
|
||||||
categoryDropdown.Current.BindValueChanged(_ => UpdateFilter());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override FilterCriteria CreateCriteria()
|
|
||||||
{
|
|
||||||
var criteria = base.CreateCriteria();
|
|
||||||
|
|
||||||
switch (categoryDropdown.Current.Value)
|
|
||||||
{
|
|
||||||
case PlaylistsCategory.Normal:
|
|
||||||
criteria.Category = "normal";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PlaylistsCategory.Spotlight:
|
|
||||||
criteria.Category = "spotlight";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return criteria;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum PlaylistsCategory
|
|
||||||
{
|
|
||||||
Any,
|
|
||||||
Normal,
|
|
||||||
Spotlight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -140,6 +140,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|||||||
|
|
||||||
roomFlow.Remove(toRemove);
|
roomFlow.Remove(toRemove);
|
||||||
|
|
||||||
|
// selection may have a lease due to being in a sub screen.
|
||||||
|
if (!selectedRoom.Disabled)
|
||||||
selectedRoom.Value = null;
|
selectedRoom.Value = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,6 +154,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
|
if (!selectedRoom.Disabled)
|
||||||
selectedRoom.Value = null;
|
selectedRoom.Value = null;
|
||||||
return base.OnClick(e);
|
return base.OnClick(e);
|
||||||
}
|
}
|
||||||
@ -214,6 +217,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
|
|||||||
|
|
||||||
private void selectNext(int direction)
|
private void selectNext(int direction)
|
||||||
{
|
{
|
||||||
|
if (selectedRoom.Disabled)
|
||||||
|
return;
|
||||||
|
|
||||||
var visibleRooms = Rooms.AsEnumerable().Where(r => r.IsPresent);
|
var visibleRooms = Rooms.AsEnumerable().Where(r => r.IsPresent);
|
||||||
|
|
||||||
Room room;
|
Room room;
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
@ -9,18 +11,20 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Threading;
|
||||||
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||||
using osu.Game.Screens.OnlinePlay.Match;
|
using osu.Game.Screens.OnlinePlay.Match;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Lounge
|
namespace osu.Game.Screens.OnlinePlay.Lounge
|
||||||
{
|
{
|
||||||
@ -41,7 +45,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
private readonly IBindable<bool> initialRoomsReceived = new Bindable<bool>();
|
private readonly IBindable<bool> initialRoomsReceived = new Bindable<bool>();
|
||||||
private readonly IBindable<bool> operationInProgress = new Bindable<bool>();
|
private readonly IBindable<bool> operationInProgress = new Bindable<bool>();
|
||||||
|
|
||||||
private FilterControl filter;
|
|
||||||
private LoadingLayer loadingLayer;
|
private LoadingLayer loadingLayer;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
@ -53,31 +56,37 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private OngoingOperationTracker ongoingOperationTracker { get; set; }
|
private OngoingOperationTracker ongoingOperationTracker { get; set; }
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private Bindable<FilterCriteria> filter { get; set; }
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||||
|
|
||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
private IDisposable joiningRoomOperation { get; set; }
|
private IDisposable joiningRoomOperation { get; set; }
|
||||||
|
|
||||||
private RoomsContainer roomsContainer;
|
private RoomsContainer roomsContainer;
|
||||||
|
private SearchTextBox searchTextBox;
|
||||||
|
private Dropdown<RoomStatusFilter> statusDropdown;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private LeasedBindable<Room> selectionLease;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
filter ??= new Bindable<FilterCriteria>(new FilterCriteria());
|
||||||
|
|
||||||
OsuScrollContainer scrollContainer;
|
OsuScrollContainer scrollContainer;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
new Box
|
loadingLayer = new LoadingLayer(true),
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = 100,
|
|
||||||
Colour = Color4.Black,
|
|
||||||
Alpha = 0.5f,
|
|
||||||
},
|
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Top = 20,
|
|
||||||
Left = WaveOverlayContainer.WIDTH_PADDING,
|
Left = WaveOverlayContainer.WIDTH_PADDING,
|
||||||
Right = WaveOverlayContainer.WIDTH_PADDING,
|
Right = WaveOverlayContainer.WIDTH_PADDING,
|
||||||
},
|
},
|
||||||
@ -86,28 +95,52 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
RowDimensions = new[]
|
RowDimensions = new[]
|
||||||
{
|
{
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
new Dimension(GridSizeMode.Absolute, Header.HEIGHT),
|
||||||
|
new Dimension(GridSizeMode.Absolute, 25),
|
||||||
new Dimension(GridSizeMode.Absolute, 20)
|
new Dimension(GridSizeMode.Absolute, 20)
|
||||||
},
|
},
|
||||||
Content = new[]
|
Content = new[]
|
||||||
{
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
searchTextBox = new LoungeSearchTextBox
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Width = 0.6f,
|
||||||
|
},
|
||||||
|
},
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Height = 70,
|
Depth = float.MinValue, // Contained filters should appear over the top of rooms.
|
||||||
Depth = -1,
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
filter = CreateFilterControl(),
|
|
||||||
Buttons.WithChild(CreateNewRoomButton().With(d =>
|
Buttons.WithChild(CreateNewRoomButton().With(d =>
|
||||||
{
|
{
|
||||||
d.Size = new Vector2(150, 25);
|
d.Anchor = Anchor.BottomLeft;
|
||||||
|
d.Origin = Anchor.BottomLeft;
|
||||||
|
d.Size = new Vector2(150, 37.5f);
|
||||||
d.Action = () => Open();
|
d.Action = () => Open();
|
||||||
|
})),
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(10),
|
||||||
|
ChildrenEnumerable = CreateFilterControls().Select(f => f.With(d =>
|
||||||
|
{
|
||||||
|
d.Anchor = Anchor.TopRight;
|
||||||
|
d.Origin = Anchor.TopRight;
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
@ -123,13 +156,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
ScrollbarOverlapsContent = false,
|
ScrollbarOverlapsContent = false,
|
||||||
Child = roomsContainer = new RoomsContainer()
|
Child = roomsContainer = new RoomsContainer()
|
||||||
},
|
},
|
||||||
loadingLayer = new LoadingLayer(true),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// scroll selected room into view on selection.
|
// scroll selected room into view on selection.
|
||||||
@ -145,6 +177,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
|
searchTextBox.Current.BindValueChanged(_ => updateFilterDebounced());
|
||||||
|
ruleset.BindValueChanged(_ => UpdateFilter());
|
||||||
|
|
||||||
initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived);
|
initialRoomsReceived.BindTo(RoomManager.InitialRoomsReceived);
|
||||||
initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer());
|
initialRoomsReceived.BindValueChanged(_ => updateLoadingLayer());
|
||||||
|
|
||||||
@ -153,13 +188,50 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
operationInProgress.BindTo(ongoingOperationTracker.InProgress);
|
operationInProgress.BindTo(ongoingOperationTracker.InProgress);
|
||||||
operationInProgress.BindValueChanged(_ => updateLoadingLayer(), true);
|
operationInProgress.BindValueChanged(_ => updateLoadingLayer(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnFocus(FocusEvent e)
|
#region Filtering
|
||||||
|
|
||||||
|
protected void UpdateFilter() => Scheduler.AddOnce(updateFilter);
|
||||||
|
|
||||||
|
private ScheduledDelegate scheduledFilterUpdate;
|
||||||
|
|
||||||
|
private void updateFilterDebounced()
|
||||||
{
|
{
|
||||||
filter.TakeFocus();
|
scheduledFilterUpdate?.Cancel();
|
||||||
|
scheduledFilterUpdate = Scheduler.AddDelayed(UpdateFilter, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateFilter()
|
||||||
|
{
|
||||||
|
scheduledFilterUpdate?.Cancel();
|
||||||
|
filter.Value = CreateFilterCriteria();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual FilterCriteria CreateFilterCriteria() => new FilterCriteria
|
||||||
|
{
|
||||||
|
SearchString = searchTextBox.Current.Value,
|
||||||
|
Ruleset = ruleset.Value,
|
||||||
|
Status = statusDropdown.Current.Value
|
||||||
|
};
|
||||||
|
|
||||||
|
protected virtual IEnumerable<Drawable> CreateFilterControls()
|
||||||
|
{
|
||||||
|
statusDropdown = new SlimEnumDropdown<RoomStatusFilter>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.None,
|
||||||
|
Width = 160,
|
||||||
|
};
|
||||||
|
|
||||||
|
statusDropdown.Current.BindValueChanged(_ => UpdateFilter());
|
||||||
|
|
||||||
|
yield return statusDropdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
public override void OnEntering(IScreen last)
|
public override void OnEntering(IScreen last)
|
||||||
{
|
{
|
||||||
base.OnEntering(last);
|
base.OnEntering(last);
|
||||||
@ -171,6 +243,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
{
|
{
|
||||||
base.OnResuming(last);
|
base.OnResuming(last);
|
||||||
|
|
||||||
|
Debug.Assert(selectionLease != null);
|
||||||
|
|
||||||
|
selectionLease.Return();
|
||||||
|
selectionLease = null;
|
||||||
|
|
||||||
if (selectedRoom.Value?.RoomID.Value == null)
|
if (selectedRoom.Value?.RoomID.Value == null)
|
||||||
selectedRoom.Value = new Room();
|
selectedRoom.Value = new Room();
|
||||||
|
|
||||||
@ -191,14 +268,19 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
base.OnSuspending(next);
|
base.OnSuspending(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnFocus(FocusEvent e)
|
||||||
|
{
|
||||||
|
searchTextBox.TakeFocus();
|
||||||
|
}
|
||||||
|
|
||||||
private void onReturning()
|
private void onReturning()
|
||||||
{
|
{
|
||||||
filter.HoldFocus = true;
|
searchTextBox.HoldFocus = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onLeaving()
|
private void onLeaving()
|
||||||
{
|
{
|
||||||
filter.HoldFocus = false;
|
searchTextBox.HoldFocus = false;
|
||||||
|
|
||||||
// ensure any password prompt is dismissed.
|
// ensure any password prompt is dismissed.
|
||||||
this.HidePopover();
|
this.HidePopover();
|
||||||
@ -238,13 +320,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
|
|
||||||
protected virtual void OpenNewRoom(Room room)
|
protected virtual void OpenNewRoom(Room room)
|
||||||
{
|
{
|
||||||
selectedRoom.Value = room;
|
selectionLease = selectedRoom.BeginLease(false);
|
||||||
|
Debug.Assert(selectionLease != null);
|
||||||
|
selectionLease.Value = room;
|
||||||
|
|
||||||
this.Push(CreateRoomSubScreen(room));
|
this.Push(CreateRoomSubScreen(room));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract FilterControl CreateFilterControl();
|
|
||||||
|
|
||||||
protected abstract OsuButton CreateNewRoomButton();
|
protected abstract OsuButton CreateNewRoomButton();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -262,5 +344,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
else
|
else
|
||||||
loadingLayer.Hide();
|
loadingLayer.Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class LoungeSearchTextBox : SearchTextBox
|
||||||
|
{
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
BackgroundUnfocused = OsuColour.Gray(0.06f);
|
||||||
|
BackgroundFocused = OsuColour.Gray(0.12f);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,10 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -62,8 +64,15 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
|||||||
|
|
||||||
protected RoomSubScreen()
|
protected RoomSubScreen()
|
||||||
{
|
{
|
||||||
|
Padding = new MarginPadding { Top = Header.HEIGHT };
|
||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4Extensions.FromHex(@"3e3a44") // This is super temporary.
|
||||||
|
},
|
||||||
BeatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker
|
BeatmapAvailabilityTracker = new OnlinePlayBeatmapAvailabilityTracker
|
||||||
{
|
{
|
||||||
SelectedItem = { BindTarget = SelectedItem }
|
SelectedItem = { BindTarget = SelectedItem }
|
||||||
|
@ -1,17 +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.Game.Screens.OnlinePlay.Lounge.Components;
|
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|
||||||
{
|
|
||||||
public class MultiplayerFilterControl : FilterControl
|
|
||||||
{
|
|
||||||
protected override FilterCriteria CreateCriteria()
|
|
||||||
{
|
|
||||||
var criteria = base.CreateCriteria();
|
|
||||||
criteria.Category = "realtime";
|
|
||||||
return criteria;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,7 +21,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private MultiplayerClient client { get; set; }
|
private MultiplayerClient client { get; set; }
|
||||||
|
|
||||||
protected override FilterControl CreateFilterControl() => new MultiplayerFilterControl();
|
protected override FilterCriteria CreateFilterCriteria()
|
||||||
|
{
|
||||||
|
var criteria = base.CreateFilterCriteria();
|
||||||
|
criteria.Category = @"realtime";
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
|
||||||
protected override OsuButton CreateNewRoomButton() => new CreateMultiplayerMatchButton();
|
protected override OsuButton CreateNewRoomButton() => new CreateMultiplayerMatchButton();
|
||||||
|
|
||||||
|
@ -274,7 +274,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
isConnected.BindValueChanged(connected =>
|
isConnected.BindValueChanged(connected =>
|
||||||
{
|
{
|
||||||
if (!connected.NewValue)
|
if (!connected.NewValue)
|
||||||
Schedule(this.Exit);
|
handleRoomLost();
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
currentRoom.BindValueChanged(room =>
|
currentRoom.BindValueChanged(room =>
|
||||||
@ -284,7 +284,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
// the room has gone away.
|
// the room has gone away.
|
||||||
// this could mean something happened during the join process, or an external connection issue occurred.
|
// this could mean something happened during the join process, or an external connection issue occurred.
|
||||||
// one specific scenario is where the underlying room is created, but the signalr server returns an error during the join process. this triggers a PartRoom operation (see https://github.com/ppy/osu/blob/7654df94f6f37b8382be7dfcb4f674e03bd35427/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs#L97)
|
// one specific scenario is where the underlying room is created, but the signalr server returns an error during the join process. this triggers a PartRoom operation (see https://github.com/ppy/osu/blob/7654df94f6f37b8382be7dfcb4f674e03bd35427/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerRoomManager.cs#L97)
|
||||||
Schedule(this.Exit);
|
handleRoomLost();
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
@ -448,9 +448,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
|
|
||||||
private void onRoomUpdated()
|
private void onRoomUpdated()
|
||||||
{
|
{
|
||||||
|
// may happen if the client is kicked or otherwise removed from the room.
|
||||||
|
if (client.Room == null)
|
||||||
|
{
|
||||||
|
handleRoomLost();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Scheduler.AddOnce(UpdateMods);
|
Scheduler.AddOnce(UpdateMods);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleRoomLost() => Schedule(() =>
|
||||||
|
{
|
||||||
|
if (this.IsCurrentScreen())
|
||||||
|
this.Exit();
|
||||||
|
else
|
||||||
|
ValidForResume = false;
|
||||||
|
});
|
||||||
|
|
||||||
private void onLoadRequested()
|
private void onLoadRequested()
|
||||||
{
|
{
|
||||||
if (BeatmapAvailability.Value.State != DownloadState.LocallyAvailable)
|
if (BeatmapAvailability.Value.State != DownloadState.LocallyAvailable)
|
||||||
|
@ -181,7 +181,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
protected override ResultsScreen CreateResults(ScoreInfo score)
|
protected override ResultsScreen CreateResults(ScoreInfo score)
|
||||||
{
|
{
|
||||||
Debug.Assert(RoomId.Value != null);
|
Debug.Assert(RoomId.Value != null);
|
||||||
return new MultiplayerResultsScreen(score, RoomId.Value.Value, PlaylistItem);
|
return leaderboard.TeamScores.Count == 2
|
||||||
|
? new MultiplayerTeamResultsScreen(score, RoomId.Value.Value, PlaylistItem, leaderboard.TeamScores)
|
||||||
|
: new MultiplayerResultsScreen(score, RoomId.Value.Value, PlaylistItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
|
@ -0,0 +1,152 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Localisation;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||||
|
{
|
||||||
|
public class MultiplayerTeamResultsScreen : MultiplayerResultsScreen
|
||||||
|
{
|
||||||
|
private readonly SortedDictionary<int, BindableInt> teamScores;
|
||||||
|
|
||||||
|
private Container winnerBackground;
|
||||||
|
private Drawable winnerText;
|
||||||
|
|
||||||
|
public MultiplayerTeamResultsScreen(ScoreInfo score, long roomId, PlaylistItem playlistItem, SortedDictionary<int, BindableInt> teamScores)
|
||||||
|
: base(score, roomId, playlistItem)
|
||||||
|
{
|
||||||
|
if (teamScores.Count != 2)
|
||||||
|
throw new NotSupportedException(@"This screen currently only supports 2 teams");
|
||||||
|
|
||||||
|
this.teamScores = teamScores;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; }
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
const float winner_background_half_height = 250;
|
||||||
|
|
||||||
|
VerticalScrollContent.Anchor = VerticalScrollContent.Origin = Anchor.TopCentre;
|
||||||
|
VerticalScrollContent.Scale = new Vector2(0.9f);
|
||||||
|
VerticalScrollContent.Y = 75;
|
||||||
|
|
||||||
|
var redScore = teamScores.First().Value;
|
||||||
|
var blueScore = teamScores.Last().Value;
|
||||||
|
|
||||||
|
LocalisableString winner;
|
||||||
|
Colour4 winnerColour;
|
||||||
|
|
||||||
|
int comparison = redScore.Value.CompareTo(blueScore.Value);
|
||||||
|
|
||||||
|
if (comparison < 0)
|
||||||
|
{
|
||||||
|
// team name should eventually be coming from the multiplayer match state.
|
||||||
|
winner = MultiplayerTeamResultsScreenStrings.TeamWins(@"Blue");
|
||||||
|
winnerColour = colours.TeamColourBlue;
|
||||||
|
}
|
||||||
|
else if (comparison > 0)
|
||||||
|
{
|
||||||
|
// team name should eventually be coming from the multiplayer match state.
|
||||||
|
winner = MultiplayerTeamResultsScreenStrings.TeamWins(@"Red");
|
||||||
|
winnerColour = colours.TeamColourRed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
winner = MultiplayerTeamResultsScreenStrings.TheTeamsAreTied;
|
||||||
|
winnerColour = Colour4.White.Opacity(0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddRangeInternal(new Drawable[]
|
||||||
|
{
|
||||||
|
new MatchScoreDisplay
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Team1Score = { BindTarget = redScore },
|
||||||
|
Team2Score = { BindTarget = blueScore },
|
||||||
|
},
|
||||||
|
winnerBackground = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Alpha = 0,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = winner_background_half_height,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
Colour = ColourInfo.GradientVertical(Colour4.Black.Opacity(0), Colour4.Black.Opacity(0.4f))
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = winner_background_half_height,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
Colour = ColourInfo.GradientVertical(Colour4.Black.Opacity(0.4f), Colour4.Black.Opacity(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(winnerText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Alpha = 0,
|
||||||
|
Font = OsuFont.Torus.With(size: 80, weight: FontWeight.Bold),
|
||||||
|
Text = winner,
|
||||||
|
Blending = BlendingParameters.Additive
|
||||||
|
}).WithEffect(new GlowEffect
|
||||||
|
{
|
||||||
|
Colour = winnerColour,
|
||||||
|
}).With(e =>
|
||||||
|
{
|
||||||
|
e.Anchor = Anchor.Centre;
|
||||||
|
e.Origin = Anchor.Centre;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(300))
|
||||||
|
{
|
||||||
|
const double fade_in_duration = 600;
|
||||||
|
|
||||||
|
winnerText.FadeInFromZero(fade_in_duration, Easing.InQuint);
|
||||||
|
winnerBackground.FadeInFromZero(fade_in_duration, Easing.InQuint);
|
||||||
|
|
||||||
|
winnerText
|
||||||
|
.ScaleTo(10)
|
||||||
|
.ScaleTo(1, 600, Easing.InQuad)
|
||||||
|
.Then()
|
||||||
|
.ScaleTo(1.02f, 1600, Easing.OutQuint)
|
||||||
|
.FadeOut(5000, Easing.InQuad);
|
||||||
|
winnerBackground.Delay(2200).FadeOut(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Drawables;
|
using osu.Game.Beatmaps.Drawables;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Input;
|
using osu.Game.Input;
|
||||||
@ -21,8 +22,9 @@ using osu.Game.Screens.Menu;
|
|||||||
using osu.Game.Screens.OnlinePlay.Components;
|
using osu.Game.Screens.OnlinePlay.Components;
|
||||||
using osu.Game.Screens.OnlinePlay.Lounge;
|
using osu.Game.Screens.OnlinePlay.Lounge;
|
||||||
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
using osu.Game.Screens.OnlinePlay.Lounge.Components;
|
||||||
using osu.Game.Screens.OnlinePlay.Match;
|
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Screens.OnlinePlay
|
namespace osu.Game.Screens.OnlinePlay
|
||||||
{
|
{
|
||||||
@ -71,9 +73,6 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private OsuLogo logo { get; set; }
|
private OsuLogo logo { get; set; }
|
||||||
|
|
||||||
private Drawable header;
|
|
||||||
private Drawable headerBackground;
|
|
||||||
|
|
||||||
protected OnlinePlayScreen()
|
protected OnlinePlayScreen()
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
@ -104,44 +103,23 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Top = Header.HEIGHT },
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
header = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.X,
|
|
||||||
Height = 400,
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
headerBackground = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Width = 1.25f,
|
|
||||||
Masking = true,
|
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new HeaderBackgroundSprite
|
new BeatmapBackgroundSprite
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.Both
|
||||||
Height = 400 // Keep a static height so the header doesn't change as it's resized between subscreens
|
|
||||||
},
|
},
|
||||||
}
|
new Box
|
||||||
},
|
|
||||||
new Container
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Padding = new MarginPadding { Bottom = -1 }, // 1px padding to avoid a 1px gap due to masking
|
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.9f), Color4.Black.Opacity(0.6f))
|
||||||
Child = new Box
|
},
|
||||||
|
screenStack = new OnlinePlaySubScreenStack
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both
|
||||||
Colour = ColourInfo.GradientVertical(backgroundColour.Opacity(0.5f), backgroundColour)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
screenStack = new OnlinePlaySubScreenStack { RelativeSizeAxes = Axes.Both }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new Header(ScreenTitle, screenStack),
|
new Header(ScreenTitle, screenStack),
|
||||||
RoomManager,
|
RoomManager,
|
||||||
ongoingOperationTracker
|
ongoingOperationTracker
|
||||||
@ -292,19 +270,6 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
|
|
||||||
private void subScreenChanged(IScreen lastScreen, IScreen newScreen)
|
private void subScreenChanged(IScreen lastScreen, IScreen newScreen)
|
||||||
{
|
{
|
||||||
switch (newScreen)
|
|
||||||
{
|
|
||||||
case LoungeSubScreen _:
|
|
||||||
header.Delay(OnlinePlaySubScreen.RESUME_TRANSITION_DELAY).ResizeHeightTo(400, OnlinePlaySubScreen.APPEAR_DURATION, Easing.OutQuint);
|
|
||||||
headerBackground.MoveToX(0, OnlinePlaySubScreen.X_MOVE_DURATION, Easing.OutQuint);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RoomSubScreen _:
|
|
||||||
header.ResizeHeightTo(135, OnlinePlaySubScreen.APPEAR_DURATION, Easing.OutQuint);
|
|
||||||
headerBackground.MoveToX(-OnlinePlaySubScreen.X_SHIFT, OnlinePlaySubScreen.X_MOVE_DURATION, Easing.OutQuint);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastScreen is IOsuScreen lastOsuScreen)
|
if (lastScreen is IOsuScreen lastOsuScreen)
|
||||||
Activity.UnbindFrom(lastOsuScreen.Activity);
|
Activity.UnbindFrom(lastOsuScreen.Activity);
|
||||||
|
|
||||||
@ -335,13 +300,48 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class HeaderBackgroundSprite : OnlinePlayBackgroundSprite
|
private class BeatmapBackgroundSprite : OnlinePlayBackgroundSprite
|
||||||
{
|
{
|
||||||
protected override UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new BackgroundSprite { RelativeSizeAxes = Axes.Both };
|
protected override UpdateableBeatmapBackgroundSprite CreateBackgroundSprite() => new BlurredBackgroundSprite(BeatmapSetCoverType) { RelativeSizeAxes = Axes.Both };
|
||||||
|
|
||||||
private class BackgroundSprite : UpdateableBeatmapBackgroundSprite
|
public class BlurredBackgroundSprite : UpdateableBeatmapBackgroundSprite
|
||||||
{
|
{
|
||||||
protected override double TransformDuration => 200;
|
public BlurredBackgroundSprite(BeatmapSetCoverType type)
|
||||||
|
: base(type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override double LoadDelay => 200;
|
||||||
|
|
||||||
|
protected override Drawable CreateDrawable(BeatmapInfo model) =>
|
||||||
|
new BufferedLoader(base.CreateDrawable(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This class is an unfortunate requirement due to `LongRunningLoad` requiring direct async loading.
|
||||||
|
// It means that if the web request fetching the beatmap background takes too long, it will suddenly appear.
|
||||||
|
internal class BufferedLoader : BufferedContainer
|
||||||
|
{
|
||||||
|
private readonly Drawable drawable;
|
||||||
|
|
||||||
|
public BufferedLoader(Drawable drawable)
|
||||||
|
{
|
||||||
|
this.drawable = drawable;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
BlurSigma = new Vector2(10);
|
||||||
|
FrameBufferScale = new Vector2(0.5f);
|
||||||
|
CacheDrawnFrameBuffer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
LoadComponentAsync(drawable, d =>
|
||||||
|
{
|
||||||
|
Add(d);
|
||||||
|
ForceRedraw();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +59,8 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
LeftArea.Padding = new MarginPadding { Top = Header.HEIGHT };
|
||||||
|
|
||||||
initialBeatmap = Beatmap.Value;
|
initialBeatmap = Beatmap.Value;
|
||||||
initialRuleset = Ruleset.Value;
|
initialRuleset = Ruleset.Value;
|
||||||
initialMods = Mods.Value.ToList();
|
initialMods = Mods.Value.ToList();
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
@ -16,7 +20,38 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private IAPIProvider api { get; set; }
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
protected override FilterControl CreateFilterControl() => new PlaylistsFilterControl();
|
private Dropdown<PlaylistsCategory> categoryDropdown;
|
||||||
|
|
||||||
|
protected override IEnumerable<Drawable> CreateFilterControls()
|
||||||
|
{
|
||||||
|
categoryDropdown = new SlimEnumDropdown<PlaylistsCategory>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.None,
|
||||||
|
Width = 160,
|
||||||
|
};
|
||||||
|
|
||||||
|
categoryDropdown.Current.BindValueChanged(_ => UpdateFilter());
|
||||||
|
|
||||||
|
return base.CreateFilterControls().Append(categoryDropdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override FilterCriteria CreateFilterCriteria()
|
||||||
|
{
|
||||||
|
var criteria = base.CreateFilterCriteria();
|
||||||
|
|
||||||
|
switch (categoryDropdown.Current.Value)
|
||||||
|
{
|
||||||
|
case PlaylistsCategory.Normal:
|
||||||
|
criteria.Category = @"normal";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PlaylistsCategory.Spotlight:
|
||||||
|
criteria.Category = @"spotlight";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
|
||||||
protected override OsuButton CreateNewRoomButton() => new CreatePlaylistsRoomButton();
|
protected override OsuButton CreateNewRoomButton() => new CreatePlaylistsRoomButton();
|
||||||
|
|
||||||
@ -30,5 +65,12 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room);
|
protected override RoomSubScreen CreateRoomSubScreen(Room room) => new PlaylistsRoomSubScreen(room);
|
||||||
|
|
||||||
|
private enum PlaylistsCategory
|
||||||
|
{
|
||||||
|
Any,
|
||||||
|
Normal,
|
||||||
|
Spotlight
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,29 +6,58 @@ using System.Linq;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play.HUD
|
namespace osu.Game.Screens.Play.HUD
|
||||||
{
|
{
|
||||||
public class GameplayLeaderboard : FillFlowContainer<GameplayLeaderboardScore>
|
public class GameplayLeaderboard : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
private readonly int maxPanels;
|
||||||
private readonly Cached sorting = new Cached();
|
private readonly Cached sorting = new Cached();
|
||||||
|
|
||||||
public Bindable<bool> Expanded = new Bindable<bool>();
|
public Bindable<bool> Expanded = new Bindable<bool>();
|
||||||
|
|
||||||
public GameplayLeaderboard()
|
protected readonly FillFlowContainer<GameplayLeaderboardScore> Flow;
|
||||||
|
|
||||||
|
private bool requiresScroll;
|
||||||
|
private readonly OsuScrollContainer scroll;
|
||||||
|
|
||||||
|
private GameplayLeaderboardScore trackedScore;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new leaderboard.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="maxPanels">The maximum panels to show at once. Defines the maximum height of this component.</param>
|
||||||
|
public GameplayLeaderboard(int maxPanels = 8)
|
||||||
{
|
{
|
||||||
|
this.maxPanels = maxPanels;
|
||||||
|
|
||||||
Width = GameplayLeaderboardScore.EXTENDED_WIDTH + GameplayLeaderboardScore.SHEAR_WIDTH;
|
Width = GameplayLeaderboardScore.EXTENDED_WIDTH + GameplayLeaderboardScore.SHEAR_WIDTH;
|
||||||
|
|
||||||
Direction = FillDirection.Vertical;
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
Spacing = new Vector2(2.5f);
|
scroll = new InputDisabledScrollContainer
|
||||||
|
{
|
||||||
LayoutDuration = 250;
|
RelativeSizeAxes = Axes.Both,
|
||||||
LayoutEasing = Easing.OutQuint;
|
Child = Flow = new FillFlowContainer<GameplayLeaderboardScore>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
X = GameplayLeaderboardScore.SHEAR_WIDTH,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(2.5f),
|
||||||
|
LayoutDuration = 450,
|
||||||
|
LayoutEasing = Easing.OutQuint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -46,26 +75,87 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
/// Whether the player should be tracked on the leaderboard.
|
/// Whether the player should be tracked on the leaderboard.
|
||||||
/// Set to <c>true</c> for the local player or a player whose replay is currently being played.
|
/// Set to <c>true</c> for the local player or a player whose replay is currently being played.
|
||||||
/// </param>
|
/// </param>
|
||||||
public ILeaderboardScore AddPlayer([CanBeNull] User user, bool isTracked)
|
public ILeaderboardScore Add([CanBeNull] User user, bool isTracked)
|
||||||
{
|
{
|
||||||
var drawable = CreateLeaderboardScoreDrawable(user, isTracked);
|
var drawable = CreateLeaderboardScoreDrawable(user, isTracked);
|
||||||
|
|
||||||
|
if (isTracked)
|
||||||
|
{
|
||||||
|
if (trackedScore != null)
|
||||||
|
throw new InvalidOperationException("Cannot track more than one score.");
|
||||||
|
|
||||||
|
trackedScore = drawable;
|
||||||
|
}
|
||||||
|
|
||||||
drawable.Expanded.BindTo(Expanded);
|
drawable.Expanded.BindTo(Expanded);
|
||||||
|
|
||||||
base.Add(drawable);
|
Flow.Add(drawable);
|
||||||
drawable.TotalScore.BindValueChanged(_ => sorting.Invalidate(), true);
|
drawable.TotalScore.BindValueChanged(_ => sorting.Invalidate(), true);
|
||||||
|
|
||||||
Height = Count * (GameplayLeaderboardScore.PANEL_HEIGHT + Spacing.Y);
|
int displayCount = Math.Min(Flow.Count, maxPanels);
|
||||||
|
Height = displayCount * (GameplayLeaderboardScore.PANEL_HEIGHT + Flow.Spacing.Y);
|
||||||
|
requiresScroll = displayCount != Flow.Count;
|
||||||
|
|
||||||
return drawable;
|
return drawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
Flow.Clear();
|
||||||
|
trackedScore = null;
|
||||||
|
scroll.ScrollToStart(false);
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual GameplayLeaderboardScore CreateLeaderboardScoreDrawable(User user, bool isTracked) =>
|
protected virtual GameplayLeaderboardScore CreateLeaderboardScoreDrawable(User user, bool isTracked) =>
|
||||||
new GameplayLeaderboardScore(user, isTracked);
|
new GameplayLeaderboardScore(user, isTracked);
|
||||||
|
|
||||||
public sealed override void Add(GameplayLeaderboardScore drawable)
|
protected override void Update()
|
||||||
{
|
{
|
||||||
throw new NotSupportedException($"Use {nameof(AddPlayer)} instead.");
|
base.Update();
|
||||||
|
|
||||||
|
if (requiresScroll && trackedScore != null)
|
||||||
|
{
|
||||||
|
float scrollTarget = scroll.GetChildPosInContent(trackedScore) + trackedScore.DrawHeight / 2 - scroll.DrawHeight / 2;
|
||||||
|
scroll.ScrollTo(scrollTarget, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const float panel_height = GameplayLeaderboardScore.PANEL_HEIGHT;
|
||||||
|
|
||||||
|
float fadeBottom = scroll.Current + scroll.DrawHeight;
|
||||||
|
float fadeTop = scroll.Current + panel_height;
|
||||||
|
|
||||||
|
if (scroll.Current <= 0) fadeTop -= panel_height;
|
||||||
|
if (!scroll.IsScrolledToEnd()) fadeBottom -= panel_height;
|
||||||
|
|
||||||
|
// logic is mostly shared with Leaderboard, copied here for simplicity.
|
||||||
|
foreach (var c in Flow.Children)
|
||||||
|
{
|
||||||
|
float topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, Flow).Y;
|
||||||
|
float bottomY = topY + panel_height;
|
||||||
|
|
||||||
|
bool requireTopFade = requiresScroll && topY <= fadeTop;
|
||||||
|
bool requireBottomFade = requiresScroll && bottomY >= fadeBottom;
|
||||||
|
|
||||||
|
if (!requireTopFade && !requireBottomFade)
|
||||||
|
c.Colour = Color4.White;
|
||||||
|
else if (topY > fadeBottom + panel_height || bottomY < fadeTop - panel_height)
|
||||||
|
c.Colour = Color4.Transparent;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (requireBottomFade)
|
||||||
|
{
|
||||||
|
c.Colour = ColourInfo.GradientVertical(
|
||||||
|
Color4.White.Opacity(Math.Min(1 - (topY - fadeBottom) / panel_height, 1)),
|
||||||
|
Color4.White.Opacity(Math.Min(1 - (bottomY - fadeBottom) / panel_height, 1)));
|
||||||
|
}
|
||||||
|
else if (requiresScroll)
|
||||||
|
{
|
||||||
|
c.Colour = ColourInfo.GradientVertical(
|
||||||
|
Color4.White.Opacity(Math.Min(1 - (fadeTop - topY) / panel_height, 1)),
|
||||||
|
Color4.White.Opacity(Math.Min(1 - (fadeTop - bottomY) / panel_height, 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sort()
|
private void sort()
|
||||||
@ -73,15 +163,26 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
if (sorting.IsValid)
|
if (sorting.IsValid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var orderedByScore = this.OrderByDescending(i => i.TotalScore.Value).ToList();
|
var orderedByScore = Flow.OrderByDescending(i => i.TotalScore.Value).ToList();
|
||||||
|
|
||||||
for (int i = 0; i < Count; i++)
|
for (int i = 0; i < Flow.Count; i++)
|
||||||
{
|
{
|
||||||
SetLayoutPosition(orderedByScore[i], i);
|
Flow.SetLayoutPosition(orderedByScore[i], i);
|
||||||
orderedByScore[i].ScorePosition = i + 1;
|
orderedByScore[i].ScorePosition = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sorting.Validate();
|
sorting.Validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class InputDisabledScrollContainer : OsuScrollContainer
|
||||||
|
{
|
||||||
|
public InputDisabledScrollContainer()
|
||||||
|
{
|
||||||
|
ScrollbarVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HandlePositionalInput => false;
|
||||||
|
public override bool HandleNonPositionalInput => false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,10 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
[CanBeNull]
|
[CanBeNull]
|
||||||
public User User { get; }
|
public User User { get; }
|
||||||
|
|
||||||
private readonly bool trackedPlayer;
|
/// <summary>
|
||||||
|
/// Whether this score is the local user or a replay player (and should be focused / always visible).
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool Tracked;
|
||||||
|
|
||||||
private Container mainFillContainer;
|
private Container mainFillContainer;
|
||||||
|
|
||||||
@ -97,11 +100,11 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
/// Creates a new <see cref="GameplayLeaderboardScore"/>.
|
/// Creates a new <see cref="GameplayLeaderboardScore"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user">The score's player.</param>
|
/// <param name="user">The score's player.</param>
|
||||||
/// <param name="trackedPlayer">Whether the player is the local user or a replay player.</param>
|
/// <param name="tracked">Whether the player is the local user or a replay player.</param>
|
||||||
public GameplayLeaderboardScore([CanBeNull] User user, bool trackedPlayer)
|
public GameplayLeaderboardScore([CanBeNull] User user, bool tracked)
|
||||||
{
|
{
|
||||||
User = user;
|
User = user;
|
||||||
this.trackedPlayer = trackedPlayer;
|
Tracked = tracked;
|
||||||
|
|
||||||
AutoSizeAxes = Axes.X;
|
AutoSizeAxes = Axes.X;
|
||||||
Height = PANEL_HEIGHT;
|
Height = PANEL_HEIGHT;
|
||||||
@ -338,7 +341,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
panelColour = BackgroundColour ?? Color4Extensions.FromHex("7fcc33");
|
panelColour = BackgroundColour ?? Color4Extensions.FromHex("7fcc33");
|
||||||
textColour = TextColour ?? Color4.White;
|
textColour = TextColour ?? Color4.White;
|
||||||
}
|
}
|
||||||
else if (trackedPlayer)
|
else if (Tracked)
|
||||||
{
|
{
|
||||||
widthExtension = true;
|
widthExtension = true;
|
||||||
panelColour = BackgroundColour ?? Color4Extensions.FromHex("ffd966");
|
panelColour = BackgroundColour ?? Color4Extensions.FromHex("ffd966");
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
@ -104,7 +105,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Team1Score.BindValueChanged(_ => updateScores());
|
Team1Score.BindValueChanged(_ => updateScores());
|
||||||
Team2Score.BindValueChanged(_ => updateScores());
|
Team2Score.BindValueChanged(_ => updateScores(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateScores()
|
private void updateScores()
|
||||||
@ -171,6 +172,8 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
=> displayedSpriteText.Font = winning
|
=> displayedSpriteText.Font = winning
|
||||||
? OsuFont.Torus.With(weight: FontWeight.Bold, size: font_size, fixedWidth: true)
|
? OsuFont.Torus.With(weight: FontWeight.Bold, size: font_size, fixedWidth: true)
|
||||||
: OsuFont.Torus.With(weight: FontWeight.Regular, size: font_size * 0.8f, fixedWidth: true);
|
: OsuFont.Torus.With(weight: FontWeight.Regular, size: font_size * 0.8f, fixedWidth: true);
|
||||||
|
|
||||||
|
protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(@"N0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
|
|
||||||
var trackedUser = UserScores[user.Id];
|
var trackedUser = UserScores[user.Id];
|
||||||
|
|
||||||
var leaderboardScore = AddPlayer(user, user.Id == api.LocalUser.Value.Id);
|
var leaderboardScore = Add(user, user.Id == api.LocalUser.Value.Id);
|
||||||
leaderboardScore.Accuracy.BindTo(trackedUser.Accuracy);
|
leaderboardScore.Accuracy.BindTo(trackedUser.Accuracy);
|
||||||
leaderboardScore.TotalScore.BindTo(trackedUser.Score);
|
leaderboardScore.TotalScore.BindTo(trackedUser.Score);
|
||||||
leaderboardScore.Combo.BindTo(trackedUser.CurrentCombo);
|
leaderboardScore.Combo.BindTo(trackedUser.CurrentCombo);
|
||||||
@ -184,7 +184,7 @@ namespace osu.Game.Screens.Play.HUD
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (TeamScores.TryGetValue(u.Team.Value, out var team))
|
if (TeamScores.TryGetValue(u.Team.Value, out var team))
|
||||||
team.Value += (int)u.Score.Value;
|
team.Value += (int)Math.Round(u.Score.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,8 +57,6 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private Bindable<HUDVisibilityMode> configVisibilityMode;
|
private Bindable<HUDVisibilityMode> configVisibilityMode;
|
||||||
|
|
||||||
private readonly Container visibilityContainer;
|
|
||||||
|
|
||||||
private readonly BindableBool replayLoaded = new BindableBool();
|
private readonly BindableBool replayLoaded = new BindableBool();
|
||||||
|
|
||||||
private static bool hasShownNotificationOnce;
|
private static bool hasShownNotificationOnce;
|
||||||
@ -72,7 +70,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private readonly SkinnableTargetContainer mainComponents;
|
private readonly SkinnableTargetContainer mainComponents;
|
||||||
|
|
||||||
private IEnumerable<Drawable> hideTargets => new Drawable[] { visibilityContainer, KeyCounter, topRightElements };
|
private IEnumerable<Drawable> hideTargets => new Drawable[] { mainComponents, KeyCounter, topRightElements };
|
||||||
|
|
||||||
public HUDOverlay(DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods)
|
public HUDOverlay(DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods)
|
||||||
{
|
{
|
||||||
@ -84,13 +82,9 @@ namespace osu.Game.Screens.Play
|
|||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
CreateFailingLayer(),
|
CreateFailingLayer(),
|
||||||
visibilityContainer = new Container
|
mainComponents = new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = mainComponents = new SkinnableTargetContainer(SkinnableTarget.MainHUDComponents)
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
topRightElements = new FillFlowContainer
|
topRightElements = new FillFlowContainer
|
||||||
{
|
{
|
||||||
|
@ -125,7 +125,7 @@ namespace osu.Game.Screens.Play
|
|||||||
Objects = drawableRuleset.Objects;
|
Objects = drawableRuleset.Objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.BindWith(OsuSetting.ShowProgressGraph, ShowGraph);
|
config.BindWith(OsuSetting.ShowDifficultyGraph, ShowGraph);
|
||||||
|
|
||||||
graph.FillColour = bar.FillColour = colours.BlueLighter;
|
graph.FillColour = bar.FillColour = colours.BlueLighter;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,8 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
protected ScorePanelList ScorePanelList { get; private set; }
|
protected ScorePanelList ScorePanelList { get; private set; }
|
||||||
|
|
||||||
|
protected VerticalScrollContainer VerticalScrollContent { get; private set; }
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private Player player { get; set; }
|
private Player player { get; set; }
|
||||||
|
|
||||||
@ -77,7 +79,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
{
|
{
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
new VerticalScrollContainer
|
VerticalScrollContent = new VerticalScrollContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
ScrollbarVisible = false,
|
ScrollbarVisible = false,
|
||||||
@ -343,7 +345,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private class VerticalScrollContainer : OsuScrollContainer
|
protected class VerticalScrollContainer : OsuScrollContainer
|
||||||
{
|
{
|
||||||
protected override Container<Drawable> Content => content;
|
protected override Container<Drawable> Content => content;
|
||||||
|
|
||||||
@ -351,6 +353,8 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
public VerticalScrollContainer()
|
public VerticalScrollContainer()
|
||||||
{
|
{
|
||||||
|
Masking = false;
|
||||||
|
|
||||||
base.Content.Add(content = new Container { RelativeSizeAxes = Axes.X });
|
base.Content.Add(content = new Container { RelativeSizeAxes = Axes.X });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
public class BeatmapInfoWedge : VisibilityContainer
|
public class BeatmapInfoWedge : VisibilityContainer
|
||||||
{
|
{
|
||||||
|
public const float BORDER_THICKNESS = 2.5f;
|
||||||
private const float shear_width = 36.75f;
|
private const float shear_width = 36.75f;
|
||||||
|
|
||||||
private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0);
|
private static readonly Vector2 wedged_container_shear = new Vector2(shear_width / SongSelect.WEDGE_HEIGHT, 0);
|
||||||
@ -59,7 +60,7 @@ namespace osu.Game.Screens.Select
|
|||||||
Shear = wedged_container_shear;
|
Shear = wedged_container_shear;
|
||||||
Masking = true;
|
Masking = true;
|
||||||
BorderColour = new Color4(221, 255, 255, 255);
|
BorderColour = new Color4(221, 255, 255, 255);
|
||||||
BorderThickness = 2.5f;
|
BorderThickness = BORDER_THICKNESS;
|
||||||
Alpha = 0;
|
Alpha = 0;
|
||||||
EdgeEffect = new EdgeEffectParameters
|
EdgeEffect = new EdgeEffectParameters
|
||||||
{
|
{
|
||||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Screens.Select
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
searchText = value;
|
searchText = value;
|
||||||
SearchTerms = searchText.Split(new[] { ',', ' ', '!' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
|
SearchTerms = searchText.Split(' ', StringSplitOptions.RemoveEmptyEntries).ToArray();
|
||||||
|
|
||||||
SearchNumber = null;
|
SearchNumber = null;
|
||||||
|
|
||||||
|
@ -79,6 +79,8 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
protected BeatmapCarousel Carousel { get; private set; }
|
protected BeatmapCarousel Carousel { get; private set; }
|
||||||
|
|
||||||
|
protected Container LeftArea { get; private set; }
|
||||||
|
|
||||||
private BeatmapInfoWedge beatmapInfoWedge;
|
private BeatmapInfoWedge beatmapInfoWedge;
|
||||||
private DialogOverlay dialogOverlay;
|
private DialogOverlay dialogOverlay;
|
||||||
|
|
||||||
@ -186,12 +188,12 @@ namespace osu.Game.Screens.Select
|
|||||||
{
|
{
|
||||||
new Drawable[]
|
new Drawable[]
|
||||||
{
|
{
|
||||||
new Container
|
LeftArea = new Container
|
||||||
{
|
{
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Padding = new MarginPadding { Top = left_area_padding },
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
beatmapInfoWedge = new BeatmapInfoWedge
|
beatmapInfoWedge = new BeatmapInfoWedge
|
||||||
@ -200,8 +202,8 @@ namespace osu.Game.Screens.Select
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Margin = new MarginPadding
|
Margin = new MarginPadding
|
||||||
{
|
{
|
||||||
Top = left_area_padding,
|
|
||||||
Right = left_area_padding,
|
Right = left_area_padding,
|
||||||
|
Left = -BeatmapInfoWedge.BORDER_THICKNESS, // Hide the left border
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
@ -210,7 +212,7 @@ namespace osu.Game.Screens.Select
|
|||||||
Padding = new MarginPadding
|
Padding = new MarginPadding
|
||||||
{
|
{
|
||||||
Bottom = Footer.HEIGHT,
|
Bottom = Footer.HEIGHT,
|
||||||
Top = WEDGE_HEIGHT + left_area_padding,
|
Top = WEDGE_HEIGHT,
|
||||||
Left = left_area_padding,
|
Left = left_area_padding,
|
||||||
Right = left_area_padding * 2,
|
Right = left_area_padding * 2,
|
||||||
},
|
},
|
||||||
|
@ -178,7 +178,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
Debug.Assert(Room != null);
|
Debug.Assert(Room != null);
|
||||||
|
|
||||||
return ((IMultiplayerClient)this).UserLeft(Room.Users.Single(u => u.UserID == userId));
|
return ((IMultiplayerClient)this).UserKicked(Room.Users.Single(u => u.UserID == userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task ChangeSettings(MultiplayerRoomSettings settings)
|
public override async Task ChangeSettings(MultiplayerRoomSettings settings)
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="10.3.0" />
|
<PackageReference Include="Realm" Version="10.3.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2021.813.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2021.813.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.810.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.813.0" />
|
||||||
<PackageReference Include="Sentry" Version="3.8.3" />
|
<PackageReference Include="Sentry" Version="3.8.3" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
<PackageReference Include="SharpCompress" Version="0.28.3" />
|
||||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.813.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2021.813.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.810.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.813.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
<!-- See https://github.com/dotnet/runtime/issues/35988 (can be removed after Xamarin uses net5.0 / net6.0) -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
Loading…
Reference in New Issue
Block a user