1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-13 19:12:54 +08:00

Merge branch 'master' into fix-changelog-crash

This commit is contained in:
Dean Herbert 2020-07-07 21:32:59 +09:00 committed by GitHub
commit bfcf2f1d92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 474 additions and 292 deletions

View File

@ -9,7 +9,7 @@ using osu.Framework.Android;
namespace osu.Android namespace osu.Android
{ {
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullSensor, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = true)] [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullSensor, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false)]
public class OsuGameActivity : AndroidGameActivity public class OsuGameActivity : AndroidGameActivity
{ {
protected override Framework.Game CreateGame() => new OsuGameAndroid(); protected override Framework.Game CreateGame() => new OsuGameAndroid();

View File

@ -10,3 +10,5 @@ Hit100: mania/hit100
Hit200: mania/hit200 Hit200: mania/hit200
Hit300: mania/hit300 Hit300: mania/hit300
Hit300g: mania/hit300g Hit300g: mania/hit300g
StageLeft: mania/stage-left
StageRight: mania/stage-right

View File

@ -14,6 +14,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
@ -193,9 +194,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
SpmCounter.SetRotation(Disc.RotationAbsolute); SpmCounter.SetRotation(Disc.RotationAbsolute);
float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight; float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
Disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint); float targetScale = relativeCircleScale + (1 - relativeCircleScale) * Progress;
Disc.Scale = new Vector2((float)Interpolation.Lerp(Disc.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1)));
symbol.RotateTo(Disc.Rotation / 2, 500, Easing.OutQuint); symbol.Rotation = (float)Interpolation.Lerp(symbol.Rotation, Disc.RotationAbsolute / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1));
} }
protected override void UpdateInitialTransforms() protected override void UpdateInitialTransforms()

View File

@ -1,32 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Screens.Multi.Match.Components;
using osuTK;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMatchLeaderboardChatDisplay : MultiplayerTestScene
{
protected override bool UseOnlineAPI => true;
public TestSceneMatchLeaderboardChatDisplay()
{
Room.RoomID.Value = 7;
Add(new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500),
Child = new LeaderboardChatDisplay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
});
}
}
}

View File

