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 ;
2018-04-13 17:19:50 +08:00
using System.IO ;
using System.Linq ;
2019-09-04 14:59:09 +08:00
using JetBrains.Annotations ;
2018-04-13 17:19:50 +08:00
using osu.Framework.Audio.Sample ;
2019-09-03 16:57:34 +08:00
using osu.Framework.Bindables ;
2018-04-13 17:19:50 +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-03-23 12:14:56 +08:00
using osu.Game.Database ;
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 ;
2019-07-30 22:06:18 +08:00
using osuTK.Graphics ;
2018-04-13 17:19:50 +08:00
namespace osu.Game.Skinning
{
public class LegacySkin : Skin
{
2020-04-01 22:32:33 +08:00
/// <summary>
/// Whether texture for the keys exists.
/// Used to determine if the mania ruleset is skinned.
/// </summary>
private readonly Lazy < bool > hasKeyTexture ;
protected virtual bool AllowManiaSkin = > hasKeyTexture . Value ;
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-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>
2022-03-23 14:02:01 +08:00
/// <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>
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>
2022-03-23 23:21:19 +08:00
protected LegacySkin ( SkinInfo skin , IStorageResourceProvider ? resources , IResourceStore < byte [ ] > ? storage , string configurationFilename = @"skin.ini" )
2022-03-23 13:53:00 +08:00
: base ( skin , resources , storage , configurationFilename )
2018-04-13 17:19:50 +08:00
{
2020-04-01 22:32:33 +08:00
// todo: this shouldn't really be duplicated here (from ManiaLegacySkinTransformer). we need to come up with a better solution.
hasKeyTexture = new Lazy < bool > ( ( ) = > this . GetAnimation (
2020-04-07 15:53:29 +08:00
lookupForMania < string > ( new LegacyManiaSkinConfigurationLookup ( 4 , LegacyManiaSkinConfigurationLookups . KeyImage , 0 ) ) ? . Value ? ? "mania-key1" , true ,
true ) ! = null ) ;
2018-04-13 17:19:50 +08:00
}
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 ;
}
}
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
{
switch ( lookup )
{
2020-02-07 13:58:07 +08:00
case GlobalSkinColours colour :
switch ( colour )
2019-09-03 16:57:34 +08:00
{
2020-02-07 13:58:07 +08:00
case GlobalSkinColours . ComboColours :
2019-11-07 04:20:36 +08:00
var comboColours = Configuration . ComboColours ;
if ( comboColours ! = null )
2019-11-07 20:54:30 +08:00
return SkinUtils . As < TValue > ( new Bindable < IReadOnlyList < Color4 > > ( comboColours ) ) ;
2019-10-10 02:08:07 +08:00
break ;
2020-02-07 13:58:07 +08:00
default :
2020-04-02 16:56:12 +08:00
return SkinUtils . As < TValue > ( getCustomColour ( Configuration , colour . ToString ( ) ) ) ;
2019-09-03 16:57:34 +08:00
}
break ;
2021-05-05 12:12:17 +08:00
case SkinComboColourLookup comboColour :
return SkinUtils . As < TValue > ( GetComboColour ( Configuration , comboColour . ColourIndex , comboColour . Combo ) ) ;
2019-09-03 16:57:34 +08:00
case SkinCustomColourLookup customColour :
2020-04-02 16:56:12 +08:00
return SkinUtils . As < TValue > ( getCustomColour ( Configuration , customColour . Lookup . ToString ( ) ) ) ;
2019-09-03 16:57:34 +08:00
2020-03-31 17:22:46 +08:00
case LegacyManiaSkinConfigurationLookup maniaLookup :
2020-03-31 09:14:36 +08:00
if ( ! AllowManiaSkin )
2021-05-31 14:27:14 +08:00
break ;
2020-03-31 09:14:36 +08:00
2020-04-01 22:46:50 +08:00
var result = lookupForMania < TValue > ( maniaLookup ) ;
if ( result ! = null )
return result ;
2020-03-31 09:14:36 +08:00
break ;
2021-10-22 13:41:59 +08:00
case SkinConfiguration . LegacySetting legacy :
2020-08-03 01:50:17 +08:00
return legacySettingLookup < TValue > ( legacy ) ;
2020-07-29 15:34:09 +08:00
2019-09-03 16:57:34 +08:00
default :
2020-08-03 01:46:29 +08:00
return genericLookup < TLookup , TValue > ( lookup ) ;
2019-09-03 16:57:34 +08:00
}
return null ;
}
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
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.5 m )
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
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.5 m )
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 ) ) ;
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 )
2021-10-27 12:04:41 +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
{
2021-10-27 12:04:41 +08:00
if ( Configuration . ConfigDictionary . TryGetValue ( lookup . ToString ( ) , 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 )
bindable . Parse ( val ) ;
return bindable ;
}
}
catch
{
}
return null ;
}
2022-03-23 23:21:19 +08:00
public override Drawable ? GetDrawableComponent ( ISkinComponent component )
2018-04-13 17:19:50 +08:00
{
2021-05-10 21:43:48 +08:00
if ( base . GetDrawableComponent ( component ) is Drawable c )
return c ;
2019-08-30 14:12:03 +08:00
switch ( component )
2018-04-13 17:19:50 +08:00
{
2021-05-10 21:43:48 +08:00
case SkinnableTargetComponent target :
switch ( target . Target )
2020-10-14 16:21:56 +08:00
{
2021-05-10 21:43:48 +08:00
case SkinnableTarget . MainHUDComponents :
2021-05-13 17:51:23 +08:00
var skinnableTargetWrapper = new SkinnableTargetComponentsContainer ( 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 ;
songProgress . X = - accuracy . ScreenSpaceDeltaToParentSpace ( accuracy . ScreenSpaceDrawQuad . Size ) . X - 10 ;
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 ;
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 ( ) ,
new LegacyHealthDisplay ( ) ,
2022-07-27 08:52:27 +08:00
new LegacySongProgress ( ) ,
2022-03-24 11:36:16 +08:00
new BarHitErrorMeter ( ) ,
}
2021-05-10 21:43:48 +08:00
} ;
2021-05-11 12:12:24 +08:00
return skinnableTargetWrapper ;
2020-10-14 16:21:56 +08:00
}
return null ;
2019-08-30 14:10:11 +08:00
case GameplaySkinComponent < HitResult > resultComponent :
2021-06-06 10:08:54 +08:00
// TODO: this should be inside the judgement pieces.
2022-03-23 23:21:19 +08:00
Func < Drawable ? > createDrawable = ( ) = > getJudgementAnimation ( resultComponent . Component ) ;
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.
2020-11-18 14:38:26 +08:00
if ( createDrawable ( ) ! = null )
{
2021-01-15 13:51:26 +08:00
var particle = getParticleTexture ( resultComponent . Component ) ;
if ( particle ! = null )
return new LegacyJudgementPieceNew ( resultComponent . Component , createDrawable , particle ) ;
2020-11-18 14:38:26 +08:00
else
return new LegacyJudgementPieceOld ( resultComponent . Component , createDrawable ) ;
}
2019-04-01 11:16:05 +08:00
2022-04-23 05:35:45 +08:00
return null ;
2019-04-01 11:16:05 +08:00
2022-04-23 06:08:53 +08:00
case SkinnableSprite . SpriteComponent sprite :
return this . GetAnimation ( sprite . LookupName , false , false ) ;
2022-04-23 06:06:35 +08:00
default :
2022-04-23 06:08:53 +08:00
throw new UnsupportedSkinComponentException ( component ) ;
2022-04-23 06:06:35 +08:00
}
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 )
{
case HitResult . Miss :
return this . GetAnimation ( "hit0" , true , false ) ;
2019-08-30 14:12:03 +08:00
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-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
}
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
{
2021-10-27 12:04:41 +08:00
foreach ( string name in getFallbackNames ( componentName ) )
2020-04-06 18:02:50 +08:00
{
2022-01-17 02:44:04 +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.
string lookupName = name . Replace ( @"@2x" , string . Empty ) ;
2020-04-06 18:02:50 +08:00
float ratio = 2 ;
2022-04-07 20:07:14 +08:00
string twoTimesFilename = $"{Path.ChangeExtension(lookupName, null)}@2x{Path.GetExtension(lookupName)}" ;
2022-04-07 13:16:16 +08:00
var texture = Textures ? . Get ( twoTimesFilename , wrapModeS , wrapModeT ) ;
2019-08-27 16:18:32 +08:00
2020-04-06 18:02:50 +08:00
if ( texture = = null )
{
ratio = 1 ;
2022-01-17 02:44:04 +08:00
texture = Textures ? . Get ( lookupName , wrapModeS , wrapModeT ) ;
2020-04-06 18:02:50 +08:00
}
2019-08-27 16:18:32 +08:00
2020-04-08 04:50:25 +08:00
if ( texture = = null )
continue ;
2019-08-27 16:18:32 +08:00
2020-04-08 04:50:25 +08:00
texture . ScaleAdjust = ratio ;
2020-04-06 18:02:50 +08:00
return texture ;
2019-08-27 16:18:32 +08:00
}
2020-04-06 18:02:50 +08:00
return null ;
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
{
lookupNames = sampleInfo . LookupNames . SelectMany ( getFallbackNames ) ;
}
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 )
{
2020-10-30 10:14:08 +08:00
var lookupNames = hitSample . LookupNames . SelectMany ( getFallbackNames ) ;
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 ;
}
private IEnumerable < string > getFallbackNames ( string componentName )
{
// May be something like "Gameplay/osu/approachcircle" from lazer, or "Arrows/note1" from a user skin.
yield return componentName ;
2020-07-31 04:07:07 +08:00
2020-10-30 10:14:08 +08:00
// Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle").
2021-06-01 17:00:24 +08:00
yield return componentName . Split ( '/' ) . Last ( ) ;
2020-07-29 05:52:09 +08:00
}
2018-04-13 17:19:50 +08:00
}
}