1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 13:22:55 +08:00

Merge branch 'master' into channel-selector-tab-item

This commit is contained in:
jorolf 2017-05-26 13:18:50 +02:00 committed by GitHub
commit f6f1ea42ee
51 changed files with 3171 additions and 281 deletions

@ -1 +1 @@
Subproject commit c6f030d6f1ab65a48de9ff1d0c424acb686e0149
Subproject commit 8baad1b9484b9f35724e2f965c18cfe710907d80

View File

@ -0,0 +1,199 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Overlays;
namespace osu.Desktop.VisualTests.Tests
{
public class TestCaseDirect : TestCase
{
public override string Description => @"osu!direct overlay";
private DirectOverlay direct;
private RulesetDatabase rulesets;
public override void Reset()
{
base.Reset();
Add(direct = new DirectOverlay());
newBeatmaps();
AddStep(@"toggle", direct.ToggleVisibility);
AddStep(@"result counts", () => direct.ResultAmounts = new DirectOverlay.ResultCounts(1, 4, 13));
}
[BackgroundDependencyLoader]
private void load(RulesetDatabase rulesets)
{
this.rulesets = rulesets;
}
private void newBeatmaps()
{
var ruleset = rulesets.GetRuleset(0);
direct.BeatmapSets = new[]
{
new BeatmapSetInfo
{
Metadata = new BeatmapMetadata
{
Title = @"OrVid",
Artist = @"An",
Author = @"RLC",
Source = @"",
},
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 5.35f,
Metadata = new BeatmapMetadata(),
OnlineInfo = new BeatmapOnlineInfo
{
Covers = new[] { @"https://assets.ppy.sh//beatmaps/578332/covers/cover.jpg?1494591390" },
Preview = @"https://b.ppy.sh/preview/578332.mp3",
PlayCount = 97,
FavouriteCount = 72,
},
},
},
},
new BeatmapSetInfo
{
Metadata = new BeatmapMetadata
{
Title = @"tiny lamp",
Artist = @"fhana",
Author = @"Sotarks",
Source = @"ぎんぎつね",
},
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 5.81f,
Metadata = new BeatmapMetadata(),
OnlineInfo = new BeatmapOnlineInfo
{
Covers = new[] { @"https://assets.ppy.sh//beatmaps/599627/covers/cover.jpg?1494539318" },
Preview = @"https//b.ppy.sh/preview/599627.mp3",
PlayCount = 3082,
FavouriteCount = 14,
},
},
},
},
new BeatmapSetInfo
{
Metadata = new BeatmapMetadata
{
Title = @"At Gwanghwamun",
Artist = @"KYUHYUN",
Author = @"Cerulean Veyron",
Source = @"",
},
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 0.9f,
Metadata = new BeatmapMetadata(),
OnlineInfo = new BeatmapOnlineInfo
{
Covers = new[] { @"https://assets.ppy.sh//beatmaps/513268/covers/cover.jpg?1494502863" },
Preview = @"https//b.ppy.sh/preview/513268.mp3",
PlayCount = 2762,
FavouriteCount = 15,
},
},
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 1.1f,
},
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 2.02f,
},
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 3.49f,
},
},
},
new BeatmapSetInfo
{
Metadata = new BeatmapMetadata
{
Title = @"RHAPSODY OF BLUE SKY",
Artist = @"fhana",
Author = @"[Kamiya]",
Source = @"小林さんちのメイドラゴン",
},
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 1.26f,
Metadata = new BeatmapMetadata(),
OnlineInfo = new BeatmapOnlineInfo
{
Covers = new[] { @"https://assets.ppy.sh//beatmaps/586841/covers/cover.jpg?1494052741" },
Preview = @"https//b.ppy.sh/preview/586841.mp3",
PlayCount = 62317,
FavouriteCount = 161,
},
},
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 2.01f,
},
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 2.87f,
},
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 3.76f,
},
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 3.93f,
},
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 4.37f,
},
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 5.13f,
},
new BeatmapInfo
{
Ruleset = ruleset,
StarDifficulty = 5.42f,
},
},
},
};
}
}
}

View File

@ -0,0 +1,74 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Screens.Multiplayer;
using osu.Game.Online.Multiplayer;
using osu.Game.Users;
using osu.Game.Database;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseDrawableRoom : TestCase
{
public override string Description => @"Select your favourite room";
public override void Reset()
{
base.Reset();
DrawableRoom first;
DrawableRoom second;
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Y,
Width = 500f,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
first = new DrawableRoom(new Room()),
second = new DrawableRoom(new Room()),
}
});
first.Room.Name.Value = @"Great Room Right Here";
first.Room.Host.Value = new User { Username = @"Naeferith", Id = 9492835, Country = new Country { FlagName = @"FR" }};
first.Room.Status.Value = new RoomStatusOpen();
first.Room.Beatmap.Value = new BeatmapMetadata { Title = @"Seiryu", Artist = @"Critical Crystal" };
second.Room.Name.Value = @"Relax It's The Weekend";
second.Room.Host.Value = new User { Username = @"peppy", Id = 2, Country = new Country { FlagName = @"AU" }};
second.Room.Status.Value = new RoomStatusPlaying();
second.Room.Beatmap.Value = new BeatmapMetadata { Title = @"ZAQ", Artist = @"Serendipity" };
AddStep(@"change state", () =>
{
first.Room.Status.Value = new RoomStatusPlaying();
});
AddStep(@"change name", () =>
{
first.Room.Name.Value = @"I Changed Name";
});
AddStep(@"change host", () =>
{
first.Room.Host.Value = new User { Username = @"DrabWeb", Id = 6946022, Country = new Country { FlagName = @"CA" } };
});
AddStep(@"change beatmap", () =>
{
first.Room.Beatmap.Value = null;
});
AddStep(@"change state", () =>
{
first.Room.Status.Value = new RoomStatusOpen();
});
}
}
}

View File

@ -0,0 +1,57 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Testing;
using osu.Framework.Graphics;
using osu.Game.Users;
using osu.Framework.Graphics.Containers;
using OpenTK;
namespace osu.Desktop.VisualTests.Tests
{
internal class TestCaseUserPanel : TestCase
{
public override string Description => @"Panels for displaying a user's status";
public override void Reset()
{
base.Reset();
UserPanel flyte;
UserPanel peppy;
Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(10f),
Children = new[]
{
flyte = new UserPanel(new User
{
Username = @"flyte",
Id = 3103765,
Country = new Country { FlagName = @"JP" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/3103765/5b012e13611d5761caa7e24fecb3d3a16e1cf48fc2a3032cfd43dd444af83d82.jpeg"
}) { Width = 300 },
peppy = new UserPanel(new User
{
Username = @"peppy",
Id = 2,
Country = new Country { FlagName = @"AU" },
CoverUrl = @"https://assets.ppy.sh/user-profile-covers/2/08cad88747c235a64fca5f1b770e100f120827ded1ffe3b66bfcd19c940afa65.jpeg"
}) { Width = 300 },
},
});
flyte.Status.Value = new UserStatusOnline();
peppy.Status.Value = new UserStatusSoloGame();
AddStep(@"spectating", () => { flyte.Status.Value = new UserStatusSpectating(); });
AddStep(@"multiplaying", () => { flyte.Status.Value = new UserStatusMultiplayerGame(); });
AddStep(@"modding", () => { flyte.Status.Value = new UserStatusModding(); });
AddStep(@"offline", () => { flyte.Status.Value = new UserStatusOffline(); });
AddStep(@"null status", () => { flyte.Status.Value = null; });
}
}
}

View File

@ -220,6 +220,9 @@
<Compile Include="Tests\TestCaseLeaderboard.cs" />
<Compile Include="Beatmaps\TestWorkingBeatmap.cs" />
<Compile Include="Tests\TestCaseBeatmapDetailArea.cs" />
<Compile Include="Tests\TestCaseDrawableRoom.cs" />
<Compile Include="Tests\TestCaseUserPanel.cs" />
<Compile Include="Tests\TestCaseDirect.cs" />
</ItemGroup>
<ItemGroup />
<ItemGroup />

View File

@ -24,6 +24,9 @@ namespace osu.Game.Rulesets.Osu.Objects
base.ApplyDefaults(controlPointInfo, difficulty);
SpinsRequired = (int)(Duration / 1000 * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5));
// spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being.
SpinsRequired = (int)(SpinsRequired * 0.6);
}
}
}

View File