@ -58,6 +58,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddUntilStep("wait for load", () => match.IsCurrentScreen()); AddUntilStep("wait for load", () => match.IsCurrentScreen());
} }
[Test]
public void TestLoadSimpleMatch()
{
AddStep("set room properties", () =>
{
Room.RoomID.Value = 1;
Room.Name.Value = "my awesome room";
Room.Host.Value = new User { Id = 2, Username = "peppy" };
Room.RecentParticipants.Add(Room.Host.Value);
Room.Playlist.Add(new PlaylistItem
{
Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo },
Ruleset = { Value = new OsuRuleset().RulesetInfo }
});
});
}
[Test] [Test]
public void TestPlaylistItemSelectedOnCreate() public void TestPlaylistItemSelectedOnCreate()
{ {

View File

@ -2,9 +2,11 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using JetBrains.Annotations;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Configuration; using osu.Game.Configuration;
@ -13,12 +15,30 @@ namespace osu.Game.IO
{ {
public class OsuStorage : WrappedStorage public class OsuStorage : WrappedStorage
{ {
/// <summary>
/// Indicates the error (if any) that occurred when initialising the custom storage during initial startup.
/// </summary>
public readonly OsuStorageError Error;
/// <summary>
/// The custom storage path as selected by the user.
/// </summary>
[CanBeNull]
public string CustomStoragePath => storageConfig.Get<string>(StorageConfig.FullPath);
/// <summary>
/// The default storage path to be used if a custom storage path hasn't been selected or is not accessible.
/// </summary>
[NotNull]
public string DefaultStoragePath => defaultStorage.GetFullPath(".");
private readonly GameHost host; private readonly GameHost host;
private readonly StorageConfigManager storageConfig; private readonly StorageConfigManager storageConfig;
private readonly Storage defaultStorage;
internal static readonly string[] IGNORE_DIRECTORIES = { "cache" }; public static readonly string[] IGNORE_DIRECTORIES = { "cache" };
internal static readonly string[] IGNORE_FILES = public static readonly string[] IGNORE_FILES =
{ {
"framework.ini", "framework.ini",
"storage.ini" "storage.ini"
@ -28,13 +48,53 @@ namespace osu.Game.IO
: base(defaultStorage, string.Empty) : base(defaultStorage, string.Empty)
{ {
this.host = host; this.host = host;
this.defaultStorage = defaultStorage;
storageConfig = new StorageConfigManager(defaultStorage); storageConfig = new StorageConfigManager(defaultStorage);
var customStoragePath = storageConfig.Get<string>(StorageConfig.FullPath); if (!string.IsNullOrEmpty(CustomStoragePath))
TryChangeToCustomStorage(out Error);
}
if (!string.IsNullOrEmpty(customStoragePath)) /// <summary>
ChangeTargetStorage(host.GetStorage(customStoragePath)); /// Resets the custom storage path, changing the target storage to the default location.
/// </summary>
public void ResetCustomStoragePath()
{
storageConfig.Set(StorageConfig.FullPath, string.Empty);
storageConfig.Save();
ChangeTargetStorage(defaultStorage);
}
/// <summary>
/// Attempts to change to the user's custom storage path.
/// </summary>
/// <param name="error">The error that occurred.</param>
/// <returns>Whether the custom storage path was used successfully. If not, <paramref name="error"/> will be populated with the reason.</returns>
public bool TryChangeToCustomStorage(out OsuStorageError error)
{
Debug.Assert(!string.IsNullOrEmpty(CustomStoragePath));
error = OsuStorageError.None;
Storage lastStorage = UnderlyingStorage;
try
{
Storage userStorage = host.GetStorage(CustomStoragePath);
if (!userStorage.ExistsDirectory(".") || !userStorage.GetFiles(".").Any())
error = OsuStorageError.AccessibleButEmpty;
ChangeTargetStorage(userStorage);
}
catch
{
error = OsuStorageError.NotAccessible;
ChangeTargetStorage(lastStorage);
}
return error == OsuStorageError.None;
} }
protected override void ChangeTargetStorage(Storage newStorage) protected override void ChangeTargetStorage(Storage newStorage)
@ -145,4 +205,23 @@ namespace osu.Game.IO
} }
} }
} }
public enum OsuStorageError
{
/// <summary>
/// No error.
/// </summary>
None,
/// <summary>
/// Occurs when the target storage directory is accessible but does not already contain game files.
/// Only happens when the user changes the storage directory and then moves the files manually or mounts a different device to the same path.
/// </summary>
AccessibleButEmpty,
/// <summary>
/// Occurs when the target storage directory cannot be accessed at all.
/// </summary>
NotAccessible,
}
} }

View File

@ -42,25 +42,34 @@ namespace osu.Game.Overlays.Dialog
set => icon.Icon = value; set => icon.Icon = value;
} }
private string text; private string headerText;
public string HeaderText public string HeaderText
{ {
get => text; get => headerText;
set set
{ {
if (text == value) if (headerText == value)
return; return;
text = value; headerText = value;
header.Text = value; header.Text = value;
} }
} }
private string bodyText;
public string BodyText public string BodyText
{ {
set => body.Text = value; get => bodyText;
set
{
if (bodyText == value)
return;
bodyText = value;
body.Text = value;
}
} }
public IEnumerable<PopupDialogButton> Buttons public IEnumerable<PopupDialogButton> Buttons

View File

