From 42c9cac72e9de2ace6e99df99434763aa27684ee Mon Sep 17 00:00:00 2001 From: thomeval Date: Wed, 23 Oct 2024 00:09:59 +0000 Subject: [PATCH] Improved solo high scores, basic band high scores (#883) * WIP Implementation of better solo high scores. Solo high scores are now displayed when there is exactly one non-bot player present. High scores are now retrieved per player, song and instrument. Added a new cache for the above when retrieving high scores for the same player + instrument for many songs in a row. Removed instrument icon for high scores on Music Library song list, replaced with a FC indicator sprite (using a placeholder sprite for now). Moved retrieval of solo high scores per song to MusicLibraryMenu. Marked several methods as Obsolete * Reworked Player High Scores due to feedback. Player High Scores now keep track of the best percentage and best score (matches existing behaviour) Added basic implementation for Band High Scores Removed magic number in RecommandedSongs * Update Band High Scores cache when a new Band High Score is set. Fixed Best percentage scores not being loaded. Fixed Best percentage score not being displayed if its different to the best score. * Fixed bug when saving a band high score for the first time. Fixed best percentage not loading when multiple profiles have played the same song+instrument combination. * Added new difficulty icons, and display them in solo high scores. * Removed obsolete method from ScoreContainer Added comments for GetBestPercentageScore() * Update YARG.Core * Resolved issues from PR feedback * Tweaked "Score not saved" message --- Assets/Art/Menu/Common/DifficultyIcons.png | 3 + .../Art/Menu/Common/DifficultyIcons.png.meta | 221 +++++++++ Assets/DifficultySprites.asset | 433 ++++++++++++++++++ Assets/DifficultySprites.asset.meta | 8 + .../Prefabs/Menu/MusicLibrary/SongView.prefab | 2 +- Assets/Script/Gameplay/GameManager.Loading.cs | 6 +- .../Menu/MusicLibrary/MusicLibraryMenu.cs | 41 +- .../Menu/MusicLibrary/RecommendedSongs.cs | 6 +- .../MusicLibrary/ViewTypes/SongViewType.cs | 51 ++- Assets/Script/Player/PlayerContainer.cs | 5 + .../Script/Scores/ScoreContainer.Queries.cs | 28 +- Assets/Script/Scores/ScoreContainer.cs | 219 ++++++--- .../AssetGroups/Default Local Group.asset | 5 + Assets/StreamingAssets/lang/en-US.json | 2 +- 14 files changed, 942 insertions(+), 88 deletions(-) create mode 100644 Assets/Art/Menu/Common/DifficultyIcons.png create mode 100644 Assets/Art/Menu/Common/DifficultyIcons.png.meta create mode 100644 Assets/DifficultySprites.asset create mode 100644 Assets/DifficultySprites.asset.meta diff --git a/Assets/Art/Menu/Common/DifficultyIcons.png b/Assets/Art/Menu/Common/DifficultyIcons.png new file mode 100644 index 0000000000..b46abdea53 --- /dev/null +++ b/Assets/Art/Menu/Common/DifficultyIcons.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbc299d648b9a0d21a5ee3c61e4f349ef58b8c0e6b72afe376cab3b803160e20 +size 475567 diff --git a/Assets/Art/Menu/Common/DifficultyIcons.png.meta b/Assets/Art/Menu/Common/DifficultyIcons.png.meta new file mode 100644 index 0000000000..a529a46bf8 --- /dev/null +++ b/Assets/Art/Menu/Common/DifficultyIcons.png.meta @@ -0,0 +1,221 @@ +fileFormatVersion: 2 +guid: f3ceabe704bcf1745866d8e7135c98ab +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 1 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: + - serializedVersion: 2 + name: Easy + rect: + serializedVersion: 2 + x: 512 + y: 512 + width: 512 + height: 512 + alignment: 0 + pivot: {x: 0.5, y: 0.5} + border: {x: 0, y: 0, z: 0, w: 0} + outline: [] + physicsShape: [] + tessellationDetail: 0 + bones: [] + spriteID: af58aba2d1b4ba74182a6307abb7cc73 + internalID: 383874496 + vertices: [] + indices: + edges: [] + weights: [] + - serializedVersion: 2 + name: Medium + rect: + serializedVersion: 2 + x: 1024 + y: 512 + width: 512 + height: 512 + alignment: 0 + pivot: {x: 0.5, y: 0.5} + border: {x: 0, y: 0, z: 0, w: 0} + outline: [] + physicsShape: [] + tessellationDetail: 0 + bones: [] + spriteID: 4b5ee3b91c9ac6d4fbcd4802d9a3a1c5 + internalID: 703388571 + vertices: [] + indices: + edges: [] + weights: [] + - serializedVersion: 2 + name: Hard + rect: + serializedVersion: 2 + x: 1536 + y: 512 + width: 512 + height: 512 + alignment: 0 + pivot: {x: 0.5, y: 0.5} + border: {x: 0, y: 0, z: 0, w: 0} + outline: [] + physicsShape: [] + tessellationDetail: 0 + bones: [] + spriteID: 10ad9e7596ad6684799a88feeee6b20a + internalID: -118138551 + vertices: [] + indices: + edges: [] + weights: [] + - serializedVersion: 2 + name: Expert + rect: + serializedVersion: 2 + x: 0 + y: 0 + width: 512 + height: 512 + alignment: 0 + pivot: {x: 0.5, y: 0.5} + border: {x: 0, y: 0, z: 0, w: 0} + outline: [] + physicsShape: [] + tessellationDetail: 0 + bones: [] + spriteID: ac6616e21c98afe47b0aaf344646cecf + internalID: -1843738572 + vertices: [] + indices: + edges: [] + weights: [] + - serializedVersion: 2 + name: ExpertPlus + rect: + serializedVersion: 2 + x: 512 + y: 0 + width: 512 + height: 512 + alignment: 0 + pivot: {x: 0.5, y: 0.5} + border: {x: 0, y: 0, z: 0, w: 0} + outline: [] + physicsShape: [] + tessellationDetail: 0 + bones: [] + spriteID: e9a2c8c869f9cea4aa29477e3e5f9e8d + internalID: -25976495 + vertices: [] + indices: + edges: [] + weights: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 6cf8484e4acb04c45a7f933ab24af260 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: + Easy: 383874496 + Expert: -1843738572 + ExpertPlus: -25976495 + Hard: -118138551 + Medium: 703388571 + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/DifficultySprites.asset b/Assets/DifficultySprites.asset new file mode 100644 index 0000000000..769dca19f0 --- /dev/null +++ b/Assets/DifficultySprites.asset @@ -0,0 +1,433 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &-8242456345906921184 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: TextMeshPro/Sprite + m_Shader: {fileID: 4800000, guid: cf81c85f95fe47e1a27f6ae460cf182c, type: 3} + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _MainTex: + m_Texture: {fileID: 2800000, guid: f3ceabe704bcf1745866d8e7135c98ab, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _ColorMask: 15 + - _CullMode: 0 + - _Stencil: 0 + - _StencilComp: 8 + - _StencilOp: 0 + - _StencilReadMask: 255 + - _StencilWriteMask: 255 + - _UseUIAlphaClip: 0 + m_Colors: + - _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767} + - _Color: {r: 1, g: 1, b: 1, a: 1} + m_BuildTextureStacks: [] +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 84a92b25f83d49b9bc132d206b370281, type: 3} + m_Name: DifficultySprites + m_EditorClassIdentifier: + hashCode: -1138371127 + material: {fileID: -8242456345906921184} + materialHashCode: 0 + m_Version: 1.1.0 + m_FaceInfo: + m_FaceIndex: 0 + m_FamilyName: + m_StyleName: + m_PointSize: 0 + m_Scale: 0 + m_UnitsPerEM: 0 + m_LineHeight: 0 + m_AscentLine: 0 + m_CapLine: 0 + m_MeanLine: 0 + m_Baseline: 0 + m_DescentLine: 0 + m_SuperscriptOffset: 0 + m_SuperscriptSize: 0 + m_SubscriptOffset: 0 + m_SubscriptSize: 0 + m_UnderlineOffset: 0 + m_UnderlineThickness: 0 + m_StrikethroughOffset: 0 + m_StrikethroughThickness: 0 + m_TabWidth: 0 + spriteSheet: {fileID: 2800000, guid: f3ceabe704bcf1745866d8e7135c98ab, type: 3} + m_SpriteCharacterTable: + - m_ElementType: 2 + m_Unicode: 65534 + m_GlyphIndex: 9 + m_Scale: 1 + m_Name: Easy + m_HashCode: 2377774 + - m_ElementType: 2 + m_Unicode: 65534 + m_GlyphIndex: 10 + m_Scale: 1 + m_Name: Medium + m_HashCode: -1397837123 + - m_ElementType: 2 + m_Unicode: 65534 + m_GlyphIndex: 11 + m_Scale: 1 + m_Name: Hard + m_HashCode: 2553343 + - m_ElementType: 2 + m_Unicode: 65534 + m_GlyphIndex: 12 + m_Scale: 1 + m_Name: Expert + m_HashCode: -1679542706 + - m_ElementType: 2 + m_Unicode: 65534 + m_GlyphIndex: 13 + m_Scale: 1 + m_Name: ExpertPlus + m_HashCode: -994053164 + m_SpriteGlyphTable: + - m_Index: 0 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 0 + m_Y: 1536 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -1834431651, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 1 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 512 + m_Y: 1536 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: 1445730593, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 2 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 1024 + m_Y: 1536 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -1591131112, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 3 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 1536 + m_Y: 1536 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -1340302279, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 4 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 0 + m_Y: 1024 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: 894865564, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 5 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 512 + m_Y: 1024 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: 1760769726, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 6 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 1024 + m_Y: 1024 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -844217933, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 7 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 1536 + m_Y: 1024 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -1173955509, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 8 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 0 + m_Y: 512 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -212029294, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 9 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 512 + m_Y: 512 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: 1676829369, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 10 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 1024 + m_Y: 512 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -1041038840, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 11 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 1536 + m_Y: 512 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: 1095511443, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 12 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 0 + m_Y: 0 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -413506960, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 13 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 512 + m_Y: 0 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -2104244439, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 14 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: 0 + m_HorizontalBearingY: 375 + m_HorizontalAdvance: 600 + m_GlyphRect: + m_X: 1024 + m_Y: 0 + m_Width: 512 + m_Height: 512 + m_Scale: 1.3 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: 1405555635, guid: 66fae0a679753ea4a9110a183b8d2af9, type: 3} + - m_Index: 15 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: -256 + m_HorizontalBearingY: 256 + m_HorizontalAdvance: 512 + m_GlyphRect: + m_X: 512 + m_Y: 512 + m_Width: 512 + m_Height: 512 + m_Scale: 1 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: 383874496, guid: f3ceabe704bcf1745866d8e7135c98ab, type: 3} + - m_Index: 16 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: -256 + m_HorizontalBearingY: 256 + m_HorizontalAdvance: 512 + m_GlyphRect: + m_X: 1024 + m_Y: 512 + m_Width: 512 + m_Height: 512 + m_Scale: 1 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: 703388571, guid: f3ceabe704bcf1745866d8e7135c98ab, type: 3} + - m_Index: 17 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: -256 + m_HorizontalBearingY: 256 + m_HorizontalAdvance: 512 + m_GlyphRect: + m_X: 1536 + m_Y: 512 + m_Width: 512 + m_Height: 512 + m_Scale: 1 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -118138551, guid: f3ceabe704bcf1745866d8e7135c98ab, type: 3} + - m_Index: 18 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: -256 + m_HorizontalBearingY: 256 + m_HorizontalAdvance: 512 + m_GlyphRect: + m_X: 0 + m_Y: 0 + m_Width: 512 + m_Height: 512 + m_Scale: 1 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -1843738572, guid: f3ceabe704bcf1745866d8e7135c98ab, type: 3} + - m_Index: 19 + m_Metrics: + m_Width: 512 + m_Height: 512 + m_HorizontalBearingX: -256 + m_HorizontalBearingY: 256 + m_HorizontalAdvance: 512 + m_GlyphRect: + m_X: 512 + m_Y: 0 + m_Width: 512 + m_Height: 512 + m_Scale: 1 + m_AtlasIndex: 0 + m_ClassDefinitionType: 0 + sprite: {fileID: -25976495, guid: f3ceabe704bcf1745866d8e7135c98ab, type: 3} + spriteInfoList: [] + fallbackSpriteAssets: [] diff --git a/Assets/DifficultySprites.asset.meta b/Assets/DifficultySprites.asset.meta new file mode 100644 index 0000000000..edfd809905 --- /dev/null +++ b/Assets/DifficultySprites.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4fe54f44a04e7364db2baafd68cda60c +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefabs/Menu/MusicLibrary/SongView.prefab b/Assets/Prefabs/Menu/MusicLibrary/SongView.prefab index 0195b9e60c..ff8534f3ca 100644 --- a/Assets/Prefabs/Menu/MusicLibrary/SongView.prefab +++ b/Assets/Prefabs/Menu/MusicLibrary/SongView.prefab @@ -774,7 +774,7 @@ MonoBehaviour: bottomLeft: {r: 1, g: 1, b: 1, a: 1} bottomRight: {r: 1, g: 1, b: 1, a: 1} m_fontColorGradientPreset: {fileID: 0} - m_spriteAsset: {fileID: 0} + m_spriteAsset: {fileID: 11400000, guid: 4fe54f44a04e7364db2baafd68cda60c, type: 2} m_tintAllSprites: 0 m_StyleSheet: {fileID: 0} m_TextStyleHashCode: -1183493901 diff --git a/Assets/Script/Gameplay/GameManager.Loading.cs b/Assets/Script/Gameplay/GameManager.Loading.cs index b5cda4504a..94a02c5047 100644 --- a/Assets/Script/Gameplay/GameManager.Loading.cs +++ b/Assets/Script/Gameplay/GameManager.Loading.cs @@ -342,9 +342,9 @@ private void CreatePlayers() player.RefreshPresets(); } - var lastHighScore = ScoreContainer - .GetHighScoreByInstrument(Song.Hash, player.Profile.CurrentInstrument)? - .Score; + var lastHighScore = ScoreContainer.GetHighScore(Song.Hash, player.Profile.Id, player.Profile.CurrentInstrument, false)?.Score; + YargLogger.LogFormatInfo("Current high score for player {0} on {1}: {2}", + player.Profile.Name, player.Profile.CurrentInstrument, lastHighScore ?? 0); if (player.Profile.GameMode != GameMode.Vocals) { diff --git a/Assets/Script/Menu/MusicLibrary/MusicLibraryMenu.cs b/Assets/Script/Menu/MusicLibrary/MusicLibraryMenu.cs index 9d97504ba8..62935fb4a9 100644 --- a/Assets/Script/Menu/MusicLibrary/MusicLibraryMenu.cs +++ b/Assets/Script/Menu/MusicLibrary/MusicLibraryMenu.cs @@ -12,6 +12,7 @@ using YARG.Menu.Navigation; using YARG.Player; using YARG.Playlists; +using YARG.Scores; using YARG.Settings; using YARG.Song; using static YARG.Menu.Navigation.Navigator; @@ -223,6 +224,8 @@ protected override List CreateViewList() return viewList; } + private bool ShouldDisplaySoloHighScores => PlayerContainer.Players.Count(e => !e.Profile.IsBot) == 1; + private List CreateNormalViewList() { var list = new List(); @@ -275,7 +278,7 @@ private List CreateNormalViewList() foreach (var song in _recommendedSongs) { - list.Add(new SongViewType(this, song)); + list.Add(new SongViewType(this, song, GetHighScoreForSong(song), GetBandHighScoreForSong(song))); } _primaryHeaderIndex += _recommendedSongs.Length + 1; } @@ -313,13 +316,43 @@ private List CreateNormalViewList() foreach (var song in section.Songs) { - list.Add(new SongViewType(this, song)); + list.Add(new SongViewType(this, song, GetHighScoreForSong(song), GetBandHighScoreForSong(song))); } } CalculateCategoryHeaderIndices(list); return list; } + private PlayerScoreRecord GetHighScoreForSong(SongEntry song) + { + if (!ShouldDisplaySoloHighScores) + { + return null; + } + + var player = PlayerContainer.Players.First(e => !e.Profile.IsBot); + var result = ScoreContainer.GetHighScore(song.Hash, player.Profile.Id, player.Profile.CurrentInstrument, true); + var percResult = ScoreContainer.GetBestPercentageScore(song.Hash, player.Profile.Id, player.Profile.CurrentInstrument, true); + + if (result != null) + { + Debug.Assert(percResult != null, "Best Percentage score is missing!"); + result.Percent = percResult?.GetPercent() ?? 0; + } + + return result; + } + + private GameRecord GetBandHighScoreForSong(SongEntry song) + { + if (ShouldDisplaySoloHighScores) + { + return null; + } + + return ScoreContainer.GetBandHighScore(song.Hash); + } + private List CreatePlaylistViewList() { var list = new List @@ -339,7 +372,7 @@ private List CreatePlaylistViewList() list.Add(new SortHeaderViewType(section.Category.ToUpperInvariant(), section.Songs.Length, section.CategoryGroup)); foreach (var song in section.Songs) { - list.Add(new SongViewType(this, song)); + list.Add(new SongViewType(this, song, GetHighScoreForSong(song), GetBandHighScoreForSong(song))); } } @@ -387,7 +420,7 @@ private void CalculateCategoryHeaderIndices(List list) private void SetRecommendedSongs() { - if (SongContainer.Count > 5) + if (SongContainer.Count > RecommendedSongs.RECOMMEND_SONGS_COUNT) { _recommendedSongs = RecommendedSongs.GetRecommendedSongs(); } diff --git a/Assets/Script/Menu/MusicLibrary/RecommendedSongs.cs b/Assets/Script/Menu/MusicLibrary/RecommendedSongs.cs index 256b0bc4d8..59fe2a541b 100644 --- a/Assets/Script/Menu/MusicLibrary/RecommendedSongs.cs +++ b/Assets/Script/Menu/MusicLibrary/RecommendedSongs.cs @@ -10,9 +10,11 @@ namespace YARG.Menu.MusicLibrary { public static class RecommendedSongs { + public const int RECOMMEND_SONGS_COUNT = 5; + public static SongEntry[] GetRecommendedSongs() { - var songs = new SongEntry[5]; + var songs = new SongEntry[RECOMMEND_SONGS_COUNT]; int index = 0; AddMostPlayedSongs(songs, ref index); AddRandomSongs(songs, ref index); @@ -45,7 +47,7 @@ private static void AddRandomSongs(SongEntry[] songs, ref int index) SongContainer.Sources.TryGetValue(_YARGSOURCE, out var yargSongs); float yargSongRNG = yargSongs != null ? STARTING_RNG : 0; - while (index < 5) + while (index < RECOMMEND_SONGS_COUNT) { SongEntry song; if (Random.value <= yargSongRNG) diff --git a/Assets/Script/Menu/MusicLibrary/ViewTypes/SongViewType.cs b/Assets/Script/Menu/MusicLibrary/ViewTypes/SongViewType.cs index 1036cbb0c2..e7a4406d5d 100644 --- a/Assets/Script/Menu/MusicLibrary/ViewTypes/SongViewType.cs +++ b/Assets/Script/Menu/MusicLibrary/ViewTypes/SongViewType.cs @@ -1,9 +1,7 @@ using Cysharp.Text; -using Cysharp.Threading.Tasks; using UnityEngine; using YARG.Core.Game; using YARG.Core.Song; -using YARG.Helpers.Extensions; using YARG.Player; using YARG.Playlists; using YARG.Scores; @@ -27,11 +25,15 @@ public class SongViewType : ViewType private readonly MusicLibraryMenu _musicLibrary; public readonly SongEntry SongEntry; + public readonly PlayerScoreRecord PlayerScoreRecord; + public readonly GameRecord BandScoreRecord; - public SongViewType(MusicLibraryMenu musicLibrary, SongEntry songEntry) + public SongViewType(MusicLibraryMenu musicLibrary, SongEntry songEntry, PlayerScoreRecord playerScoreRecord, GameRecord bandScoreRecord) { _musicLibrary = musicLibrary; SongEntry = songEntry; + PlayerScoreRecord = playerScoreRecord; + BandScoreRecord = bandScoreRecord; } public override string GetPrimaryText(bool selected) @@ -53,28 +55,36 @@ public override string GetSecondaryText(bool selected) public override string GetSideText(bool selected) { - var score = ScoreContainer.GetHighScore(SongEntry.Hash); - var bestPctScore = ScoreContainer.GetBestPercentageScore(SongEntry.Hash); + using var builder = ZString.CreateStringBuilder(); + + if (BandScoreRecord != null) + { + // Append the band score if the setting is enabled + if (SettingsManager.Settings.HighScoreInfo.Value == HighScoreInfoMode.Score) + { + builder.AppendFormat(" {0:N0}", BandScoreRecord.BandScore); + } + + return builder.ToString(); + } // Never played! - if (score is null) + if (PlayerScoreRecord is null) { return string.Empty; } - var instrument = score.Instrument.ToResourceName(); - var difficultyChar = score.Difficulty.ToChar(); - var percent = Mathf.Floor(bestPctScore.GetPercent() * 100f); + var difficultySprite = PlayerScoreRecord.Difficulty.ToString(); + var percent = Mathf.Floor(PlayerScoreRecord.GetPercent() * 100f); + var percentColor = PlayerScoreRecord.IsFc ? "#fcd13c" : "#ffffff"; - using var builder = ZString.CreateStringBuilder(); - builder.AppendFormat(" {1} {2:N0}%", - instrument, difficultyChar, percent); + builder.AppendFormat( " {2:N0}%", difficultySprite, percentColor, percent); // Append the score if the setting is enabled if (SettingsManager.Settings.HighScoreInfo.Value == HighScoreInfoMode.Score) { - builder.AppendFormat(" {0:N0}", score.Score); - } + builder.AppendFormat(" {0:N0}", PlayerScoreRecord.Score); + } return builder.ToString(); } @@ -87,8 +97,12 @@ public override string GetSideText(bool selected) return null; } - var score = ScoreContainer.GetHighScore(SongEntry.Hash); - return score?.Stars; + if (BandScoreRecord != null) + { + return BandScoreRecord.BandStars; + } + + return PlayerScoreRecord?.Stars; } public override FavoriteInfo GetFavoriteInfo() @@ -110,7 +124,10 @@ public override void PrimaryButtonClick() { base.PrimaryButtonClick(); - if (PlayerContainer.Players.Count <= 0) return; + if (PlayerContainer.Players.Count <= 0) + { + return; + } GlobalVariables.State.CurrentSong = SongEntry; MenuManager.Instance.PushMenu(MenuManager.Menu.DifficultySelect); diff --git a/Assets/Script/Player/PlayerContainer.cs b/Assets/Script/Player/PlayerContainer.cs index 8449057ea6..4ea4266978 100644 --- a/Assets/Script/Player/PlayerContainer.cs +++ b/Assets/Script/Player/PlayerContainer.cs @@ -100,6 +100,11 @@ public static bool IsProfileTaken(YargProfile profile) return _playersByProfile.ContainsKey(profile); } + public static bool HasBotsActive() + { + return _players.Exists(i => i.Profile.IsBot); + } + public static YargPlayer CreatePlayerFromProfile(YargProfile profile, bool resolveDevices) { if (!_profiles.Contains(profile)) diff --git a/Assets/Script/Scores/ScoreContainer.Queries.cs b/Assets/Script/Scores/ScoreContainer.Queries.cs index 62ba27e552..783d08ac10 100644 --- a/Assets/Script/Scores/ScoreContainer.Queries.cs +++ b/Assets/Script/Scores/ScoreContainer.Queries.cs @@ -8,8 +8,19 @@ public static partial class ScoreContainer private const string QUERY_HIGH_SCORES = @" SELECT *, MAX(Score) FROM PlayerScores INNER JOIN GameRecords ON PlayerScores.GameRecordId = GameRecords.Id + WHERE PlayerId = ? AND Instrument = ? GROUP BY GameRecords.SongChecksum"; + private const string QUERY_SINGLE_PLAYER_HIGH_SCORE = @" + SELECT * + FROM PlayerScores + INNER JOIN GameRecords ON PlayerScores.GameRecordId = GameRecords.Id + WHERE GameRecords.SongChecksum = ? + AND PlayerScores.Instrument = ? + AND PlayerScores.PlayerId = ? + ORDER BY Score DESC + LIMIT 1"; + private const string QUERY_UPDATE_NULL_PERCENTS = @" UPDATE PlayerScores SET Percent = cast(NotesHit as REAL) / (NotesHit + NotesMissed) @@ -20,18 +31,27 @@ UPDATE PlayerScores /* For each song, this retrieves the records with the best score */ BestScore as (SELECT *, max(Score) FROM PlayerScores INNER JOIN GameRecords ON PlayerScores.GameRecordId = GameRecords.Id - GROUP BY GameRecords.SongChecksum), + GROUP BY GameRecords.SongChecksum, PlayerScores.Instrument, PlayerScores.PlayerId), /* For each song, instrument, and difficulty, this retrieves the records with the best score by percent */ BestPercents as (SELECT *, max(Percent) FROM PlayerScores INNER JOIN GameRecords ON PlayerScores.GameRecordId = GameRecords.Id - GROUP BY GameRecords.SongChecksum, PlayerScores.Instrument, PlayerScores.Difficulty) + GROUP BY GameRecords.SongChecksum, PlayerScores.Instrument, PlayerScores.PlayerId, PlayerScores.Difficulty) /* Filter the lines from the BestPercents temporary table above where the instrument and difficulty match the instrument and difficulty when the highest score was recorded */ SELECT BestPercents.* - FROM BestPercents INNER JOIN BestScore ON BestScore.Instrument = BestPercents.Instrument + FROM BestPercents + INNER JOIN BestScore ON BestScore.Instrument = BestPercents.Instrument AND BestScore.Difficulty = BestPercents.Difficulty - AND BestScore.SongChecksum = BestPercents.SongChecksum"; + AND BestScore.SongChecksum = BestPercents.SongChecksum + AND BestScore.PlayerId = BestPercents.PlayerId + WHERE BestScore.PlayerId = ? AND BestScore.Instrument = ?; + "; + + private const string QUERY_BAND_BEST_SCORES = @" + SELECT *, MAX(BandScore) FROM GameRecords + GROUP BY GameRecords.SongChecksum + "; } } \ No newline at end of file diff --git a/Assets/Script/Scores/ScoreContainer.cs b/Assets/Script/Scores/ScoreContainer.cs index 02b86399f5..05a4398741 100644 --- a/Assets/Script/Scores/ScoreContainer.cs +++ b/Assets/Script/Scores/ScoreContainer.cs @@ -15,15 +15,19 @@ namespace YARG.Scores { public static partial class ScoreContainer { - public static string ScoreDirectory { get; private set; } + public static string ScoreDirectory { get; private set; } public static string ScoreReplayDirectory { get; private set; } private static string _scoreDatabaseFile; private static SQLiteConnection _db; - private static readonly Dictionary SongHighScores = new(); - private static readonly Dictionary SongHighScoresByPct = new(); + private static readonly Dictionary PlayerHighScores = new(); + private static readonly Dictionary PlayerHighScoresByPct = new(); + private static readonly Dictionary BandHighScores = new(); + + private static Instrument _currentInstrument = Instrument.Band; + private static Guid _currentPlayerId; public static void Init() { @@ -41,7 +45,7 @@ public static void Init() _db = new SQLiteConnection(_scoreDatabaseFile); InitDatabase(); UpdateNullPercents(); - FetchHighScores(); + FetchBandHighScores(); } catch (Exception e) { @@ -49,6 +53,24 @@ public static void Init() } } + private static void FetchBandHighScores() + { + try + { + BandHighScores.Clear(); + var result = _db.Query(QUERY_BAND_BEST_SCORES); + + foreach (var record in result) + { + BandHighScores.Add(HashWrapper.Create(record.SongChecksum), record); + } + } + catch (Exception e) + { + YargLogger.LogException(e, "Failed to load all GameRecords from database."); + } + } + private static void InitDatabase() { _db.CreateTable(); @@ -108,34 +130,14 @@ public static void RecordScore(GameRecord gameRecord, List pl var songChecksum = HashWrapper.Create(gameRecord.SongChecksum); - var bestScore = playerEntries.Find(p => p.Score == playerEntries.Max(x => x.Score)); - - if (SongHighScores.TryGetValue(songChecksum, out var highScore)) - { - if (bestScore.Score > highScore.Score) - { - SongHighScores[songChecksum] = bestScore; - - if (bestScore.Instrument != highScore.Instrument || bestScore.Difficulty != highScore.Difficulty) - { - SongHighScoresByPct[songChecksum] = bestScore; - } - } - - if (bestScore.Instrument == highScore.Instrument && bestScore.Difficulty == highScore.Difficulty) - { - if (bestScore.GetPercent() > highScore.GetPercent()) - { - SongHighScoresByPct[songChecksum] = bestScore; - } - } - } - else + if (playerEntries.Count == 1) { - SongHighScores.Add(songChecksum, bestScore); - SongHighScoresByPct.Add(songChecksum, bestScore); + // Update the cached player high scores. Only relevant if there is a single human player. + UpdatePlayerHighScores(songChecksum, playerEntries.First()); } + UpdateBandHighScore(songChecksum, gameRecord); + YargLogger.LogInfo("Recorded high score for song."); } catch (Exception e) @@ -144,6 +146,49 @@ public static void RecordScore(GameRecord gameRecord, List pl } } + private static void UpdateBandHighScore(HashWrapper songChecksum, GameRecord currentBandScore) + { + if (!BandHighScores.TryGetValue(songChecksum, out var currentBest)) + { + BandHighScores.Add(songChecksum, currentBandScore); + return; + } + if (currentBest.BandScore >= currentBandScore.BandScore) + { + return; + } + + BandHighScores[songChecksum] = currentBandScore; + } + + private static void UpdatePlayerHighScores(HashWrapper songChecksum, PlayerScoreRecord newScore) + { + PlayerHighScoresByPct.TryGetValue(songChecksum, out var currentBestPct); + if (!PlayerHighScores.TryGetValue(songChecksum, out var currentBest)) + { + PlayerHighScores.Add(songChecksum, newScore); + PlayerHighScoresByPct.Add(songChecksum, newScore); + return; + } + + if (newScore.Score > currentBest.Score) + { + PlayerHighScores[songChecksum] = newScore; + + // If there's a new best score on a different difficulty, the old best percentage entry is irrelevant. Use the new score as the new best percentage entry. + if (newScore.Difficulty != currentBest.Difficulty || newScore.GetPercent() > currentBest.GetPercent()) + { + PlayerHighScoresByPct[songChecksum] = newScore; + } + } + + // Update the best percentage entry if appropriate. + if (newScore.Difficulty == currentBestPct.Difficulty || newScore.GetPercent() > currentBest.GetPercent()) + { + PlayerHighScoresByPct[songChecksum] = newScore; + } + } + public static void RecordPlayerInfo(Guid id, string name) { var query = $"SELECT * FROM Players WHERE Id = '{id}'"; @@ -192,14 +237,81 @@ public static List GetAllPlayerScores(Guid id) return null; } - public static PlayerScoreRecord GetHighScore(HashWrapper songChecksum) + /// + /// Get the highest score (in points) for a specific song, player and instrument. + /// If `allowCacheUpdate` is true, all high scores for the player and instrument will be fetched from the database and cached, to speed up subsequent player high score requests. + /// + /// The ID of the song to retrieve a high score for. + /// The ID of the player profile to retrieve a high score for. + /// The instrument to retrieve a high score for. + /// Sets whether all high scores for this player and instrument should be cached. Set this to true when fetching a large number of high scores for the same player and instrument + /// (such as when displaying high scores on the Music Library). Set this to false when fetching multiple high scores for different players in a row. + /// The highest score for the provided song, player and instrument, or null if no high score exists. + public static PlayerScoreRecord GetHighScore(HashWrapper songChecksum, Guid playerId, Instrument instrument, bool allowCacheUpdate = true) { - return SongHighScores?.GetValueOrDefault(songChecksum); + if (allowCacheUpdate) + { + FetchHighScores(playerId, instrument); + } + + if (_currentInstrument == instrument && _currentPlayerId == playerId) + { + return PlayerHighScores.GetValueOrDefault(songChecksum); + } + + return GetHighScoreFromDatabase(songChecksum, playerId, instrument); + } + + public static GameRecord GetBandHighScore(HashWrapper songChecksum) + { + if (BandHighScores.TryGetValue(songChecksum, out var record)) + { + return record; + } + + return null; + } + + private static PlayerScoreRecord GetHighScoreFromDatabase(HashWrapper songChecksum, Guid playerId, Instrument instrument) + { + var query = QUERY_SINGLE_PLAYER_HIGH_SCORE; + + try + { + return _db.FindWithQuery(query, songChecksum.ToString(), playerId, (int) instrument); + } + catch (Exception e) + { + YargLogger.LogException(e, $"Failed to load high score from database for player with ID {playerId}."); + return null; + } } - public static PlayerScoreRecord GetBestPercentageScore(HashWrapper songChecksum) + /// + /// Get the highest score percentage (regardless of points) for a specific song, player and instrument. + /// Note that this can be different from the highest score in points, as it is possible to get a higher percentage with a lower score. + /// If `allowCacheUpdate` is true, all high scores for the player and instrument will be fetched from the database and cached, to speed up subsequent player high score requests. + /// + /// The ID of the song to retrieve a high score for. + /// The ID of the player profile to retrieve a high score for. + /// The instrument to retrieve a high score for. + /// Sets whether all high scores for this player and instrument should be cached. Set this to true when fetching a large number of high scores for the same player and instrument + /// (such as when displaying high scores on the Music Library). Set this to false when fetching multiple high scores for different players in a row. + /// The highest score percentage for the provided song, player and instrument, or null if no high score exists. + public static PlayerScoreRecord GetBestPercentageScore(HashWrapper songChecksum, Guid playerId, Instrument instrument, bool allowCacheUpdate = true) { - return SongHighScoresByPct?.GetValueOrDefault(songChecksum); + if (allowCacheUpdate) + { + FetchHighScores(playerId, instrument); + } + + if (_currentInstrument == instrument && _currentPlayerId == playerId) + { + return PlayerHighScoresByPct.GetValueOrDefault(songChecksum); + } + + // TODO: Fetch best % from database if there's a cache miss. Not currently used. + throw new NotImplementedException("Attempted to load best solo score percentage from database. Not implemented."); } public static void UpdateNullPercents() @@ -218,12 +330,22 @@ public static void UpdateNullPercents() } } - public static void FetchHighScores() + + private static void FetchHighScores(Guid playerId, Instrument instrument) { + if (_currentPlayerId == playerId && _currentInstrument == instrument && PlayerHighScores.Any()) + { + // Already cached. No need to fetch again from the database. + return; + } + try { - var scoreResults = _db.Query(QUERY_HIGH_SCORES); - var songResults = _db.Query(QUERY_SONGS); + PlayerHighScores.Clear(); + PlayerHighScoresByPct.Clear(); + + var scoreResults = _db.Query(QUERY_HIGH_SCORES, playerId, (int) instrument); + var songResults = _db.Query(QUERY_SONGS, playerId, (int) instrument); foreach (var score in scoreResults) { var song = songResults.FirstOrDefault(x => x.Id == score.GameRecordId); @@ -232,10 +354,10 @@ public static void FetchHighScores() continue; } - SongHighScores.Add(HashWrapper.Create(song.SongChecksum), score); + PlayerHighScores.Add(HashWrapper.Create(song.SongChecksum), score); } - var scoreResultsByPct = _db.Query(QUERY_BEST_SCORES_BY_PERCENT); + var scoreResultsByPct = _db.Query(QUERY_BEST_SCORES_BY_PERCENT, playerId, (int) instrument); foreach (var score in scoreResultsByPct) { var song = songResults.FirstOrDefault(x => x.Id == score.GameRecordId); @@ -244,31 +366,16 @@ public static void FetchHighScores() continue; } - SongHighScoresByPct.Add(HashWrapper.Create(song.SongChecksum), score); + PlayerHighScoresByPct.Add(HashWrapper.Create(song.SongChecksum), score); } - } - catch (Exception e) - { - YargLogger.LogException(e, "Failed to load high score from database."); - } - } - public static PlayerScoreRecord GetHighScoreByInstrument(HashWrapper songChecksum, Instrument instrument) - { - try - { - var query = - $"SELECT * FROM PlayerScores INNER JOIN GameRecords ON PlayerScores.GameRecordId = GameRecords.Id WHERE " + - $"GameRecords.SongChecksum = x'{songChecksum.ToString()}' AND PlayerScores.Instrument = {(int) instrument} " + - $"ORDER BY Score DESC LIMIT 1"; - return _db.FindWithQuery(query); + _currentInstrument = instrument; + _currentPlayerId = playerId; } catch (Exception e) { YargLogger.LogException(e, "Failed to load high score from database."); } - - return null; } public static List GetMostPlayedSongs(int maxCount) diff --git a/Assets/Settings/AddressableAssetsData/AssetGroups/Default Local Group.asset b/Assets/Settings/AddressableAssetsData/AssetGroups/Default Local Group.asset index 1f46c38ca9..433c0f3fc9 100644 --- a/Assets/Settings/AddressableAssetsData/AssetGroups/Default Local Group.asset +++ b/Assets/Settings/AddressableAssetsData/AssetGroups/Default Local Group.asset @@ -212,6 +212,11 @@ MonoBehaviour: m_ReadOnly: 0 m_SerializedLabels: [] FlaggedDuringContentUpdateRestriction: 0 + - m_GUID: f3ceabe704bcf1745866d8e7135c98ab + m_Address: DifficultyIcons + m_ReadOnly: 0 + m_SerializedLabels: [] + FlaggedDuringContentUpdateRestriction: 0 m_ReadOnly: 0 m_Settings: {fileID: 11400000, guid: e05274e5aee7ff54bb550557e8a635f2, type: 2} m_SchemaSet: diff --git a/Assets/StreamingAssets/lang/en-US.json b/Assets/StreamingAssets/lang/en-US.json index 54f1c2c4e2..652bac76e4 100644 --- a/Assets/StreamingAssets/lang/en-US.json +++ b/Assets/StreamingAssets/lang/en-US.json @@ -288,7 +288,7 @@ "Players" : "Players" }, "ScoreScreen" : { - "BandScoreNotSaved" : "(Band score not saved)" + "BandScoreNotSaved" : "(Score not saved)" }, "Settings": { "NoFolder": "No Folder",