@ -7,6 +7,8 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Backgrounds;
using OpenTK.Graphics;
using osu.Game.Beatmaps.ControlPoints;
using osu.Framework.Audio.Track;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
{
@ -22,6 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
public const float SYMBOL_SIZE = TaikoHitObject.DEFAULT_CIRCLE_DIAMETER * 0.45f;
public const float SYMBOL_BORDER = 8;
public const float SYMBOL_INNER_SIZE = SYMBOL_SIZE - 2 * SYMBOL_BORDER;
private const double pre_beat_transition_time = 80;
/// <summary>
/// The colour of the inner circle and outer glows.
@ -63,6 +66,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
public CirclePiece(bool isStrong = false)
{
EarlyActivationMilliseconds = pre_beat_transition_time;
AddInternal(new Drawable[]
{
background = new CircularContainer
@ -139,14 +144,31 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
Content.Width = 1 / Content.Scale.X;
}
private const float edge_alpha_kiai = 0.5f;
private void resetEdgeEffects()
{
background.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Colour = AccentColour,
Radius = KiaiMode ? 50 : 8
Colour = AccentColour.Opacity(KiaiMode ? edge_alpha_kiai : 1f),
Radius = KiaiMode ? 32 : 8
};
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
{
if (!effectPoint.KiaiMode)
return;
if (beatIndex % (int)timingPoint.TimeSignature != 0)
return;
double duration = timingPoint.BeatLength * 2;
background.FadeEdgeEffectTo(1, pre_beat_transition_time, EasingTypes.OutQuint);
using (background.BeginDelayedSequence(pre_beat_transition_time))
background.FadeEdgeEffectTo(edge_alpha_kiai, duration, EasingTypes.OutQuint);
}
}
}

View File

@ -1,14 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Graphics.Containers;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
{
public class TaikoPiece : Container, IHasAccentColour
public class TaikoPiece : BeatSyncedContainer, IHasAccentColour
{
private Color4 accentColour;
/// <summary>
@ -17,10 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces
public virtual Color4 AccentColour
{
get { return accentColour; }
set
{
accentColour = value;
}
set { accentColour = value; }
}
private bool kiaiMode;

View File

@ -13,12 +13,12 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// <summary>
/// Diameter of a circle relative to the size of the <see cref="TaikoPlayfield"/>.
/// </summary>
public const float PLAYFIELD_RELATIVE_DIAMETER = 0.5f;
public const float PLAYFIELD_RELATIVE_DIAMETER = 0.45f;
/// <summary>
/// Scale multiplier for a strong circle.
/// </summary>
public const float STRONG_CIRCLE_DIAMETER_SCALE = 1.5f;
public const float STRONG_CIRCLE_DIAMETER_SCALE = 1.4f;
/// <summary>
/// Default circle diameter.

View File

@ -18,22 +18,23 @@ namespace osu.Game.Rulesets.Taiko.UI
/// </summary>
internal class HitExplosion : CircularContainer
{
/// <summary>
/// The judgement this hit explosion visualises.
/// </summary>
public readonly TaikoJudgement Judgement;
private readonly Box innerFill;
public HitExplosion(TaikoJudgement judgement)
{
Judgement = judgement;
private readonly bool isRim;
Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER);
public HitExplosion(TaikoJudgement judgement, bool isRim)
{
this.isRim = isRim;
Judgement = judgement;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Size = new Vector2(TaikoPlayfield.HIT_TARGET_OFFSET + TaikoHitObject.DEFAULT_CIRCLE_DIAMETER);
RelativePositionAxes = Axes.Both;
BorderColour = Color4.White;
@ -54,22 +55,14 @@ namespace osu.Game.Rulesets.Taiko.UI
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
switch (Judgement.TaikoResult)
{
case TaikoHitResult.Good:
innerFill.Colour = colours.Green;
break;
case TaikoHitResult.Great:
innerFill.Colour = colours.Blue;
break;
}
innerFill.Colour = isRim ? colours.BlueDarker : colours.PinkDarker;
}
protected override void LoadComplete()
{
base.LoadComplete();
ScaleTo(5f, 1000, EasingTypes.OutQuint);
ScaleTo(3f, 1000, EasingTypes.OutQuint);
FadeOut(500);
Expire();
@ -80,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.UI
/// </summary>
public void VisualiseSecondHit()
{
ResizeTo(Size * TaikoHitObject.STRONG_CIRCLE_DIAMETER_SCALE, 50);
ResizeTo(new Vector2(TaikoPlayfield.HIT_TARGET_OFFSET + TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER), 50);
}
}
}

View File

@ -0,0 +1,68 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.UI
{
public class KiaiHitExplosion : CircularContainer
{
public readonly TaikoJudgement Judgement;
private readonly bool isRim;
public KiaiHitExplosion(TaikoJudgement judgement, bool isRim)
{
this.isRim = isRim;
Judgement = judgement;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Y;
Size = new Vector2(TaikoHitObject.DEFAULT_CIRCLE_DIAMETER, 1);
Masking = true;
Alpha = 0.25f;
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Colour = isRim ? colours.BlueDarker : colours.PinkDarker,
Radius = 60,
};
}
protected override void LoadComplete()
{
base.LoadComplete();
ScaleTo(new Vector2(1, 3f), 500, EasingTypes.OutQuint);
FadeOut(250);
Expire();
}
}
}

View File

@ -24,12 +24,12 @@ namespace osu.Game.Rulesets.Taiko.UI
/// <summary>
/// The default play field height.
/// </summary>
public const float DEFAULT_PLAYFIELD_HEIGHT = 168f;
public const float DEFAULT_PLAYFIELD_HEIGHT = 178f;
/// <summary>
/// The offset from <see cref="left_area_size"/> which the center of the hit target lies at.
/// </summary>
private const float hit_target_offset = TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER / 2f + 40;
public const float HIT_TARGET_OFFSET = TaikoHitObject.DEFAULT_STRONG_CIRCLE_DIAMETER / 2f + 40;
/// <summary>
/// The size of the left area of the playfield. This area contains the input drum.
@ -39,15 +39,18 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override Container<Drawable> Content => hitObjectContainer;
private readonly Container<HitExplosion> hitExplosionContainer;
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
private readonly Container<DrawableBarLine> barLineContainer;
private readonly Container<DrawableTaikoJudgement> judgementContainer;
private readonly Container hitObjectContainer;
private readonly Container topLevelHitContainer;
private readonly Container leftBackgroundContainer;
private readonly Container rightBackgroundContainer;
private readonly Box leftBackground;
private readonly Box rightBackground;
private readonly Container overlayBackgroundContainer;
private readonly Container backgroundContainer;
private readonly Box overlayBackground;
private readonly Box background;
public TaikoPlayfield()
{
@ -59,7 +62,7 @@ namespace osu.Game.Rulesets.Taiko.UI
Height = DEFAULT_PLAYFIELD_HEIGHT,
Children = new[]
{
rightBackgroundContainer = new Container
backgroundContainer = new Container
{
Name = "Transparent playfield background",
RelativeSizeAxes = Axes.Both,
@ -73,7 +76,7 @@ namespace osu.Game.Rulesets.Taiko.UI
},
Children = new Drawable[]
{
rightBackground = new Box
background = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.6f
@ -82,24 +85,23 @@ namespace osu.Game.Rulesets.Taiko.UI
},
new Container
{
Name = "Transparent playfield elements",
Name = "Right area",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = left_area_size },
Margin = new MarginPadding { Left = left_area_size },
Children = new Drawable[]
{
new Container
{
Name = "Hit target container",
X = hit_target_offset,
Name = "Masked elements",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
Masking = true,
Children = new Drawable[]
{
hitExplosionContainer = new Container<HitExplosion>
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
BlendingMode = BlendingMode.Additive
BlendingMode = BlendingMode.Additive,
},
barLineContainer = new Container<DrawableBarLine>
{
@ -114,23 +116,32 @@ namespace osu.Game.Rulesets.Taiko.UI
{
RelativeSizeAxes = Axes.Both,
},
judgementContainer = new Container<DrawableTaikoJudgement>
{
RelativeSizeAxes = Axes.Y,
BlendingMode = BlendingMode.Additive
},
},
}
},
kiaiExplosionContainer = new Container<KiaiHitExplosion>
{
Name = "Kiai hit explosions",
RelativeSizeAxes = Axes.Y,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
BlendingMode = BlendingMode.Additive
},
judgementContainer = new Container<DrawableTaikoJudgement>
{
Name = "Judgements",
RelativeSizeAxes = Axes.Y,
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
BlendingMode = BlendingMode.Additive
},
}
},
leftBackgroundContainer = new Container
overlayBackgroundContainer = new Container
{
Name = "Left overlay",
Size = new Vector2(left_area_size, DEFAULT_PLAYFIELD_HEIGHT),
BorderThickness = 1,
Children = new Drawable[]
{
leftBackground = new Box
overlayBackground = new Box
{
RelativeSizeAxes = Axes.Both,
},
@ -164,11 +175,11 @@ namespace osu.Game.Rulesets.Taiko.UI
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
leftBackgroundContainer.BorderColour = colours.Gray0;
leftBackground.Colour = colours.Gray1;
overlayBackgroundContainer.BorderColour = colours.Gray0;
overlayBackground.Colour = colours.Gray1;
rightBackgroundContainer.BorderColour = colours.Gray1;
rightBackground.Colour = colours.Gray0;
backgroundContainer.BorderColour = colours.Gray1;
background.Colour = colours.Gray0;
}
public override void Add(DrawableHitObject<TaikoHitObject, TaikoJudgement> h)
@ -204,6 +215,8 @@ namespace osu.Game.Rulesets.Taiko.UI
if (!wasHit)
return;
bool isRim = judgedObject.HitObject is RimHit;
if (!secondHit)
{
if (judgedObject.X >= -0.05f && !(judgedObject is DrawableSwell))
@ -212,7 +225,11 @@ namespace osu.Game.Rulesets.Taiko.UI
topLevelHitContainer.Add(judgedObject.CreateProxy());
}
hitExplosionContainer.Add(new HitExplosion(judgedObject.Judgement));
hitExplosionContainer.Add(new HitExplosion(judgedObject.Judgement, isRim));
if (judgedObject.HitObject.Kiai)
kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject.Judgement, isRim));
}
else
hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit();
@ -221,7 +238,7 @@ namespace osu.Game.Rulesets.Taiko.UI
/// <summary>
/// This is a very special type of container. It serves a similar purpose to <see cref="FillMode.Fit"/>, however unlike <see cref="FillMode.Fit"/>,
/// this will only adjust the scale relative to the height of its parent and will maintain the original width relative to its parent.
///
///
/// <para>
/// By adjusting the scale relative to the height of its parent, the aspect ratio of this container's children is maintained, however this is undesirable
/// in the case where the hit object container should not have its width adjusted by scale. To counteract this, another container is nested inside this

View File

@ -87,6 +87,7 @@
<Compile Include="Scoring\TaikoScoreProcessor.cs" />
<Compile Include="UI\HitTarget.cs" />
<Compile Include="UI\InputDrum.cs" />
<Compile Include="UI\KiaiHitExplosion.cs" />
<Compile Include="UI\DrawableTaikoJudgement.cs" />
<Compile Include="UI\HitExplosion.cs" />
<Compile Include="UI\TaikoHitRenderer.cs" />

View File

@ -55,7 +55,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// </summary>
/// <param name="time">The time to find the timing control point at.</param>
/// <returns>The timing control point.</returns>
public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time);
public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.FirstOrDefault());
/// <summary>
/// Finds the maximum BPM represented by any timing control point.
@ -75,14 +75,21 @@ namespace osu.Game.Beatmaps.ControlPoints
public double BPMMode =>
60000 / (TimingPoints.GroupBy(c => c.BeatLength).OrderByDescending(grp => grp.Count()).FirstOrDefault()?.FirstOrDefault() ?? new TimingControlPoint()).BeatLength;
private T binarySearch<T>(SortedList<T> list, double time)
/// <summary>
/// Binary searches one of the control point lists to find the active control point at <paramref name="time"/>.
/// </summary>
/// <param name="list">The list to search.</param>
/// <param name="time">The time to find the control point at.</param>
/// <param name="prePoint">The control point to use when <paramref name="time"/> is before any control points. If null, a new control point will be constructed.</param>
/// <returns>The active control point at <paramref name="time"/>.</returns>
private T binarySearch<T>(SortedList<T> list, double time, T prePoint = null)
where T : ControlPoint, new()
{
if (list.Count == 0)
return new T();
if (time < list[0].Time)
return new T();
return prePoint ?? new T();
int index = list.BinarySearch(new T() { Time = time });
@ -92,8 +99,8 @@ namespace osu.Game.Beatmaps.ControlPoints
index = ~index;
if (index == list.Count)
return list[list.Count - 1];
// BinarySearch will return the index of the first element _greater_ than the search
// This is the inactive point - the active point is the one before it (index - 1)
return list[index - 1];
}
}

View File