@ -47,9 +47,25 @@ namespace osu.Game.Rulesets.Mods
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{ {
Combo.BindTo(scoreProcessor.Combo); Combo.BindTo(scoreProcessor.Combo);
// Default value of ScoreProcessor's Rank in Flashlight Mod should be SS+
scoreProcessor.Rank.Value = ScoreRank.XH;
} }
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; public ScoreRank AdjustRank(ScoreRank rank, double accuracy)
{
switch (rank)
{
case ScoreRank.X:
return ScoreRank.XH;
case ScoreRank.S:
return ScoreRank.SH;
default:
return rank;
}
}
public virtual void ApplyToDrawableRuleset(DrawableRuleset<T> drawableRuleset) public virtual void ApplyToDrawableRuleset(DrawableRuleset<T> drawableRuleset)
{ {

View File

@ -0,0 +1,34 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Graphics.Sprites;
using osu.Game.Overlays.Dialog;
namespace osu.Game.Screens.Menu
{
public class ConfirmExitDialog : PopupDialog
{
public ConfirmExitDialog(Action confirm, Action cancel)
{
HeaderText = "Are you sure you want to exit?";
BodyText = "Last chance to back out.";
Icon = FontAwesome.Solid.ExclamationTriangle;
Buttons = new PopupDialogButton[]
{
new PopupDialogOkButton
{
Text = @"Goodbye",
Action = confirm
},
new PopupDialogCancelButton
{
Text = @"Just a little more",
Action = cancel
},
};
}
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq; using System.Linq;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -9,15 +8,14 @@ using osu.Framework.Allocation;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.IO;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Multi; using osu.Game.Screens.Multi;
@ -171,6 +169,9 @@ namespace osu.Game.Screens.Menu
return s; return s;
} }
[Resolved]
private Storage storage { get; set; }
public override void OnEntering(IScreen last) public override void OnEntering(IScreen last)
{ {
base.OnEntering(last); base.OnEntering(last);
@ -187,6 +188,9 @@ namespace osu.Game.Screens.Menu
Track.Start(); Track.Start();
} }
} }
if (storage is OsuStorage osuStorage && osuStorage.Error != OsuStorageError.None)
dialogOverlay?.Push(new StorageErrorDialog(osuStorage, osuStorage.Error));
} }
private bool exitConfirmed; private bool exitConfirmed;
@ -283,30 +287,5 @@ namespace osu.Game.Screens.Menu
this.FadeOut(3000); this.FadeOut(3000);
return base.OnExiting(next); return base.OnExiting(next);
} }
private class ConfirmExitDialog : PopupDialog
{
public ConfirmExitDialog(Action confirm, Action cancel)
{
HeaderText = "Are you sure you want to exit?";
BodyText = "Last chance to back out.";
Icon = FontAwesome.Solid.ExclamationTriangle;
Buttons = new PopupDialogButton[]
{
new PopupDialogOkButton
{
Text = @"Goodbye",
Action = confirm
},
new PopupDialogCancelButton
{
Text = @"Just a little more",
Action = cancel
},
};
}
}
} }
} }

View File

@ -0,0 +1,79 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Sprites;
using osu.Game.IO;
using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
namespace osu.Game.Screens.Menu
{
public class StorageErrorDialog : PopupDialog
{
[Resolved]
private DialogOverlay dialogOverlay { get; set; }
[Resolved]
private OsuGameBase osuGame { get; set; }
public StorageErrorDialog(OsuStorage storage, OsuStorageError error)
{
HeaderText = "osu! storage error";
Icon = FontAwesome.Solid.ExclamationTriangle;
var buttons = new List<PopupDialogButton>();
switch (error)
{
case OsuStorageError.NotAccessible:
BodyText = $"The specified osu! data location (\"{storage.CustomStoragePath}\") is not accessible. If it is on external storage, please reconnect the device and try again.";
buttons.AddRange(new PopupDialogButton[]
{
new PopupDialogCancelButton
{
Text = "Try again",
Action = () =>
{
if (!storage.TryChangeToCustomStorage(out var nextError))
dialogOverlay.Push(new StorageErrorDialog(storage, nextError));
}
},
new PopupDialogCancelButton
{
Text = "Use default location until restart",
},
new PopupDialogOkButton
{
Text = "Reset to default location",
Action = storage.ResetCustomStoragePath
},
});
break;
case OsuStorageError.AccessibleButEmpty:
BodyText = $"The specified osu! data location (\"{storage.CustomStoragePath}\") is empty. If you have moved the files, please close osu! and move them back.";
// Todo: Provide the option to search for the files similar to migration.
buttons.AddRange(new PopupDialogButton[]
{
new PopupDialogCancelButton
{
Text = "Start fresh at specified location"
},
new PopupDialogOkButton
{
Text = "Reset to default location",
Action = storage.ResetCustomStoragePath
},
});
break;
}
Buttons = buttons;
}
}
}

