2019-08-21 14:11:33 +08:00
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
2019-01-24 16:43:03 +08:00
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2020-04-01 22:32:33 +08:00
|
|
|
using System;
|
2019-09-03 16:57:34 +08:00
|
|
|
using System.Collections.Generic;
|
2020-04-01 12:38:03 +08:00
|
|
|
using System.Diagnostics;
|
2023-09-06 23:57:26 +08:00
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2023-12-13 13:07:38 +08:00
|
|
|
using System.Globalization;
|
2018-03-05 20:27:37 +08:00
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
2019-09-04 14:59:09 +08:00
|
|
|
using JetBrains.Annotations;
|
2018-02-22 16:16:48 +08:00
|
|
|
using osu.Framework.Audio.Sample;
|
2019-09-03 16:57:34 +08:00
|
|
|
using osu.Framework.Bindables;
|
2022-11-09 12:44:59 +08:00
|
|
|
using osu.Framework.Extensions.ObjectExtensions;
|
2018-02-22 16:16:48 +08:00
|
|
|
using osu.Framework.Graphics;
|
|
|
|
using osu.Framework.Graphics.Textures;
|
|
|
|
using osu.Framework.IO.Stores;
|
2019-08-23 19:32:43 +08:00
|
|
|
using osu.Game.Audio;
|
2020-04-02 16:56:12 +08:00
|
|
|
using osu.Game.Beatmaps.Formats;
|
2022-07-27 08:52:27 +08:00
|
|
|
using osu.Game.Extensions;
|
2019-09-10 06:43:30 +08:00
|
|
|
using osu.Game.IO;
|
2021-05-05 12:12:17 +08:00
|
|
|
using osu.Game.Rulesets.Objects.Types;
|
2019-08-30 14:12:03 +08:00
|
|
|
using osu.Game.Rulesets.Scoring;
|
2020-10-14 16:21:56 +08:00
|
|
|
using osu.Game.Screens.Play.HUD;
|
2021-05-18 14:50:40 +08:00
|
|
|
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
2023-06-15 18:01:38 +08:00
|
|
|
using osuTK;
|
2019-07-30 22:06:18 +08:00
|
|
|
using osuTK.Graphics;
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2018-02-22 16:16:48 +08:00
|
|
|
namespace osu.Game.Skinning
|
|
|
|
{
|
|
|
|
public class LegacySkin : Skin
|
|
|
|
{
|
2023-09-06 17:00:49 +08:00
|
|
|
protected virtual bool AllowManiaConfigLookups => true;
|
2020-03-31 09:14:36 +08:00
|
|
|
|
2020-07-29 05:52:09 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Whether this skin can use samples with a custom bank (custom sample set in stable terminology).
|
|
|
|
/// Added in order to match sample lookup logic from stable (in stable, only the beatmap skin could use samples with a custom sample bank).
|
|
|
|
/// </summary>
|
|
|
|
protected virtual bool UseCustomSampleBanks => false;
|
|
|
|
|
2020-03-31 09:14:36 +08:00
|
|
|
private readonly Dictionary<int, LegacyManiaSkinConfiguration> maniaConfigurations = new Dictionary<int, LegacyManiaSkinConfiguration>();
|
|
|
|
|
2021-05-13 04:18:15 +08:00
|
|
|
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedWithFixedConstructorSignature)]
|
2020-12-21 14:14:32 +08:00
|
|
|
public LegacySkin(SkinInfo skin, IStorageResourceProvider resources)
|
2022-03-23 14:05:01 +08:00
|
|
|
: this(skin, resources, null)
|
2018-02-22 16:16:48 +08:00
|
|
|
{
|
2018-03-22 11:50:23 +08:00
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2021-05-31 14:13:56 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Construct a new legacy skin instance.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="skin">The model for this skin.</param>
|
|
|
|
/// <param name="resources">Access to raw game resources.</param>
|
2023-11-16 19:16:23 +08:00
|
|
|
/// <param name="fallbackStore">An optional fallback store which will be used for file lookups that are not serviced by realm user storage.</param>
|
2021-05-31 14:13:56 +08:00
|
|
|
/// <param name="configurationFilename">The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file.</param>
|
2023-11-16 19:16:23 +08:00
|
|
|
protected LegacySkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore<byte[]>? fallbackStore, string configurationFilename = @"skin.ini")
|
|
|
|
: base(skin, resources, fallbackStore, configurationFilename)
|
2018-03-22 11:50:23 +08:00
|
|
|
{
|
2018-03-14 19:45:04 +08:00
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2024-02-06 19:01:35 +08:00
|
|
|
protected override IResourceStore<TextureUpload> CreateTextureLoaderStore(IStorageResourceProvider resources, IResourceStore<byte[]> storage)
|
|
|
|
=> new LegacyTextureLoaderStore(base.CreateTextureLoaderStore(resources, storage));
|
|
|
|
|
2021-10-22 13:41:59 +08:00
|
|
|
protected override void ParseConfigurationStream(Stream stream)
|
|
|
|
{
|
|
|
|
base.ParseConfigurationStream(stream);
|
|
|
|
|
|
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
|
|
|
|
|
|
using (LineBufferedReader reader = new LineBufferedReader(stream))
|
|
|
|
{
|
|
|
|
var maniaList = new LegacyManiaSkinDecoder().Decode(reader);
|
|
|
|
|
|
|
|
foreach (var config in maniaList)
|
|
|
|
maniaConfigurations[config.Keys] = config;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-06 23:57:26 +08:00
|
|
|
[SuppressMessage("ReSharper", "RedundantAssignment")] // for `wasHit` assignments used in `finally` debug logic
|
2022-03-23 23:21:19 +08:00
|
|
|
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
2019-09-03 16:57:34 +08:00
|
|
|
{
|
2023-09-06 16:37:17 +08:00
|
|
|
bool wasHit = true;
|
|
|
|
|
|
|
|
try
|
2019-09-03 16:57:34 +08:00
|
|
|
{
|
2023-09-06 16:37:17 +08:00
|
|
|
switch (lookup)
|
|
|
|
{
|
|
|
|
case GlobalSkinColours colour:
|
|
|
|
switch (colour)
|
|
|
|
{
|
|
|
|
case GlobalSkinColours.ComboColours:
|
|
|
|
var comboColours = Configuration.ComboColours;
|
|
|
|
if (comboColours != null)
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<IReadOnlyList<Color4>>(comboColours));
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return SkinUtils.As<TValue>(getCustomColour(Configuration, colour.ToString()));
|
|
|
|
}
|
2019-10-10 02:08:07 +08:00
|
|
|
|
2023-09-06 16:37:17 +08:00
|
|
|
break;
|
2020-02-07 13:58:07 +08:00
|
|
|
|
2023-09-06 16:37:17 +08:00
|
|
|
case SkinComboColourLookup comboColour:
|
|
|
|
return SkinUtils.As<TValue>(GetComboColour(Configuration, comboColour.ColourIndex, comboColour.Combo));
|
2019-09-03 16:57:34 +08:00
|
|
|
|
2023-09-06 16:37:17 +08:00
|
|
|
case SkinCustomColourLookup customColour:
|
|
|
|
return SkinUtils.As<TValue>(getCustomColour(Configuration, customColour.Lookup.ToString() ?? string.Empty));
|
2019-09-03 16:57:34 +08:00
|
|
|
|
2023-09-06 16:37:17 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookup maniaLookup:
|
2023-09-06 17:00:49 +08:00
|
|
|
if (!AllowManiaConfigLookups)
|
2023-09-06 16:37:17 +08:00
|
|
|
break;
|
2021-05-05 12:12:17 +08:00
|
|
|
|
2023-09-06 16:37:17 +08:00
|
|
|
var result = lookupForMania<TValue>(maniaLookup);
|
|
|
|
if (result != null)
|
|
|
|
return result;
|
2019-09-03 16:57:34 +08:00
|
|
|
|
2021-05-31 14:27:14 +08:00
|
|
|
break;
|
2020-03-31 09:14:36 +08:00
|
|
|
|
2023-09-06 16:37:17 +08:00
|
|
|
case SkinConfiguration.LegacySetting legacy:
|
|
|
|
return legacySettingLookup<TValue>(legacy);
|
2020-03-31 09:14:36 +08:00
|
|
|
|
2023-09-06 16:37:17 +08:00
|
|
|
default:
|
|
|
|
return genericLookup<TLookup, TValue>(lookup);
|
|
|
|
}
|
2020-03-31 09:14:36 +08:00
|
|
|
|
2023-09-06 16:37:17 +08:00
|
|
|
wasHit = false;
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
LogLookupDebug(this, lookup, wasHit ? LookupDebugType.Hit : LookupDebugType.Miss);
|
2019-09-03 16:57:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-23 23:21:19 +08:00
|
|
|
private IBindable<TValue>? lookupForMania<TValue>(LegacyManiaSkinConfigurationLookup maniaLookup)
|
2020-04-01 22:46:50 +08:00
|
|
|
{
|
2022-10-05 18:21:38 +08:00
|
|
|
if (!maniaConfigurations.TryGetValue(maniaLookup.TotalColumns, out var existing))
|
|
|
|
maniaConfigurations[maniaLookup.TotalColumns] = existing = new LegacyManiaSkinConfiguration(maniaLookup.TotalColumns);
|
2020-04-01 22:46:50 +08:00
|
|
|
|
|
|
|
switch (maniaLookup.Lookup)
|
|
|
|
{
|
|
|
|
case LegacyManiaSkinConfigurationLookups.ColumnWidth:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.ColumnWidth[maniaLookup.ColumnIndex.Value]));
|
2020-04-01 22:46:50 +08:00
|
|
|
|
2023-02-10 18:18:41 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.WidthForNoteHeightScale:
|
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.WidthForNoteHeightScale));
|
|
|
|
|
2020-04-01 22:46:50 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.ColumnSpacing:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.ColumnSpacing[maniaLookup.ColumnIndex.Value]));
|
2020-04-01 22:46:50 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.HitPosition:
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.HitPosition));
|
|
|
|
|
2020-12-14 00:00:46 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.ScorePosition:
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.ScorePosition));
|
|
|
|
|
2020-04-01 22:46:50 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.LightPosition:
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.LightPosition));
|
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.ShowJudgementLine:
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<bool>(existing.ShowJudgementLine));
|
2020-04-02 13:29:16 +08:00
|
|
|
|
2020-08-25 14:35:37 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.ExplosionImage:
|
|
|
|
return SkinUtils.As<TValue>(getManiaImage(existing, "LightingN"));
|
|
|
|
|
2020-04-02 13:29:16 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.ExplosionScale:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
2020-04-02 13:29:16 +08:00
|
|
|
|
2021-10-22 13:41:59 +08:00
|
|
|
if (GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value < 2.5m)
|
2020-04-02 13:29:16 +08:00
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(1));
|
|
|
|
|
2022-10-06 13:26:29 +08:00
|
|
|
if (existing.ExplosionWidth[maniaLookup.ColumnIndex.Value] != 0)
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.ExplosionWidth[maniaLookup.ColumnIndex.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE));
|
2020-04-02 13:29:16 +08:00
|
|
|
|
2022-10-06 13:26:29 +08:00
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.ColumnWidth[maniaLookup.ColumnIndex.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE));
|
2020-04-02 22:57:03 +08:00
|
|
|
|
2020-04-02 17:10:17 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.ColumnLineColour:
|
|
|
|
return SkinUtils.As<TValue>(getCustomColour(existing, "ColourColumnLine"));
|
2020-04-07 15:50:08 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.JudgementLineColour:
|
|
|
|
return SkinUtils.As<TValue>(getCustomColour(existing, "ColourJudgementLine"));
|
2020-04-07 15:53:29 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(getCustomColour(existing, $"Colour{maniaLookup.ColumnIndex + 1}"));
|
2020-04-07 15:53:29 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.ColumnLightColour:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(getCustomColour(existing, $"ColourLight{maniaLookup.ColumnIndex + 1}"));
|
2020-04-07 22:36:42 +08:00
|
|
|
|
2020-04-07 15:07:18 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.MinimumColumnWidth:
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.MinimumColumnWidth));
|
2020-04-07 21:41:22 +08:00
|
|
|
|
2023-02-10 18:30:41 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.NoteBodyStyle:
|
|
|
|
|
|
|
|
if (existing.NoteBodyStyle != null)
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<LegacyNoteBodyStyle>(existing.NoteBodyStyle.Value));
|
|
|
|
|
|
|
|
if (GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value < 2.5m)
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<LegacyNoteBodyStyle>(LegacyNoteBodyStyle.Stretch));
|
|
|
|
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<LegacyNoteBodyStyle>(LegacyNoteBodyStyle.RepeatBottom));
|
|
|
|
|
2020-04-06 18:04:02 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.NoteImage:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(getManiaImage(existing, $"NoteImage{maniaLookup.ColumnIndex}"));
|
2020-04-06 18:04:02 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(getManiaImage(existing, $"NoteImage{maniaLookup.ColumnIndex}H"));
|
2020-04-06 18:04:02 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.HoldNoteTailImage:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(getManiaImage(existing, $"NoteImage{maniaLookup.ColumnIndex}T"));
|
2020-04-06 18:04:02 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.HoldNoteBodyImage:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(getManiaImage(existing, $"NoteImage{maniaLookup.ColumnIndex}L"));
|
2020-04-06 18:04:02 +08:00
|
|
|
|
2020-08-26 19:21:41 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.HoldNoteLightImage:
|
|
|
|
return SkinUtils.As<TValue>(getManiaImage(existing, "LightingL"));
|
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.HoldNoteLightScale:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
2020-08-26 19:21:41 +08:00
|
|
|
|
2021-10-22 13:41:59 +08:00
|
|
|
if (GetConfig<SkinConfiguration.LegacySetting, decimal>(SkinConfiguration.LegacySetting.Version)?.Value < 2.5m)
|
2020-08-26 19:21:41 +08:00
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(1));
|
|
|
|
|
2022-10-06 13:26:29 +08:00
|
|
|
if (existing.HoldNoteLightWidth[maniaLookup.ColumnIndex.Value] != 0)
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.HoldNoteLightWidth[maniaLookup.ColumnIndex.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE));
|
2020-08-26 19:21:41 +08:00
|
|
|
|
2022-10-06 13:26:29 +08:00
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.ColumnWidth[maniaLookup.ColumnIndex.Value] / LegacyManiaSkinConfiguration.DEFAULT_COLUMN_SIZE));
|
2020-08-26 19:21:41 +08:00
|
|
|
|
2020-04-06 18:04:02 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.KeyImage:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(getManiaImage(existing, $"KeyImage{maniaLookup.ColumnIndex}"));
|
2020-04-06 18:04:02 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.KeyImageDown:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(getManiaImage(existing, $"KeyImage{maniaLookup.ColumnIndex}D"));
|
2020-04-08 14:36:07 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.LeftStageImage:
|
|
|
|
return SkinUtils.As<TValue>(getManiaImage(existing, "StageLeft"));
|
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.RightStageImage:
|
|
|
|
return SkinUtils.As<TValue>(getManiaImage(existing, "StageRight"));
|
2020-04-21 16:14:04 +08:00
|
|
|
|
2020-07-05 13:02:50 +08:00
|
|
|
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"));
|
|
|
|
|
2020-04-21 16:14:04 +08:00
|
|
|
case LegacyManiaSkinConfigurationLookups.LeftLineWidth:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.ColumnLineWidth[maniaLookup.ColumnIndex.Value]));
|
2020-04-21 16:14:04 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.RightLineWidth:
|
2022-10-06 13:26:29 +08:00
|
|
|
Debug.Assert(maniaLookup.ColumnIndex != null);
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<float>(existing.ColumnLineWidth[maniaLookup.ColumnIndex.Value + 1]));
|
2020-06-12 21:22:22 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.Hit0:
|
|
|
|
case LegacyManiaSkinConfigurationLookups.Hit50:
|
|
|
|
case LegacyManiaSkinConfigurationLookups.Hit100:
|
|
|
|
case LegacyManiaSkinConfigurationLookups.Hit200:
|
|
|
|
case LegacyManiaSkinConfigurationLookups.Hit300:
|
|
|
|
case LegacyManiaSkinConfigurationLookups.Hit300g:
|
2020-06-13 20:19:06 +08:00
|
|
|
return SkinUtils.As<TValue>(getManiaImage(existing, maniaLookup.Lookup.ToString()));
|
2020-08-26 14:37:16 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.KeysUnderNotes:
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<bool>(existing.KeysUnderNotes));
|
2023-10-19 01:56:17 +08:00
|
|
|
|
|
|
|
case LegacyManiaSkinConfigurationLookups.LightFramePerSecond:
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<int>(existing.LightFramePerSecond));
|
2020-04-01 22:46:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2021-05-05 12:12:17 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Retrieves the correct combo colour for a given colour index and information on the combo.
|
|
|
|
/// </summary>
|
|
|
|
/// <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="combo">Information on the combo whose using the returned colour.</param>
|
2022-03-23 23:21:19 +08:00
|
|
|
protected virtual IBindable<Color4>? GetComboColour(IHasComboColours source, int colourIndex, IHasComboInformation combo)
|
2021-05-05 12:12:17 +08:00
|
|
|
{
|
|
|
|
var colour = source.ComboColours?[colourIndex % source.ComboColours.Count];
|
|
|
|
return colour.HasValue ? new Bindable<Color4>(colour.Value) : null;
|
|
|
|
}
|
|
|
|
|
2022-03-23 23:21:19 +08:00
|
|
|
private IBindable<Color4>? getCustomColour(IHasCustomColours source, string lookup)
|
2020-04-02 16:56:12 +08:00
|
|
|
=> source.CustomColours.TryGetValue(lookup, out var col) ? new Bindable<Color4>(col) : null;
|
2019-09-03 16:57:34 +08:00
|
|
|
|
2022-03-23 23:21:19 +08:00
|
|
|
private IBindable<string>? getManiaImage(LegacyManiaSkinConfiguration source, string lookup)
|
2022-12-16 17:16:26 +08:00
|
|
|
=> source.ImageLookups.TryGetValue(lookup, out string? image) ? new Bindable<string>(image) : null;
|
2020-04-06 18:04:02 +08:00
|
|
|
|
2022-03-23 23:21:19 +08:00
|
|
|
private IBindable<TValue>? legacySettingLookup<TValue>(SkinConfiguration.LegacySetting legacySetting)
|
2022-03-25 14:53:55 +08:00
|
|
|
where TValue : notnull
|
2020-08-03 01:50:17 +08:00
|
|
|
{
|
|
|
|
switch (legacySetting)
|
|
|
|
{
|
2021-10-22 13:41:59 +08:00
|
|
|
case SkinConfiguration.LegacySetting.Version:
|
|
|
|
return SkinUtils.As<TValue>(new Bindable<decimal>(Configuration.LegacyVersion ?? SkinConfiguration.LATEST_VERSION));
|
2020-08-03 01:50:17 +08:00
|
|
|
|
|
|
|
default:
|
2021-10-22 13:41:59 +08:00
|
|
|
return genericLookup<SkinConfiguration.LegacySetting, TValue>(legacySetting);
|
2020-08-03 01:50:17 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-23 23:21:19 +08:00
|
|
|
private IBindable<TValue>? genericLookup<TLookup, TValue>(TLookup lookup)
|
2022-03-25 14:53:55 +08:00
|
|
|
where TLookup : notnull
|
|
|
|
where TValue : notnull
|
2020-08-03 01:46:29 +08:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2022-12-16 17:16:26 +08:00
|
|
|
if (Configuration.ConfigDictionary.TryGetValue(lookup.ToString() ?? string.Empty, out string? val))
|
2020-08-03 01:46:29 +08:00
|
|
|
{
|
|
|
|
// special case for handling skins which use 1 or 0 to signify a boolean state.
|
2022-06-06 18:43:08 +08:00
|
|
|
// ..or in some cases 2 (https://github.com/ppy/osu/issues/18579).
|
2020-08-03 01:46:29 +08:00
|
|
|
if (typeof(TValue) == typeof(bool))
|
2022-06-06 18:43:08 +08:00
|
|
|
{
|
|
|
|
val = bool.TryParse(val, out bool boolVal)
|
|
|
|
? Convert.ChangeType(boolVal, typeof(bool)).ToString()
|
|
|
|
: Convert.ChangeType(Convert.ToInt32(val), typeof(bool)).ToString();
|
|
|
|
}
|
2020-08-03 01:46:29 +08:00
|
|
|
|
|
|
|
var bindable = new Bindable<TValue>();
|
|
|
|
if (val != null)
|
2023-12-13 13:07:38 +08:00
|
|
|
bindable.Parse(val, CultureInfo.InvariantCulture);
|
2020-08-03 01:46:29 +08:00
|
|
|
return bindable;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2022-11-09 15:04:56 +08:00
|
|
|
public override Drawable? GetDrawableComponent(ISkinComponentLookup lookup)
|
2018-02-22 16:16:48 +08:00
|
|
|
{
|
2022-11-09 13:11:41 +08:00
|
|
|
if (base.GetDrawableComponent(lookup) is Drawable c)
|
2021-05-10 21:43:48 +08:00
|
|
|
return c;
|
|
|
|
|
2022-11-09 13:11:41 +08:00
|
|
|
switch (lookup)
|
2018-03-12 10:30:13 +08:00
|
|
|
{
|
2023-02-16 14:33:56 +08:00
|
|
|
case SkinComponentsContainerLookup containerLookup:
|
2023-02-17 17:22:10 +08:00
|
|
|
// Only handle global level defaults for now.
|
|
|
|
if (containerLookup.Ruleset != null)
|
|
|
|
return null;
|
|
|
|
|
2023-02-16 14:33:56 +08:00
|
|
|
switch (containerLookup.Target)
|
2020-10-14 16:21:56 +08:00
|
|
|
{
|
2023-02-15 17:31:55 +08:00
|
|
|
case SkinComponentsContainerLookup.TargetArea.MainHUDComponents:
|
2023-02-16 18:25:55 +08:00
|
|
|
return new DefaultSkinComponentsContainer(container =>
|
2021-05-11 12:12:24 +08:00
|
|
|
{
|
|
|
|
var score = container.OfType<LegacyScoreCounter>().FirstOrDefault();
|
|
|
|
var accuracy = container.OfType<GameplayAccuracyCounter>().FirstOrDefault();
|
|
|
|
|
|
|
|
if (score != null && accuracy != null)
|
|
|
|
{
|
|
|
|
accuracy.Y = container.ToLocalSpace(score.ScreenSpaceDrawQuad.BottomRight).Y;
|
|
|
|
}
|
2021-05-18 14:50:40 +08:00
|
|
|
|
2022-07-27 08:52:27 +08:00
|
|
|
var songProgress = container.OfType<LegacySongProgress>().FirstOrDefault();
|
|
|
|
|
|
|
|
if (songProgress != null && accuracy != null)
|
|
|
|
{
|
|
|
|
songProgress.Anchor = Anchor.TopRight;
|
|
|
|
songProgress.Origin = Anchor.CentreRight;
|
2023-07-21 16:25:38 +08:00
|
|
|
songProgress.X = -accuracy.ScreenSpaceDeltaToParentSpace(accuracy.ScreenSpaceDrawQuad.Size).X - 18;
|
2022-07-27 08:52:27 +08:00
|
|
|
songProgress.Y = container.ToLocalSpace(accuracy.ScreenSpaceDrawQuad.TopLeft).Y + (accuracy.ScreenSpaceDeltaToParentSpace(accuracy.ScreenSpaceDrawQuad.Size).Y / 2);
|
|
|
|
}
|
2021-05-18 14:50:40 +08:00
|
|
|
|
|
|
|
var hitError = container.OfType<HitErrorMeter>().FirstOrDefault();
|
|
|
|
|
|
|
|
if (hitError != null)
|
|
|
|
{
|
|
|
|
hitError.Anchor = Anchor.BottomCentre;
|
|
|
|
hitError.Origin = Anchor.CentreLeft;
|
|
|
|
hitError.Rotation = -90;
|
2024-07-23 19:05:14 +08:00
|
|
|
}
|
2023-06-15 18:01:38 +08:00
|
|
|
|
2024-07-23 19:05:14 +08:00
|
|
|
var keyCounter = container.OfType<LegacyKeyCounterDisplay>().FirstOrDefault();
|
2023-06-26 15:04:16 +08:00
|
|
|
|
2024-07-23 19:05:14 +08:00
|
|
|
if (keyCounter != null)
|
|
|
|
{
|
|
|
|
keyCounter.Rotation = 90f;
|
2024-07-23 21:12:55 +08:00
|
|
|
// set the anchor to top right so that it won't squash to the return button to the top
|
2024-07-23 22:11:28 +08:00
|
|
|
keyCounter.Anchor = Anchor.CentreRight;
|
2024-07-23 19:05:14 +08:00
|
|
|
keyCounter.Origin = Anchor.TopCentre;
|
2024-07-23 21:12:55 +08:00
|
|
|
keyCounter.X = 0;
|
2024-07-23 22:11:28 +08:00
|
|
|
// 340px is the default height inherit from stable
|
|
|
|
keyCounter.Y = container.ToLocalSpace(new Vector2(0, container.ScreenSpaceDrawQuad.Centre.Y - 340f)).Y;
|
2021-05-23 15:46:32 +08:00
|
|
|
}
|
2021-05-11 12:12:24 +08:00
|
|
|
})
|
2021-05-10 21:43:48 +08:00
|
|
|
{
|
2022-03-24 11:36:16 +08:00
|
|
|
Children = new Drawable[]
|
|
|
|
{
|
|
|
|
new LegacyComboCounter(),
|
|
|
|
new LegacyScoreCounter(),
|
|
|
|
new LegacyAccuracyCounter(),
|
2022-07-27 08:52:27 +08:00
|
|
|
new LegacySongProgress(),
|
2023-07-19 16:48:19 +08:00
|
|
|
new LegacyHealthDisplay(),
|
2022-03-24 11:36:16 +08:00
|
|
|
new BarHitErrorMeter(),
|
2024-07-23 19:05:14 +08:00
|
|
|
new LegacyKeyCounterDisplay(),
|
2022-03-24 11:36:16 +08:00
|
|
|
}
|
2021-05-10 21:43:48 +08:00
|
|
|
};
|
2020-10-14 16:21:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
2022-11-09 15:04:56 +08:00
|
|
|
case GameplaySkinComponentLookup<HitResult> resultComponent:
|
2020-11-18 14:38:26 +08:00
|
|
|
|
2020-11-18 16:15:45 +08:00
|
|
|
// kind of wasteful that we throw this away, but should do for now.
|
2022-11-09 12:44:59 +08:00
|
|
|
if (getJudgementAnimation(resultComponent.Component) != null)
|
2020-11-18 14:38:26 +08:00
|
|
|
{
|
2022-11-09 12:44:59 +08:00
|
|
|
// TODO: this should be inside the judgement pieces.
|
|
|
|
Func<Drawable> createDrawable = () => getJudgementAnimation(resultComponent.Component).AsNonNull();
|
|
|
|
|
2021-01-15 13:51:26 +08:00
|
|
|
var particle = getParticleTexture(resultComponent.Component);
|
|
|
|
|
|
|
|
if (particle != null)
|
|
|
|
return new LegacyJudgementPieceNew(resultComponent.Component, createDrawable, particle);
|
2022-10-12 16:28:06 +08:00
|
|
|
|
|
|
|
return new LegacyJudgementPieceOld(resultComponent.Component, createDrawable);
|
2020-11-18 14:38:26 +08:00
|
|
|
}
|
2019-04-01 11:16:05 +08:00
|
|
|
|
2022-04-23 05:35:45 +08:00
|
|
|
return null;
|
2022-04-23 06:06:35 +08:00
|
|
|
}
|
2022-10-12 16:28:06 +08:00
|
|
|
|
|
|
|
return null;
|
2020-11-17 14:44:15 +08:00
|
|
|
}
|
2019-04-01 11:16:05 +08:00
|
|
|
|
2022-03-23 23:21:19 +08:00
|
|
|
private Texture? getParticleTexture(HitResult result)
|
2020-11-18 14:38:26 +08:00
|
|
|
{
|
|
|
|
switch (result)
|
|
|
|
{
|
|
|
|
case HitResult.Meh:
|
2020-11-19 14:47:02 +08:00
|
|
|
return GetTexture("particle50");
|
2020-11-18 14:38:26 +08:00
|
|
|
|
|
|
|
case HitResult.Ok:
|
2020-11-19 14:47:02 +08:00
|
|
|
return GetTexture("particle100");
|
2020-11-18 14:38:26 +08:00
|
|
|
|
|
|
|
case HitResult.Great:
|
2020-11-19 14:47:02 +08:00
|
|
|
return GetTexture("particle300");
|
2020-11-18 14:38:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2022-03-23 23:21:19 +08:00
|
|
|
private Drawable? getJudgementAnimation(HitResult result)
|
2020-11-17 14:44:15 +08:00
|
|
|
{
|
|
|
|
switch (result)
|
|
|
|
{
|
2023-12-25 16:17:23 +08:00
|
|
|
case HitResult.Miss:
|
|
|
|
return this.GetAnimation("hit0", true, false);
|
|
|
|
|
|
|
|
case HitResult.LargeTickMiss:
|
|
|
|
return this.GetAnimation("slidertickmiss", true, false);
|
|
|
|
|
|
|
|
case HitResult.IgnoreMiss:
|
|
|
|
return this.GetAnimation("sliderendmiss", true, false);
|
|
|
|
|
2020-11-17 14:44:15 +08:00
|
|
|
case HitResult.Meh:
|
|
|
|
return this.GetAnimation("hit50", true, false);
|
|
|
|
|
|
|
|
case HitResult.Ok:
|
|
|
|
return this.GetAnimation("hit100", true, false);
|
|
|
|
|
|
|
|
case HitResult.Great:
|
|
|
|
return this.GetAnimation("hit300", true, false);
|
2018-03-12 10:30:13 +08:00
|
|
|
}
|
2018-04-13 17:19:50 +08:00
|
|
|
|
2020-11-17 14:44:15 +08:00
|
|
|
return null;
|
2019-08-19 18:23:54 +08:00
|
|
|
}
|
|
|
|
|
2024-01-17 16:21:44 +08:00
|
|
|
/// <summary>
|
|
|
|
/// Whether high-resolution textures ("@2x"-suffixed) are allowed to be used by <see cref="GetTexture"/> when available.
|
|
|
|
/// </summary>
|
|
|
|
protected virtual bool AllowHighResolutionSprites => true;
|
|
|
|
|
2022-03-23 23:21:19 +08:00
|
|
|
public override Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT)
|
2019-08-27 16:18:32 +08:00
|
|
|
{
|
2023-07-25 16:46:05 +08:00
|
|
|
switch (componentName)
|
|
|
|
{
|
|
|
|
case "Menu/fountain-star":
|
|
|
|
componentName = "star2";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-01-17 18:27:44 +08:00
|
|
|
Texture? texture = null;
|
|
|
|
float ratio = 1;
|
|
|
|
|
|
|
|
if (AllowHighResolutionSprites)
|
2020-04-06 18:02:50 +08:00
|
|
|
{
|
2024-01-17 18:27:44 +08:00
|
|
|
// some component names (especially user-controlled ones, like `HitX` in mania)
|
|
|
|
// may contain `@2x` scale specifications.
|
|
|
|
// stable happens to check for that and strip them, so do the same to match stable behaviour.
|
|
|
|
componentName = componentName.Replace(@"@2x", string.Empty);
|
2022-01-17 02:44:04 +08:00
|
|
|
|
2024-01-17 18:27:44 +08:00
|
|
|
string twoTimesFilename = $"{Path.ChangeExtension(componentName, null)}@2x{Path.GetExtension(componentName)}";
|
2024-01-17 16:21:44 +08:00
|
|
|
|
2024-01-17 18:27:44 +08:00
|
|
|
texture = Textures?.Get(twoTimesFilename, wrapModeS, wrapModeT);
|
2022-04-07 13:16:16 +08:00
|
|
|
|
2024-01-17 18:27:44 +08:00
|
|
|
if (texture != null)
|
2024-01-17 16:21:44 +08:00
|
|
|
ratio = 2;
|
2024-01-17 18:27:44 +08:00
|
|
|
}
|
2019-08-27 16:18:32 +08:00
|
|
|
|
2024-01-17 18:27:44 +08:00
|
|
|
texture ??= Textures?.Get(componentName, wrapModeS, wrapModeT);
|
2019-08-27 16:18:32 +08:00
|
|
|
|
2024-01-17 18:27:44 +08:00
|
|
|
if (texture != null)
|
2020-04-08 04:50:25 +08:00
|
|
|
texture.ScaleAdjust = ratio;
|
2019-08-27 16:18:32 +08:00
|
|
|
|
2024-01-17 18:27:44 +08:00
|
|
|
return texture;
|
2019-08-27 16:18:32 +08:00
|
|
|
}
|
|
|
|
|
2022-03-23 23:21:19 +08:00
|
|
|
public override ISample? GetSample(ISampleInfo sampleInfo)
|
2019-08-22 17:50:47 +08:00
|
|
|
{
|
2020-10-30 11:28:40 +08:00
|
|
|
IEnumerable<string> lookupNames;
|
2020-07-29 05:52:09 +08:00
|
|
|
|
|
|
|
if (sampleInfo is HitSampleInfo hitSample)
|
|
|
|
lookupNames = getLegacyLookupNames(hitSample);
|
2020-10-30 10:14:08 +08:00
|
|
|
else
|
|
|
|
{
|
2024-01-17 18:27:44 +08:00
|
|
|
lookupNames = sampleInfo.LookupNames.SelectMany(getFallbackSampleNames);
|
2020-10-30 10:14:08 +08:00
|
|
|
}
|
2020-07-29 05:52:09 +08:00
|
|
|
|
2021-10-27 12:04:41 +08:00
|
|
|
foreach (string lookup in lookupNames)
|
2019-08-22 17:50:47 +08:00
|
|
|
{
|
2020-01-02 13:07:22 +08:00
|
|
|
var sample = Samples?.Get(lookup);
|
2019-08-23 19:32:43 +08:00
|
|
|
|
|
|
|
if (sample != null)
|
2021-06-01 17:56:22 +08:00
|
|
|
{
|
2019-08-23 19:32:43 +08:00
|
|
|
return sample;
|
2021-06-01 17:56:22 +08:00
|
|
|
}
|
2019-08-22 17:50:47 +08:00
|
|
|
}
|
|
|
|
|
2019-08-23 19:32:43 +08:00
|
|
|
return null;
|
2019-08-22 17:50:47 +08:00
|
|
|
}
|
2019-08-27 16:18:32 +08:00
|
|
|
|
2020-07-29 05:52:09 +08:00
|
|
|
private IEnumerable<string> getLegacyLookupNames(HitSampleInfo hitSample)
|
|
|
|
{
|
2024-01-17 18:27:44 +08:00
|
|
|
var lookupNames = hitSample.LookupNames.SelectMany(getFallbackSampleNames);
|
2020-07-29 05:52:09 +08:00
|
|
|
|
|
|
|
if (!UseCustomSampleBanks && !string.IsNullOrEmpty(hitSample.Suffix))
|
2020-10-30 10:14:08 +08:00
|
|
|
{
|
2020-07-29 05:52:09 +08:00
|
|
|
// for compatibility with stable, exclude the lookup names with the custom sample bank suffix, if they are not valid for use in this skin.
|
|
|
|
// using .EndsWith() is intentional as it ensures parity in all edge cases
|
|
|
|
// (see LegacyTaikoSampleInfo for an example of one - prioritising the taiko prefix should still apply, but the sample bank should not).
|
2020-10-30 21:33:05 +08:00
|
|
|
lookupNames = lookupNames.Where(name => !name.EndsWith(hitSample.Suffix, StringComparison.Ordinal));
|
2020-10-30 10:14:08 +08:00
|
|
|
}
|
2020-07-29 05:52:09 +08:00
|
|
|
|
2021-10-27 12:04:41 +08:00
|
|
|
foreach (string l in lookupNames)
|
2020-10-30 21:33:05 +08:00
|
|
|
yield return l;
|
|
|
|
|
2020-07-31 04:07:07 +08:00
|
|
|
// also for compatibility, try falling back to non-bank samples (so-called "universal" samples) as the last resort.
|
|
|
|
// going forward specifying banks shall always be required, even for elements that wouldn't require it on stable,
|
|
|
|
// which is why this is done locally here.
|
2020-10-30 10:14:08 +08:00
|
|
|
yield return hitSample.Name;
|
|
|
|
}
|
|
|
|
|
2024-01-17 18:27:44 +08:00
|
|
|
private IEnumerable<string> getFallbackSampleNames(string name)
|
2020-10-30 10:14:08 +08:00
|
|
|
{
|
2024-01-17 18:27:44 +08:00
|
|
|
// May be something like "Gameplay/normal-hitnormal" from lazer.
|
|
|
|
yield return name;
|
2020-07-31 04:07:07 +08:00
|
|
|
|
2024-01-17 18:27:44 +08:00
|
|
|
// Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/normal-hitnormal" -> "normal-hitnormal").
|
|
|
|
yield return name.Split('/').Last();
|
2020-07-29 05:52:09 +08:00
|
|
|
}
|
2018-02-22 16:16:48 +08:00
|
|
|
}
|
|
|
|
}
|