@ -9,7 +9,7 @@ using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
internal class DifficultyColouredContainer : Container, IHasAccentColour
public class DifficultyColouredContainer : Container, IHasAccentColour
{
public Color4 AccentColour { get; set; }

View File

@ -11,7 +11,7 @@ using OpenTK.Graphics;
namespace osu.Game.Beatmaps.Drawables
{
internal class DifficultyIcon : DifficultyColouredContainer
public class DifficultyIcon : DifficultyColouredContainer
{
private readonly BeatmapInfo beatmap;

View File

@ -43,6 +43,9 @@ namespace osu.Game.Database
[Ignore]
public BeatmapMetrics Metrics { get; set; }
[Ignore]
public BeatmapOnlineInfo OnlineInfo { get; set; }
public string Path { get; set; }
[JsonProperty("file_md5")]

View File

@ -0,0 +1,38 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
using System.Collections.Generic;
namespace osu.Game.Database
{
/// <summary>
/// Beatmap info retrieved for previewing locally without having the beatmap downloaded.
/// </summary>
public class BeatmapOnlineInfo
{
/// <summary>
/// The different sizes of cover art for this beatmap: cover, cover@2x, card, card@2x, list, list@2x.
/// </summary>
[JsonProperty(@"covers")]
public IEnumerable<string> Covers { get; set; }
/// <summary>
/// A small sample clip of this beatmap's song.
/// </summary>
[JsonProperty(@"previewUrl")]
public string Preview { get; set; }
/// <summary>
/// The amount of plays this beatmap has.
/// </summary>
[JsonProperty(@"play_count")]
public int PlayCount { get; set; }
/// <summary>
/// The amount of people who have favourited this map.
/// </summary>
[JsonProperty(@"favourite_count")]
public int FavouriteCount { get; set; }
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps;
namespace osu.Game.Database
{
internal class OnlineWorkingBeatmap : WorkingBeatmap
{
private readonly TextureStore textures;
private readonly TrackManager tracks;
public OnlineWorkingBeatmap(BeatmapInfo beatmapInfo, TextureStore textures, TrackManager tracks) : base(beatmapInfo)
{
this.textures = textures;
this.tracks = tracks;
}
protected override Beatmap GetBeatmap()
{
return new Beatmap();
}
protected override Texture GetBackground()
{
return textures.Get(BeatmapInfo.OnlineInfo.Covers.FirstOrDefault());
}
protected override Track GetTrack()
{
return tracks.Get(BeatmapInfo.OnlineInfo.Preview);
}
}
}

View File

@ -0,0 +1,23 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Database
{
public enum RankStatus
{
Any = 7,
[Description("Ranked & Approved")]
RankedApproved = 0,
Approved = 1,
Loved = 8,
Favourites = 2,
[Description("Mod Requests")]
ModRequests = 3,
Pending = 4,
Graveyard = 5,
[Description("My Maps")]
MyMaps = 6,
}
}

View File

@ -1,20 +1,29 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.MathUtils;
using OpenTK;
using OpenTK.Graphics;
using System;
using osu.Framework.Graphics.OpenGL;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
using OpenTK.Graphics.ES30;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Allocation;
using System.Collections.Generic;
using osu.Framework.Graphics.Batches;
using osu.Framework.Lists;
namespace osu.Game.Graphics.Backgrounds
{
public class Triangles : Container<Triangle>
public class Triangles : Drawable
{
private const float triangle_size = 100;
public override bool HandleInput => false;
public Color4 ColourLight = Color4.White;
@ -44,6 +53,33 @@ namespace osu.Game.Graphics.Backgrounds
/// </summary>
public bool HideAlphaDiscrepancies = true;
/// <summary>
/// The relative velocity of the triangles. Default is 1.
/// </summary>
public float Velocity = 1;
private readonly SortedList<TriangleParticle> parts = new SortedList<TriangleParticle>(Comparer<TriangleParticle>.Default);
private Shader shader;
private readonly Texture texture;
public Triangles()
{
texture = Texture.WhitePixel;
}
[BackgroundDependencyLoader]
private void load(ShaderManager shaders)
{
shader = shaders?.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED);
}
protected override void LoadComplete()
{
base.LoadComplete();
addTriangles(true);
}
public float TriangleScale
{
get { return triangleScale; }
@ -52,42 +88,71 @@ namespace osu.Game.Graphics.Backgrounds
float change = value / triangleScale;
triangleScale = value;
if (change != 1)
Children.ForEach(t => t.Scale *= change);
for (int i = 0; i < parts.Count; i++)
{
TriangleParticle newParticle = parts[i];
newParticle.Scale *= change;
parts[i] = newParticle;
}
}
}
protected override void LoadComplete()
{
base.LoadComplete();
addTriangles(true);
}
private int aimTriangleCount => (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio);
protected override void Update()
{
base.Update();
float adjustedAlpha = HideAlphaDiscrepancies ?
// Cubically scale alpha to make it drop off more sharply.
(float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) :
1;
Invalidate(Invalidation.DrawNode, shallPropagate: false);
foreach (var t in Children)
for (int i = 0; i < parts.Count; i++)
{
t.Alpha = adjustedAlpha;
t.Position -= new Vector2(0, (float)(t.Scale.X * (50 / DrawHeight) * (Time.Elapsed / 950)) / triangleScale);
if (ExpireOffScreenTriangles && t.DrawPosition.Y + t.DrawSize.Y * t.Scale.Y < 0)
t.Expire();
TriangleParticle newParticle = parts[i];
float adjustedAlpha = HideAlphaDiscrepancies ?
// Cubically scale alpha to make it drop off more sharply.
(float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) :
1;
newParticle.Position += new Vector2(0, -(parts[i].Scale * (50 / DrawHeight)) / triangleScale * Velocity) * ((float)Time.Elapsed / 950);
newParticle.Colour.A = adjustedAlpha;
parts[i] = newParticle;
if (!CreateNewTriangles)
continue;
float bottomPos = parts[i].Position.Y + triangle_size * parts[i].Scale * 0.866f / DrawHeight;
if (bottomPos < 0)
parts.RemoveAt(i);
}
if (CreateNewTriangles)
addTriangles(false);
addTriangles(false);
}
protected virtual Triangle CreateTriangle()
private void addTriangles(bool randomY)
{
int aimTriangleCount = (int)(DrawWidth * DrawHeight * 0.002f / (triangleScale * triangleScale) * SpawnRatio);
for (int i = 0; i < aimTriangleCount - parts.Count; i++)
parts.Add(createTriangle(randomY));
}
private TriangleParticle createTriangle(bool randomY)
{
TriangleParticle particle = CreateTriangle();
particle.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() : 1);
particle.Colour = CreateTriangleShade();
return particle;
}
/// <summary>
/// Creates a triangle particle with a random scale.
/// </summary>
/// <returns>The triangle particle.</returns>
protected virtual TriangleParticle CreateTriangle()
{
const float std_dev = 0.16f;
const float mean = 0.5f;
@ -97,32 +162,100 @@ namespace osu.Game.Graphics.Backgrounds
float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1)
var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); //random normal(mean,stdDev^2)
const float size = 100;
return new EquilateralTriangle
{
Origin = Anchor.TopCentre,
RelativePositionAxes = Axes.Both,
Size = new Vector2(size),
Scale = new Vector2(scale),
EdgeSmoothness = new Vector2(1),
Colour = GetTriangleShade(),
Depth = scale,
};
return new TriangleParticle { Scale = scale };
}
protected virtual Color4 GetTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1);
/// <summary>
/// Creates a shade of colour for the triangles.
/// </summary>
/// <returns>The colour.</returns>
protected virtual Color4 CreateTriangleShade() => Interpolation.ValueAt(RNG.NextSingle(), ColourDark, ColourLight, 0, 1);
private void addTriangles(bool randomY)
protected override DrawNode CreateDrawNode() => new TrianglesDrawNode();
private readonly TrianglesDrawNodeSharedData sharedData = new TrianglesDrawNodeSharedData();
protected override void ApplyDrawNode(DrawNode node)
{
int addCount = aimTriangleCount - Children.Count();
for (int i = 0; i < addCount; i++)
base.ApplyDrawNode(node);
var trianglesNode = (TrianglesDrawNode)node;
trianglesNode.Shader = shader;
trianglesNode.Texture = texture;
trianglesNode.Size = DrawSize;
trianglesNode.Shared = sharedData;
trianglesNode.Parts.Clear();
trianglesNode.Parts.AddRange(parts);
}
private class TrianglesDrawNodeSharedData
{
public readonly LinearBatch<TexturedVertex2D> VertexBatch = new LinearBatch<TexturedVertex2D>(100 * 3, 10, PrimitiveType.Triangles);
}
private class TrianglesDrawNode : DrawNode
{
public Shader Shader;
public Texture Texture;
public TrianglesDrawNodeSharedData Shared;
public readonly List<TriangleParticle> Parts = new List<TriangleParticle>();
public Vector2 Size;
public override void Draw(Action<TexturedVertex2D> vertexAction)
{
var sprite = CreateTriangle();
float triangleHeight = sprite.DrawHeight / DrawHeight;
sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1);
Add(sprite);
base.Draw(vertexAction);
Shader.Bind();
Texture.TextureGL.Bind();
foreach (TriangleParticle particle in Parts)
{
var offset = new Vector2(particle.Scale * 0.5f, particle.Scale * 0.866f);
var triangle = new Triangle(
particle.Position * Size * DrawInfo.Matrix,
(particle.Position * Size + offset * triangle_size) * DrawInfo.Matrix,
(particle.Position * Size + new Vector2(-offset.X, offset.Y) * triangle_size) * DrawInfo.Matrix
);
ColourInfo colourInfo = DrawInfo.Colour;
colourInfo.ApplyChild(particle.Colour);
Texture.DrawTriangle(triangle, colourInfo, null, Shared.VertexBatch.Add);
}
Shader.Unbind();
}
}
protected struct TriangleParticle : IComparable<TriangleParticle>
{
/// <summary>
/// The position of the top vertex of the triangle.
/// </summary>
public Vector2 Position;
/// <summary>
/// The colour of the triangle.
/// </summary>
public Color4 Colour;
/// <summary>
/// The scale of the triangle.
/// </summary>
public float Scale;
/// <summary>
/// Compares two <see cref="TriangleParticle"/>s. This is a reverse comparer because when the
/// triangles are added to the particles list, they should be drawn from largest to smallest
/// such that the smaller triangles appear on top.
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public int CompareTo(TriangleParticle other) => other.Scale.CompareTo(Scale);
}
}
}

View File