View File

@ -35,6 +35,18 @@ namespace osu.Game.Screens.Multi.Components
} }
} }
private bool showLine = true;
public bool ShowLine
{
get => showLine;
set
{
showLine = value;
line.Alpha = value ? 1 : 0;
}
}
protected string Details protected string Details
{ {
set => details.Text = value; set => details.Text = value;
@ -72,9 +84,12 @@ namespace osu.Game.Screens.Multi.Components
new OsuSpriteText new OsuSpriteText
{ {
Text = title, Text = title,
Font = OsuFont.GetFont(size: 14) Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold)
},
details = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold)
}, },
details = new OsuSpriteText { Font = OsuFont.GetFont(size: 14) },
} }
}, },
}, },

View File

@ -79,7 +79,7 @@ namespace osu.Game.Screens.Multi.Components
Direction = Direction, Direction = Direction,
AutoSizeAxes = AutoSizeAxes, AutoSizeAxes = AutoSizeAxes,
RelativeSizeAxes = RelativeSizeAxes, RelativeSizeAxes = RelativeSizeAxes,
Spacing = new Vector2(10) Spacing = Vector2.One
}; };
for (int i = 0; i < RecentParticipants.Count; i++) for (int i = 0; i < RecentParticipants.Count; i++)

View File

@ -1,12 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using Humanizer;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -19,41 +20,41 @@ namespace osu.Game.Screens.Multi
{ {
public class Header : Container public class Header : Container
{ {
public const float HEIGHT = 121; public const float HEIGHT = 80;
private readonly HeaderBreadcrumbControl breadcrumbs;
public Header(ScreenStack stack) public Header(ScreenStack stack)
{ {
MultiHeaderTitle title;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = HEIGHT; Height = HEIGHT;
HeaderBreadcrumbControl breadcrumbs;
MultiHeaderTitle title;
Children = new Drawable[] Children = new Drawable[]
{ {
new Box new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex(@"2f2043"), Colour = Color4Extensions.FromHex(@"#1f1921"),
}, },
new Container new Container
{ {
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = SearchableListOverlay.WIDTH_PADDING + OsuScreen.HORIZONTAL_OVERFLOW_PADDING }, Padding = new MarginPadding { Left = SearchableListOverlay.WIDTH_PADDING + OsuScreen.HORIZONTAL_OVERFLOW_PADDING },
Children = new Drawable[] Children = new Drawable[]
{ {
title = new MultiHeaderTitle title = new MultiHeaderTitle
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
X = -MultiHeaderTitle.ICON_WIDTH,
}, },
breadcrumbs = new HeaderBreadcrumbControl(stack) breadcrumbs = new HeaderBreadcrumbControl(stack)
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft
RelativeSizeAxes = Axes.X, }
},
}, },
}, },
}; };
@ -67,32 +68,16 @@ namespace osu.Game.Screens.Multi
breadcrumbs.Current.TriggerChange(); breadcrumbs.Current.TriggerChange();
} }
[BackgroundDependencyLoader] private class MultiHeaderTitle : CompositeDrawable
private void load(OsuColour colours)
{ {
breadcrumbs.StripColour = colours.Green;
}
private class MultiHeaderTitle : CompositeDrawable, IHasAccentColour
{
public const float ICON_WIDTH = icon_size + spacing;
private const float icon_size = 25;
private const float spacing = 6; private const float spacing = 6;
private const int text_offset = 2;
private readonly SpriteIcon iconSprite; private readonly OsuSpriteText dot;
private readonly OsuSpriteText title, pageText; private readonly OsuSpriteText pageTitle;
public IMultiplayerSubScreen Screen public IMultiplayerSubScreen Screen
{ {
set => pageText.Text = value.ShortTitle.ToLowerInvariant(); set => pageTitle.Text = value.ShortTitle.Titleize();
}
public Color4 AccentColour
{
get => pageText.Colour;
set => pageText.Colour = value;
} }
public MultiHeaderTitle() public MultiHeaderTitle()
@ -108,32 +93,26 @@ namespace osu.Game.Screens.Multi
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
Children = new Drawable[] Children = new Drawable[]
{ {
iconSprite = new SpriteIcon new OsuSpriteText
{
Size = new Vector2(icon_size),
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
title = new OsuSpriteText
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 20, weight: FontWeight.Bold), Font = OsuFont.GetFont(size: 24),
Margin = new MarginPadding { Bottom = text_offset } Text = "Multiplayer"
}, },
new Circle dot = new OsuSpriteText
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(4), Font = OsuFont.GetFont(size: 24),
Colour = Color4.Gray, Text = "·"
}, },
pageText = new OsuSpriteText pageTitle = new OsuSpriteText
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 20), Font = OsuFont.GetFont(size: 24),
Margin = new MarginPadding { Bottom = text_offset } Text = "Lounge"
} }
} }
}, },
@ -143,9 +122,7 @@ namespace osu.Game.Screens.Multi
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
title.Text = "multi"; pageTitle.Colour = dot.Colour = colours.Yellow;
iconSprite.Icon = OsuIcon.Multi;
AccentColour = colours.Yellow;
} }
} }
@ -154,12 +131,28 @@ namespace osu.Game.Screens.Multi
public HeaderBreadcrumbControl(ScreenStack stack) public HeaderBreadcrumbControl(ScreenStack stack)
: base(stack) : base(stack)
{ {
RelativeSizeAxes = Axes.X;
StripColour = Color4.Transparent;
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
AccentColour = Color4.White; AccentColour = Color4Extensions.FromHex("#e35c99");
}
protected override TabItem<IScreen> CreateTabItem(IScreen value) => new HeaderBreadcrumbTabItem(value)
{
AccentColour = AccentColour
};
private class HeaderBreadcrumbTabItem : BreadcrumbTabItem
{
public HeaderBreadcrumbTabItem(IScreen value)
: base(value)
{
Bar.Colour = Color4.Transparent;
}
} }
} }
} }

