mirror of
https://github.com/ppy/osu.git
synced 2025-01-15 14:12:54 +08:00
Merge branch 'master' into countdown-button-icon
This commit is contained in:
commit
11ee78b395
@ -163,6 +163,25 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddUntilStep("second user crown visible", () => this.ChildrenOfType<ParticipantPanel>().ElementAt(1).ChildrenOfType<SpriteIcon>().First().Alpha == 1);
|
AddUntilStep("second user crown visible", () => this.ChildrenOfType<ParticipantPanel>().ElementAt(1).ChildrenOfType<SpriteIcon>().First().Alpha == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHostGetsPinnedToTop()
|
||||||
|
{
|
||||||
|
AddStep("add user", () => MultiplayerClient.AddUser(new APIUser
|
||||||
|
{
|
||||||
|
Id = 3,
|
||||||
|
Username = "Second",
|
||||||
|
CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg",
|
||||||
|
}));
|
||||||
|
|
||||||
|
AddStep("make second user host", () => MultiplayerClient.TransferHost(3));
|
||||||
|
AddAssert("second user above first", () =>
|
||||||
|
{
|
||||||
|
var first = this.ChildrenOfType<ParticipantPanel>().ElementAt(0);
|
||||||
|
var second = this.ChildrenOfType<ParticipantPanel>().ElementAt(1);
|
||||||
|
return second.Y < first.Y;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestKickButtonOnlyPresentWhenHost()
|
public void TestKickButtonOnlyPresentWhenHost()
|
||||||
{
|
{
|
||||||
@ -202,9 +221,11 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestManyUsers()
|
public void TestManyUsers()
|
||||||
{
|
{
|
||||||
|
const int users_count = 20;
|
||||||
|
|
||||||
AddStep("add many users", () =>
|
AddStep("add many users", () =>
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 20; i++)
|
for (int i = 0; i < users_count; i++)
|
||||||
{
|
{
|
||||||
MultiplayerClient.AddUser(new APIUser
|
MultiplayerClient.AddUser(new APIUser
|
||||||
{
|
{
|
||||||
@ -243,6 +264,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddRepeatStep("switch hosts", () => MultiplayerClient.TransferHost(RNG.Next(0, users_count)), 10);
|
||||||
|
AddStep("give host back", () => MultiplayerClient.TransferHost(API.LocalUser.Value.Id));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class TestSceneModSettingsArea : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestModToggleArea()
|
||||||
|
{
|
||||||
|
ModSettingsArea modSettingsArea = null;
|
||||||
|
|
||||||
|
AddStep("create content", () => Child = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Child = modSettingsArea = new ModSettingsArea()
|
||||||
|
});
|
||||||
|
AddStep("set DT", () => modSettingsArea.SelectedMods.Value = new[] { new OsuModDoubleTime() });
|
||||||
|
AddStep("set DA", () => modSettingsArea.SelectedMods.Value = new Mod[] { new OsuModDifficultyAdjust() });
|
||||||
|
AddStep("set FL+WU+DA+AD", () => modSettingsArea.SelectedMods.Value = new Mod[] { new OsuModFlashlight(), new ModWindUp(), new OsuModDifficultyAdjust(), new OsuModApproachDifferent() });
|
||||||
|
AddStep("set empty", () => modSettingsArea.SelectedMods.Value = Array.Empty<Mod>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
176
osu.Game/Overlays/Mods/ModSettingsArea.cs
Normal file
176
osu.Game/Overlays/Mods/ModSettingsArea.cs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.Mods
|
||||||
|
{
|
||||||
|
public class ModSettingsArea : CompositeDrawable
|
||||||
|
{
|
||||||
|
public Bindable<IReadOnlyList<Mod>> SelectedMods { get; } = new Bindable<IReadOnlyList<Mod>>();
|
||||||
|
|
||||||
|
private readonly Box background;
|
||||||
|
private readonly FillFlowContainer modSettingsFlow;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; }
|
||||||
|
|
||||||
|
public ModSettingsArea()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
Height = 250;
|
||||||
|
|
||||||
|
Anchor = Anchor.BottomRight;
|
||||||
|
Origin = Anchor.BottomRight;
|
||||||
|
|
||||||
|
InternalChild = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
BorderThickness = 2,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both
|
||||||
|
},
|
||||||
|
new OsuScrollContainer(Direction.Horizontal)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
ScrollbarOverlapsContent = false,
|
||||||
|
Child = modSettingsFlow = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding { Vertical = 7, Horizontal = 70 },
|
||||||
|
Spacing = new Vector2(7),
|
||||||
|
Direction = FillDirection.Horizontal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
background.Colour = colourProvider.Dark3;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
SelectedMods.BindValueChanged(_ => updateMods());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMods()
|
||||||
|
{
|
||||||
|
modSettingsFlow.Clear();
|
||||||
|
|
||||||
|
foreach (var mod in SelectedMods.Value.OrderBy(mod => mod.Type).ThenBy(mod => mod.Acronym))
|
||||||
|
{
|
||||||
|
var settings = mod.CreateSettingsControls().ToList();
|
||||||
|
|
||||||
|
if (settings.Count > 0)
|
||||||
|
{
|
||||||
|
if (modSettingsFlow.Any())
|
||||||
|
{
|
||||||
|
modSettingsFlow.Add(new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = 2,
|
||||||
|
Colour = colourProvider.Dark4,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
modSettingsFlow.Add(new ModSettingsColumn(mod, settings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e) => true;
|
||||||
|
protected override bool OnHover(HoverEvent e) => true;
|
||||||
|
|
||||||
|
private class ModSettingsColumn : CompositeDrawable
|
||||||
|
{
|
||||||
|
public ModSettingsColumn(Mod mod, IEnumerable<Drawable> settingsControls)
|
||||||
|
{
|
||||||
|
Width = 250;
|
||||||
|
RelativeSizeAxes = Axes.Y;
|
||||||
|
Padding = new MarginPadding { Bottom = 7 };
|
||||||
|
|
||||||
|
InternalChild = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
RowDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
new Dimension(GridSizeMode.Absolute, 10),
|
||||||
|
new Dimension()
|
||||||
|
},
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(7),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new ModSwitchTiny(mod)
|
||||||
|
{
|
||||||
|
Active = { Value = true },
|
||||||
|
Scale = new Vector2(0.6f),
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Anchor = Anchor.CentreLeft
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = mod.Name,
|
||||||
|
Font = OsuFont.Default.With(size: 16, weight: FontWeight.SemiBold),
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Margin = new MarginPadding { Bottom = 2 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new[] { Empty() },
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuScrollContainer(Direction.Vertical)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding { Right = 7 },
|
||||||
|
ChildrenEnumerable = settingsControls,
|
||||||
|
Spacing = new Vector2(0, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,7 +47,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match
|
|||||||
countdown = room?.Countdown;
|
countdown = room?.Countdown;
|
||||||
|
|
||||||
if (room?.Countdown != null)
|
if (room?.Countdown != null)
|
||||||
countdownUpdateDelegate ??= Scheduler.AddDelayed(updateButtonText, 1000, true);
|
countdownUpdateDelegate ??= Scheduler.AddDelayed(updateButtonText, 100, true);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
countdownUpdateDelegate?.Cancel();
|
countdownUpdateDelegate?.Cancel();
|
||||||
|
@ -198,15 +198,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
|||||||
else
|
else
|
||||||
userModsDisplay.FadeOut(fade_time);
|
userModsDisplay.FadeOut(fade_time);
|
||||||
|
|
||||||
if (Client.IsHost && !User.Equals(Client.LocalUser))
|
kickButton.Alpha = Client.IsHost && !User.Equals(Client.LocalUser) ? 1 : 0;
|
||||||
kickButton.FadeIn(fade_time);
|
crown.Alpha = Room.Host?.Equals(User) == true ? 1 : 0;
|
||||||
else
|
|
||||||
kickButton.FadeOut(fade_time);
|
|
||||||
|
|
||||||
if (Room.Host?.Equals(User) == true)
|
|
||||||
crown.FadeIn(fade_time);
|
|
||||||
else
|
|
||||||
crown.FadeOut(fade_time);
|
|
||||||
|
|
||||||
// If the mods are updated at the end of the frame, the flow container will skip a reflow cycle: https://github.com/ppy/osu-framework/issues/4187
|
// If the mods are updated at the end of the frame, the flow container will skip a reflow cycle: https://github.com/ppy/osu-framework/issues/4187
|
||||||
// This looks particularly jarring here, so re-schedule the update to that start of our frame as a fix.
|
// This looks particularly jarring here, so re-schedule the update to that start of our frame as a fix.
|
||||||
|
@ -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.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -15,6 +16,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
|||||||
{
|
{
|
||||||
private FillFlowContainer<ParticipantPanel> panels;
|
private FillFlowContainer<ParticipantPanel> panels;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private ParticipantPanel currentHostPanel;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
@ -55,6 +59,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants
|
|||||||
// Add panels for all users new to the room.
|
// Add panels for all users new to the room.
|
||||||
foreach (var user in Room.Users.Except(panels.Select(p => p.User)))
|
foreach (var user in Room.Users.Except(panels.Select(p => p.User)))
|
||||||
panels.Add(new ParticipantPanel(user));
|
panels.Add(new ParticipantPanel(user));
|
||||||
|
|
||||||
|
if (currentHostPanel == null || !currentHostPanel.User.Equals(Room.Host))
|
||||||
|
{
|
||||||
|
// Reset position of previous host back to normal, if one existing.
|
||||||
|
if (currentHostPanel != null && panels.Contains(currentHostPanel))
|
||||||
|
panels.SetLayoutPosition(currentHostPanel, 0);
|
||||||
|
|
||||||
|
currentHostPanel = null;
|
||||||
|
|
||||||
|
// Change position of new host to display above all participants.
|
||||||
|
if (Room.Host != null)
|
||||||
|
{
|
||||||
|
currentHostPanel = panels.SingleOrDefault(u => u.User.Equals(Room.Host));
|
||||||
|
|
||||||
|
if (currentHostPanel != null)
|
||||||
|
panels.SetLayoutPosition(currentHostPanel, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// 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 JetBrains.Annotations;
|
#nullable enable
|
||||||
|
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -21,16 +22,14 @@ namespace osu.Game.Skinning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="component">The requested component.</param>
|
/// <param name="component">The requested component.</param>
|
||||||
/// <returns>A drawable representation for the requested component, or null if unavailable.</returns>
|
/// <returns>A drawable representation for the requested component, or null if unavailable.</returns>
|
||||||
[CanBeNull]
|
Drawable? GetDrawableComponent(ISkinComponent component);
|
||||||
Drawable GetDrawableComponent(ISkinComponent component);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a <see cref="Texture"/>.
|
/// Retrieve a <see cref="Texture"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="componentName">The requested texture.</param>
|
/// <param name="componentName">The requested texture.</param>
|
||||||
/// <returns>A matching texture, or null if unavailable.</returns>
|
/// <returns>A matching texture, or null if unavailable.</returns>
|
||||||
[CanBeNull]
|
Texture? GetTexture(string componentName) => GetTexture(componentName, default, default);
|
||||||
Texture GetTexture(string componentName) => GetTexture(componentName, default, default);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a <see cref="Texture"/>.
|
/// Retrieve a <see cref="Texture"/>.
|
||||||
@ -39,23 +38,22 @@ namespace osu.Game.Skinning
|
|||||||
/// <param name="wrapModeS">The texture wrap mode in horizontal direction.</param>
|
/// <param name="wrapModeS">The texture wrap mode in horizontal direction.</param>
|
||||||
/// <param name="wrapModeT">The texture wrap mode in vertical direction.</param>
|
/// <param name="wrapModeT">The texture wrap mode in vertical direction.</param>
|
||||||
/// <returns>A matching texture, or null if unavailable.</returns>
|
/// <returns>A matching texture, or null if unavailable.</returns>
|
||||||
[CanBeNull]
|
Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT);
|
||||||
Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a <see cref="SampleChannel"/>.
|
/// Retrieve a <see cref="SampleChannel"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sampleInfo">The requested sample.</param>
|
/// <param name="sampleInfo">The requested sample.</param>
|
||||||
/// <returns>A matching sample channel, or null if unavailable.</returns>
|
/// <returns>A matching sample channel, or null if unavailable.</returns>
|
||||||
[CanBeNull]
|
ISample? GetSample(ISampleInfo sampleInfo);
|
||||||
ISample GetSample(ISampleInfo sampleInfo);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a configuration value.
|
/// Retrieve a configuration value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lookup">The requested configuration value.</param>
|
/// <param name="lookup">The requested configuration value.</param>
|
||||||
/// <returns>A matching value boxed in an <see cref="IBindable{TValue}"/>, or null if unavailable.</returns>
|
/// <returns>A matching value boxed in an <see cref="IBindable{TValue}"/>, or null if unavailable.</returns>
|
||||||
[CanBeNull]
|
IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
|
where TLookup : notnull
|
||||||
|
where TValue : notnull;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using JetBrains.Annotations;
|
#nullable enable
|
||||||
|
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
@ -26,14 +28,14 @@ namespace osu.Game.Skinning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="beatmapInfo">The model for this beatmap.</param>
|
/// <param name="beatmapInfo">The model for this beatmap.</param>
|
||||||
/// <param name="resources">Access to raw game resources.</param>
|
/// <param name="resources">Access to raw game resources.</param>
|
||||||
public LegacyBeatmapSkin(BeatmapInfo beatmapInfo, [CanBeNull] IStorageResourceProvider resources)
|
public LegacyBeatmapSkin(BeatmapInfo beatmapInfo, IStorageResourceProvider? resources)
|
||||||
: base(createSkinInfo(beatmapInfo), resources, createRealmBackedStore(beatmapInfo, resources), beatmapInfo.Path)
|
: base(createSkinInfo(beatmapInfo), resources, createRealmBackedStore(beatmapInfo, resources), beatmapInfo.Path.AsNonNull())
|
||||||
{
|
{
|
||||||
// Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer)
|
// Disallow default colours fallback on beatmap skins to allow using parent skin combo colours. (via SkinProvidingContainer)
|
||||||
Configuration.AllowDefaultComboColoursFallback = false;
|
Configuration.AllowDefaultComboColoursFallback = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IResourceStore<byte[]> createRealmBackedStore(BeatmapInfo beatmapInfo, [CanBeNull] IStorageResourceProvider resources)
|
private static IResourceStore<byte[]> createRealmBackedStore(BeatmapInfo beatmapInfo, IStorageResourceProvider? resources)
|
||||||
{
|
{
|
||||||
if (resources == null)
|
if (resources == null)
|
||||||
// should only ever be used in tests.
|
// should only ever be used in tests.
|
||||||
@ -42,7 +44,7 @@ namespace osu.Game.Skinning
|
|||||||
return new RealmBackedResourceStore(beatmapInfo.BeatmapSet, resources.Files, new[] { @"ogg" });
|
return new RealmBackedResourceStore(beatmapInfo.BeatmapSet, resources.Files, new[] { @"ogg" });
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
public override Drawable? GetDrawableComponent(ISkinComponent component)
|
||||||
{
|
{
|
||||||
if (component is SkinnableTargetComponent targetComponent)
|
if (component is SkinnableTargetComponent targetComponent)
|
||||||
{
|
{
|
||||||
@ -61,7 +63,7 @@ namespace osu.Game.Skinning
|
|||||||
return base.GetDrawableComponent(component);
|
return base.GetDrawableComponent(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
{
|
{
|
||||||
switch (lookup)
|
switch (lookup)
|
||||||
{
|
{
|
||||||
@ -77,10 +79,10 @@ namespace osu.Game.Skinning
|
|||||||
return base.GetConfig<TLookup, TValue>(lookup);
|
return base.GetConfig<TLookup, TValue>(lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IBindable<Color4> GetComboColour(IHasComboColours source, int comboIndex, IHasComboInformation combo)
|
protected override IBindable<Color4>? GetComboColour(IHasComboColours source, int comboIndex, IHasComboInformation combo)
|
||||||
=> base.GetComboColour(source, combo.ComboIndexWithOffsets, combo);
|
=> base.GetComboColour(source, combo.ComboIndexWithOffsets, combo);
|
||||||
|
|
||||||
public override ISample GetSample(ISampleInfo sampleInfo)
|
public override ISample? GetSample(ISampleInfo sampleInfo)
|
||||||
{
|
{
|
||||||
if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy && legacy.CustomSampleBank == 0)
|
if (sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy && legacy.CustomSampleBank == 0)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@ -57,7 +59,7 @@ namespace osu.Game.Skinning
|
|||||||
/// <param name="resources">Access to raw game resources.</param>
|
/// <param name="resources">Access to raw game resources.</param>
|
||||||
/// <param name="storage">An optional store which will be used for looking up skin resources. If null, one will be created from realm <see cref="IHasRealmFiles"/> pattern.</param>
|
/// <param name="storage">An optional store which will be used for looking up skin resources. If null, one will be created from realm <see cref="IHasRealmFiles"/> pattern.</param>
|
||||||
/// <param name="configurationFilename">The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file.</param>
|
/// <param name="configurationFilename">The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file.</param>
|
||||||
protected LegacySkin(SkinInfo skin, [CanBeNull] IStorageResourceProvider resources, [CanBeNull] IResourceStore<byte[]> storage, string configurationFilename = @"skin.ini")
|
protected LegacySkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? storage, string configurationFilename = @"skin.ini")
|
||||||
: base(skin, resources, storage, configurationFilename)
|
: base(skin, resources, storage, configurationFilename)
|
||||||
{
|
{
|
||||||
// todo: this shouldn't really be duplicated here (from ManiaLegacySkinTransformer). we need to come up with a better solution.
|
// todo: this shouldn't really be duplicated here (from ManiaLegacySkinTransformer). we need to come up with a better solution.
|
||||||
@ -81,7 +83,7 @@ namespace osu.Game.Skinning
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
{
|
{
|
||||||
switch (lookup)
|
switch (lookup)
|
||||||
{
|
{
|
||||||
@ -127,7 +129,7 @@ namespace osu.Game.Skinning
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBindable<TValue> lookupForMania<TValue>(LegacyManiaSkinConfigurationLookup maniaLookup)
|
private IBindable<TValue>? lookupForMania<TValue>(LegacyManiaSkinConfigurationLookup maniaLookup)
|
||||||
{
|
{
|
||||||
if (!maniaConfigurations.TryGetValue(maniaLookup.Keys, out var existing))
|
if (!maniaConfigurations.TryGetValue(maniaLookup.Keys, out var existing))
|
||||||
maniaConfigurations[maniaLookup.Keys] = existing = new LegacyManiaSkinConfiguration(maniaLookup.Keys);
|
maniaConfigurations[maniaLookup.Keys] = existing = new LegacyManiaSkinConfiguration(maniaLookup.Keys);
|
||||||
@ -267,20 +269,20 @@ namespace osu.Game.Skinning
|
|||||||
/// <param name="source">The source to retrieve the combo colours from.</param>
|
/// <param name="source">The source to retrieve the combo colours from.</param>
|
||||||
/// <param name="colourIndex">The preferred index for retrieving the combo colour with.</param>
|
/// <param name="colourIndex">The preferred index for retrieving the combo colour with.</param>
|
||||||
/// <param name="combo">Information on the combo whose using the returned colour.</param>
|
/// <param name="combo">Information on the combo whose using the returned colour.</param>
|
||||||
protected virtual IBindable<Color4> GetComboColour(IHasComboColours source, int colourIndex, IHasComboInformation combo)
|
protected virtual IBindable<Color4>? GetComboColour(IHasComboColours source, int colourIndex, IHasComboInformation combo)
|
||||||
{
|
{
|
||||||
var colour = source.ComboColours?[colourIndex % source.ComboColours.Count];
|
var colour = source.ComboColours?[colourIndex % source.ComboColours.Count];
|
||||||
return colour.HasValue ? new Bindable<Color4>(colour.Value) : null;
|
return colour.HasValue ? new Bindable<Color4>(colour.Value) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBindable<Color4> getCustomColour(IHasCustomColours source, string lookup)
|
private IBindable<Color4>? getCustomColour(IHasCustomColours source, string lookup)
|
||||||
=> source.CustomColours.TryGetValue(lookup, out var col) ? new Bindable<Color4>(col) : null;
|
=> source.CustomColours.TryGetValue(lookup, out var col) ? new Bindable<Color4>(col) : null;
|
||||||
|
|
||||||
private IBindable<string> getManiaImage(LegacyManiaSkinConfiguration source, string lookup)
|
private IBindable<string>? getManiaImage(LegacyManiaSkinConfiguration source, string lookup)
|
||||||
=> source.ImageLookups.TryGetValue(lookup, out string image) ? new Bindable<string>(image) : null;
|
=> source.ImageLookups.TryGetValue(lookup, out string image) ? new Bindable<string>(image) : null;
|
||||||
|
|
||||||
[CanBeNull]
|
private IBindable<TValue>? legacySettingLookup<TValue>(SkinConfiguration.LegacySetting legacySetting)
|
||||||
private IBindable<TValue> legacySettingLookup<TValue>(SkinConfiguration.LegacySetting legacySetting)
|
where TValue : notnull
|
||||||
{
|
{
|
||||||
switch (legacySetting)
|
switch (legacySetting)
|
||||||
{
|
{
|
||||||
@ -292,8 +294,9 @@ namespace osu.Game.Skinning
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CanBeNull]
|
private IBindable<TValue>? genericLookup<TLookup, TValue>(TLookup lookup)
|
||||||
private IBindable<TValue> genericLookup<TLookup, TValue>(TLookup lookup)
|
where TLookup : notnull
|
||||||
|
where TValue : notnull
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -316,7 +319,7 @@ namespace osu.Game.Skinning
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Drawable GetDrawableComponent(ISkinComponent component)
|
public override Drawable? GetDrawableComponent(ISkinComponent component)
|
||||||
{
|
{
|
||||||
if (base.GetDrawableComponent(component) is Drawable c)
|
if (base.GetDrawableComponent(component) is Drawable c)
|
||||||
return c;
|
return c;
|
||||||
@ -374,7 +377,7 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
case GameplaySkinComponent<HitResult> resultComponent:
|
case GameplaySkinComponent<HitResult> resultComponent:
|
||||||
// TODO: this should be inside the judgement pieces.
|
// TODO: this should be inside the judgement pieces.
|
||||||
Func<Drawable> createDrawable = () => getJudgementAnimation(resultComponent.Component);
|
Func<Drawable?> createDrawable = () => getJudgementAnimation(resultComponent.Component);
|
||||||
|
|
||||||
// kind of wasteful that we throw this away, but should do for now.
|
// kind of wasteful that we throw this away, but should do for now.
|
||||||
if (createDrawable() != null)
|
if (createDrawable() != null)
|
||||||
@ -393,7 +396,7 @@ namespace osu.Game.Skinning
|
|||||||
return this.GetAnimation(component.LookupName, false, false);
|
return this.GetAnimation(component.LookupName, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Texture getParticleTexture(HitResult result)
|
private Texture? getParticleTexture(HitResult result)
|
||||||
{
|
{
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
@ -410,7 +413,7 @@ namespace osu.Game.Skinning
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Drawable getJudgementAnimation(HitResult result)
|
private Drawable? getJudgementAnimation(HitResult result)
|
||||||
{
|
{
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
@ -430,7 +433,7 @@ namespace osu.Game.Skinning
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT)
|
public override Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT)
|
||||||
{
|
{
|
||||||
foreach (string name in getFallbackNames(componentName))
|
foreach (string name in getFallbackNames(componentName))
|
||||||
{
|
{
|
||||||
@ -458,7 +461,7 @@ namespace osu.Game.Skinning
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ISample GetSample(ISampleInfo sampleInfo)
|
public override ISample? GetSample(ISampleInfo sampleInfo)
|
||||||
{
|
{
|
||||||
IEnumerable<string> lookupNames;
|
IEnumerable<string> lookupNames;
|
||||||
|
|
||||||
|
@ -46,7 +46,10 @@ namespace osu.Game.Skinning
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup) => null;
|
public IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
|
where TLookup : notnull
|
||||||
|
where TValue : notnull
|
||||||
|
=> null;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -27,14 +29,12 @@ namespace osu.Game.Skinning
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A texture store which can be used to perform user file lookups for this skin.
|
/// A texture store which can be used to perform user file lookups for this skin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[CanBeNull]
|
protected TextureStore? Textures { get; }
|
||||||
protected TextureStore Textures { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A sample store which can be used to perform user file lookups for this skin.
|
/// A sample store which can be used to perform user file lookups for this skin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[CanBeNull]
|
protected ISampleStore? Samples { get; }
|
||||||
protected ISampleStore Samples { get; }
|
|
||||||
|
|
||||||
public readonly Live<SkinInfo> SkinInfo;
|
public readonly Live<SkinInfo> SkinInfo;
|
||||||
|
|
||||||
@ -44,13 +44,15 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
private readonly Dictionary<SkinnableTarget, SkinnableInfo[]> drawableComponentInfo = new Dictionary<SkinnableTarget, SkinnableInfo[]>();
|
private readonly Dictionary<SkinnableTarget, SkinnableInfo[]> drawableComponentInfo = new Dictionary<SkinnableTarget, SkinnableInfo[]>();
|
||||||
|
|
||||||
public abstract ISample GetSample(ISampleInfo sampleInfo);
|
public abstract ISample? GetSample(ISampleInfo sampleInfo);
|
||||||
|
|
||||||
public Texture GetTexture(string componentName) => GetTexture(componentName, default, default);
|
public Texture? GetTexture(string componentName) => GetTexture(componentName, default, default);
|
||||||
|
|
||||||
public abstract Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT);
|
public abstract Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT);
|
||||||
|
|
||||||
public abstract IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup);
|
public abstract IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
|
where TLookup : notnull
|
||||||
|
where TValue : notnull;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Construct a new skin.
|
/// Construct a new skin.
|
||||||
@ -59,13 +61,11 @@ namespace osu.Game.Skinning
|
|||||||
/// <param name="resources">Access to game-wide resources.</param>
|
/// <param name="resources">Access to game-wide resources.</param>
|
||||||
/// <param name="storage">An optional store which will *replace* all file lookups that are usually sourced from <paramref name="skin"/>.</param>
|
/// <param name="storage">An optional store which will *replace* all file lookups that are usually sourced from <paramref name="skin"/>.</param>
|
||||||
/// <param name="configurationFilename">An optional filename to read the skin configuration from. If not provided, the configuration will be retrieved from the storage using "skin.ini".</param>
|
/// <param name="configurationFilename">An optional filename to read the skin configuration from. If not provided, the configuration will be retrieved from the storage using "skin.ini".</param>
|
||||||
protected Skin(SkinInfo skin, [CanBeNull] IStorageResourceProvider resources, [CanBeNull] IResourceStore<byte[]> storage = null, [CanBeNull] string configurationFilename = @"skin.ini")
|
protected Skin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? storage = null, string configurationFilename = @"skin.ini")
|
||||||
{
|
{
|
||||||
if (resources != null)
|
if (resources != null)
|
||||||
{
|
{
|
||||||
SkinInfo = resources.RealmAccess != null
|
SkinInfo = skin.ToLive(resources.RealmAccess);
|
||||||
? skin.ToLive(resources.RealmAccess)
|
|
||||||
: skin.ToLiveUnmanaged();
|
|
||||||
|
|
||||||
storage ??= new RealmBackedResourceStore(skin, resources.Files, new[] { @"ogg" });
|
storage ??= new RealmBackedResourceStore(skin, resources.Files, new[] { @"ogg" });
|
||||||
|
|
||||||
@ -76,12 +76,20 @@ namespace osu.Game.Skinning
|
|||||||
Samples = samples;
|
Samples = samples;
|
||||||
Textures = new TextureStore(resources.CreateTextureLoaderStore(storage));
|
Textures = new TextureStore(resources.CreateTextureLoaderStore(storage));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Generally only used for tests.
|
||||||
|
SkinInfo = skin.ToLiveUnmanaged();
|
||||||
|
}
|
||||||
|
|
||||||
var configurationStream = storage?.GetStream(configurationFilename);
|
var configurationStream = storage?.GetStream(configurationFilename);
|
||||||
|
|
||||||
if (configurationStream != null)
|
if (configurationStream != null)
|
||||||
|
{
|
||||||
// stream will be closed after use by LineBufferedReader.
|
// stream will be closed after use by LineBufferedReader.
|
||||||
ParseConfigurationStream(configurationStream);
|
ParseConfigurationStream(configurationStream);
|
||||||
|
Debug.Assert(Configuration != null);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
Configuration = new SkinConfiguration();
|
Configuration = new SkinConfiguration();
|
||||||
|
|
||||||
@ -90,7 +98,7 @@ namespace osu.Game.Skinning
|
|||||||
{
|
{
|
||||||
string filename = $"{skinnableTarget}.json";
|
string filename = $"{skinnableTarget}.json";
|
||||||
|
|
||||||
byte[] bytes = storage?.Get(filename);
|
byte[]? bytes = storage?.Get(filename);
|
||||||
|
|
||||||
if (bytes == null)
|
if (bytes == null)
|
||||||
continue;
|
continue;
|
||||||
@ -136,7 +144,7 @@ namespace osu.Game.Skinning
|
|||||||
DrawableComponentInfo[targetContainer.Target] = targetContainer.CreateSkinnableInfo().ToArray();
|
DrawableComponentInfo[targetContainer.Target] = targetContainer.CreateSkinnableInfo().ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Drawable GetDrawableComponent(ISkinComponent component)
|
public virtual Drawable? GetDrawableComponent(ISkinComponent component)
|
||||||
{
|
{
|
||||||
switch (component)
|
switch (component)
|
||||||
{
|
{
|
||||||
|
@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual
|
|||||||
},
|
},
|
||||||
new OsuSpriteText
|
new OsuSpriteText
|
||||||
{
|
{
|
||||||
Text = skin?.SkinInfo?.Value.Name ?? "none",
|
Text = skin?.SkinInfo.Value.Name ?? "none",
|
||||||
Scale = new Vector2(1.5f),
|
Scale = new Vector2(1.5f),
|
||||||
Padding = new MarginPadding(5),
|
Padding = new MarginPadding(5),
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user