@ -2,59 +2,65 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
namespace osu.Game.Graphics.Containers
{
public class BeatSyncedContainer : Container
{
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
protected readonly Bindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
private int lastBeat;
private TimingControlPoint lastTimingPoint;
/// <summary>
/// The amount of time before a beat we should fire <see cref="OnNewBeat(int, TimingControlPoint, EffectControlPoint, TrackAmplitudes)"/>.
/// This allows for adding easing to animations that may be synchronised to the beat.
/// </summary>
protected double EarlyActivationMilliseconds;
protected override void Update()
{
if (beatmap.Value?.Track == null)
if (Beatmap.Value?.Track == null)
return;
double currentTrackTime = beatmap.Value.Track.CurrentTime;
double currentTrackTime = Beatmap.Value.Track.CurrentTime + EarlyActivationMilliseconds;
TimingControlPoint timingPoint = beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
EffectControlPoint effectPoint = beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
TimingControlPoint timingPoint = Beatmap.Value.Beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
EffectControlPoint effectPoint = Beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
if (timingPoint.BeatLength == 0)
return;
int beat = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength);
int beatIndex = (int)((currentTrackTime - timingPoint.Time) / timingPoint.BeatLength);
// The beats before the start of the first control point are off by 1, this should do the trick
if (currentTrackTime < timingPoint.Time)
beat--;
beatIndex--;
if (timingPoint == lastTimingPoint && beat == lastBeat)
if (timingPoint == lastTimingPoint && beatIndex == lastBeat)
return;
double offsetFromBeat = (timingPoint.Time - currentTrackTime) % timingPoint.BeatLength;
using (BeginDelayedSequence(offsetFromBeat, true))
OnNewBeat(beat, timingPoint.BeatLength, timingPoint.TimeSignature, effectPoint.KiaiMode);
OnNewBeat(beatIndex, timingPoint, effectPoint, Beatmap.Value.Track.CurrentAmplitudes);
lastBeat = beat;
lastBeat = beatIndex;
lastTimingPoint = timingPoint;
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
beatmap.BindTo(game.Beatmap);
Beatmap.BindTo(game.Beatmap);
}
protected virtual void OnNewBeat(int newBeat, double beatLength, TimeSignatures timeSignature, bool kiai)
protected virtual void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
{
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Graphics.Containers
{
public class ReverseDepthFillFlowContainer<T> : FillFlowContainer<T> where T : Drawable
{
protected override IComparer<Drawable> DepthComparer => new ReverseCreationOrderDepthComparer();
protected override IEnumerable<T> FlowingChildren => base.FlowingChildren.Reverse();
}
}

View File

@ -42,7 +42,7 @@ namespace osu.Game.Graphics.UserInterface
protected override DropdownMenuItem<T> CreateMenuItem(string text, T value) => new OsuDropdownMenuItem(text, value) { AccentColour = AccentColour };
private class OsuDropdownMenuItem : DropdownMenuItem<T>
public class OsuDropdownMenuItem : DropdownMenuItem<T>
{
public OsuDropdownMenuItem(string text, T current) : base(text, current)
{
@ -60,7 +60,7 @@ namespace osu.Game.Graphics.UserInterface
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
chevron = new TextAwesome
Chevron = new TextAwesome
{
AlwaysPresent = true,
Icon = FontAwesome.fa_chevron_right,
@ -84,12 +84,12 @@ namespace osu.Game.Graphics.UserInterface
private Color4? accentColour;
private readonly TextAwesome chevron;
protected readonly TextAwesome Chevron;
protected override void FormatForeground(bool hover = false)
{
base.FormatForeground(hover);
chevron.Alpha = hover ? 1 : 0;
Chevron.Alpha = hover ? 1 : 0;
}
public Color4 AccentColour
@ -113,13 +113,13 @@ namespace osu.Game.Graphics.UserInterface
}
}
protected class OsuDropdownHeader : DropdownHeader
public class OsuDropdownHeader : DropdownHeader
{
private readonly SpriteText label;
protected readonly SpriteText Text;
protected override string Label
{
get { return label.Text; }
set { label.Text = value; }
get { return Text.Text; }
set { Text.Text = value; }
}
protected readonly TextAwesome Icon;
@ -146,7 +146,7 @@ namespace osu.Game.Graphics.UserInterface
Foreground.Children = new Drawable[]
{
label = new OsuSpriteText
Text = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,

View File

@ -0,0 +1,32 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.ComponentModel;
using System.Reflection;
using System.Collections.Generic;
namespace osu.Game.Graphics.UserInterface
{
public class OsuEnumDropdown<T> : OsuDropdown<T>
{
public OsuEnumDropdown()
{
if (!typeof(T).IsEnum)
throw new InvalidOperationException("OsuEnumDropdown only supports enums as the generic type argument");
List<KeyValuePair<string, T>> items = new List<KeyValuePair<string, T>>();
foreach(var val in (T[])Enum.GetValues(typeof(T)))
{
var field = typeof(T).GetField(Enum.GetName(typeof(T), val));
items.Add(
new KeyValuePair<string, T>(
field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? Enum.GetName(typeof(T), val),
val
)
);
}
Items = items;
}
}
}

View File

@ -57,9 +57,9 @@ namespace osu.Game.Graphics.UserInterface
}
}
private class OsuTabItem : TabItem<T>
public class OsuTabItem : TabItem<T>
{
private readonly SpriteText text;
protected readonly SpriteText Text;
private readonly Box box;
private Color4? accentColour;
@ -70,7 +70,7 @@ namespace osu.Game.Graphics.UserInterface
{
accentColour = value;
if (!Active)
text.Colour = value;
Text.Colour = value;
}
}
@ -94,13 +94,13 @@ namespace osu.Game.Graphics.UserInterface
private void fadeActive()
{
box.FadeIn(transition_length, EasingTypes.OutQuint);
text.FadeColour(Color4.White, transition_length, EasingTypes.OutQuint);
Text.FadeColour(Color4.White, transition_length, EasingTypes.OutQuint);
}
private void fadeInactive()
{
box.FadeOut(transition_length, EasingTypes.OutQuint);
text.FadeColour(AccentColour, transition_length, EasingTypes.OutQuint);
Text.FadeColour(AccentColour, transition_length, EasingTypes.OutQuint);
}
protected override bool OnHover(InputState state)
@ -130,7 +130,7 @@ namespace osu.Game.Graphics.UserInterface
Children = new Drawable[]
{
text = new OsuSpriteText
Text = new OsuSpriteText
{
Margin = new MarginPadding { Top = 5, Bottom = 5 },
Origin = Anchor.BottomLeft,

View File

@ -0,0 +1,17 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
using osu.Game.Database;
using osu.Game.Users;
namespace osu.Game.Online.Multiplayer
{
public class Room
{
public Bindable<string> Name = new Bindable<string>();
public Bindable<User> Host = new Bindable<User>();
public Bindable<RoomStatus> Status = new Bindable<RoomStatus>();
public Bindable<BeatmapMetadata> Beatmap = new Bindable<BeatmapMetadata>();
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Game.Graphics;
namespace osu.Game.Online.Multiplayer
{
public abstract class RoomStatus
{
public abstract string Message { get; }
public abstract Color4 GetAppropriateColour(OsuColour colours);
}
public class RoomStatusOpen : RoomStatus
{
public override string Message => @"Welcoming Players";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenLight;
}
public class RoomStatusPlaying : RoomStatus
{
public override string Message => @"Now Playing";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.Purple;
}
}

View File

@ -41,6 +41,8 @@ namespace osu.Game
private DialogOverlay dialogOverlay;
private DirectOverlay direct;
private Intro intro
{
get
@ -70,6 +72,8 @@ namespace osu.Game
public void ToggleSettings() => settings.ToggleVisibility();
public void ToggleDirect() => direct.ToggleVisibility();
[BackgroundDependencyLoader]
private void load()
{
@ -160,6 +164,7 @@ namespace osu.Game
});
//overlay elements
LoadComponentAsync(direct = new DirectOverlay { Depth = -1 }, mainContent.Add);
LoadComponentAsync(chat = new ChatOverlay { Depth = -1 }, mainContent.Add);
LoadComponentAsync(settings = new SettingsOverlay { Depth = -1 }, overlayContent.Add);
LoadComponentAsync(musicController = new MusicController
@ -249,6 +254,9 @@ namespace osu.Game
case Key.O:
settings.ToggleVisibility();
return true;
case Key.D:
direct.ToggleVisibility();
return true;
}
}
@ -280,6 +288,7 @@ namespace osu.Game
Toolbar.State = Visibility.Hidden;
musicController.State = Visibility.Hidden;
chat.State = Visibility.Hidden;
direct.State = Visibility.Hidden;
}
else
{

View File

@ -0,0 +1,193 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Localisation;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays.Direct
{
public class DirectGridPanel : DirectPanel
{
private const float horizontal_padding = 10;
private const float vertical_padding = 5;
private FillFlowContainer bottomPanel;
public DirectGridPanel(BeatmapSetInfo beatmap) : base(beatmap)
{
Height = 140 + vertical_padding; //full height of all the elements plus vertical padding (autosize uses the image)
CornerRadius = 4;
Masking = true;
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 1f),
Radius = 3f,
Colour = Color4.Black.Opacity(0.25f),
};
}
protected override void LoadComplete()
{
base.LoadComplete();
FadeInFromZero(200, EasingTypes.Out);
bottomPanel.LayoutDuration = 200;
bottomPanel.LayoutEasing = EasingTypes.Out;
bottomPanel.Origin = Anchor.BottomLeft;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation, TextureStore textures)
{
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
GetBackground(textures),
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f),
},
bottomPanel = new FillFlowContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft,
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, vertical_padding),
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = horizontal_padding, Right = horizontal_padding },
Direction = FillDirection.Vertical,
Children = new[]
{
new OsuSpriteText
{
Text = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Text = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Font = @"Exo2.0-BoldItalic",
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding
{
Top = vertical_padding,
Bottom = vertical_padding,
Left = horizontal_padding,
Right = horizontal_padding,
},
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
new OsuSpriteText
{
Text = "mapped by ",
TextSize = 14,
Shadow = false,
Colour = colours.Gray5,
},
new OsuSpriteText
{
Text = SetInfo.Metadata.Author,
TextSize = 14,
Font = @"Exo2.0-SemiBoldItalic",
Shadow = false,
Colour = colours.BlueDark,
},
},
},
new Container
{
AutoSizeAxes = Axes.X,
Height = 14,
Children = new[]
{
new OsuSpriteText
{
Text = $"from {SetInfo.Metadata.Source}",
TextSize = 14,
Shadow = false,
Colour = colours.Gray5,
Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f,
},
},
},
new FillFlowContainer
{
AutoSizeAxes = Axes.X,
Height = 20,
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
Children = GetDifficultyIcons(),
},
},
},
},
},
},
},
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = vertical_padding, Right = vertical_padding },
Children = new[]
{
new Statistic(FontAwesome.fa_play_circle, SetInfo.Beatmaps.FirstOrDefault()?.OnlineInfo.PlayCount ?? 0)
{
Margin = new MarginPadding { Right = 1 },
},
new Statistic(FontAwesome.fa_heart, SetInfo.Beatmaps.FirstOrDefault()?.OnlineInfo.FavouriteCount ?? 0),
},
},
};
}
}
}

View File

@ -0,0 +1,197 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Colour;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Database;
using osu.Framework.Allocation;
using osu.Framework.Localisation;
using osu.Framework.Graphics.Textures;
using System.Linq;
using osu.Framework.Input;
namespace osu.Game.Overlays.Direct
{
public class DirectListPanel : DirectPanel
{
private const float horizontal_padding = 10;
private const float vertical_padding = 5;
private const float height = 70;
public DirectListPanel(BeatmapSetInfo beatmap) : base(beatmap)
{
RelativeSizeAxes = Axes.X;
Height = height;
CornerRadius = 5;
Masking = true;
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Offset = new Vector2(0f, 1f),
Radius = 3f,
Colour = Color4.Black.Opacity(0.25f),
};
}
protected override void LoadComplete()
{
base.LoadComplete();
FadeInFromZero(200, EasingTypes.Out);
}
[BackgroundDependencyLoader]
private void load(LocalisationEngine localisation, TextureStore textures)
{
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
GetBackground(textures),
new Box
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.25f), Color4.Black.Opacity(0.75f)),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding, Left = horizontal_padding, Right = vertical_padding },
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
TextSize = 18,
Font = @"Exo2.0-BoldItalic",
},
new OsuSpriteText
{
Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
Font = @"Exo2.0-BoldItalic",
},
new FillFlowContainer
{
AutoSizeAxes = Axes.X,
Height = 20,
Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding },
Children = GetDifficultyIcons(),
},
},
},
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Right = height - vertical_padding * 2 + vertical_padding },
Children = new Drawable[]
{
new Statistic(FontAwesome.fa_play_circle, SetInfo.Beatmaps.FirstOrDefault()?.OnlineInfo.PlayCount ?? 0)
{
Margin = new MarginPadding { Right = 1 },
},
new Statistic(FontAwesome.fa_heart, SetInfo.Beatmaps.FirstOrDefault()?.OnlineInfo.FavouriteCount ?? 0),
new FillFlowContainer
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new[]
{
new OsuSpriteText
{
Text = "mapped by ",
TextSize = 14,
},
new OsuSpriteText
{
Text = SetInfo.Metadata.Author,
TextSize = 14,
Font = @"Exo2.0-SemiBoldItalic",
},
},
},
new OsuSpriteText
{
Text = $"from {SetInfo.Metadata.Source}",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 14,
Alpha = string.IsNullOrEmpty(SetInfo.Metadata.Source) ? 0f : 1f,
},
},
},
new DownloadButton
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Size = new Vector2(height - vertical_padding * 2),
},
},
},
};
}
private class DownloadButton : ClickableContainer
{
private readonly TextAwesome icon;
public DownloadButton()
{
Children = new Drawable[]
{
icon = new TextAwesome
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
UseFullGlyphHeight = false,
TextSize = 30,
Icon = FontAwesome.fa_osu_chevron_down_o,
},
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
icon.ScaleTo(0.9f, 1000, EasingTypes.Out);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
icon.ScaleTo(1f, 500, EasingTypes.OutElastic);
return base.OnMouseUp(state, args);
}
protected override bool OnHover(InputState state)
{
icon.ScaleTo(1.1f, 500, EasingTypes.OutElastic);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
icon.ScaleTo(1f, 500, EasingTypes.OutElastic);
}
}
}
}