View File

@ -16,7 +16,7 @@ namespace osu.Game.Screens.Multi.Match.Components
{ {
public class Footer : CompositeDrawable public class Footer : CompositeDrawable
{ {
public const float HEIGHT = 100; public const float HEIGHT = 50;
public Action OnStart; public Action OnStart;
public readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>(); public readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>();

View File

@ -52,7 +52,7 @@ namespace osu.Game.Screens.Multi.Match.Components
Font = OsuFont.GetFont(size: 30), Font = OsuFont.GetFont(size: 30),
Current = { BindTarget = RoomName } Current = { BindTarget = RoomName }
}, },
hostText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold)) hostText = new LinkFlowContainer(s => s.Font = OsuFont.GetFont(size: 20))
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal, Direction = FillDirection.Horizontal,
@ -71,7 +71,7 @@ namespace osu.Game.Screens.Multi.Match.Components
if (host.NewValue != null) if (host.NewValue != null)
{ {
hostText.AddText("hosted by "); hostText.AddText("hosted by ");
hostText.AddUserLink(host.NewValue); hostText.AddUserLink(host.NewValue, s => s.Font = s.Font.With(weight: FontWeight.SemiBold));
} }
}, true); }, true);
} }

View File

@ -1,100 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Multi.Match.Components
{
public class LeaderboardChatDisplay : MultiplayerComposite
{
private const double fade_duration = 100;
private readonly OsuTabControl<DisplayMode> tabControl;
private readonly MatchLeaderboard leaderboard;
private readonly MatchChatDisplay chat;
public LeaderboardChatDisplay()
{
RelativeSizeAxes = Axes.Both;
InternalChild = new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
tabControl = new DisplayModeTabControl
{
RelativeSizeAxes = Axes.X,
Height = 24,
}
},
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 10 },
Children = new Drawable[]
{
leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both },
chat = new MatchChatDisplay
{
RelativeSizeAxes = Axes.Both,
Alpha = 0
}
}
}
},
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
tabControl.AccentColour = colours.Yellow;
}
protected override void LoadComplete()
{
base.LoadComplete();
tabControl.Current.BindValueChanged(changeTab);
}
public void RefreshScores() => leaderboard.RefreshScores();
private void changeTab(ValueChangedEvent<DisplayMode> mode)
{
chat.FadeTo(mode.NewValue == DisplayMode.Chat ? 1 : 0, fade_duration);
leaderboard.FadeTo(mode.NewValue == DisplayMode.Leaderboard ? 1 : 0, fade_duration);
}
private class DisplayModeTabControl : OsuTabControl<DisplayMode>
{
protected override TabItem<DisplayMode> CreateTabItem(DisplayMode value) => base.CreateTabItem(value).With(d =>
{
d.Anchor = Anchor.Centre;
d.Origin = Anchor.Centre;
});
}
private enum DisplayMode
{
Leaderboard,
Chat,
}
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Screens.Multi.Components;
namespace osu.Game.Screens.Multi.Match.Components
{
public class OverlinedChatDisplay : OverlinedDisplay
{
public OverlinedChatDisplay()
: base("Chat")
{
Content.Add(new MatchChatDisplay
{
RelativeSizeAxes = Axes.Both
});
}
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Screens.Multi.Components;
namespace osu.Game.Screens.Multi.Match.Components
{
public class OverlinedLeaderboard : OverlinedDisplay
{
private readonly MatchLeaderboard leaderboard;
public OverlinedLeaderboard()
: base("Leaderboard")
{
Content.Add(leaderboard = new MatchLeaderboard
{
RelativeSizeAxes = Axes.Both
});
}
public void RefreshScores() => leaderboard.RefreshScores();
}
}

View File

@ -52,8 +52,8 @@ namespace osu.Game.Screens.Multi.Match
protected readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>(); protected readonly Bindable<PlaylistItem> SelectedItem = new Bindable<PlaylistItem>();
private LeaderboardChatDisplay leaderboardChatDisplay;
private MatchSettingsOverlay settingsOverlay; private MatchSettingsOverlay settingsOverlay;
private OverlinedLeaderboard leaderboard;
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated; private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
@ -87,21 +87,29 @@ namespace osu.Game.Screens.Multi.Match
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Content = new[] Content = new[]
{ {
new Drawable[] { new Components.Header() }, new Drawable[]
{
new Components.Header()
},
new Drawable[] new Drawable[]
{ {
new Container new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.X,
Padding = new MarginPadding { Top = 65 }, AutoSizeAxes = Axes.Y,
Child = new GridContainer Margin = new MarginPadding { Top = 10 },
Child = new OverlinedParticipants(Direction.Horizontal)
{ {
ColumnDimensions = new[] RelativeSizeAxes = Axes.X,
{ AutoSizeAxes = Axes.Y,
new Dimension(minSize: 160), ShowLine = false
new Dimension(minSize: 360), }
new Dimension(minSize: 400), }
}, },
new Drawable[]
{
new GridContainer
{
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Content = new[] Content = new[]
{ {
@ -111,12 +119,6 @@ namespace osu.Game.Screens.Multi.Match
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Right = 5 }, Padding = new MarginPadding { Right = 5 },
Child = new OverlinedParticipants(Direction.Vertical) { RelativeSizeAxes = Axes.Both }
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 5 },
Child = new GridContainer Child = new GridContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -149,20 +151,43 @@ namespace osu.Game.Screens.Multi.Match
} }
} }
}, },
new Container null,
new GridContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = 5 }, Content = new[]
Child = leaderboardChatDisplay = new LeaderboardChatDisplay() {
new Drawable[]
{
leaderboard = new OverlinedLeaderboard { RelativeSizeAxes = Axes.Both },
},
new Drawable[]
{
new OverlinedChatDisplay { RelativeSizeAxes = Axes.Both }
} }
}, },
RowDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.Relative, size: 0.4f, minSize: 240),
} }
},
null
},
},
ColumnDimensions = new[]
{
new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400),
new Dimension(),
new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600),
new Dimension(),
} }
} }
} }
}, },
RowDimensions = new[] RowDimensions = new[]
{ {
new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize),
new Dimension(), new Dimension(),
} }
@ -261,7 +286,7 @@ namespace osu.Game.Screens.Multi.Match
case GameTypeTimeshift _: case GameTypeTimeshift _:
multiplayer?.Push(new PlayerLoader(() => new TimeshiftPlayer(SelectedItem.Value) multiplayer?.Push(new PlayerLoader(() => new TimeshiftPlayer(SelectedItem.Value)
{ {
Exited = () => leaderboardChatDisplay.RefreshScores() Exited = () => leaderboard.RefreshScores()
})); }));
break; break;
} }