View File

@ -0,0 +1,88 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Overlays.Direct
{
public abstract class DirectPanel : Container
{
protected readonly BeatmapSetInfo SetInfo;
protected DirectPanel(BeatmapSetInfo setInfo)
{
SetInfo = setInfo;
}
protected IEnumerable<DifficultyIcon> GetDifficultyIcons()
{
var icons = new List<DifficultyIcon>();
foreach (var b in SetInfo.Beatmaps)
icons.Add(new DifficultyIcon(b));
return icons;
}
protected Drawable GetBackground(TextureStore textures)
{
return new AsyncLoadWrapper(new BeatmapBackgroundSprite(new OnlineWorkingBeatmap(SetInfo.Beatmaps.FirstOrDefault(), textures, null))
{
FillMode = FillMode.Fill,
OnLoadComplete = d => d.FadeInFromZero(400, EasingTypes.Out),
}) { RelativeSizeAxes = Axes.Both };
}
public class Statistic : FillFlowContainer
{
private readonly SpriteText text;
private int value;
public int Value
{
get { return value; }
set
{
this.value = value;
text.Text = Value.ToString(@"N0");
}
}
public Statistic(FontAwesome icon, int value = 0)
{
Anchor = Anchor.TopRight;
Origin = Anchor.TopRight;
AutoSizeAxes = Axes.Both;
Direction = FillDirection.Horizontal;
Spacing = new Vector2(5f, 0f);
Children = new Drawable[]
{
text = new OsuSpriteText
{
Font = @"Exo2.0-SemiBoldItalic",
},
new TextAwesome
{
Icon = icon,
Shadow = true,
TextSize = 14,
Margin = new MarginPadding { Top = 1 },
},
};
Value = value;
}
}
}
}

View File

@ -0,0 +1,233 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Direct
{
public class FilterControl : Container
{
public static readonly float HEIGHT = 35 + 32 + 30 + padding * 2; // search + mode toggle buttons + sort tabs + padding
private const float padding = 10;
private readonly Box tabStrip;
private readonly FillFlowContainer<RulesetToggleButton> modeButtons;
public readonly SearchTextBox Search;
public readonly SortTabControl SortTabs;
public readonly OsuEnumDropdown<RankStatus> RankStatusDropdown;
public readonly Bindable<DirectOverlay.PanelDisplayStyle> DisplayStyle = new Bindable<DirectOverlay.PanelDisplayStyle>();
protected override bool InternalContains(Vector2 screenSpacePos) => base.InternalContains(screenSpacePos) || RankStatusDropdown.Contains(screenSpacePos);
public FilterControl()
{
RelativeSizeAxes = Axes.X;
Height = HEIGHT;
DisplayStyle.Value = DirectOverlay.PanelDisplayStyle.Grid;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"384552"),
Alpha = 0.9f,
},
tabStrip = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.TopLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = DirectOverlay.WIDTH_PADDING, Right = DirectOverlay.WIDTH_PADDING },
Children = new Drawable[]
{
Search = new DirectSearchTextBox
{
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding { Top = padding },
},
modeButtons = new FillFlowContainer<RulesetToggleButton>
{
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(padding, 0f),
Margin = new MarginPadding { Top = padding },
},
SortTabs = new SortTabControl
{
RelativeSizeAxes = Axes.X,
},
},
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Spacing = new Vector2(10f, 0f),
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Top = HEIGHT - SlimEnumDropdown<DirectTab>.HEIGHT - padding, Right = DirectOverlay.WIDTH_PADDING },
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(5f, 0f),
Direction = FillDirection.Horizontal,
Children = new[]
{
new DisplayStyleToggleButton(FontAwesome.fa_th_large, DirectOverlay.PanelDisplayStyle.Grid, DisplayStyle),
new DisplayStyleToggleButton(FontAwesome.fa_list_ul, DirectOverlay.PanelDisplayStyle.List, DisplayStyle),
},
},
RankStatusDropdown = new SlimEnumDropdown<RankStatus>
{
RelativeSizeAxes = Axes.None,
Width = 160f,
},
},
},
};
RankStatusDropdown.Current.Value = RankStatus.RankedApproved;
SortTabs.Current.Value = SortCriteria.Title;
SortTabs.Current.TriggerChange();
}
[BackgroundDependencyLoader(true)]
private void load(OsuGame game, RulesetDatabase rulesets, OsuColour colours)
{
tabStrip.Colour = colours.Yellow;
RankStatusDropdown.AccentColour = colours.BlueDark;
var b = new Bindable<RulesetInfo>(); //backup bindable incase the game is null
foreach (var r in rulesets.AllRulesets)
{
modeButtons.Add(new RulesetToggleButton(game?.Ruleset ?? b, r));
}
}
private class DirectSearchTextBox : SearchTextBox
{
protected override Color4 BackgroundUnfocused => backgroundColour;
protected override Color4 BackgroundFocused => backgroundColour;
private Color4 backgroundColour;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
backgroundColour = colours.Gray2.Opacity(0.9f);
}
}
private class RulesetToggleButton : ClickableContainer
{
private readonly TextAwesome icon;
private RulesetInfo ruleset;
public RulesetInfo Ruleset
{
get { return ruleset; }
set
{
ruleset = value;
icon.Icon = Ruleset.CreateInstance().Icon;
}
}
private readonly Bindable<RulesetInfo> bindable;
private void Bindable_ValueChanged(RulesetInfo obj)
{
icon.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100);
}
public RulesetToggleButton(Bindable<RulesetInfo> bindable, RulesetInfo ruleset)
{
this.bindable = bindable;
AutoSizeAxes = Axes.Both;
Children = new[]
{
icon = new TextAwesome
{
Origin = Anchor.TopLeft,
Anchor = Anchor.TopLeft,
TextSize = 32,
}
};
Ruleset = ruleset;
bindable.ValueChanged += Bindable_ValueChanged;
Bindable_ValueChanged(bindable.Value);
Action = () => bindable.Value = Ruleset;
}
protected override void Dispose(bool isDisposing)
{
if (bindable != null)
bindable.ValueChanged -= Bindable_ValueChanged;
base.Dispose(isDisposing);
}
}
private class DisplayStyleToggleButton : ClickableContainer
{
private readonly TextAwesome icon;
private readonly DirectOverlay.PanelDisplayStyle style;
private readonly Bindable<DirectOverlay.PanelDisplayStyle> bindable;
public DisplayStyleToggleButton(FontAwesome icon, DirectOverlay.PanelDisplayStyle style, Bindable<DirectOverlay.PanelDisplayStyle> bindable)
{
this.bindable = bindable;
this.style = style;
Size = new Vector2(SlimEnumDropdown<DirectTab>.HEIGHT);
Children = new Drawable[]
{
this.icon = new TextAwesome
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Icon = icon,
TextSize = 18,
UseFullGlyphHeight = false,
Alpha = 0.5f,
},
};
bindable.ValueChanged += Bindable_ValueChanged;
Bindable_ValueChanged(bindable.Value);
Action = () => bindable.Value = this.style;
}
private void Bindable_ValueChanged(DirectOverlay.PanelDisplayStyle style)
{
icon.FadeTo(style == this.style ? 1.0f : 0.5f, 100);
}
protected override void Dispose(bool isDisposing)
{
bindable.ValueChanged -= Bindable_ValueChanged;
}
}
}
}

View File

@ -0,0 +1,124 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using Container = osu.Framework.Graphics.Containers.Container;
namespace osu.Game.Overlays.Direct
{
public class Header : Container
{
public static readonly float HEIGHT = 90;
private readonly Box tabStrip;
public readonly OsuTabControl<DirectTab> Tabs;
public Header()
{
Height = HEIGHT;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"252f3a"),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = DirectOverlay.WIDTH_PADDING, Right = DirectOverlay.WIDTH_PADDING },
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomLeft,
Position = new Vector2(-35f, 5f),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f),
Children = new Drawable[]
{
new TextAwesome
{
TextSize = 25,
Icon = FontAwesome.fa_osu_chevron_down_o,
},
new OsuSpriteText
{
TextSize = 25,
Text = @"osu!direct",
},
},
},
tabStrip = new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Width = 282, //todo: make this actually match the tab control's width instead of hardcoding
Height = 1,
},
Tabs = new DirectTabControl
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
},
},
},
};
Tabs.Current.Value = DirectTab.Search;
Tabs.Current.TriggerChange();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
tabStrip.Colour = colours.Green;
}
private class DirectTabControl : OsuTabControl<DirectTab>
{
protected override TabItem<DirectTab> CreateTabItem(DirectTab value) => new DirectTabItem(value);
public DirectTabControl()
{
Height = 25;
AccentColour = Color4.White;
}
private class DirectTabItem : OsuTabItem
{
public DirectTabItem(DirectTab value) : base(value)
{
Text.TextSize = 15;
}
}
}
}
public enum DirectTab
{
Search,
[Description("Newest Maps")]
NewestMaps,
[Description("Top Rated")]
TopRated,
[Description("Most Played")]
MostPlayed
}
}

View File

@ -0,0 +1,43 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Direct
{
public class SlimEnumDropdown<T> : OsuEnumDropdown<T>
{
public const float HEIGHT = 25;
protected override DropdownHeader CreateHeader() => new SlimDropdownHeader { AccentColour = AccentColour };
protected override Menu CreateMenu() => new SlimMenu();
private class SlimDropdownHeader : OsuDropdownHeader
{
public SlimDropdownHeader()
{
Height = HEIGHT;
Icon.TextSize = 16;
Foreground.Padding = new MarginPadding { Top = 4, Bottom = 4, Left = 8, Right = 4 };
}
protected override void LoadComplete()
{
base.LoadComplete();
BackgroundColour = Color4.Black.Opacity(0.25f);
}
}
private class SlimMenu : OsuMenu
{
public SlimMenu()
{
Background.Colour = Color4.Black.Opacity(0.25f);
}
}
}
}

View File