View File

@ -117,7 +117,7 @@ namespace osu.Game.Screens.Multi
Child = new Box Child = new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(backgroundColour.Opacity(0.7f), backgroundColour) Colour = ColourInfo.GradientVertical(backgroundColour.Opacity(0.5f), backgroundColour)
}, },
} }
} }

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -9,6 +10,7 @@ using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring; using osu.Game.Scoring;
using osuTK; using osuTK;
@ -191,18 +193,26 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
Padding = new MarginPadding { Vertical = -15, Horizontal = -20 }, Padding = new MarginPadding { Vertical = -15, Horizontal = -20 },
Children = new[] Children = new[]
{ {
new RankBadge(1f, ScoreRank.X), new RankBadge(1f, getRank(ScoreRank.X)),
new RankBadge(0.95f, ScoreRank.S), new RankBadge(0.95f, getRank(ScoreRank.S)),
new RankBadge(0.9f, ScoreRank.A), new RankBadge(0.9f, getRank(ScoreRank.A)),
new RankBadge(0.8f, ScoreRank.B), new RankBadge(0.8f, getRank(ScoreRank.B)),
new RankBadge(0.7f, ScoreRank.C), new RankBadge(0.7f, getRank(ScoreRank.C)),
new RankBadge(0.35f, ScoreRank.D), new RankBadge(0.35f, getRank(ScoreRank.D)),
} }
}, },
rankText = new RankText(score.Rank) rankText = new RankText(score.Rank)
}; };
} }
private ScoreRank getRank(ScoreRank rank)
{
foreach (var mod in score.Mods.OfType<IApplicableToScoreProcessor>())
rank = mod.AdjustRank(rank, score.Accuracy);
return rank;
}
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();