@ -0,0 +1,117 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Direct
{
public class SortTabControl : OsuTabControl<SortCriteria>
{
protected override TabItem<SortCriteria> CreateTabItem(SortCriteria value) => new SortTabItem(value);
public SortTabControl()
{
Height = 30;
}
private class SortTabItem : TabItem<SortCriteria>
{
private const float transition_duration = 100;
private readonly Box box;
public override bool Active
{
get { return base.Active; }
set
{
if (Active == value) return;
if (value)
slideActive();
else
slideInactive();
base.Active = value;
}
}
public SortTabItem(SortCriteria value) : base(value)
{
AutoSizeAxes = Axes.X;
RelativeSizeAxes = Axes.Y;
Children = new Drawable[]
{
new OsuSpriteText
{
Margin = new MarginPadding { Top = 8, Bottom = 8 },
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Text = (value as Enum).GetDescription() ?? value.ToString(),
TextSize = 14,
Font = @"Exo2.0-Bold",
},
box = new Box
{
RelativeSizeAxes = Axes.X,
Height = 5,
Scale = new Vector2(1f, 0f),
Colour = Color4.White,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
box.Colour = colours.Yellow;
}
protected override bool OnHover(InputState state)
{
if (!Active)
slideActive();
return true;
}
protected override void OnHoverLost(InputState state)
{
if (!Active)
slideInactive();
}
private void slideActive()
{
box.ScaleTo(new Vector2(1f), transition_duration);
}
private void slideInactive()
{
box.ScaleTo(new Vector2(1f, 0f), transition_duration);
}
}
}
public enum SortCriteria
{
Title,
Artist,
Creator,
Difficulty,
Ranked,
Rating,
}
}

View File

@ -0,0 +1,230 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Direct;
using Container = osu.Framework.Graphics.Containers.Container;
namespace osu.Game.Overlays
{
public class DirectOverlay : WaveOverlayContainer
{
public static readonly int WIDTH_PADDING = 80;
private const float panel_padding = 10f;
private readonly FilterControl filter;
private readonly FillFlowContainer resultCountsContainer;
private readonly OsuSpriteText resultCountsText;
private readonly FillFlowContainer<DirectPanel> panels;
private IEnumerable<BeatmapSetInfo> beatmapSets;
public IEnumerable<BeatmapSetInfo> BeatmapSets
{
get { return beatmapSets; }
set
{
if (beatmapSets?.Equals(value) ?? false) return;
beatmapSets = value;
recreatePanels(filter.DisplayStyle.Value);
}
}
private ResultCounts resultAmounts;
public ResultCounts ResultAmounts
{
get { return resultAmounts; }
set
{
if (value == ResultAmounts) return;
resultAmounts = value;
updateResultCounts();
}
}
public DirectOverlay()
{
RelativeSizeAxes = Axes.Both;
// osu!direct colours are not part of the standard palette
FirstWaveColour = OsuColour.FromHex(@"19b0e2");
SecondWaveColour = OsuColour.FromHex(@"2280a2");
ThirdWaveColour = OsuColour.FromHex(@"005774");
FourthWaveColour = OsuColour.FromHex(@"003a4e");
Header header;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"485e74"),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new[]
{
new Triangles
{
RelativeSizeAxes = Axes.Both,
TriangleScale = 5,
ColourLight = OsuColour.FromHex(@"465b71"),
ColourDark = OsuColour.FromHex(@"3f5265"),
},
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = Header.HEIGHT + FilterControl.HEIGHT },
Children = new[]
{
new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollDraggerVisible = false,
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
resultCountsContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Left = WIDTH_PADDING, Top = 6 },
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Found ",
TextSize = 15,
},
resultCountsText = new OsuSpriteText
{
TextSize = 15,
Font = @"Exo2.0-Bold",
},
}
},
panels = new FillFlowContainer<DirectPanel>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Top = panel_padding, Bottom = panel_padding, Left = WIDTH_PADDING, Right = WIDTH_PADDING },
Spacing = new Vector2(panel_padding),
},
},
},
},
},
},
},
filter = new FilterControl
{
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding { Top = Header.HEIGHT },
},
header = new Header
{
RelativeSizeAxes = Axes.X,
},
};
header.Tabs.Current.ValueChanged += tab => { if (tab != DirectTab.Search) filter.Search.Current.Value = string.Empty; };
filter.Search.Exit = Hide;
filter.Search.Current.ValueChanged += text => { if (text != string.Empty) header.Tabs.Current.Value = DirectTab.Search; };
filter.DisplayStyle.ValueChanged += recreatePanels;
updateResultCounts();
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
resultCountsContainer.Colour = colours.Yellow;
}
private void updateResultCounts()
{
resultCountsContainer.FadeTo(ResultAmounts == null ? 0f : 1f, 200, EasingTypes.Out);
if (ResultAmounts == null) return;
resultCountsText.Text = pluralize("Artist", ResultAmounts.Artists) + ", " +
pluralize("Song", ResultAmounts.Songs) + ", " +
pluralize("Tag", ResultAmounts.Tags);
}
private string pluralize(string prefix, int value)
{
return $@"{value} {prefix}" + (value == 1 ? string.Empty : @"s");
}
private void recreatePanels(PanelDisplayStyle displayStyle)
{
if (BeatmapSets == null) return;
panels.Children = BeatmapSets.Select(b => displayStyle == PanelDisplayStyle.Grid ? (DirectPanel)new DirectGridPanel(b) { Width = 400 } : new DirectListPanel(b));
}
protected override bool OnFocus(InputState state)
{
filter.Search.TriggerFocus();
return false;
}
protected override void PopIn()
{
base.PopIn();
filter.Search.HoldFocus = true;
}
protected override void PopOut()
{
base.PopOut();
filter.Search.HoldFocus = false;
}
public class ResultCounts
{
public readonly int Artists;
public readonly int Songs;
public readonly int Tags;
public ResultCounts(int artists, int songs, int tags)
{
Artists = artists;
Songs = songs;
Tags = tags;
}
}
public enum PanelDisplayStyle
{
Grid,
List,
}
}
}

View File

@ -12,15 +12,24 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using OpenTK;
using osu.Framework.Input;
using osu.Game.Users;
using System.ComponentModel;
using osu.Game.Graphics;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using Container = osu.Framework.Graphics.Containers.Container;
namespace osu.Game.Overlays.Settings.Sections.General
{
public class LoginSettings : SettingsSubsection, IOnlineComponent
public class LoginSettings : FillFlowContainer, IOnlineComponent
{
private bool bounding = true;
private LoginForm form;
private OsuColour colours;
protected override string Header => "Account";
private UserPanel panel;
private UserDropdown dropdown;
public override RectangleF BoundingBox => bounding ? base.BoundingBox : RectangleF.Empty;
@ -34,9 +43,18 @@ namespace osu.Game.Overlays.Settings.Sections.General
}
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api)
public LoginSettings()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Direction = FillDirection.Vertical;
Spacing = new Vector2(0f, 5f);
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuColour colours, APIAccess api)
{
this.colours = colours;
api?.Register(this);
}
@ -49,6 +67,12 @@ namespace osu.Game.Overlays.Settings.Sections.General
case APIState.Offline:
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "ACCOUNT",
Margin = new MarginPadding { Bottom = 5 },
Font = @"Exo2.0-Black",
},
form = new LoginForm()
};
break;
@ -66,24 +90,73 @@ namespace osu.Game.Overlays.Settings.Sections.General
{
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "Connecting...",
Margin = new MarginPadding { Top = 10, Bottom = 10 },
},
};
break;
case APIState.Online:
Children = new Drawable[]
{
new OsuSpriteText
{
Text = $"Connected as {api.Username}!",
},
new OsuButton
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
Text = "Sign out",
Action = api.Logout
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = 20, Right = 20 },
Direction = FillDirection.Vertical,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "Signed in",
TextSize = 18,
Font = @"Exo2.0-Bold",
Margin = new MarginPadding { Top = 5, Bottom = 5 },
},
},
},
panel = new UserPanel(api.LocalUser.Value) { RelativeSizeAxes = Axes.X },
dropdown = new UserDropdown { RelativeSizeAxes = Axes.X },
},
},
};
panel.Status.BindTo(api.LocalUser.Value.Status);
dropdown.Current.ValueChanged += newValue =>
{
switch (newValue)
{
case UserAction.Online:
api.LocalUser.Value.Status.Value = new UserStatusOnline();
dropdown.StatusColour = colours.Green;
break;
case UserAction.DoNotDisturb:
api.LocalUser.Value.Status.Value = new UserStatusDoNotDisturb();
dropdown.StatusColour = colours.Red;
break;
case UserAction.AppearOffline:
api.LocalUser.Value.Status.Value = new UserStatusOffline();
dropdown.StatusColour = colours.Gray7;
break;
case UserAction.SignOut:
api.Logout();
break;
}
};
dropdown.Current.TriggerChange();
break;
}
@ -170,5 +243,119 @@ namespace osu.Game.Overlays.Settings.Sections.General
return base.OnFocus(state);
}
}
private class UserDropdown : OsuEnumDropdown<UserAction>
{
protected override DropdownHeader CreateHeader() => new UserDropdownHeader { AccentColour = AccentColour };
protected override Menu CreateMenu() => new UserDropdownMenu();
protected override DropdownMenuItem<UserAction> CreateMenuItem(string text, UserAction value) => new UserDropdownMenuItem(text, value) { AccentColour = AccentColour };
public Color4 StatusColour
{
set
{
var h = Header as UserDropdownHeader;
if (h == null) return;
h.StatusColour = value;
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AccentColour = colours.Gray5;
}
private class UserDropdownHeader : OsuDropdownHeader
{
public const float LABEL_LEFT_MARGIN = 20;
private readonly TextAwesome statusIcon;
public Color4 StatusColour
{
set
{
statusIcon.FadeColour(value, 500, EasingTypes.OutQuint);
}
}
public UserDropdownHeader()
{
Foreground.Padding = new MarginPadding { Left = 10, Right = 10 };
Margin = new MarginPadding { Bottom = 5 };
Masking = true;
CornerRadius = 5;
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.25f),
Radius = 4,
};
Icon.TextSize = 14;
Icon.Margin = new MarginPadding(0);
Foreground.Add(statusIcon = new TextAwesome
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Icon = FontAwesome.fa_circle_o,
TextSize = 14,
});
Text.Margin = new MarginPadding { Left = LABEL_LEFT_MARGIN };
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
BackgroundColour = colours.Gray3;
}
}
private class UserDropdownMenu : OsuMenu
{
public UserDropdownMenu()
{
Margin = new MarginPadding { Bottom = 5 };
CornerRadius = 5;
ItemsContainer.Padding = new MarginPadding(0);
Masking = true;
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.25f),
Radius = 4,
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Background.Colour = colours.Gray3;
}
}
private class UserDropdownMenuItem : OsuDropdownMenuItem
{
public UserDropdownMenuItem(string text, UserAction current) : base(text, current)
{
Foreground.Padding = new MarginPadding { Top = 5, Bottom = 5, Left = UserDropdownHeader.LABEL_LEFT_MARGIN, Right = 5 };
Chevron.Margin = new MarginPadding { Left = 2, Right = 3 };
CornerRadius = 5;
}
}
}
private enum UserAction
{
Online,
[Description(@"Do not disturb")]
DoNotDisturb,
[Description(@"Appear offline")]
AppearOffline,
[Description(@"Sign out")]
SignOut,
}
}
}

View File

@ -1,32 +1,17 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Reflection;
using System.ComponentModel;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings
{
public class SettingsEnumDropdown<T> : SettingsDropdown<T>
{
public SettingsEnumDropdown()
protected override Drawable CreateControl() => new OsuEnumDropdown<T>
{
if (!typeof(T).IsEnum)
throw new InvalidOperationException("SettingsDropdown only supports enums as the generic type argument");
List<KeyValuePair<string, T>> items = new List<KeyValuePair<string, T>>();
foreach(var val in (T[])Enum.GetValues(typeof(T)))
{
var field = typeof(T).GetField(Enum.GetName(typeof(T), val));
items.Add(
new KeyValuePair<string, T>(
field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? Enum.GetName(typeof(T), val),
val
)
);
}
Items = items;
}
Margin = new MarginPadding { Top = 5 },
RelativeSizeAxes = Axes.X,
};
}
}

View File

@ -48,7 +48,8 @@ namespace osu.Game.Screens.Menu
OnSolo = delegate { Push(consumeSongSelect()); },
OnMulti = delegate { Push(new Lobby()); },
OnExit = delegate { Exit(); },
}
},
new MenuSideFlashes(),
}
}
};
@ -60,6 +61,7 @@ namespace osu.Game.Screens.Menu
LoadComponentAsync(background);
buttons.OnSettings = game.ToggleSettings;
buttons.OnDirect = game.ToggleDirect;
preloadSongSelect();
}

View File

@ -0,0 +1,98 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using System;
namespace osu.Game.Screens.Menu
{
public class MenuSideFlashes : BeatSyncedContainer
{
public override bool HandleInput => false;
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private readonly Box leftBox;
private readonly Box rightBox;
private const float amplitude_dead_zone = 0.25f;
private const float alpha_multiplier = (1 - amplitude_dead_zone) / 0.55f;
private const float kiai_multiplier = (1 - amplitude_dead_zone * 0.95f) / 0.8f;
private const int box_max_alpha = 200;
private const double box_fade_in_time = 65;
private const int box_width = 200;
public MenuSideFlashes()
{
EarlyActivationMilliseconds = box_fade_in_time;
RelativeSizeAxes = Axes.Both;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Children = new Drawable[]
{
leftBox = new Box
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Y,
Width = box_width,
Alpha = 0,
BlendingMode = BlendingMode.Additive,
},
rightBox = new Box
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.Y,
Width = box_width,
Alpha = 0,
BlendingMode = BlendingMode.Additive,
}
};
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game, OsuColour colours)
{
beatmap.BindTo(game.Beatmap);
// linear colour looks better in this case, so let's use it for now.
Color4 gradientDark = colours.Blue.Opacity(0).ToLinear();
Color4 gradientLight = colours.Blue.Opacity(0.3f).ToLinear();
leftBox.ColourInfo = ColourInfo.GradientHorizontal(gradientLight, gradientDark);
rightBox.ColourInfo = ColourInfo.GradientHorizontal(gradientDark, gradientLight);
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
{
if (beatIndex < 0)
return;
if (effectPoint.KiaiMode ? beatIndex % 2 == 0 : beatIndex % (int)timingPoint.TimeSignature == 0)
flash(leftBox, timingPoint.BeatLength, effectPoint.KiaiMode, amplitudes);
if (effectPoint.KiaiMode ? beatIndex % 2 == 1 : beatIndex % (int)timingPoint.TimeSignature == 0)
flash(rightBox, timingPoint.BeatLength, effectPoint.KiaiMode, amplitudes);
}
private void flash(Drawable d, double beatLength, bool kiai, TrackAmplitudes amplitudes)
{
d.FadeTo(Math.Max(0, ((d.Equals(leftBox) ? amplitudes.LeftChannel : amplitudes.RightChannel) - amplitude_dead_zone) / (kiai ? kiai_multiplier : alpha_multiplier)), box_fade_in_time);
using (d.BeginDelayedSequence(box_fade_in_time))
d.FadeOut(beatLength, EasingTypes.In);
}
}
}

View File

@ -5,13 +5,17 @@ using System;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Containers;
using OpenTK;
using OpenTK.Graphics;
@ -20,19 +24,23 @@ namespace osu.Game.Screens.Menu
/// <summary>
/// osu! logo and its attachments (pulsing, visualiser etc.)
/// </summary>
public class OsuLogo : Container
public class OsuLogo : BeatSyncedContainer
{
public readonly Color4 OsuPink = OsuColour.FromHex(@"e967a1");
private readonly Sprite logo;
private readonly CircularContainer logoContainer;
private readonly Container logoBounceContainer;
private readonly Container logoBeatContainer;
private readonly Container logoAmplitudeContainer;
private readonly Container logoHoverContainer;
private SampleChannel sampleClick;
private readonly Container colourAndTriangles;
private readonly Triangles triangles;
public Action Action;
public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X * 0.74f;
@ -67,8 +75,12 @@ namespace osu.Game.Screens.Menu
private const float default_size = 480;
private const double beat_in_time = 60;
public OsuLogo()
{
EarlyActivationMilliseconds = beat_in_time;
Size = new Vector2(default_size);
Anchor = Anchor.Centre;
@ -78,67 +90,16 @@ namespace osu.Game.Screens.Menu
Children = new Drawable[]
{
logoBounceContainer = new Container
logoHoverContainer = new Container
{
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
logoHoverContainer = new Container
logoBounceContainer = new Container
{
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new BufferedContainer
{
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
logoContainer = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.88f),
Masking = true,
Children = new Drawable[]
{
colourAndTriangles = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuPink,
},
new Triangles
{
TriangleScale = 4,
ColourLight = OsuColour.FromHex(@"ff7db7"),
ColourDark = OsuColour.FromHex(@"de5b95"),
RelativeSizeAxes = Axes.Both,
},
}
},
flashLayer = new Box
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Colour = Color4.White,
Alpha = 0,
},
},
},
logo = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
}
},
rippleContainer = new Container
{
Anchor = Anchor.Centre,
@ -151,36 +112,101 @@ namespace osu.Game.Screens.Menu
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BlendingMode = BlendingMode.Additive,
Alpha = 0.15f
Alpha = 0
}
}
},
impactContainer = new CircularContainer
logoAmplitudeContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Alpha = 0,
BorderColour = Color4.White,
RelativeSizeAxes = Axes.Both,
BorderThickness = 10,
Masking = true,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
logoBeatContainer = new Container
{
RelativeSizeAxes = Axes.Both,
AlwaysPresent = true,
Alpha = 0,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new BufferedContainer
{
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
logoContainer = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.88f),
Masking = true,
Children = new Drawable[]
{
colourAndTriangles = new Container
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuPink,
},
triangles = new Triangles
{
TriangleScale = 4,
ColourLight = OsuColour.FromHex(@"ff7db7"),
ColourDark = OsuColour.FromHex(@"de5b95"),
RelativeSizeAxes = Axes.Both,
},
}
},
flashLayer = new Box
{
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Colour = Color4.White,
Alpha = 0,
},
},
},
logo = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
}
},
impactContainer = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Alpha = 0,
BorderColour = Color4.White,
RelativeSizeAxes = Axes.Both,
BorderThickness = 10,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
AlwaysPresent = true,
Alpha = 0,
}
}
},
new MenuVisualisation
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Alpha = 0.2f,
}
}
}
}
},
new MenuVisualisation
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Alpha = 0.2f,
}
}
}
@ -197,13 +223,56 @@ namespace osu.Game.Screens.Menu
ripple.Texture = textures.Get(@"Menu/logo");
}
protected override void LoadComplete()
{
base.LoadComplete();
private int lastBeatIndex;
ripple.ScaleTo(ripple.Scale * 1.1f, 500);
ripple.FadeOut(500);
ripple.Loop(300);
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, TrackAmplitudes amplitudes)
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
lastBeatIndex = beatIndex;
var beatLength = timingPoint.BeatLength;
float amplitudeAdjust = Math.Min(1, 0.4f + amplitudes.Maximum);
if (beatIndex < 0) return;
logoBeatContainer.ScaleTo(1 - 0.02f * amplitudeAdjust, beat_in_time, EasingTypes.Out);
using (logoBeatContainer.BeginDelayedSequence(beat_in_time))
logoBeatContainer.ScaleTo(1, beatLength * 2, EasingTypes.OutQuint);
ripple.ClearTransforms();
ripple.ScaleTo(logoAmplitudeContainer.Scale);
ripple.Alpha = 0.15f * amplitudeAdjust;
ripple.ScaleTo(logoAmplitudeContainer.Scale * (1 + 0.04f * amplitudeAdjust), beatLength, EasingTypes.OutQuint);
ripple.FadeOut(beatLength, EasingTypes.OutQuint);
if (effectPoint.KiaiMode && flashLayer.Alpha < 0.4f)
{
flashLayer.ClearTransforms();
flashLayer.FadeTo(0.2f * amplitudeAdjust, beat_in_time, EasingTypes.Out);
using (flashLayer.BeginDelayedSequence(beat_in_time))
flashLayer.FadeOut(beatLength);
}
}
protected override void Update()
{
base.Update();
const float scale_adjust_cutoff = 0.4f;
const float velocity_adjust_cutoff = 0.98f;
var maxAmplitude = lastBeatIndex >= 0 ? Beatmap.Value?.Track?.CurrentAmplitudes.Maximum ?? 0 : 0;
logoAmplitudeContainer.ScaleTo(1 - Math.Max(0, maxAmplitude - scale_adjust_cutoff) * 0.04f, 75, EasingTypes.OutQuint);
if (maxAmplitude > velocity_adjust_cutoff)
triangles.Velocity = 1 + Math.Max(0, maxAmplitude - velocity_adjust_cutoff) * 50;
else
triangles.Velocity = (float)Interpolation.Damp(triangles.Velocity, 1, 0.995f, Time.Elapsed);
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
@ -254,4 +323,4 @@ namespace osu.Game.Screens.Menu
impactContainer.ScaleTo(1.12f, 250);
}
}
}
}

View File

@ -0,0 +1,259 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Multiplayer;
using osu.Game.Users;
namespace osu.Game.Screens.Multiplayer
{
public class DrawableRoom : ClickableContainer
{
private const float content_padding = 5;
private const float height = 90;
private readonly Box sideStrip;
private readonly UpdateableAvatar avatar;
private readonly OsuSpriteText name;
private readonly Container flagContainer;
private readonly OsuSpriteText host;
private readonly OsuSpriteText rankBounds;
private readonly OsuSpriteText status;
private readonly FillFlowContainer<OsuSpriteText> beatmapInfoFlow;
private readonly OsuSpriteText beatmapTitle;
private readonly OsuSpriteText beatmapDash;
private readonly OsuSpriteText beatmapArtist;
private OsuColour colours;
private LocalisationEngine localisation;
public readonly Room Room;
public DrawableRoom(Room room)
{
Room = room;
RelativeSizeAxes = Axes.X;
Height = height;
CornerRadius = 5;
Masking = true;
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(40),
Radius = 5,
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(34),
},
sideStrip = new Box
{
RelativeSizeAxes = Axes.Y,
Width = content_padding,
},
avatar = new UpdateableAvatar
{
Size = new Vector2(Height - content_padding* 2),
Masking = true,
CornerRadius = 5f,
Margin = new MarginPadding { Left = content_padding * 2, Top = content_padding },
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = content_padding, Bottom = content_padding, Left = Height + content_padding * 2, Right = content_padding },
Children = new Drawable[]
{
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(5f),
Children = new Drawable[]
{
name = new OsuSpriteText
{
TextSize = 18,
},
new Container
{
RelativeSizeAxes = Axes.X,
Height = 20f,
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.X,
RelativeSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5f, 0f),
Children = new Drawable[]
{
flagContainer = new Container
{
Width = 30f,
RelativeSizeAxes = Axes.Y,
},
new Container
{
Width = 40f,
RelativeSizeAxes = Axes.Y,
},
new OsuSpriteText
{
Text = "hosted by",
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
TextSize = 14,
},
host = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
TextSize = 14,
Font = @"Exo2.0-BoldItalic",
},
},
},
rankBounds = new OsuSpriteText
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Text = "#0 - #0",
TextSize = 14,
Margin = new MarginPadding { Right = 10 },
},
},
},
},
},
new FillFlowContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Bottom = content_padding },
Children = new Drawable[]
{
status = new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
},
beatmapInfoFlow = new FillFlowContainer<OsuSpriteText>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Horizontal,
Children = new[]
{
beatmapTitle = new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-BoldItalic",
},
beatmapDash = new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-RegularItalic",
},
beatmapArtist = new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-RegularItalic",
},
},
},
},
},
},
},
};
Room.Name.ValueChanged += displayName;
Room.Host.ValueChanged += displayUser;
Room.Status.ValueChanged += displayStatus;
Room.Beatmap.ValueChanged += displayBeatmap;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation)
{
this.localisation = localisation;
this.colours = colours;
beatmapInfoFlow.Colour = rankBounds.Colour = colours.Gray9;
host.Colour = colours.Blue;
displayStatus(Room.Status.Value);
}
private void displayName(string value)
{
name.Text = value;
}
private void displayUser(User value)
{
avatar.User = value;
host.Text = value.Username;
flagContainer.Children = new[] { new DrawableFlag(value.Country?.FlagName ?? @"__") { RelativeSizeAxes = Axes.Both } };
}
private void displayStatus(RoomStatus value)
{
if (value == null) return;
status.Text = value.Message;
foreach (Drawable d in new Drawable[] { sideStrip, status })
d.FadeColour(value.GetAppropriateColour(colours), 100);
}
private void displayBeatmap(BeatmapMetadata value)
{
if (value != null)
{
beatmapTitle.Current = localisation.GetUnicodePreference(value.TitleUnicode, value.Title);
beatmapDash.Text = @" - ";
beatmapArtist.Current = localisation.GetUnicodePreference(value.ArtistUnicode, value.Artist);
}
else
{
beatmapTitle.Current = null;
beatmapArtist.Current = null;
beatmapTitle.Text = @"Changing map";
beatmapDash.Text = string.Empty;
beatmapArtist.Text = string.Empty;
}
}
protected override void Dispose(bool isDisposing)
{
Room.Name.ValueChanged -= displayName;
Room.Host.ValueChanged -= displayUser;
Room.Status.ValueChanged -= displayStatus;
Room.Beatmap.ValueChanged -= displayBeatmap;
base.Dispose(isDisposing);
}
}
}

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -12,6 +11,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using OpenTK;
using osu.Framework.Input;
using osu.Game.Graphics.Containers;
namespace osu.Game.Screens.Play.HUD
{
@ -27,7 +27,7 @@ namespace osu.Game.Screens.Play.HUD
{
Children = new Drawable[]
{
iconsContainer = new IconFlow
iconsContainer = new ReverseDepthFillFlowContainer<ModIcon>
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
@ -93,12 +93,5 @@ namespace osu.Game.Screens.Play.HUD
contract();
base.OnHoverLost(state);
}
private class IconFlow : FillFlowContainer<ModIcon>
{
// just reverses the depth of flow contents.
protected override IComparer<Drawable> DepthComparer => new ReverseCreationOrderDepthComparer();
protected override IEnumerable<ModIcon> FlowingChildren => base.FlowingChildren.Reverse();
}
}
}