View File

@ -115,6 +115,7 @@ namespace osu.Game.Skinning
case string _ when pair.Key.StartsWith("NoteImage"): case string _ when pair.Key.StartsWith("NoteImage"):
case string _ when pair.Key.StartsWith("KeyImage"): case string _ when pair.Key.StartsWith("KeyImage"):
case string _ when pair.Key.StartsWith("Hit"): case string _ when pair.Key.StartsWith("Hit"):
case string _ when pair.Key.StartsWith("Stage"):
currentConfig.ImageLookups[pair.Key] = pair.Value; currentConfig.ImageLookups[pair.Key] = pair.Value;
break; break;
} }

View File

@ -250,6 +250,15 @@ namespace osu.Game.Skinning
case LegacyManiaSkinConfigurationLookups.RightStageImage: case LegacyManiaSkinConfigurationLookups.RightStageImage:
return SkinUtils.As<TValue>(getManiaImage(existing, "StageRight")); return SkinUtils.As<TValue>(getManiaImage(existing, "StageRight"));
case LegacyManiaSkinConfigurationLookups.BottomStageImage:
return SkinUtils.As<TValue>(getManiaImage(existing, "StageBottom"));
case LegacyManiaSkinConfigurationLookups.LightImage:
return SkinUtils.As<TValue>(getManiaImage(existing, "StageLight"));
case LegacyManiaSkinConfigurationLookups.HitTargetImage:
return SkinUtils.As<TValue>(getManiaImage(existing, "StageHint"));
case LegacyManiaSkinConfigurationLookups.LeftLineWidth: case LegacyManiaSkinConfigurationLookups.LeftLineWidth:
Debug.Assert(maniaLookup.TargetColumn != null); Debug.Assert(maniaLookup.TargetColumn != null);
return SkinUtils.As<TValue>(new Bindable<float>(existing.ColumnLineWidth[maniaLookup.TargetColumn.Value])); return SkinUtils.As<TValue>(new Bindable<float>(existing.ColumnLineWidth[maniaLookup.TargetColumn.Value]));