View File

@ -170,8 +170,8 @@ namespace osu.Game.Screens.Select
List<BeatmapGroup> visibleGroups = groups.Where(selectGroup => selectGroup.State != BeatmapGroupState.Hidden).ToList();
if (visibleGroups.Count < 1)
return;
BeatmapGroup group = visibleGroups[RNG.Next(visibleGroups.Count)];
BeatmapGroup group = visibleGroups[RNG.Next(visibleGroups.Count)];
BeatmapPanel panel = group.BeatmapPanels[RNG.Next(group.BeatmapPanels.Count)];
selectGroup(group, panel);

View File

@ -2,8 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -12,6 +10,7 @@ using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Game.Graphics.Containers;
namespace osu.Game.Screens.Select.Options
{
@ -71,7 +70,7 @@ namespace osu.Game.Screens.Select.Options
Scale = new Vector2(1, 0),
Colour = Color4.Black.Opacity(0.5f),
},
buttonsContainer = new ButtonFlow
buttonsContainer = new ReverseDepthFillFlowContainer<BeatmapOptionsButton>
{
Height = height,
RelativePositionAxes = Axes.X,
@ -109,16 +108,5 @@ namespace osu.Game.Screens.Select.Options
HotKey = hotkey
});
}
private class ButtonFlow : FillFlowContainer<BeatmapOptionsButton>
{
protected override IComparer<Drawable> DepthComparer => new ReverseCreationOrderDepthComparer();
protected override IEnumerable<BeatmapOptionsButton> FlowingChildren => base.FlowingChildren.Reverse();
public ButtonFlow()
{
Direction = FillDirection.Horizontal;
}
}
}
}

View File

@ -316,11 +316,12 @@ namespace osu.Game.Screens.Select
/// </summary>
private void selectionChanged(BeatmapInfo beatmap)
{
bool beatmapSetChange = false;
selectionChangedDebounce?.Cancel();
if (beatmap.Equals(Beatmap?.BeatmapInfo))
return;
bool beatmapSetChange = false;
if (beatmap.BeatmapSetInfoID == selectionChangeNoBounce?.BeatmapSetInfoID)
sampleChangeDifficulty.Play();
else
@ -331,7 +332,6 @@ namespace osu.Game.Screens.Select
selectionChangeNoBounce = beatmap;
selectionChangedDebounce?.Cancel();
selectionChangedDebounce = Scheduler.AddDelayed(delegate
{
Beatmap = database.GetWorkingBeatmap(beatmap, Beatmap);

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using Newtonsoft.Json;
using osu.Framework.Configuration;
namespace osu.Game.Users
{
@ -19,6 +20,8 @@ namespace osu.Game.Users
[JsonProperty(@"country")]
public Country Country;
public Bindable<UserStatus> Status = new Bindable<UserStatus>();
//public Team Team;
[JsonProperty(@"profile_colour")]

214
osu.Game/Users/UserPanel.cs Normal file
View File

@ -0,0 +1,214 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Users
{
public class UserPanel : Container
{
private const float height = 100;
private const float content_padding = 10;
private const float status_height = 30;
private OsuColour colours;
private readonly Container statusBar;
private readonly Box statusBg;
private readonly OsuSpriteText statusMessage;
public readonly Bindable<UserStatus> Status = new Bindable<UserStatus>();
public UserPanel(User user)
{
Height = height - status_height;
Masking = true;
CornerRadius = 5;
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.25f),
Radius = 4,
};
Children = new Drawable[]
{
new AsyncLoadWrapper(new CoverBackgroundSprite(user)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
OnLoadComplete = d => d.FadeInFromZero(200),
}) { RelativeSizeAxes = Axes.Both },
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.7f),
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Top = content_padding, Left = content_padding, Right = content_padding },
Children = new Drawable[]
{
new UpdateableAvatar
{
Size = new Vector2(height - status_height - content_padding * 2),
User = user,
Masking = true,
CornerRadius = 5,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.25f),
Radius = 4,
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = height - status_height - content_padding },
Children = new Drawable[]
{
new OsuSpriteText
{
Text = user.Username,
TextSize = 18,
Font = @"Exo2.0-SemiBoldItalic",
},
new FillFlowContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.X,
Height = 20f,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5f, 0f),
Children = new Drawable[]
{
new DrawableFlag(user.Country?.FlagName ?? @"__")
{
Width = 30f,
RelativeSizeAxes = Axes.Y,
},
new Container
{
Width = 40f,
RelativeSizeAxes = Axes.Y,
},
new CircularContainer
{
Width = 20f,
RelativeSizeAxes = Axes.Y,
},
},
},
},
},
},
},
statusBar = new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Alpha = 0f,
Children = new Drawable[]
{
statusBg = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.5f,
},
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(5f, 0f),
Children = new[]
{
new TextAwesome
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Icon = FontAwesome.fa_circle_o,
Shadow = true,
TextSize = 14,
},
statusMessage = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = @"Exo2.0-Semibold",
},
},
},
},
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
this.colours = colours;
Status.ValueChanged += displayStatus;
}
protected override void LoadComplete()
{
base.LoadComplete();
Status.TriggerChange();
}
private void displayStatus(UserStatus status)
{
const float transition_duration = 500;
if (status == null)
{
statusBar.ResizeHeightTo(0f, transition_duration, EasingTypes.OutQuint);
statusBar.FadeOut(transition_duration, EasingTypes.OutQuint);
ResizeHeightTo(height - status_height, transition_duration, EasingTypes.OutQuint);
}
else
{
statusBar.ResizeHeightTo(status_height, transition_duration, EasingTypes.OutQuint);
statusBar.FadeIn(transition_duration, EasingTypes.OutQuint);
ResizeHeightTo(height, transition_duration, EasingTypes.OutQuint);
statusBg.FadeColour(status.GetAppropriateColour(colours), 500, EasingTypes.OutQuint);
statusMessage.Text = status.Message;
}
}
private class CoverBackgroundSprite : Sprite
{
private readonly User user;
public CoverBackgroundSprite(User user)
{
this.user = user;
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
if (!string.IsNullOrEmpty(user.CoverUrl))
Texture = textures.Get(user.CoverUrl);
}
}
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK.Graphics;
using osu.Game.Graphics;
namespace osu.Game.Users
{
public abstract class UserStatus
{
public abstract string Message { get; }
public abstract Color4 GetAppropriateColour(OsuColour colours);
}
public abstract class UserStatusAvailable : UserStatus
{
public override Color4 GetAppropriateColour(OsuColour colours) => colours.BlueDarker;
}
public abstract class UserStatusBusy : UserStatus
{
public override Color4 GetAppropriateColour(OsuColour colours) => colours.YellowDark;
}
public class UserStatusOffline : UserStatus
{
public override string Message => @"Offline";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray7;
}
public class UserStatusOnline : UserStatusAvailable
{
public override string Message => @"Online";
}
public class UserStatusSpectating : UserStatusAvailable
{
public override string Message => @"Spectating a game";
}
public class UserStatusInLobby : UserStatusAvailable
{
public override string Message => @"in Multiplayer Lobby";
}
public class UserStatusSoloGame : UserStatusBusy
{
public override string Message => @"Solo Game";
}
public class UserStatusMultiplayerGame: UserStatusBusy
{
public override string Message => @"Multiplaying";
}
public class UserStatusModding : UserStatus
{
public override string Message => @"Modding a map";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.PurpleDark;
}
public class UserStatusDoNotDisturb : UserStatus
{
public override string Message => @"Do not disturb";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.RedDark;
}
}

View File

@ -189,6 +189,7 @@
<Compile Include="Database\RulesetDatabase.cs" />
<Compile Include="Rulesets\Scoring\Score.cs" />
<Compile Include="Rulesets\Scoring\ScoreProcessor.cs" />
<Compile Include="Screens\Menu\MenuSideFlashes.cs" />
<Compile Include="Screens\Play\HUD\HealthDisplay.cs" />
<Compile Include="Screens\Play\HUDOverlay.cs" />
<Compile Include="Screens\Play\HUD\StandardHealthDisplay.cs" />
@ -431,6 +432,24 @@
<Compile Include="Overlays\Music\PlaylistOverlay.cs" />
<Compile Include="Rulesets\Replays\IAutoGenerator.cs" />
<Compile Include="Rulesets\Replays\AutoGenerator.cs" />
<Compile Include="Screens\Multiplayer\DrawableRoom.cs" />
<Compile Include="Online\Multiplayer\Room.cs" />
<Compile Include="Online\Multiplayer\RoomStatus.cs" />
<Compile Include="Users\UserPanel.cs" />
<Compile Include="Users\UserStatus.cs" />
<Compile Include="Overlays\DirectOverlay.cs" />
<Compile Include="Overlays\Direct\FilterControl.cs" />
<Compile Include="Overlays\Direct\Header.cs" />
<Compile Include="Overlays\Direct\SortTabControl.cs" />
<Compile Include="Graphics\UserInterface\OsuEnumDropdown.cs" />
<Compile Include="Overlays\Direct\DirectPanel.cs" />
<Compile Include="Overlays\Direct\DirectGridPanel.cs" />
<Compile Include="Overlays\Direct\DirectListPanel.cs" />
<Compile Include="Database\OnlineWorkingBeatmap.cs" />
<Compile Include="Database\BeatmapOnlineInfo.cs" />
<Compile Include="Overlays\Direct\SlimEnumDropdown.cs" />
<Compile Include="Graphics\Containers\ReverseDepthFillFlowContainer.cs" />
<Compile Include="Database\RankStatus.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu-framework\osu.Framework\osu.Framework.csproj">