加入用户最近100场战绩记录并实现展示

This commit is contained in:
FloatGaming
2026-03-16 23:18:58 +08:00
parent ec42f2e34b
commit f5c6f143c0
106 changed files with 12120 additions and 642 deletions
@@ -1,5 +1,12 @@
fileFormatVersion: 2
guid: 931f6455943555649a61ed7f1c76cbf6
AssetOrigin:
serializedVersion: 1
productId: 135898
packageName: Airy UI 2 - Easy UI Animation
packageVersion: 2.0.2
assetPath: Assets/Airy UI/Sprites/Sprites/Square Button Violet.png
uploadId: 760159
TextureImporter:
internalIDToNameTable:
- first:
@@ -46,13 +53,13 @@ TextureImporter:
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteBorder: {x: 18, y: 18, z: 18, w: 18}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
@@ -160,7 +167,7 @@ TextureImporter:
width: 138
height: 149
alignment: 0
pivot: {x: 0, y: 0}
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
@@ -177,8 +184,8 @@ TextureImporter:
customData:
physicsShape: []
bones: []
spriteID:
internalID: 0
spriteID: 5e97eb03825dee720800000000000000
internalID: 1537655665
vertices: []
indices:
edges: []
@@ -193,10 +200,3 @@ TextureImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 135898
packageName: Airy UI 2 - Easy UI Animation
packageVersion: 2.0.2
assetPath: Assets/Airy UI/Sprites/Sprites/Square Button Violet.png
uploadId: 760159
@@ -0,0 +1,22 @@
using System;
using MCPForUnity.Editor.Helpers;
using Newtonsoft.Json.Linq;
namespace MCPForUnity.Editor.Tools
{
[McpForUnityTool("get_tool_states")]
public static class GetToolStates
{
public static object HandleCommand(JObject @params)
{
var empty = Array.Empty<object>();
return new SuccessResponse(
"Tool states are not tracked on the Unity side.",
new
{
toolStates = empty,
states = empty
});
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2b1d95ff0b9b68840b934b5f7c1b0aca
@@ -0,0 +1 @@
{"TestSuite":"","Date":0,"Player":{"Development":false,"ScreenWidth":0,"ScreenHeight":0,"ScreenRefreshRate":0,"Fullscreen":false,"Vsync":0,"AntiAliasing":0,"Batchmode":false,"RenderThreadingMode":"GraphicsJobs","GpuSkinning":false,"Platform":"","ColorSpace":"","AnisotropicFiltering":"","BlendWeights":"","GraphicsApi":"","ScriptingBackend":"Mono2x","AndroidTargetSdkVersion":"AndroidApiLevelAuto","AndroidBuildSystem":"Gradle","BuildTarget":"StandaloneWindows64","StereoRenderingPath":"MultiPass"},"Hardware":{"OperatingSystem":"","DeviceModel":"","DeviceName":"","ProcessorType":"","ProcessorCount":0,"GraphicsDeviceName":"","SystemMemorySizeMB":0},"Editor":{"Version":"6000.0.49f1","Branch":"6000.0/staging","Changeset":"840e0a9776d9","Date":1746604669},"Dependencies":["com.unity.addressables@2.4.6","com.unity.collab-proxy@2.7.1","com.unity.feature.2d@2.0.1","com.unity.ide.rider@3.0.34","com.unity.ide.visualstudio@2.0.23","com.unity.inputsystem@1.11.2","com.unity.nuget.newtonsoft-json@3.2.1","com.unity.render-pipelines.universal@17.0.4","com.unity.test-framework@1.5.1","com.unity.textmeshpro@5.0.0","com.unity.timeline@1.7.6","com.unity.ugui@2.0.0","com.unity.modules.ai@1.0.0","com.unity.modules.androidjni@1.0.0","com.unity.modules.animation@1.0.0","com.unity.modules.assetbundle@1.0.0","com.unity.modules.audio@1.0.0","com.unity.modules.director@1.0.0","com.unity.modules.imageconversion@1.0.0","com.unity.modules.imgui@1.0.0","com.unity.modules.jsonserialize@1.0.0","com.unity.modules.particlesystem@1.0.0","com.unity.modules.physics@1.0.0","com.unity.modules.physics2d@1.0.0","com.unity.modules.screencapture@1.0.0","com.unity.modules.terrain@1.0.0","com.unity.modules.terrainphysics@1.0.0","com.unity.modules.tilemap@1.0.0","com.unity.modules.ui@1.0.0","com.unity.modules.uielements@1.0.0","com.unity.modules.umbra@1.0.0","com.unity.modules.unityanalytics@1.0.0","com.unity.modules.unitywebrequest@1.0.0","com.unity.modules.unitywebrequestassetbundle@1.0.0","com.unity.modules.unitywebrequestaudio@1.0.0","com.unity.modules.unitywebrequesttexture@1.0.0","com.unity.modules.unitywebrequestwww@1.0.0","com.unity.modules.vehicles@1.0.0","com.unity.modules.video@1.0.0","com.unity.modules.vr@1.0.0","com.unity.modules.wind@1.0.0","com.unity.modules.xr@1.0.0","com.esotericsoftware.spine.timeline@4.3.20","com.esotericsoftware.spine.urp-shaders@4.3.25","com.unity.modules.subsystems@1.0.0","com.unity.modules.hierarchycore@1.0.0","com.unity.ext.nunit@2.0.5","com.unity.render-pipelines.core@17.0.4","com.unity.shadergraph@17.0.4","com.unity.render-pipelines.universal-config@17.0.3","com.unity.2d.animation@10.1.4","com.unity.2d.pixel-perfect@5.0.3","com.unity.2d.psdimporter@9.0.3","com.unity.2d.sprite@1.0.0","com.unity.2d.spriteshape@10.0.7","com.unity.2d.tilemap@1.0.0","com.unity.2d.tilemap.extras@4.1.0","com.unity.2d.aseprite@1.1.9","com.unity.profiling.core@1.0.2","com.unity.scriptablebuildpipeline@2.3.8","com.unity.searcher@4.9.3","com.unity.burst@1.8.21","com.unity.mathematics@1.3.2","com.unity.collections@2.5.1","com.unity.rendering.light-transport@1.0.1","com.unity.2d.common@9.0.7","com.unity.nuget.mono-cecil@1.11.4","com.unity.test-framework.performance@3.1.0"],"Results":[]}
@@ -1,8 +1,7 @@
fileFormatVersion: 2
guid: 47734c47c246ac641a0f9740f760dc30
NativeFormatImporter:
guid: 6fc567e65fed39341a6019f7397a49e4
TextScriptImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1 @@
{"MeasurementCount":-1}
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 324d0b1f8cae7764f8355f60f935af49
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -13,18 +13,17 @@ MonoBehaviour:
m_Name: playerData_uniqueData
m_EditorClassIdentifier:
player_currentEXP: 0
player_currentLevel: 0
player_coins: 1897049
player_coins: 1892049
player_material: 0
emptyExpBottle78000: 0
commonExpBottle78001: 38
mediumExpBottle78002: 1003
superiorExpBottle78003: 0
supremeExpBottle78004: 0
extraordinaryExpBottle78005: 0
celestialExpBottle78006: 0
uRankingScore: 0
dushMaterial78021: 0
dushMaterial78022: 0
dushMaterial78023: 0
dushMaterial78024: 0
firstLaunchDate:
firstLaunchDate: 2026-03-16
@@ -15,6 +15,7 @@ MonoBehaviour:
ally_heroName: "\u9065\u96EA\u97F3"
ally_heroDesignation: ' '
ally_heroID: 30201
isUnlocked: 1
ally_heroImage: {fileID: 21300000, guid: 991800b03b1fd7f44ad3b3e8eb63d7e0, type: 3}
ally_heroProfile: {fileID: 21300000, guid: 991800b03b1fd7f44ad3b3e8eb63d7e0, type: 3}
ally_heroSelectIcon: {fileID: 21300000, guid: a993087d325aff74383e7a39ed17df56, type: 3}
@@ -15,6 +15,7 @@ MonoBehaviour:
ally_heroName: "\u8DC3\u6843"
ally_heroDesignation: ' '
ally_heroID: 30202
isUnlocked: 1
ally_heroImage: {fileID: 21300000, guid: 5f93d010e49316d4594c1065ce5c6d85, type: 3}
ally_heroProfile: {fileID: 21300000, guid: 5f93d010e49316d4594c1065ce5c6d85, type: 3}
ally_heroSelectIcon: {fileID: 21300000, guid: a1dae3698f08f314fb44b43933d63665, type: 3}
@@ -15,6 +15,7 @@ MonoBehaviour:
ally_heroName: "\u58A8\u5F69\u79BB"
ally_heroDesignation: ' '
ally_heroID: 30203
isUnlocked: 1
ally_heroImage: {fileID: 21300000, guid: 893e9ceec19c4cc4d9d0326a6a48e2bd, type: 3}
ally_heroProfile: {fileID: 21300000, guid: 893e9ceec19c4cc4d9d0326a6a48e2bd, type: 3}
ally_heroSelectIcon: {fileID: 21300000, guid: c42ce7fbbee4c9b44abfba00300ed031, type: 3}
@@ -15,6 +15,7 @@ MonoBehaviour:
ally_heroName: "\u6D1B\u514B"
ally_heroDesignation: ' '
ally_heroID: 30204
isUnlocked: 1
ally_heroImage: {fileID: 21300000, guid: 76c21cd5ddf49f14286f14aae71c0053, type: 3}
ally_heroProfile: {fileID: 21300000, guid: 76c21cd5ddf49f14286f14aae71c0053, type: 3}
ally_heroSelectIcon: {fileID: 21300000, guid: 5780fe6bc6ba1d84ba49fe8a7e2a58fb, type: 3}
@@ -15,6 +15,7 @@ MonoBehaviour:
ally_heroName: "\u6E29\u59AE"
ally_heroDesignation: ' '
ally_heroID: 30205
isUnlocked: 1
ally_heroImage: {fileID: 21300000, guid: 37d714dfd7a21f44982af7f86853fa04, type: 3}
ally_heroProfile: {fileID: 21300000, guid: 37d714dfd7a21f44982af7f86853fa04, type: 3}
ally_heroSelectIcon: {fileID: 21300000, guid: 036e42c18f21faa45975ec21640c76c4, type: 3}
@@ -15,6 +15,7 @@ MonoBehaviour:
ally_heroName: "\u7231\u7433\u8FBE\u96C5"
ally_heroDesignation: ' '
ally_heroID: 30206
isUnlocked: 1
ally_heroImage: {fileID: 21300000, guid: b00fdd5328fcc314088a1c0481cea979, type: 3}
ally_heroProfile: {fileID: 21300000, guid: b00fdd5328fcc314088a1c0481cea979, type: 3}
ally_heroSelectIcon: {fileID: 21300000, guid: 8ec62ab8ffad7644fa1b78bb4b8a273d, type: 3}
@@ -94,6 +95,7 @@ MonoBehaviour:
damageMultiplierPerfect: 1
missHpLossBase: 10
ally_currentEXP: 1000
ally_battleDeployCount: 0
availableSkills: []
selectedSkillIndices:
primarySkillIndex: -1
@@ -20,13 +20,15 @@ MonoBehaviour:
itemDescription:
itemDetailedDescription: "\u54C8\u54C8\u6211\u8BF4\u6211\u662F\u5B54\u5609\u5F6C\u4ED6\u7239\u4F60\u8FD8\u4E0D\u4FE1\u54C8\u54C8\u54C8\u54C8\u54C8\u54C8\u54C8\u554A\u54C8\u54C8\u554A\u54C8\u4F60\u54C8\u725B\u9B54\u916C\u5BBE"
isOnShelf: 1
canbepurchased: 1
canbepurchased: 0
isHot: 0
user_has_read: 1
itemType: 0
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements:
- currencyType: 0
amount: 5000
@@ -10,23 +10,25 @@ MonoBehaviour:
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 74be3a451e4df4c4ab7060c54c58d0d2, type: 3}
m_Name: 77001_defaultItem 1
m_Name: 77001_30206
m_EditorClassIdentifier:
itemName: "\u65B0\u7248\u9065\u96EA\u97F3"
itemIcon: {fileID: 21300000, guid: eec334d03c52af847bfa12dc839ac536, type: 3}
itemName: "\u7231\u7433\u8FBE\u96C5\u53D8\u8EAB\u5668"
itemIcon: {fileID: 21300000, guid: a2753bc864b1ff84093beb30d9d04892, type: 3}
itemID: 77001
itemSinglePurchaseQty: 1
itemPurchaseQuota: 1
itemDescription:
itemDetailedDescription:
itemDescription: "\u53EC\u5524<b>\u7231\u7433\u8FBE\u96C5</b>\u52A0\u5165\u4F60\u7684\u961F\u4F0D\u3002"
itemDetailedDescription: "\u51C6\u5165\u5076\u50CF\u6863\u6848\u4EE5\u5C55\u5F00\u5BF9\u8BE5\u5076\u50CF\u6863\u6848\u7684\u8BB0\u5F55/\u590D\u6F14\u5DE5\u4F5C\u3002\n\n<b>\u201C\u6211\u53EF\u4E0D\u662F\u4EBA\u8D29\u5B50\u201D</b>"
isOnShelf: 1
canbepurchased: 0
canbepurchased: 1
isHot: 1
user_has_read: 1
itemType: 0
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 11400000, guid: 593c39b83808fb742851fb93eaa05963, type: 2}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements:
- currencyType: 0
amount: 5000
@@ -1,34 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !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: 74be3a451e4df4c4ab7060c54c58d0d2, type: 3}
m_Name: 77002_defaultItem 2
m_EditorClassIdentifier:
itemName: "\u65B0\u7248\u9065\u96EA\u97F3"
itemIcon: {fileID: 21300000, guid: eec334d03c52af847bfa12dc839ac536, type: 3}
itemID: 77002
itemSinglePurchaseQty: 1
itemPurchaseQuota: 20
itemDescription:
itemDetailedDescription:
isOnShelf: 1
canbepurchased: 1
isHot: 1
user_has_read: 1
itemType: 0
associatedPlayer: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
costRequirements:
- currencyType: 0
amount: 5000
unlockRequirements: []
purchasedCount: 20
@@ -20,13 +20,15 @@ MonoBehaviour:
itemDescription:
itemDetailedDescription:
isOnShelf: 1
canbepurchased: 1
canbepurchased: 0
isHot: 1
user_has_read: 1
itemType: 1
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements:
- currencyType: 0
amount: 114514
@@ -20,13 +20,15 @@ MonoBehaviour:
itemDescription:
itemDetailedDescription:
isOnShelf: 1
canbepurchased: 1
canbepurchased: 0
isHot: 1
user_has_read: 1
itemType: 1
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements: []
unlockRequirements: []
purchasedCount: 0
@@ -12,7 +12,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 74be3a451e4df4c4ab7060c54c58d0d2, type: 3}
m_Name: 78000_emperorXPb
m_EditorClassIdentifier:
itemName: "\u7687\u5E1D\u7684\u836F\u6C34"
itemName: "\u7687\u5E1D\u7684\u590D\u6F14\u5355\u5143"
itemIcon: {fileID: 21300000, guid: 98a480c46e6c6554c9f5ccf48baa6642, type: 3}
itemID: 78000
itemSinglePurchaseQty: 1
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements:
- currencyType: 0
amount: 1
@@ -12,7 +12,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 74be3a451e4df4c4ab7060c54c58d0d2, type: 3}
m_Name: 78001_normalXPb
m_EditorClassIdentifier:
itemName: "<color=#5F9B6B>\u51E1\u54C1\u5076\u50CF\u7075\u6DB2</color>"
itemName: "<color=#5F9B6B>\u5165\u95E8\u81EA\u52A8\u590D\u6F14\u5355\u5143</color>"
itemIcon: {fileID: 21300000, guid: f1d975f2a96c80f45b92690b57431550, type: 3}
itemID: 78001
itemSinglePurchaseQty: 1
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements:
- currencyType: 0
amount: 2000
@@ -12,7 +12,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 74be3a451e4df4c4ab7060c54c58d0d2, type: 3}
m_Name: 78002_mediumXPb
m_EditorClassIdentifier:
itemName: "<color=#4A86B8>\u4E2D\u54C1\u5076\u50CF\u7075\u6DB2</color>"
itemName: "<color=#4A86B8>\u7ADE\u8D5B\u81EA\u52A8\u590D\u6F14\u5355\u5143</color>"
itemIcon: {fileID: 21300000, guid: f9fd35dd32d5a334198a062117a81489, type: 3}
itemID: 78002
itemSinglePurchaseQty: 1
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements:
- currencyType: 0
amount: 8000
@@ -12,7 +12,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 74be3a451e4df4c4ab7060c54c58d0d2, type: 3}
m_Name: 78003_superiorXPb
m_EditorClassIdentifier:
itemName: "<color=#6A4C9C>\u4E0A\u54C1\u5076\u50CF\u7075\u6DB2</color>"
itemName: "<color=#6A4C9C>\u8054\u8D5B\u81EA\u52A8\u590D\u6F14\u5355\u5143</color>"
itemIcon: {fileID: 21300000, guid: 00a08daafba61f64ea75c35fec0e2df7, type: 3}
itemID: 78003
itemSinglePurchaseQty: 1
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements: []
unlockRequirements: []
purchasedCount: 0
@@ -12,7 +12,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 74be3a451e4df4c4ab7060c54c58d0d2, type: 3}
m_Name: 78004_supremeXPb
m_EditorClassIdentifier:
itemName: "<color=#B88645>\u6781\u54C1\u5076\u50CF\u7075\u6DB2</color>"
itemName: "<color=#B88645>\u51A0\u519B\u81EA\u52A8\u590D\u6F14\u5355\u5143</color>"
itemIcon: {fileID: 21300000, guid: 0e7ed0e47c2cf774d9c60ef2ed161c63, type: 3}
itemID: 78004
itemSinglePurchaseQty: 1
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements: []
unlockRequirements: []
purchasedCount: 0
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements: []
unlockRequirements: []
purchasedCount: 0
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements: []
unlockRequirements: []
purchasedCount: 0
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements:
- currencyType: 0
amount: 3000
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements: []
unlockRequirements: []
purchasedCount: 0
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements: []
unlockRequirements: []
purchasedCount: 0
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements: []
unlockRequirements: []
purchasedCount: 0
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements: []
unlockRequirements: []
purchasedCount: 0
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements: []
unlockRequirements: []
purchasedCount: 0
@@ -24,9 +24,11 @@ MonoBehaviour:
isHot: 1
user_has_read: 1
itemType: 3
associatedPlayer: {fileID: 0}
associatedAllyHero: {fileID: 0}
associatedSong: {fileID: 0}
associatedStoryPassage: {fileID: 0}
storyPassageGrantMode: 0
associatedStorySonId: -1
costRequirements: []
unlockRequirements: []
purchasedCount: 0
@@ -15,6 +15,7 @@ MonoBehaviour:
usernameID: user_TestUser@FloatGaming
songName: Beginning of Bansonic
songID: 1002001
isUnlocked: 1
songLength_second: 0
artistName: FloatGaming
painter: FloatGaming
@@ -15,6 +15,7 @@ MonoBehaviour:
usernameID: user_TestUser@FloatGaming
songName: A Lake With Endless Stars
songID: 1002002
isUnlocked: 1
songLength_second: 0
artistName: t404:null
painter: FloatGaming
@@ -15,6 +15,7 @@ MonoBehaviour:
usernameID: user_TestUser@FloatGaming
songName: CFD
songID: 1002003
isUnlocked: 1
songLength_second: 126
artistName: cybermiso
painter: FloatGaming
@@ -15,6 +15,7 @@ MonoBehaviour:
usernameID: user_TestUser@FloatGaming
songName: "\u30A2\u30C8\u30ED\u30DD\u30B9\u3068\u6700\u679C\u306E\u63A2\u6C42\u8005"
songID: 1002004
isUnlocked: 1
songLength_second: 160
artistName: CK
painter: FloatGaming
@@ -22,8 +23,8 @@ MonoBehaviour:
belongsTo_whichDLC: "\u4E13\u5458\u8BC4\u4F30\u8BA1\u5212#01"
bpm: 230
songDuration: 160
game_enterTimes: 3
time_totalPlayingTime: 677.65295
game_enterTimes: 1
time_totalPlayingTime: 11.868164
illustration: {fileID: 21300000, guid: ecddf7950818f98408cd652e2c2e703f, type: 3}
backgroudPIC: {fileID: 21300000, guid: aaf0b10d106e4bd4889cc1e7d2dc8075, type: 3}
fullscreen_songPicture: {fileID: 21300000, guid: 5a1b664fd293ec943a5b8cffee700da1, type: 3}
@@ -51,11 +52,11 @@ MonoBehaviour:
difficultyName:
chartFile: {fileID: 4900000, guid: ca9bfdf5df2b55f4284f79ad8e5f41af, type: 3}
difficultyDescription:
lastScoreForThisDifficulty: 0
chartPersonalRecordForThisDifficulty: 0
idolPersonalRecordForThisDifficulty: 0
totalPersonalRecordForThisDifficulty: 0
levelProgressForThisDifficulty: 0
lastScoreForThisDifficulty: 25758
chartPersonalRecordForThisDifficulty: 22724
idolPersonalRecordForThisDifficulty: 3034
totalPersonalRecordForThisDifficulty: 25758
levelProgressForThisDifficulty: 0.01908
experienceProvidedForThisDifficulty: 0
- difficulty: 1
difficultyLEVEL: 6
@@ -97,14 +98,14 @@ MonoBehaviour:
difficultyName:
chartFile: {fileID: 4900000, guid: dc27d9d67f3698646b7b7c3d926eea3c, type: 3}
difficultyDescription:
lastScoreForThisDifficulty: 218965
chartPersonalRecordForThisDifficulty: 330310
idolPersonalRecordForThisDifficulty: 40316
totalPersonalRecordForThisDifficulty: 370626
levelProgressForThisDifficulty: 0.27453777
lastScoreForThisDifficulty: 0
chartPersonalRecordForThisDifficulty: 0
idolPersonalRecordForThisDifficulty: 0
totalPersonalRecordForThisDifficulty: 0
levelProgressForThisDifficulty: 0
experienceProvidedForThisDifficulty: 0
personalRecord: 370626
current_levelProgress: 0.27453777
personalRecord: 25758
current_levelProgress: 0.01908
_if_level_is_EASE: 0
max_comboRecord: 0
thisLevel_selectedDifficultyID: 2
@@ -15,6 +15,7 @@ MonoBehaviour:
usernameID:
songName: Can U Feel It
songID: 1002005
isUnlocked: 1
songLength_second: 0
artistName: siqlo
painter: FloatGaming
@@ -15,6 +15,7 @@ MonoBehaviour:
usernameID: user_TestUser@FloatGaming
songName: Paraphiloplace
songID: 1002006
isUnlocked: 1
songLength_second: 141
artistName: Se-U-Ra
painter: FloatGaming
+6366 -65
View File
File diff suppressed because it is too large Load Diff
+127 -127
View File
@@ -153,9 +153,9 @@ RectTransform:
- {fileID: 392380760}
m_Father: {fileID: 425295052}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 50, y: -18.034874}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 10}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &2178223
@@ -2682,9 +2682,9 @@ RectTransform:
- {fileID: 1579840624}
m_Father: {fileID: 1181698751}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 307.76, y: -60.5}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &140317858
@@ -10072,9 +10072,9 @@ RectTransform:
- {fileID: 1328532447}
m_Father: {fileID: 590261241}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 81, y: -50}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 162, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &228531561
@@ -10524,9 +10524,9 @@ RectTransform:
- {fileID: 807575964}
m_Father: {fileID: 590261241}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 606, y: -50}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 162, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &247015384
@@ -12176,7 +12176,7 @@ RectTransform:
m_Father: {fileID: 1374351887}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 20, y: 20}
m_Pivot: {x: 0.5, y: 0.5}
@@ -12587,9 +12587,9 @@ RectTransform:
m_Children: []
m_Father: {fileID: 1121766410}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 43, y: -50}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 10, y: 12}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &315535051
@@ -14106,9 +14106,9 @@ RectTransform:
- {fileID: 55210105}
m_Father: {fileID: 1181698751}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 50, y: -60.5}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &354769595
@@ -16405,9 +16405,9 @@ RectTransform:
- {fileID: 81893893}
m_Father: {fileID: 1181698751}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 178.88, y: -60.5}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &405968940
@@ -32261,7 +32261,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
m_IsActive: 1
--- !u!4 &874962972
Transform:
m_ObjectHideFlags: 0
@@ -34467,9 +34467,9 @@ RectTransform:
m_Father: {fileID: 725274684}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: -4.44, y: 20}
m_SizeDelta: {x: 0, y: 20}
m_Pivot: {x: 0, y: 0}
--- !u!114 &985131518
MonoBehaviour:
@@ -34694,9 +34694,9 @@ RectTransform:
- {fileID: 841673123}
m_Father: {fileID: 590261241}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 256, y: -50}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 162, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &999995736
@@ -39225,9 +39225,9 @@ RectTransform:
- {fileID: 208898303}
m_Father: {fileID: 425295052}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 50, y: -5}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 10}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!4 &1154758974 stripped
@@ -46699,9 +46699,9 @@ RectTransform:
- {fileID: 1740052537}
m_Father: {fileID: 1181698751}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 436.64, y: -60.5}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &1454903926
@@ -50903,9 +50903,9 @@ RectTransform:
m_Children: []
m_Father: {fileID: 1121766410}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 24, y: -50}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 10, y: 12}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &1628161349
@@ -51442,9 +51442,9 @@ RectTransform:
m_Father: {fileID: 725274684}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: -4.44, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 1}
--- !u!114 &1658081545
MonoBehaviour:
@@ -52518,9 +52518,9 @@ RectTransform:
- {fileID: 1461351369}
m_Father: {fileID: 590261241}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 431, y: -50}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 162, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &1705000562
@@ -54221,9 +54221,9 @@ RectTransform:
- {fileID: 578526358}
m_Father: {fileID: 425295052}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 50, y: -31.069748}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 10}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &1771996335
@@ -56799,9 +56799,9 @@ RectTransform:
- {fileID: 616492490}
m_Father: {fileID: 425295052}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 50, y: -44.10462}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 10}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!1 &1904135294
@@ -59782,9 +59782,9 @@ RectTransform:
m_Children: []
m_Father: {fileID: 1121766410}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 5, y: -50}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 10, y: 12}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &2043797911
@@ -67699,115 +67699,115 @@ PrefabInstance:
m_Modifications:
- target: {fileID: 122756198372604741, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 122756198372604741, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 122756198372604741, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 122756198372604741, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -108.729996
value: 0
objectReference: {fileID: 0}
- target: {fileID: 187557561287668015, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 187557561287668015, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 187557561287668015, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 85
value: 0
objectReference: {fileID: 0}
- target: {fileID: 187557561287668015, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 412937744056933375, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 412937744056933375, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 412937744056933375, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 17.5
value: 0
objectReference: {fileID: 0}
- target: {fileID: 412937744056933375, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 678618177608988214, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 678618177608988214, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 678618177608988214, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 17.5
value: 0
objectReference: {fileID: 0}
- target: {fileID: 678618177608988214, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 796738331562133123, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 796738331562133123, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 796738331562133123, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 100
value: 0
objectReference: {fileID: 0}
- target: {fileID: 796738331562133123, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -53.66
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1813990329505698292, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1813990329505698292, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1813990329505698292, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 1813990329505698292, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -25
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2418429837761252522, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2418429837761252522, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2418429837761252522, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 100
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2418429837761252522, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -25
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2613971938742376913, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_Pivot.x
@@ -67823,7 +67823,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 2613971938742376913, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2613971938742376913, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.x
@@ -67831,7 +67831,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 2613971938742376913, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2613971938742376913, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_SizeDelta.x
@@ -67871,11 +67871,11 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 2613971938742376913, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2613971938742376913, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2613971938742376913, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
@@ -67907,19 +67907,19 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 2770405465405990285, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2770405465405990285, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2770405465405990285, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 12.5
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2770405465405990285, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4148755058004053777, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
@@ -67927,35 +67927,35 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 4704552103312105881, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4704552103312105881, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4704552103312105881, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 17.5
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4704552103312105881, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4821968732813134168, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4821968732813134168, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4821968732813134168, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 17.5
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4821968732813134168, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4929852368789569139, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
@@ -67975,67 +67975,67 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 5213525157689929386, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5213525157689929386, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5213525157689929386, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5213525157689929386, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -80.82
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5278958300364593933, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5278958300364593933, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5278958300364593933, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5278958300364593933, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -52.91
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6349925019039213472, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6349925019039213472, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6349925019039213472, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 85
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6349925019039213472, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6354168271256631497, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6354168271256631497, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6354168271256631497, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 85
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6354168271256631497, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6494845825576441926, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_LocalPosition.z
@@ -68059,19 +68059,19 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 6964822682335917845, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6964822682335917845, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6964822682335917845, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 84.55
value: 0
objectReference: {fileID: 0}
- target: {fileID: 6964822682335917845, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7228302987943925696, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.x
@@ -68099,19 +68099,19 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 7489680557431120668, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7489680557431120668, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7489680557431120668, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 84.55
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7489680557431120668, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7889622464149315732, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_IsActive
@@ -68119,19 +68119,19 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 8162043787242817865, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8162043787242817865, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8162043787242817865, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 85
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8162043787242817865, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8281064337243533176, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
@@ -68151,19 +68151,19 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 8790025443345388889, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMax.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8790025443345388889, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchorMin.y
value: 1
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8790025443345388889, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.x
value: 12.5
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8790025443345388889, guid: 83349f645e9e9764ab6c8c66e5b107a3, type: 3}
propertyPath: m_AnchoredPosition.y
value: -50
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
@@ -60,17 +60,7 @@ MonoBehaviour:
- rid: 5279208450996568088
- rid: 5279208450996568089
m_RuntimeSettings:
m_List:
- rid: 5279208450996568066
- rid: 5279208450996568070
- rid: 5279208450996568074
- rid: 5279208450996568075
- rid: 5279208450996568077
- rid: 5279208450996568078
- rid: 5279208450996568079
- rid: 5279208450996568081
- rid: 5279208450996568086
- rid: 5279208450996568088
m_List: []
m_AssetVersion: 8
m_ObsoleteDefaultVolumeProfile: {fileID: 0}
m_RenderingLayerNames:
@@ -65,6 +65,7 @@ public class notebookController : MonoBehaviour
{
if (initialized) return;
initialized = true;
StoreOwnershipLedger.EnsureInstance().InitializeIfNeeded();
LoadAllData();
categoryToggles = new[]
{
@@ -42,4 +42,17 @@ public class notebook_faterType : ScriptableObject
[Header("Son Lists")]
public List<notebook_sonClass> sonList = new List<notebook_sonClass>();
public void SetUnlocked(bool value)
{
if (isUnlocked == value)
{
return;
}
isUnlocked = value;
#if UNITY_EDITOR
EditorUtility.SetDirty(this);
#endif
}
}
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 923f20164290cd64c9a021c8cabe23a0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

@@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: c122873ea5ca2554181d0e8fdbda4ffe
TextureImporter:
internalIDToNameTable:
- first:
213: 4437205736951285066
second: deployed_code_256dp_FFFFFF_FILL0_wght400_GRAD0_opsz48_0
externalObjects: {}
serializedVersion: 13
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
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 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: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
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
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: deployed_code_256dp_FFFFFF_FILL0_wght400_GRAD0_opsz48_0
rect:
serializedVersion: 2
x: 31
y: 19
width: 194
height: 218
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
customData:
outline: []
physicsShape: []
tessellationDetail: -1
bones: []
spriteID: a458e42cb1f149d30800000000000000
internalID: 4437205736951285066
vertices: []
indices:
edges: []
weights: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable:
deployed_code_256dp_FFFFFF_FILL0_wght400_GRAD0_opsz48_0: 4437205736951285066
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 713e02abf704c454d8c992e51643195f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+443
View File
@@ -0,0 +1,443 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Steamworks;
using UnityEngine;
using UnityEngine.UI;
public class UI_Player : MonoBehaviour
{
private const string FirstLaunchDatePrefKey = "player.first_launch_date";
private const string SongResourcesPath = "song_songIndex";
private const string VersionLabel = "\u5185\u90e8\u6d4b\u8bd5v207b35";
private const string ChannelLabel = "Steam";
private static readonly Color SteamOnlineColor = new Color32(20, 120, 45, 255);
[Header("prefab")]
public GameObject pInfoPrefab;
public Transform pInfoParent;
[Header("so")]
[SerializeField] private Player_SO pSO;
[Header("uData")]
public Image userProfileImage;
public Text userNameText;
public Text userIDText;
public Text user_registDate;
[Header("uSource")]
public Text userSource;
[Header("uRecord")]
public Text fcAmount;
public Text firstFCsong;
public Text totalPlayTime;
private readonly List<GameObject> spawnedInfoEntries = new List<GameObject>();
private Color defaultInfoColor = Color.black;
private void Start()
{
StartCoroutine(InitializeAsync());
}
private IEnumerator InitializeAsync()
{
StoreOwnershipLedger.EnsureInstance().InitializeIfNeeded();
StoreOwnershipLedger.EnsureInstance().ForceSyncMirrorFlags();
string registerDate = EnsureFirstLaunchDate();
UserInfoSnapshot snapshot = BuildUserInfoSnapshot(registerDate);
ApplyLegacyTextMirrors(snapshot);
RebuildInfoEntries(snapshot);
yield return null;
yield return StartCoroutine(LoadSteamProfileAsync(snapshot.isSteamOnline));
}
private string EnsureFirstLaunchDate()
{
string playerPrefsDate = PlayerPrefs.GetString(FirstLaunchDatePrefKey, string.Empty);
string soDate = pSO != null ? pSO.firstLaunchDate : string.Empty;
string finalDate = !string.IsNullOrWhiteSpace(playerPrefsDate)
? playerPrefsDate
: (!string.IsNullOrWhiteSpace(soDate) ? soDate : DateTime.Now.ToString("yyyy-MM-dd"));
if (playerPrefsDate != finalDate)
{
PlayerPrefs.SetString(FirstLaunchDatePrefKey, finalDate);
PlayerPrefs.Save();
}
if (pSO != null && pSO.firstLaunchDate != finalDate)
{
pSO.firstLaunchDate = finalDate;
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(pSO);
#endif
}
return finalDate;
}
private UserInfoSnapshot BuildUserInfoSnapshot(string registerDate)
{
SongStats songStats = CollectSongStats();
return new UserInfoSnapshot
{
registerDate = registerDate,
ownedHeroCount = CountOwnedHeroes(),
ownedSongCount = songStats.ownedSongCount,
totalGameEnterCount = songStats.totalGameEnterCount,
totalPlaySeconds = songStats.totalPlaySeconds,
isSteamOnline = IsSteamOnline()
};
}
private void ApplyLegacyTextMirrors(UserInfoSnapshot snapshot)
{
if (user_registDate != null)
{
user_registDate.text = snapshot.registerDate;
}
if (userSource != null)
{
userSource.text = ChannelLabel;
userSource.color = snapshot.isSteamOnline ? SteamOnlineColor : Color.black;
}
if (totalPlayTime != null)
{
totalPlayTime.text = FormatPlayTime(snapshot.totalPlaySeconds);
}
if (fcAmount != null && string.IsNullOrWhiteSpace(fcAmount.text))
{
fcAmount.text = "-";
}
if (firstFCsong != null && string.IsNullOrWhiteSpace(firstFCsong.text))
{
firstFCsong.text = "-";
}
}
private void RebuildInfoEntries(UserInfoSnapshot snapshot)
{
ClearInfoEntries();
CacheDefaultInfoColor();
SpawnInfoEntry("\u6ce8\u518c\u65e5\u671f", snapshot.registerDate);
SpawnInfoEntry("\u62e5\u6709\u89d2\u8272\u6570\u91cf", snapshot.ownedHeroCount.ToString());
SpawnInfoEntry("\u62e5\u6709\u6b4c\u66f2\u6570\u91cf", snapshot.ownedSongCount.ToString());
SpawnInfoEntry("\u603b\u6e38\u620f\u6b21\u6570", snapshot.totalGameEnterCount.ToString());
SpawnInfoEntry("\u6e38\u73a9\u603b\u65f6\u957f", FormatPlayTime(snapshot.totalPlaySeconds));
SpawnInfoEntry("\u6e20\u9053", ChannelLabel, snapshot.isSteamOnline ? SteamOnlineColor : defaultInfoColor);
SpawnInfoEntry("\u7248\u672c", VersionLabel);
}
private void ClearInfoEntries()
{
for (int i = 0; i < spawnedInfoEntries.Count; i++)
{
if (spawnedInfoEntries[i] != null)
{
Destroy(spawnedInfoEntries[i]);
}
}
spawnedInfoEntries.Clear();
}
private void CacheDefaultInfoColor()
{
if (pInfoPrefab == null)
{
return;
}
pInfoPrefab sample = pInfoPrefab.GetComponent<pInfoPrefab>();
if (sample != null && sample.this_iDetail != null)
{
defaultInfoColor = sample.this_iDetail.color;
}
}
private void SpawnInfoEntry(string title, string detail, Color? detailColor = null)
{
if (pInfoPrefab == null || pInfoParent == null)
{
return;
}
GameObject entryObject = Instantiate(pInfoPrefab, pInfoParent);
spawnedInfoEntries.Add(entryObject);
pInfoPrefab entry = entryObject.GetComponent<pInfoPrefab>();
if (entry == null)
{
return;
}
if (entry.this_iTitle != null)
{
entry.this_iTitle.text = title;
}
if (entry.this_iDetail != null)
{
entry.this_iDetail.text = detail;
entry.this_iDetail.color = detailColor ?? defaultInfoColor;
}
}
private static int CountOwnedHeroes()
{
AllyHero_SO[] loadedHeroes = Resources.LoadAll<AllyHero_SO>(string.Empty);
HashSet<int> uniqueHeroIds = new HashSet<int>();
int ownedCount = 0;
AllyHeroDeployLedger ledger = AllyHeroDeployLedger.EnsureInstance();
ledger.InitializeIfNeeded();
for (int i = 0; i < loadedHeroes.Length; i++)
{
AllyHero_SO hero = loadedHeroes[i];
if (hero == null || !uniqueHeroIds.Add(hero.ally_heroID))
{
continue;
}
hero.ally_battleDeployCount = ledger.GetDeployCount(hero.ally_heroID);
if (hero.isUnlocked)
{
ownedCount++;
}
}
return ownedCount;
}
private static SongStats CollectSongStats()
{
SongData[] songs = Resources.LoadAll<SongData>(SongResourcesPath);
HashSet<int> uniqueSongIds = new HashSet<int>();
SongStats stats = new SongStats();
for (int i = 0; i < songs.Length; i++)
{
SongData song = songs[i];
if (song == null || !uniqueSongIds.Add(song.songID))
{
continue;
}
song.LoadPersistent();
if (song.isUnlocked)
{
stats.ownedSongCount++;
}
stats.totalGameEnterCount += Mathf.Max(0, song.game_enterTimes);
stats.totalPlaySeconds += Math.Max(0f, song.time_totalPlayingTime);
}
return stats;
}
private static string FormatPlayTime(double totalSeconds)
{
TimeSpan span = TimeSpan.FromSeconds(Math.Max(0d, totalSeconds));
if (span.TotalHours >= 1d)
{
return string.Format("{0:D2}\u5c0f\u65f6{1:D2}\u5206\u949f", (int)span.TotalHours, span.Minutes);
}
if (span.TotalMinutes >= 1d)
{
return string.Format("{0:D2}\u5206\u949f{1:D2}\u79d2", (int)span.TotalMinutes, span.Seconds);
}
return string.Format("{0:D2}\u79d2", Math.Max(0, span.Seconds));
}
private IEnumerator LoadSteamProfileAsync(bool isSteamOnline)
{
int initRetry = 0;
while (initRetry < 25 && !SteamManager.Initialized)
{
initRetry++;
yield return new WaitForSecondsRealtime(0.2f);
}
if (!SteamManager.Initialized)
{
ApplySteamUnavailable();
yield break;
}
int retryCount = 0;
while (retryCount < 20)
{
if (TryPopulateSteamProfile(isSteamOnline))
{
yield break;
}
retryCount++;
yield return new WaitForSecondsRealtime(0.25f);
}
ApplySteamUnavailable();
}
private void ApplySteamUnavailable()
{
if (userNameText != null)
{
userNameText.text = "Steam Unavailable";
}
if (userIDText != null)
{
userIDText.text = "-";
}
if (userSource != null)
{
userSource.text = ChannelLabel;
userSource.color = Color.black;
}
}
private static bool IsSteamOnline()
{
if (!SteamManager.Initialized)
{
return false;
}
try
{
return SteamFriends.GetPersonaState() != EPersonaState.k_EPersonaStateOffline;
}
catch
{
return false;
}
}
private bool TryPopulateSteamProfile(bool isSteamOnline)
{
try
{
CSteamID steamId = SteamUser.GetSteamID();
string personaName = SteamFriends.GetPersonaName();
if (!steamId.IsValid() || string.IsNullOrWhiteSpace(personaName))
{
return false;
}
if (userNameText != null)
{
userNameText.text = personaName;
}
if (userIDText != null)
{
userIDText.text = steamId.m_SteamID.ToString();
}
if (userSource != null)
{
userSource.text = ChannelLabel;
userSource.color = isSteamOnline ? SteamOnlineColor : Color.black;
}
return TryApplySteamAvatar(steamId) || userProfileImage == null;
}
catch
{
return false;
}
}
private bool TryApplySteamAvatar(CSteamID steamId)
{
if (userProfileImage == null)
{
return true;
}
int imageId = SteamFriends.GetLargeFriendAvatar(steamId);
if (imageId == -1)
{
return false;
}
if (imageId == 0)
{
imageId = SteamFriends.GetMediumFriendAvatar(steamId);
}
if (imageId < 0)
{
return false;
}
uint width;
uint height;
if (!SteamUtils.GetImageSize(imageId, out width, out height) || width == 0 || height == 0)
{
return false;
}
byte[] imageBuffer = new byte[width * height * 4];
if (!SteamUtils.GetImageRGBA(imageId, imageBuffer, imageBuffer.Length))
{
return false;
}
Texture2D sourceTexture = new Texture2D((int)width, (int)height, TextureFormat.RGBA32, false);
sourceTexture.LoadRawTextureData(imageBuffer);
sourceTexture.Apply();
Texture2D flippedTexture = new Texture2D((int)width, (int)height, TextureFormat.RGBA32, false);
for (int y = 0; y < (int)height; y++)
{
Color[] rowPixels = sourceTexture.GetPixels(0, y, (int)width, 1);
flippedTexture.SetPixels(0, (int)height - 1 - y, (int)width, 1, rowPixels);
}
flippedTexture.Apply();
userProfileImage.sprite = Sprite.Create(
flippedTexture,
new Rect(0, 0, flippedTexture.width, flippedTexture.height),
new Vector2(0.5f, 0.5f));
return true;
}
private struct UserInfoSnapshot
{
public string registerDate;
public int ownedHeroCount;
public int ownedSongCount;
public int totalGameEnterCount;
public double totalPlaySeconds;
public bool isSteamOnline;
}
private struct SongStats
{
public int ownedSongCount;
public int totalGameEnterCount;
public double totalPlaySeconds;
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 01cdae7333ce2284fbd8246a1d450c3e
+10
View File
@@ -0,0 +1,10 @@
using UnityEngine;
using UnityEngine.UI;
public class pInfoPrefab : MonoBehaviour
{
public Text this_iTitle;
public Text this_iDetail;
public CanvasGroup this_cGroup;
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 00d9c70de03d5584caa4f58f04471b5b
+302
View File
@@ -0,0 +1,302 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &3594751955591054238
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7844838762169233749}
- component: {fileID: 8249222467498812482}
- component: {fileID: 6848707442284099788}
m_Layer: 5
m_Name: btm
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7844838762169233749
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3594751955591054238}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1539763450377334860}
- {fileID: 5807069226747538177}
m_Father: {fileID: 7358548625640972063}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 200, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8249222467498812482
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3594751955591054238}
m_CullTransparentMesh: 1
--- !u!114 &6848707442284099788
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3594751955591054238}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 7f3b1220317607e44bbb8f0e1998044a, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &5781423114854912448
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1539763450377334860}
- component: {fileID: 3784454099600438642}
- component: {fileID: 358120151119990818}
m_Layer: 5
m_Name: title
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1539763450377334860
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5781423114854912448}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7844838762169233749}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 27.6}
m_SizeDelta: {x: 160, y: 20}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &3784454099600438642
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5781423114854912448}
m_CullTransparentMesh: 1
--- !u!114 &358120151119990818
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5781423114854912448}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: bd4be44636ad6c24a9b117a7108a75e9, type: 3}
m_FontSize: 18
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 1
m_MaxSize: 40
m_Alignment: 3
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: "\u6807\u9898"
--- !u!1 &6256686326704969565
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5807069226747538177}
- component: {fileID: 4141175763355791591}
- component: {fileID: 1252686709029270882}
m_Layer: 5
m_Name: info
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &5807069226747538177
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6256686326704969565}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7844838762169233749}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 160, y: 20}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4141175763355791591
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6256686326704969565}
m_CullTransparentMesh: 1
--- !u!114 &1252686709029270882
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6256686326704969565}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: dfa10de3e2846524089b5073175cd151, type: 3}
m_FontSize: 16
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 1
m_MaxSize: 40
m_Alignment: 3
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: "\u4FE1\u606F"
--- !u!1 &8840060924241115349
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7358548625640972063}
- component: {fileID: 6252520831482713548}
- component: {fileID: 3983549219482858809}
m_Layer: 5
m_Name: pInfoPrefab
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7358548625640972063
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8840060924241115349}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7844838762169233749}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 200, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &6252520831482713548
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8840060924241115349}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 00d9c70de03d5584caa4f58f04471b5b, type: 3}
m_Name:
m_EditorClassIdentifier:
this_iTitle: {fileID: 358120151119990818}
this_iDetail: {fileID: 1252686709029270882}
this_cGroup: {fileID: 3983549219482858809}
--- !u!225 &3983549219482858809
CanvasGroup:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8840060924241115349}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 54a993fe0ba0c4b48b7486f77418c8fc
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fe6dde895e195ad42aa4b807dec540ef
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,778 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &234771199352862083
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4123324077405398592}
- component: {fileID: 8960188264249085605}
- component: {fileID: 3250977055161125869}
- component: {fileID: 8822128272939602658}
m_Layer: 5
m_Name: Image
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4123324077405398592
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 234771199352862083}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4338761642545663007}
m_Father: {fileID: 8208094080659918358}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: -0.239}
m_SizeDelta: {x: 631.94, y: 32}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8960188264249085605
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 234771199352862083}
m_CullTransparentMesh: 1
--- !u!114 &3250977055161125869
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 234771199352862083}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 9ebd9ce7600225b48a92c3900b8188fc, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1.5
--- !u!114 &8822128272939602658
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 234771199352862083}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_ShowMaskGraphic: 1
--- !u!1 &605146429474934285
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3408883077910921622}
- component: {fileID: 914656992227190605}
- component: {fileID: 6942412580199853785}
m_Layer: 5
m_Name: ratios
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &3408883077910921622
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 605146429474934285}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8208094080659918358}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 99.94, y: 0}
m_SizeDelta: {x: 105.454, y: 33}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &914656992227190605
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 605146429474934285}
m_CullTransparentMesh: 1
--- !u!114 &6942412580199853785
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 605146429474934285}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: bd4be44636ad6c24a9b117a7108a75e9, type: 3}
m_FontSize: 11
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 0
m_MaxSize: 40
m_Alignment: 3
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: 'ACC: 99.99%
SRKS: 0'
--- !u!1 &1094181788329206573
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3373422848972785680}
- component: {fileID: 5470506153239392007}
- component: {fileID: 7919000474169282653}
m_Layer: 5
m_Name: score
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &3373422848972785680
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1094181788329206573}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8208094080659918358}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 177.90303, y: 0}
m_SizeDelta: {x: 106.604, y: 25}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5470506153239392007
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1094181788329206573}
m_CullTransparentMesh: 1
--- !u!114 &7919000474169282653
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1094181788329206573}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: dfa10de3e2846524089b5073175cd151, type: 3}
m_FontSize: 22
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 1
m_MaxSize: 40
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: 1234567
--- !u!1 &2917378665968473732
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4891152336866190808}
- component: {fileID: 137569880202737436}
- component: {fileID: 327122142813215607}
m_Layer: 5
m_Name: playtime
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4891152336866190808
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2917378665968473732}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8208094080659918358}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -261.66998, y: 0}
m_SizeDelta: {x: 92.295, y: 25}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &137569880202737436
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2917378665968473732}
m_CullTransparentMesh: 1
--- !u!114 &327122142813215607
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2917378665968473732}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: bd4be44636ad6c24a9b117a7108a75e9, type: 3}
m_FontSize: 14
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 10
m_MaxSize: 40
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: 03-11, 20:25
--- !u!1 &4621517492616896599
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7028528044515112815}
- component: {fileID: 6640949252045097570}
m_Layer: 5
m_Name: gameHistoryPrefab
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7028528044515112815
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4621517492616896599}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 8208094080659918358}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 100, y: 30}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &6640949252045097570
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4621517492616896599}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ef37b79f388055c42814b61b99307a88, type: 3}
m_Name:
m_EditorClassIdentifier:
songImg: {fileID: 2020282907842096563}
playtime: {fileID: 327122142813215607}
songName: {fileID: 5623005123688252722}
songDfct: {fileID: 3212708411851175897}
songPerf: {fileID: 6942412580199853785}
songScore: {fileID: 7919000474169282653}
songRankImg: {fileID: 122816322594050325}
rc: {fileID: 11400000, guid: 951debb97a9b89f48885c82bb601533a, type: 2}
--- !u!1 &6079840058504322514
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8635158262134080350}
- component: {fileID: 1355116519816520013}
- component: {fileID: 5623005123688252722}
m_Layer: 5
m_Name: name
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8635158262134080350
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6079840058504322514}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8208094080659918358}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -123.614, y: 0}
m_SizeDelta: {x: 155.473, y: 25}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &1355116519816520013
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6079840058504322514}
m_CullTransparentMesh: 1
--- !u!114 &5623005123688252722
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6079840058504322514}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: bd4be44636ad6c24a9b117a7108a75e9, type: 3}
m_FontSize: 14
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 10
m_MaxSize: 40
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: "\u8FD9\u4E0D\u662F\u5FEB\u5634\u6211\u6253\u5F00\u4F60\u7CFB\u7EDF\u53D1\u73B0\u4F60\u8FD8\u7528\u7684\u662FWindows7"
--- !u!1 &6320723473554693168
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4456371091438042425}
- component: {fileID: 9142928039487649196}
- component: {fileID: 3212708411851175897}
m_Layer: 5
m_Name: difficulty
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4456371091438042425
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6320723473554693168}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8208094080659918358}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 1.3, y: 0}
m_SizeDelta: {x: 53.437, y: 25}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &9142928039487649196
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6320723473554693168}
m_CullTransparentMesh: 1
--- !u!114 &3212708411851175897
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6320723473554693168}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: bd4be44636ad6c24a9b117a7108a75e9, type: 3}
m_FontSize: 14
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 10
m_MaxSize: 40
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: 18_HD
--- !u!1 &6988015895875826385
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8208094080659918358}
- component: {fileID: 5416189281710205050}
- component: {fileID: 2692334955411405539}
m_Layer: 5
m_Name: btm
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8208094080659918358
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6988015895875826385}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 4123324077405398592}
- {fileID: 4891152336866190808}
- {fileID: 8635158262134080350}
- {fileID: 4456371091438042425}
- {fileID: 3408883077910921622}
- {fileID: 3373422848972785680}
- {fileID: 849455618348688958}
m_Father: {fileID: 7028528044515112815}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 650, y: 50}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5416189281710205050
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6988015895875826385}
m_CullTransparentMesh: 1
--- !u!114 &2692334955411405539
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6988015895875826385}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 7f3b1220317607e44bbb8f0e1998044a, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &7850640493555147263
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4338761642545663007}
- component: {fileID: 1233519261338957310}
- component: {fileID: 2020282907842096563}
m_Layer: 5
m_Name: maskBtm
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4338761642545663007
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7850640493555147263}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 4123324077405398592}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 639.522, y: 86.744}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &1233519261338957310
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7850640493555147263}
m_CullTransparentMesh: 1
--- !u!114 &2020282907842096563
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7850640493555147263}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 9732ceca4a8f8ab419584bd34710c04a, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &7915518511070944398
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 849455618348688958}
- component: {fileID: 4741121237072454142}
- component: {fileID: 122816322594050325}
m_Layer: 5
m_Name: level
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &849455618348688958
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7915518511070944398}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 8208094080659918358}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 271.2, y: 0}
m_SizeDelta: {x: 80, y: 80}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4741121237072454142
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7915518511070944398}
m_CullTransparentMesh: 1
--- !u!114 &122816322594050325
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7915518511070944398}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 4f1e610ba464ef84594814374653f0a6, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8c7f1125b672ba54fb24d10e0cc4095a
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+156
View File
@@ -0,0 +1,156 @@
using UnityEngine;
using UnityEngine.UI;
public class ghPrefab : MonoBehaviour
{
[Header("hahahaha")]
public Image songImg;
public Text playtime;
public Text songName;
public Text songDfct;
public Text songPerf;
public Text songScore;
public Image songRankImg;
[Header("rank config")]
public RankConfig rc;
private static readonly Color EarlySettlementColor = new Color32(204, 51, 51, 255);
private static readonly Color AllPerfectColor = new Color32(0, 100, 0, 255);
private const string UnreadableText = "\u65e0\u6cd5\u8bfb\u53d6";
public void Setup(RecentPlayRecord record, SongData songData)
{
if (record == null)
{
ApplyUnreadableFallback();
return;
}
if (songImg != null)
{
songImg.sprite = songData != null ? songData.illustration : null;
songImg.enabled = songImg.sprite != null;
}
if (playtime != null)
{
playtime.text = string.IsNullOrWhiteSpace(record.playedAt) ? "--" : record.playedAt;
}
if (songName != null)
{
string displayName = string.IsNullOrWhiteSpace(record.songName) ? "Unknown" : record.songName;
songName.text = FitTextWithEllipsis(songName, displayName);
}
if (songDfct != null)
{
songDfct.text = string.IsNullOrWhiteSpace(record.difficultyDisplay) ? "UN" : record.difficultyDisplay;
}
if (songPerf != null)
{
songPerf.text = "ACC: " + record.accuracy.ToString("F3") + "%\nSRKS: " + record.srks.ToString("F2");
}
if (songScore != null)
{
if (!record.scoreReadable)
{
songScore.text = UnreadableText;
songScore.color = Color.black;
}
else
{
songScore.text = record.totalScore.ToString();
if (record.wasEarlySettlement)
{
songScore.color = EarlySettlementColor;
}
else if (record.wasAllPerfect)
{
songScore.color = AllPerfectColor;
}
else
{
songScore.color = Color.black;
}
}
}
if (songRankImg != null)
{
if (rc != null)
{
float rankScore = record.scoreReadable ? record.totalScore : 0f;
songRankImg.sprite = rc.GetRankSprite(rankScore);
}
else
{
songRankImg.sprite = null;
}
}
}
private void ApplyUnreadableFallback()
{
if (songImg != null)
{
songImg.sprite = null;
songImg.enabled = false;
}
if (playtime != null) playtime.text = "--";
if (songName != null) songName.text = "Unknown";
if (songDfct != null) songDfct.text = "UN";
if (songPerf != null) songPerf.text = "ACC: --\nSRKS: --";
if (songScore != null)
{
songScore.text = UnreadableText;
songScore.color = Color.black;
}
if (songRankImg != null)
{
songRankImg.sprite = rc != null ? rc.GetRankSprite(0f) : null;
}
}
private static string FitTextWithEllipsis(Text target, string value)
{
if (target == null || string.IsNullOrEmpty(value))
{
return value;
}
RectTransform rt = target.rectTransform;
float availableWidth = rt != null ? rt.rect.width : 0f;
if (availableWidth <= 0f || target.font == null)
{
return value;
}
TextGenerationSettings settings = target.GetGenerationSettings(new Vector2(availableWidth, rt.rect.height));
float preferred = target.cachedTextGeneratorForLayout.GetPreferredWidth(value, settings) / target.pixelsPerUnit;
if (preferred <= availableWidth)
{
return value;
}
const string ellipsis = "...";
string trimmed = value;
while (trimmed.Length > 0)
{
trimmed = trimmed.Substring(0, trimmed.Length - 1);
string candidate = trimmed + ellipsis;
preferred = target.cachedTextGeneratorForLayout.GetPreferredWidth(candidate, settings) / target.pixelsPerUnit;
if (preferred <= availableWidth)
{
return candidate;
}
}
return ellipsis;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ef37b79f388055c42814b61b99307a88
@@ -0,0 +1,200 @@
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
public class uRecentGameHistory : MonoBehaviour
{
private const string SongResourcesPath = "song_songIndex";
private const int MaxVisibleRecordCount = 5;
[Header("gameobjects")]
public GameObject gameHistoryPrefab;
public Transform gameHistoryParent;
public Transform dGameHistoryParent;
public GameObject more_gameHistory;
[Header("buttons")]
public Button more_gameHistoryButton;
public Button shut_gameHistoryButton;
private SongData[] cachedSongs;
private readonly List<GameObject> previewEntries = new List<GameObject>();
private readonly List<GameObject> detailEntries = new List<GameObject>();
private void Start()
{
BindButtons();
if (more_gameHistory != null)
{
more_gameHistory.SetActive(false);
}
RebuildHistory();
}
private void OnDestroy()
{
if (more_gameHistoryButton != null)
{
more_gameHistoryButton.onClick.RemoveListener(OpenDetailedHistory);
}
if (shut_gameHistoryButton != null)
{
shut_gameHistoryButton.onClick.RemoveListener(CloseDetailedHistory);
}
}
public void RebuildHistory()
{
if (gameHistoryParent == null || gameHistoryPrefab == null)
{
return;
}
ClearEntries(previewEntries, gameHistoryParent);
cachedSongs = Resources.LoadAll<SongData>(SongResourcesPath);
var records = RecentPlayHistoryStore.GetRecords();
if (records == null)
{
return;
}
int visibleCount = Mathf.Min(records.Count, MaxVisibleRecordCount);
for (int i = 0; i < visibleCount; i++)
{
RecentPlayRecord record = records[i];
if (record == null)
{
continue;
}
GameObject instance = Instantiate(gameHistoryPrefab, gameHistoryParent);
previewEntries.Add(instance);
ghPrefab controller = instance.GetComponent<ghPrefab>();
if (controller == null)
{
continue;
}
controller.Setup(record, FindSongById(record.songID));
}
}
private void OpenDetailedHistory()
{
if (more_gameHistory != null)
{
more_gameHistory.SetActive(true);
}
if (dGameHistoryParent == null || gameHistoryPrefab == null)
{
return;
}
ClearEntries(detailEntries, dGameHistoryParent);
EnsureSongCache();
var records = RecentPlayHistoryStore.GetRecords();
if (records == null)
{
return;
}
for (int i = 0; i < records.Count; i++)
{
RecentPlayRecord record = records[i];
if (record == null)
{
continue;
}
GameObject instance = Instantiate(gameHistoryPrefab, dGameHistoryParent);
detailEntries.Add(instance);
ghPrefab controller = instance.GetComponent<ghPrefab>();
if (controller == null)
{
continue;
}
controller.Setup(record, FindSongById(record.songID));
}
}
private void CloseDetailedHistory()
{
ClearEntries(detailEntries, dGameHistoryParent);
if (more_gameHistory != null)
{
more_gameHistory.SetActive(false);
}
}
private SongData FindSongById(int songId)
{
if (songId <= 0 || cachedSongs == null)
{
return null;
}
for (int i = 0; i < cachedSongs.Length; i++)
{
SongData song = cachedSongs[i];
if (song != null && song.songID == songId)
{
return song;
}
}
return null;
}
private void BindButtons()
{
if (more_gameHistoryButton != null)
{
more_gameHistoryButton.onClick.RemoveListener(OpenDetailedHistory);
more_gameHistoryButton.onClick.AddListener(OpenDetailedHistory);
}
if (shut_gameHistoryButton != null)
{
shut_gameHistoryButton.onClick.RemoveListener(CloseDetailedHistory);
shut_gameHistoryButton.onClick.AddListener(CloseDetailedHistory);
}
}
private void EnsureSongCache()
{
if (cachedSongs == null || cachedSongs.Length == 0)
{
cachedSongs = Resources.LoadAll<SongData>(SongResourcesPath);
}
}
private static void ClearEntries(List<GameObject> entries, Transform parent)
{
for (int i = entries.Count - 1; i >= 0; i--)
{
if (entries[i] != null)
{
Destroy(entries[i]);
}
}
entries.Clear();
if (parent == null)
{
return;
}
for (int i = parent.childCount - 1; i >= 0; i--)
{
Destroy(parent.GetChild(i).gameObject);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d4e289acd8282f743864eb651643e526
+30
View File
@@ -8,6 +8,7 @@ public class AllyHero_SO : ScriptableObject
public string ally_heroName;
public string ally_heroDesignation;
public int ally_heroID;
public bool isUnlocked;
[Header("Inspector")]
public Sprite ally_heroImage;
@@ -88,6 +89,7 @@ public class AllyHero_SO : ScriptableObject
[Header("Inspector")]
public int ally_currentEXP;
public int ally_battleDeployCount;
[Header("Skills")]
public SkillDefinition[] availableSkills;
@@ -201,4 +203,32 @@ public class AllyHero_SO : ScriptableObject
if (payload == null) return;
equippedSkillGroupIDs = payload.equippedSkillGroupIDs ?? new int[0];
}
public void SetUnlocked(bool value)
{
if (isUnlocked == value) return;
isUnlocked = value;
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
#endif
}
public void IncrementBattleDeployCount(int amount = 1)
{
if (amount <= 0) return;
AllyHeroDeployLedger.EnsureInstance().IncrementDeployCount(this, amount);
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
#endif
}
public void LoadBattleDeployCountFromLocal()
{
ally_battleDeployCount = AllyHeroDeployLedger.EnsureInstance().GetDeployCount(ally_heroID);
}
public void SaveBattleDeployCountToLocal()
{
AllyHeroDeployLedger.EnsureInstance().SaveNow();
}
}
+26 -20
View File
@@ -5,20 +5,23 @@ public class Player_SO : ScriptableObject
{
[Header("Inspector")]
public int player_currentEXP;
public int player_currentLevel;
[Header("economics")]
[SerializeField] private int player_coins;
[SerializeField] private int player_material;
[Header("exp bottles")]
[SerializeField] private int emptyExpBottle78000;
[SerializeField] private int commonExpBottle78001;
[SerializeField] private int mediumExpBottle78002;
[SerializeField] private int superiorExpBottle78003;
[SerializeField] private int supremeExpBottle78004;
[SerializeField] private int extraordinaryExpBottle78005;
[SerializeField] private int celestialExpBottle78006;
[Header("combat analytics")]
[SerializeField] private float uRankingScore;
[Header("dush materials")]
[SerializeField] private int dushMaterial78021;
[SerializeField] private int dushMaterial78022;
[SerializeField] private int dushMaterial78023;
@@ -37,6 +40,11 @@ public class Player_SO : ScriptableObject
get { return player_material; }
}
public float URankingScore
{
get { return uRankingScore; }
}
public void SetCoins(int value)
{
player_coins = Mathf.Max(0, value);
@@ -74,6 +82,22 @@ public class Player_SO : ScriptableObject
PersistEditorChanges();
}
public void SetURankingScore(float value)
{
uRankingScore = Mathf.Max(0f, value);
PersistEditorChanges();
}
public void AddURankingScore(float delta)
{
if (Mathf.Approximately(delta, 0f))
{
return;
}
SetURankingScore(uRankingScore + delta);
}
public bool TrySpendCoins(int amount)
{
if (amount <= 0)
@@ -96,29 +120,11 @@ public class Player_SO : ScriptableObject
PersistEditorChanges();
}
public int GetLegacyExpBottleCount(string fieldName)
{
switch (fieldName)
{
case "emptyExpBottle78000": return emptyExpBottle78000;
case "commonExpBottle78001": return commonExpBottle78001;
case "mediumExpBottle78002": return mediumExpBottle78002;
case "superiorExpBottle78003": return superiorExpBottle78003;
case "supremeExpBottle78004": return supremeExpBottle78004;
case "extraordinaryExpBottle78005": return extraordinaryExpBottle78005;
case "celestialExpBottle78006": return celestialExpBottle78006;
default: return 0;
}
}
public void SetLegacyExpBottleCount(string fieldName, int value)
{
int safeValue = Mathf.Max(0, value);
switch (fieldName)
{
case "emptyExpBottle78000":
emptyExpBottle78000 = safeValue;
break;
case "commonExpBottle78001":
commonExpBottle78001 = safeValue;
break;
+38 -62
View File
@@ -1,99 +1,75 @@
using UnityEngine;
using System.IO;
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace Bansonic.datacheck
{
public class DataCheck : MonoBehaviour
{
[Header("Settings")]
[Tooltip("是否在游戏启动时自动执行异步数据检查")]
[Tooltip("是否在游戏启动时自动校验歌曲运行时存档")]
public bool checkOnStart = true;
private const string SALT = "bansonic2026@fudongyouxi";
private const string Salt = "bansonic2026@fudongyouxi";
private const string SongRuntimeCategory = "song_runtime";
async void Start()
private void Start()
{
if (!checkOnStart)
{
Debug.Log("[DataCheck] 自动检查已关闭 (checkOnStart = false)");
Debug.Log("[DataCheck] 自动校验已关闭");
return;
}
Debug.Log("[DataCheck] 开始异步验证所有歌曲存档...");
ValidateSongRuntimeSaves();
}
// 记录开始时间
private void ValidateSongRuntimeSaves()
{
float startTime = Time.realtimeSinceStartup;
string persistentPath = Application.persistentDataPath;
string legacyRoot = persistentPath;
int totalFiles = 0;
int encryptedFileCount = SecureSaveVault.CountEncryptedFiles(SongRuntimeCategory);
int legacyFileCount = 0;
try
{
legacyFileCount = Directory.GetFiles(legacyRoot, "SongData_*.json", SearchOption.TopDirectoryOnly).Length;
}
catch (Exception ex)
{
Debug.LogWarning($"[DataCheck] 统计旧版歌曲存档失败: {ex.Message}");
}
List<string> loadedJsonList = SecureSaveVault.LoadAllRawJson(SongRuntimeCategory, legacyRoot, "SongData_*.json");
int totalFiles = Math.Max(encryptedFileCount + legacyFileCount, loadedJsonList.Count);
int corruptedFiles = 0;
// 在主线程提前获取路径,避免子线程调用 Unity API 报错
string persistentPath = Application.persistentDataPath;
// 执行异步检查任务
await Task.Run(() =>
for (int i = 0; i < loadedJsonList.Count; i++)
{
string path = persistentPath;
string[] files = Directory.GetFiles(path, "SongData_*.json");
totalFiles = files.Length;
foreach (string file in files)
{
try
{
string content = File.ReadAllText(file);
// 1. 首先尝试完整校验(包含 Hash)
if (!SongData.VerifyJsonIntegrity(content, SALT, out SongDataSerializable serializable))
{
// 2. 如果校验失败,尝试直接解析 JSON(不看 Hash)
try
{
SongDataSerializable fallbackData = JsonUtility.FromJson<SongDataSerializable>(content);
if (fallbackData != null)
{
// 属于“正常更新”:格式变动导致 Hash 不匹配,但数据可读
// 重新计算 Hash 并保存以修复文件
string jsonWithoutHash = JsonUtility.ToJson(fallbackData);
fallbackData.dataHash = SongData.StaticCalculateMD5(jsonWithoutHash + SALT);
string fixedJson = JsonUtility.ToJson(fallbackData, true);
File.WriteAllText(file, fixedJson);
Debug.Log($"[DataCheck] 已自动修复正常更新的存档: {Path.GetFileName(file)}");
}
else
{
// 彻底无法解析,视为损坏
corruptedFiles++;
Debug.LogError($"[DataCheck] 存档已损坏且无法修复: {Path.GetFileName(file)}");
}
}
catch
string json = loadedJsonList[i];
SongDataSerializable serializable;
if (!SongData.VerifyJsonIntegrity(json, Salt, out serializable))
{
corruptedFiles++;
Debug.LogError($"[DataCheck] 存档严重损坏: {Path.GetFileName(file)}");
}
}
}
catch (System.Exception e)
{
corruptedFiles++;
Debug.LogError($"[DataCheck] 读取文件失败 {file}: {e.Message}");
}
}
});
int envelopeFailedFiles = Math.Max(0, encryptedFileCount - loadedJsonList.Count + legacyFileCount);
corruptedFiles += envelopeFailedFiles;
float duration = Time.realtimeSinceStartup - startTime;
Debug.Log($"[DataCheck] 验完成。耗时: {duration:F4}s | 总文件: {totalFiles} | 损坏/篡改: {corruptedFiles}");
Debug.Log($"[DataCheck] 歌曲存档校验完成。耗时: {duration:F4}s | 总文件: {totalFiles} | 异常/篡改: {corruptedFiles}");
if (corruptedFiles > 0)
{
Debug.LogWarning("[DataCheck] 部分存档存在异常,SongData 加载时将自动修复。");
Debug.LogWarning("[DataCheck] 检测到异常歌曲存档。安全存档层已阻止被破坏的数据继续参与读取。");
}
else if (totalFiles > 0)
{
Debug.Log("[DataCheck] 所有存档通过一致性校验,数据安全。");
Debug.Log("[DataCheck] 所有歌曲运行时存档通过校验。");
}
}
}
@@ -5,6 +5,7 @@ using UnityEngine;
public class InGamePerformanceManager : MonoBehaviour
{
public static InGamePerformanceManager Instance { get; private set; }
public int HighestComboThisRun { get; private set; }
[Header("Inspector")]
[Tooltip("Achievement definitions loaded from Resources.")]
@@ -37,6 +38,7 @@ public class InGamePerformanceManager : MonoBehaviour
if (Instance == null)
{
Instance = this;
HighestComboThisRun = 0;
LoadAchievementLibrary();
}
else
@@ -98,6 +100,11 @@ public class InGamePerformanceManager : MonoBehaviour
public void UpdateCombo(int currentCombo)
{
if (currentCombo > HighestComboThisRun)
{
HighestComboThisRun = currentCombo;
}
CheckAchievementsByCategory(AchievementCategory.Combo, currentCombo);
}
+9 -8
View File
@@ -263,20 +263,21 @@ public class UI_Panel_Mail : MonoBehaviour
isReceived = mail.isReceived
});
}
var json = JsonUtility.ToJson(data, true);
File.WriteAllText(GetSavePath(), json);
SecureSaveVault.SaveJson("mail_state", "runtime", data, GetLegacySavePath());
}
MailSaveData LoadMailState()
{
var path = GetSavePath();
if (!File.Exists(path)) return null;
var json = File.ReadAllText(path);
if (string.IsNullOrEmpty(json)) return null;
return JsonUtility.FromJson<MailSaveData>(json);
MailSaveData data;
if (!SecureSaveVault.TryLoadJson("mail_state", "runtime", out data, GetLegacySavePath()))
{
return null;
}
string GetSavePath()
return data;
}
string GetLegacySavePath()
{
return Path.Combine(Application.persistentDataPath, "mail_state.json");
}
+1
View File
@@ -308,6 +308,7 @@ public class UI_Panel_Main : Singleton_Mono<UI_Panel_Main>
() =>
{
Try_Open_Prefab(ui_Panel_Encyclopendia, ref encyclopedia_Instance);
DailyTaskEventHub.ReportWatchStory();
});
if (button_Story != null)
button_Story.onClick.AddListener(
@@ -4317,6 +4317,128 @@ MonoBehaviour:
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &4051389278547494887
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5227902933222757190}
- component: {fileID: 1537305582658420602}
- component: {fileID: 7802510349101123134}
- component: {fileID: 814312480188450142}
m_Layer: 5
m_Name: Button_bag
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &5227902933222757190
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4051389278547494887}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3273746950969294248}
- {fileID: 7720263923492919812}
m_Father: {fileID: 3304862671657828245}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &1537305582658420602
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4051389278547494887}
m_CullTransparentMesh: 1
--- !u!114 &7802510349101123134
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4051389278547494887}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: -1804059736019094365, guid: 48b7354ead1c3fe49920dd1aa2340b93, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 3
--- !u!114 &814312480188450142
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4051389278547494887}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_WrapAround: 0
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 7802510349101123134}
m_OnClick:
m_PersistentCalls:
m_Calls: []
--- !u!1 &4323691831252766980
GameObject:
m_ObjectHideFlags: 0
@@ -4432,7 +4554,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 451, y: -43}
m_AnchoredPosition: {x: 521.45, y: -43}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0, y: 0}
--- !u!114 &786756074167547563
@@ -5553,6 +5675,156 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5909026714256705340}
m_CullTransparentMesh: 1
--- !u!1 &5953527693605903714
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7720263923492919812}
- component: {fileID: 6328938927363608697}
- component: {fileID: 7955779152810041871}
m_Layer: 5
m_Name: Image (1)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7720263923492919812
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5953527693605903714}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5227902933222757190}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 25, y: 25}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6328938927363608697
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5953527693605903714}
m_CullTransparentMesh: 1
--- !u!114 &7955779152810041871
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5953527693605903714}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.22745098, g: 0.30980393, b: 0.3372549, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: c122873ea5ca2554181d0e8fdbda4ffe, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &5988478905435832207
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3273746950969294248}
- component: {fileID: 5212592995083749236}
- component: {fileID: 9143070888354374478}
m_Layer: 5
m_Name: Image
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &3273746950969294248
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5988478905435832207}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5227902933222757190}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 30, y: 30}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5212592995083749236
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5988478905435832207}
m_CullTransparentMesh: 1
--- !u!114 &9143070888354374478
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5988478905435832207}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &6064513226240405794
GameObject:
m_ObjectHideFlags: 0
@@ -6540,9 +6812,10 @@ RectTransform:
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2149937566432696527}
- {fileID: 4827008926351111491}
- {fileID: 5227902933222757190}
- {fileID: 1387828356511308751}
- {fileID: 263900830868465045}
- {fileID: 4827008926351111491}
m_Father: {fileID: 6434860682258445909}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -6573,7 +6846,7 @@ MonoBehaviour:
m_CellSize: {x: 50, y: 50}
m_Spacing: {x: 20, y: 20}
m_Constraint: 1
m_ConstraintCount: 4
m_ConstraintCount: 5
--- !u!1 &7164294745953147232
GameObject:
m_ObjectHideFlags: 0
+28 -21
View File
@@ -1,48 +1,55 @@
using UnityEngine;
using System.IO;
using System.IO;
using Newtonsoft.Json;
using UnityEngine;
public static class JsonDataHandler
{
public static string File_Path_Folder => Application.persistentDataPath + "/FILE/";
static readonly string File_Suffix = ".json";
private const string SaveCategory = "generic_json_data";
private static readonly string FileSuffix = ".json";
public static string File_Path_Folder => Path.Combine(Application.persistentDataPath, "FILE");
public static string File_Path(string file_Name)
{
return File_Path_Folder + file_Name + File_Suffix;
return Path.Combine(File_Path_Folder, file_Name + FileSuffix);
}
public static void Save<T>(T data, string file_Name, string saver_Name)
{
string json = JsonConvert.SerializeObject(data, Formatting.Indented);
string legacyPath = File_Path(file_Name);
var path = File_Path(file_Name);
if (!File.Exists(path)) Directory.CreateDirectory(File_Path_Folder);
var file_Data = JsonConvert.SerializeObject(data, Formatting.Indented);
// file_Data = $"//SAVER:[{saver_Name}]\n" + file_Data;
File.WriteAllText(path, file_Data);
Debug.Log($"_Save_[{file_Name}{File_Suffix}]_FROM_[{saver_Name}]\n" + path);
if (!SecureSaveVault.SaveRawJson(SaveCategory, file_Name, json, legacyPath))
{
Debug.LogWarning($"[JsonDataHandler] Save failed: {file_Name}");
return;
}
Debug.Log($"_Save_[{file_Name}{FileSuffix}]_FROM_[{saver_Name}]\nSECURE::{SaveCategory}/{file_Name}");
}
public static bool Try_Load<T>(ref T data, string file_Name)
{
var path = File_Path(file_Name);
if (!File.Exists(path))
string json;
if (!SecureSaveVault.TryLoadRawJson(SaveCategory, file_Name, out json, File_Path(file_Name)))
{
Debug.Log($"Load_[{file_Name}]_NULL");
return false;
}
data = JsonConvert.DeserializeObject<T>(System.IO.File.ReadAllText(path));
data = JsonConvert.DeserializeObject<T>(json);
return true;
}
public static T Load<T>(string file_Name)
{
var path = File_Path(file_Name);
if (!System.IO.File.Exists(path))
string json;
if (!SecureSaveVault.TryLoadRawJson(SaveCategory, file_Name, out json, File_Path(file_Name)))
{
Debug.Log($"Load_[{file_Name}]_NULL");
return default;
//return T;
}
return JsonConvert.DeserializeObject<T>(System.IO.File.ReadAllText(path));
return JsonConvert.DeserializeObject<T>(json);
}
}
@@ -0,0 +1,215 @@
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public sealed class AllyHeroDeployLedger : MonoBehaviour
{
public static AllyHeroDeployLedger Instance { get; private set; }
private readonly Dictionary<int, int> deployCountsByHeroId = new Dictionary<int, int>();
private bool initialized;
private bool loadedFromSave;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Bootstrap()
{
EnsureInstance();
}
public static AllyHeroDeployLedger EnsureInstance()
{
if (Instance != null)
{
return Instance;
}
GameObject host = new GameObject("__runtime_ally_deploy_bridge");
host.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
DontDestroyOnLoad(host);
Instance = host.AddComponent<AllyHeroDeployLedger>();
return Instance;
}
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
InitializeIfNeeded();
}
private void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus)
{
SaveNow();
}
}
private void OnApplicationQuit()
{
SaveNow();
}
public void InitializeIfNeeded()
{
if (initialized)
{
return;
}
AllyHeroDeployLedgerPayload payload;
loadedFromSave = AllyHeroDeployLedgerStorage.TryLoad(out payload);
RebuildFromPayload(payload);
initialized = true;
SeedFromHeroAssetsIfNeeded();
SyncAllMirrorFlags();
SaveNow();
}
public int GetDeployCount(int heroId)
{
InitializeIfNeeded();
int count;
return deployCountsByHeroId.TryGetValue(heroId, out count) ? count : 0;
}
public void IncrementDeployCount(AllyHero_SO hero, int amount = 1)
{
if (hero == null || hero.ally_heroID <= 0 || amount <= 0)
{
return;
}
InitializeIfNeeded();
int current = GetDeployCount(hero.ally_heroID);
long next = (long)current + amount;
deployCountsByHeroId[hero.ally_heroID] = next > int.MaxValue ? int.MaxValue : (int)next;
hero.ally_battleDeployCount = deployCountsByHeroId[hero.ally_heroID];
MarkDirty(hero);
SaveNow();
}
public void SaveNow()
{
if (!initialized)
{
return;
}
SyncAllMirrorFlags();
AllyHeroDeployLedgerStorage.TrySave(BuildPayload());
}
private void RebuildFromPayload(AllyHeroDeployLedgerPayload payload)
{
deployCountsByHeroId.Clear();
if (payload == null || payload.entries == null)
{
return;
}
for (int i = 0; i < payload.entries.Count; i++)
{
AllyHeroDeployEntry entry = payload.entries[i];
if (entry == null || entry.heroId <= 0)
{
continue;
}
deployCountsByHeroId[entry.heroId] = Mathf.Max(0, entry.deployCount);
}
}
private void SeedFromHeroAssetsIfNeeded()
{
AllyHero_SO[] heroes = Resources.LoadAll<AllyHero_SO>(string.Empty);
bool changed = false;
for (int i = 0; i < heroes.Length; i++)
{
AllyHero_SO hero = heroes[i];
if (hero == null || hero.ally_heroID <= 0)
{
continue;
}
if (deployCountsByHeroId.ContainsKey(hero.ally_heroID))
{
continue;
}
if (hero.ally_battleDeployCount <= 0)
{
continue;
}
deployCountsByHeroId[hero.ally_heroID] = Mathf.Max(0, hero.ally_battleDeployCount);
changed = true;
}
if (changed && !loadedFromSave)
{
SaveNow();
}
}
private void SyncAllMirrorFlags()
{
AllyHero_SO[] heroes = Resources.LoadAll<AllyHero_SO>(string.Empty);
for (int i = 0; i < heroes.Length; i++)
{
AllyHero_SO hero = heroes[i];
if (hero == null || hero.ally_heroID <= 0)
{
continue;
}
int count;
if (!deployCountsByHeroId.TryGetValue(hero.ally_heroID, out count))
{
count = 0;
}
if (hero.ally_battleDeployCount == count)
{
continue;
}
hero.ally_battleDeployCount = count;
MarkDirty(hero);
}
}
private AllyHeroDeployLedgerPayload BuildPayload()
{
AllyHeroDeployLedgerPayload payload = AllyHeroDeployLedgerStorage.CreateDefaultPayload();
foreach (KeyValuePair<int, int> pair in deployCountsByHeroId)
{
payload.entries.Add(new AllyHeroDeployEntry
{
heroId = pair.Key,
deployCount = Mathf.Max(0, pair.Value)
});
}
return payload;
}
private static void MarkDirty(AllyHero_SO hero)
{
#if UNITY_EDITOR
if (hero != null)
{
EditorUtility.SetDirty(hero);
}
#endif
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b4b993c80d44994489f40e5a81dda7b3
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
[Serializable]
public class AllyHeroDeployEntry
{
public int heroId;
public int deployCount;
}
[Serializable]
public class AllyHeroDeployLedgerPayload
{
public int version;
public long lastUpdatedUtcTicks;
public List<AllyHeroDeployEntry> entries = new List<AllyHeroDeployEntry>();
}
[Serializable]
public class AllyHeroDeployLedgerEnvelope
{
public string payload;
public string signature;
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2e25750439aee8f4e93c609697703d8a
@@ -0,0 +1,187 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
public static class AllyHeroDeployLedgerStorage
{
private const string SecretSeed = "ban_total.ally_hero_deploy_ledger.v1";
private const string VaultDirectoryName = ".cache_bridge";
private const string MainFileName = ".ahd.dat";
private const string BackupFileName = ".ahd.bak";
private const string TempFileName = ".ahd.tmp";
private static string VaultDirectoryPath => Path.Combine(Application.persistentDataPath, VaultDirectoryName);
private static string MainFilePath => Path.Combine(VaultDirectoryPath, MainFileName);
private static string BackupFilePath => Path.Combine(VaultDirectoryPath, BackupFileName);
private static string TempFilePath => Path.Combine(VaultDirectoryPath, TempFileName);
public static bool TryLoad(out AllyHeroDeployLedgerPayload payload)
{
payload = CreateDefaultPayload();
if (TryReadPayload(MainFilePath, out payload))
{
return true;
}
if (TryReadPayload(BackupFilePath, out payload))
{
TrySave(payload);
return true;
}
return false;
}
public static bool TrySave(AllyHeroDeployLedgerPayload payload)
{
try
{
Directory.CreateDirectory(VaultDirectoryPath);
TryHidePath(VaultDirectoryPath);
string envelopeJson = BuildEnvelopeJson(payload);
File.WriteAllText(TempFilePath, envelopeJson, Encoding.UTF8);
TryHidePath(TempFilePath);
if (File.Exists(MainFilePath))
{
File.Copy(MainFilePath, BackupFilePath, true);
TryHidePath(BackupFilePath);
}
File.Copy(TempFilePath, MainFilePath, true);
TryHidePath(MainFilePath);
File.Delete(TempFilePath);
return true;
}
catch (Exception ex)
{
Debug.LogWarning("[AllyHeroDeployLedgerStorage] Save failed: " + ex.Message);
return false;
}
}
public static AllyHeroDeployLedgerPayload CreateDefaultPayload()
{
return new AllyHeroDeployLedgerPayload
{
version = 1,
lastUpdatedUtcTicks = DateTime.UtcNow.Ticks,
entries = new System.Collections.Generic.List<AllyHeroDeployEntry>()
};
}
private static bool TryReadPayload(string path, out AllyHeroDeployLedgerPayload payload)
{
payload = CreateDefaultPayload();
if (!File.Exists(path))
{
return false;
}
try
{
string envelopeJson = File.ReadAllText(path, Encoding.UTF8);
AllyHeroDeployLedgerEnvelope envelope = JsonUtility.FromJson<AllyHeroDeployLedgerEnvelope>(envelopeJson);
if (envelope == null || string.IsNullOrEmpty(envelope.payload) || string.IsNullOrEmpty(envelope.signature))
{
return false;
}
string expectedSignature = ComputeSignature(envelope.payload);
if (!string.Equals(expectedSignature, envelope.signature, StringComparison.Ordinal))
{
Debug.LogWarning("[AllyHeroDeployLedgerStorage] Save signature mismatch. Possible tampering detected.");
return false;
}
byte[] encryptedBytes = Convert.FromBase64String(envelope.payload);
byte[] plainBytes = XorTransform(encryptedBytes, BuildKeyBytes());
string payloadJson = Encoding.UTF8.GetString(plainBytes);
AllyHeroDeployLedgerPayload loadedPayload = JsonUtility.FromJson<AllyHeroDeployLedgerPayload>(payloadJson);
if (loadedPayload == null)
{
return false;
}
payload = loadedPayload;
return true;
}
catch (Exception ex)
{
Debug.LogWarning("[AllyHeroDeployLedgerStorage] Load failed from '" + path + "': " + ex.Message);
return false;
}
}
private static string BuildEnvelopeJson(AllyHeroDeployLedgerPayload payload)
{
payload.lastUpdatedUtcTicks = DateTime.UtcNow.Ticks;
string payloadJson = JsonUtility.ToJson(payload, false);
byte[] plainBytes = Encoding.UTF8.GetBytes(payloadJson);
byte[] encryptedBytes = XorTransform(plainBytes, BuildKeyBytes());
string payloadBase64 = Convert.ToBase64String(encryptedBytes);
AllyHeroDeployLedgerEnvelope envelope = new AllyHeroDeployLedgerEnvelope
{
payload = payloadBase64,
signature = ComputeSignature(payloadBase64)
};
return JsonUtility.ToJson(envelope, false);
}
private static string ComputeSignature(string payloadBase64)
{
string signText = payloadBase64 + "|" + Application.identifier + "|" + SecretSeed + "|" + SystemInfo.deviceUniqueIdentifier;
using (SHA256 sha = SHA256.Create())
{
byte[] bytes = Encoding.UTF8.GetBytes(signText);
byte[] hash = sha.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
}
private static byte[] BuildKeyBytes()
{
using (SHA256 sha = SHA256.Create())
{
string seed = Application.identifier + "|" + SystemInfo.deviceUniqueIdentifier + "|" + SecretSeed;
return sha.ComputeHash(Encoding.UTF8.GetBytes(seed));
}
}
private static byte[] XorTransform(byte[] source, byte[] key)
{
byte[] result = new byte[source.Length];
for (int i = 0; i < source.Length; i++)
{
result[i] = (byte)(source[i] ^ key[i % key.Length]);
}
return result;
}
private static void TryHidePath(string path)
{
try
{
if (Application.platform != RuntimePlatform.WindowsEditor && Application.platform != RuntimePlatform.WindowsPlayer)
{
return;
}
FileAttributes attributes = File.GetAttributes(path);
if ((attributes & FileAttributes.Hidden) == 0)
{
File.SetAttributes(path, attributes | FileAttributes.Hidden);
}
}
catch
{
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: eb496e1becc09464eb0ca23b9a69557d
@@ -109,6 +109,25 @@ public sealed class DushMaterialLedger : MonoBehaviour
ChangeCount(DushMaterialCatalog.GetKey(kind), amount);
}
public bool TryConsume(DushMaterialKind kind, int amount)
{
if (amount <= 0)
{
return false;
}
string key = DushMaterialCatalog.GetKey(kind);
int current = GetCountByKey(key);
if (current < amount)
{
return false;
}
ChangeCount(key, -amount);
DailyTaskEventHub.ReportUseItem(amount);
return true;
}
public bool SeedFromPlayerSo(Player_SO playerData, bool overwriteExistingCounts)
{
if (playerData == null)
@@ -0,0 +1,102 @@
using System.IO;
using UnityEngine;
public static class LegacyPlainSaveMigrator
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void MigrateLegacyPlainFiles()
{
string root = Application.persistentDataPath;
MigrateSingleFile("daily_task", "runtime", Path.Combine(root, "daily_tasks.json"));
MigrateSingleFile("mail_state", "runtime", Path.Combine(root, "mail_state.json"));
MigrateSingleFile("store_state", "runtime", Path.Combine(root, "storeSystem_state.json"));
MigratePattern("song_runtime", root, "SongData_*.json", fileNameWithoutExt =>
{
if (fileNameWithoutExt.StartsWith("SongData_"))
{
return fileNameWithoutExt.Substring("SongData_".Length);
}
return fileNameWithoutExt;
});
MigratePattern("song_export", Path.Combine(root, "SongDataJson"), "*.json", fileNameWithoutExt => fileNameWithoutExt);
MigratePattern("song_export", Path.Combine(root, "song_json_export"), "*.json", fileNameWithoutExt => fileNameWithoutExt);
}
private static void MigrateSingleFile(string category, string key, string legacyPath)
{
if (!File.Exists(legacyPath))
{
return;
}
try
{
string json = File.ReadAllText(legacyPath);
if (string.IsNullOrEmpty(json))
{
File.Delete(legacyPath);
return;
}
SecureSaveVault.SaveRawJson(category, key, json, legacyPath);
}
catch (System.Exception ex)
{
Debug.LogWarning($"[LegacyPlainSaveMigrator] Failed to migrate {legacyPath}: {ex.Message}");
}
}
private static void MigratePattern(string category, string directory, string searchPattern, System.Func<string, string> keyResolver)
{
if (!Directory.Exists(directory))
{
return;
}
string[] files = Directory.GetFiles(directory, searchPattern, SearchOption.TopDirectoryOnly);
for (int i = 0; i < files.Length; i++)
{
string legacyPath = files[i];
try
{
string json = File.ReadAllText(legacyPath);
if (string.IsNullOrEmpty(json))
{
File.Delete(legacyPath);
continue;
}
string key = keyResolver != null ? keyResolver(Path.GetFileNameWithoutExtension(legacyPath)) : Path.GetFileNameWithoutExtension(legacyPath);
SecureSaveVault.SaveRawJson(category, key, json, legacyPath);
}
catch (System.Exception ex)
{
Debug.LogWarning($"[LegacyPlainSaveMigrator] Failed to migrate {legacyPath}: {ex.Message}");
}
}
TryDeleteDirectoryIfEmpty(directory);
}
private static void TryDeleteDirectoryIfEmpty(string directory)
{
try
{
if (!Directory.Exists(directory))
{
return;
}
if (Directory.GetFiles(directory).Length == 0 && Directory.GetDirectories(directory).Length == 0)
{
Directory.Delete(directory, false);
}
}
catch
{
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f29bda46e3dc3ae4d9bf7aa6c11bcadd
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
[Serializable]
public class RecentPlayRecord
{
public string playedAt;
public int songID;
public string songName;
public string difficultyDisplay;
public float accuracy;
public float srks;
public int totalScore;
public bool scoreReadable;
public bool wasEarlySettlement;
public bool wasAllPerfect;
}
[Serializable]
public class RecentPlayHistoryPayload
{
public List<RecentPlayRecord> records = new List<RecentPlayRecord>();
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f13cbff8f75ffe64aa2aed4e7cff0609
@@ -0,0 +1,57 @@
using System.Collections.Generic;
using UnityEngine;
public static class RecentPlayHistoryStore
{
private const string Category = "recent_play_history";
private const string Key = "runs_v1";
private const int MaxRecordCount = 100;
public static IReadOnlyList<RecentPlayRecord> GetRecords()
{
return LoadPayload().records;
}
public static void Push(RecentPlayRecord record)
{
if (record == null)
{
return;
}
var payload = LoadPayload();
if (payload.records == null)
{
payload.records = new List<RecentPlayRecord>();
}
payload.records.Insert(0, record);
if (payload.records.Count > MaxRecordCount)
{
payload.records.RemoveRange(MaxRecordCount, payload.records.Count - MaxRecordCount);
}
SecureSaveVault.SaveJson(Category, Key, payload);
}
public static void Clear()
{
SecureSaveVault.Delete(Category, Key);
}
private static RecentPlayHistoryPayload LoadPayload()
{
RecentPlayHistoryPayload payload;
if (!SecureSaveVault.TryLoadJson(Category, Key, out payload) || payload == null)
{
payload = new RecentPlayHistoryPayload();
}
if (payload.records == null)
{
payload.records = new List<RecentPlayRecord>();
}
return payload;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 68da4f91f4d88174fb8da3ea658c1672
@@ -0,0 +1,555 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Reflection;
using System.Text;
using UnityEngine;
[Serializable]
public class SecureSaveEnvelope
{
public int version;
public string payload;
public string signature;
public long savedUtcTicks;
}
public static class SecureSaveVault
{
private const string SecretSeed = "ban_total.secure_save_v2";
private const string VaultDirectoryName = ".cache_bridge";
private static bool s_dpapiInitialized;
private static bool s_dpapiSupported;
private static MethodInfo s_dpapiProtectMethod;
private static MethodInfo s_dpapiUnprotectMethod;
private static object s_dpapiCurrentUserScope;
private static string VaultDirectoryPath
{
get { return Path.Combine(Application.persistentDataPath, VaultDirectoryName); }
}
public static bool SaveJson<T>(string category, string key, T data, string legacyPlainPath = null)
{
if (!typeof(T).IsValueType && (object)data == null)
{
return false;
}
string json = JsonUtility.ToJson(data, false);
return SaveRawJson(category, key, json, legacyPlainPath);
}
public static bool TryLoadJson<T>(string category, string key, out T data, string legacyPlainPath = null)
{
data = default(T);
string json;
if (!TryLoadRawJson(category, key, out json, legacyPlainPath))
{
return false;
}
if (string.IsNullOrEmpty(json))
{
return false;
}
try
{
data = JsonUtility.FromJson<T>(json);
if (typeof(T).IsValueType)
{
return true;
}
return (object)data != null;
}
catch (Exception ex)
{
Debug.LogWarning($"[SecureSaveVault] JSON parse failed: {ex.Message}");
data = default(T);
return false;
}
}
public static bool SaveRawJson(string category, string key, string json, string legacyPlainPath = null)
{
if (string.IsNullOrWhiteSpace(category) || string.IsNullOrWhiteSpace(key) || json == null)
{
return false;
}
string mainPath = GetFilePath(category, key, ".dat");
string backupPath = GetFilePath(category, key, ".bak");
string tempPath = GetFilePath(category, key, ".tmp");
try
{
string directory = Path.GetDirectoryName(mainPath);
Directory.CreateDirectory(directory);
TryHidePath(directory);
string envelopeJson = BuildEnvelopeJson(category, key, json);
File.WriteAllText(tempPath, envelopeJson, Encoding.UTF8);
TryHidePath(tempPath);
if (File.Exists(mainPath))
{
File.Copy(mainPath, backupPath, true);
TryHidePath(backupPath);
}
File.Copy(tempPath, mainPath, true);
TryHidePath(mainPath);
File.Delete(tempPath);
DeleteLegacyPlainFile(legacyPlainPath);
return true;
}
catch (Exception ex)
{
Debug.LogWarning($"[SecureSaveVault] Save failed ({category}/{key}): {ex.Message}");
return false;
}
}
public static bool TryLoadRawJson(string category, string key, out string json, string legacyPlainPath = null)
{
json = null;
if (string.IsNullOrWhiteSpace(category) || string.IsNullOrWhiteSpace(key))
{
return false;
}
string mainPath = GetFilePath(category, key, ".dat");
string backupPath = GetFilePath(category, key, ".bak");
if (TryReadEncryptedFile(category, key, mainPath, out json))
{
return true;
}
if (TryReadEncryptedFile(category, key, backupPath, out json))
{
SaveRawJson(category, key, json, legacyPlainPath);
return true;
}
if (!string.IsNullOrEmpty(legacyPlainPath) && File.Exists(legacyPlainPath))
{
try
{
json = File.ReadAllText(legacyPlainPath, Encoding.UTF8);
if (!string.IsNullOrEmpty(json))
{
SaveRawJson(category, key, json, legacyPlainPath);
return true;
}
}
catch (Exception ex)
{
Debug.LogWarning($"[SecureSaveVault] Legacy migration failed ({legacyPlainPath}): {ex.Message}");
}
}
return false;
}
public static bool Delete(string category, string key, string legacyPlainPath = null)
{
try
{
DeleteIfExists(GetFilePath(category, key, ".dat"));
DeleteIfExists(GetFilePath(category, key, ".bak"));
DeleteIfExists(GetFilePath(category, key, ".tmp"));
DeleteLegacyPlainFile(legacyPlainPath);
return true;
}
catch (Exception ex)
{
Debug.LogWarning($"[SecureSaveVault] Delete failed ({category}/{key}): {ex.Message}");
return false;
}
}
public static List<string> LoadAllRawJson(string category, string legacyDirectory = null, string legacySearchPattern = "*.json")
{
var result = new List<string>();
if (string.IsNullOrWhiteSpace(category))
{
return result;
}
string categoryDirectory = GetCategoryDirectory(category);
if (Directory.Exists(categoryDirectory))
{
string[] encryptedFiles = Directory.GetFiles(categoryDirectory, "*.dat", SearchOption.TopDirectoryOnly);
for (int i = 0; i < encryptedFiles.Length; i++)
{
string path = encryptedFiles[i];
string key = ExtractKeyFromFileName(path);
if (string.IsNullOrEmpty(key))
{
continue;
}
string json;
if (TryReadEncryptedFile(category, key, path, out json) && !string.IsNullOrEmpty(json))
{
result.Add(json);
}
}
}
if (!string.IsNullOrEmpty(legacyDirectory) && Directory.Exists(legacyDirectory))
{
string[] legacyFiles = Directory.GetFiles(legacyDirectory, legacySearchPattern, SearchOption.TopDirectoryOnly);
for (int i = 0; i < legacyFiles.Length; i++)
{
string legacyPath = legacyFiles[i];
try
{
string json = File.ReadAllText(legacyPath, Encoding.UTF8);
if (string.IsNullOrEmpty(json))
{
continue;
}
string legacyKey = Path.GetFileNameWithoutExtension(legacyPath);
SaveRawJson(category, legacyKey, json, legacyPath);
result.Add(json);
}
catch (Exception ex)
{
Debug.LogWarning($"[SecureSaveVault] Legacy bulk migration failed ({legacyPath}): {ex.Message}");
}
}
}
return result;
}
public static int CountEncryptedFiles(string category)
{
if (string.IsNullOrWhiteSpace(category))
{
return 0;
}
string categoryDirectory = GetCategoryDirectory(category);
if (!Directory.Exists(categoryDirectory))
{
return 0;
}
return Directory.GetFiles(categoryDirectory, "*.dat", SearchOption.TopDirectoryOnly).Length;
}
private static bool TryReadEncryptedFile(string category, string key, string filePath, out string json)
{
json = null;
if (!File.Exists(filePath))
{
return false;
}
try
{
string envelopeJson = File.ReadAllText(filePath, Encoding.UTF8);
var envelope = JsonUtility.FromJson<SecureSaveEnvelope>(envelopeJson);
if (envelope == null || string.IsNullOrEmpty(envelope.payload) || string.IsNullOrEmpty(envelope.signature))
{
return false;
}
string expectedSignature = ComputeSignature(category, envelope.payload);
if (!string.Equals(expectedSignature, envelope.signature, StringComparison.Ordinal))
{
Debug.LogWarning($"[SecureSaveVault] Signature mismatch ({category}/{key}). Possible tampering detected.");
return false;
}
byte[] protectedBytes = Convert.FromBase64String(envelope.payload);
byte[] plainBytes;
if (!TryUnprotectBytes(category, key, protectedBytes, out plainBytes))
{
return false;
}
json = Encoding.UTF8.GetString(plainBytes);
return true;
}
catch (Exception ex)
{
Debug.LogWarning($"[SecureSaveVault] Read failed ({category}/{key}): {ex.Message}");
return false;
}
}
private static string BuildEnvelopeJson(string category, string key, string json)
{
byte[] plainBytes = Encoding.UTF8.GetBytes(json);
byte[] protectedBytes = ProtectBytes(category, key, plainBytes);
string payloadBase64 = Convert.ToBase64String(protectedBytes);
var envelope = new SecureSaveEnvelope
{
version = 2,
payload = payloadBase64,
signature = ComputeSignature(category, payloadBase64),
savedUtcTicks = DateTime.UtcNow.Ticks
};
return JsonUtility.ToJson(envelope, false);
}
private static string GetCategoryDirectory(string category)
{
string safeCategory = ShortHash("cat|" + category);
return Path.Combine(VaultDirectoryPath, "." + safeCategory);
}
private static string GetFilePath(string category, string key, string extension)
{
string categoryDirectory = GetCategoryDirectory(category);
string safeKey = ShortHash("key|" + key);
return Path.Combine(categoryDirectory, "." + safeKey + extension);
}
private static string ExtractKeyFromFileName(string path)
{
string fileName = Path.GetFileNameWithoutExtension(path);
if (string.IsNullOrEmpty(fileName))
{
return null;
}
return fileName.StartsWith(".") ? fileName.Substring(1) : fileName;
}
private static string ComputeSignature(string category, string payloadBase64)
{
string signText = payloadBase64 + "|" + category + "|" + Application.identifier + "|" + SecretSeed + "|" + SystemInfo.deviceUniqueIdentifier;
using (var sha = SHA256.Create())
{
byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(signText));
return Convert.ToBase64String(hash);
}
}
private static byte[] ProtectBytes(string category, string key, byte[] plainBytes)
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
byte[] dpapiBytes;
if (TryProtectWithDpapi(category, plainBytes, out dpapiBytes))
{
return dpapiBytes;
}
#endif
using (var aes = Aes.Create())
{
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = BuildAesKey(category, key);
aes.GenerateIV();
using (var encryptor = aes.CreateEncryptor())
{
byte[] cipherBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
byte[] result = new byte[aes.IV.Length + cipherBytes.Length];
Buffer.BlockCopy(aes.IV, 0, result, 0, aes.IV.Length);
Buffer.BlockCopy(cipherBytes, 0, result, aes.IV.Length, cipherBytes.Length);
return result;
}
}
}
private static bool TryUnprotectBytes(string category, string key, byte[] protectedBytes, out byte[] plainBytes)
{
plainBytes = null;
try
{
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
if (TryUnprotectWithDpapi(category, protectedBytes, out plainBytes))
{
return plainBytes != null;
}
#endif
using (var aes = Aes.Create())
{
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
aes.Key = BuildAesKey(category, key);
int ivLength = aes.BlockSize / 8;
if (protectedBytes == null || protectedBytes.Length <= ivLength)
{
return false;
}
byte[] iv = new byte[ivLength];
byte[] cipher = new byte[protectedBytes.Length - ivLength];
Buffer.BlockCopy(protectedBytes, 0, iv, 0, ivLength);
Buffer.BlockCopy(protectedBytes, ivLength, cipher, 0, cipher.Length);
aes.IV = iv;
using (var decryptor = aes.CreateDecryptor())
{
plainBytes = decryptor.TransformFinalBlock(cipher, 0, cipher.Length);
return plainBytes != null;
}
}
}
catch (Exception ex)
{
Debug.LogWarning($"[SecureSaveVault] Decrypt failed ({category}/{key}): {ex.Message}");
plainBytes = null;
return false;
}
}
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
private static bool TryProtectWithDpapi(string category, byte[] plainBytes, out byte[] protectedBytes)
{
protectedBytes = null;
if (!EnsureDpapi())
{
return false;
}
try
{
protectedBytes = s_dpapiProtectMethod.Invoke(null, new object[] { plainBytes, BuildEntropy(category), s_dpapiCurrentUserScope }) as byte[];
return protectedBytes != null && protectedBytes.Length > 0;
}
catch (Exception ex)
{
Debug.LogWarning($"[SecureSaveVault] DPAPI protect failed, fallback to AES: {ex.Message}");
protectedBytes = null;
return false;
}
}
private static bool TryUnprotectWithDpapi(string category, byte[] protectedBytes, out byte[] plainBytes)
{
plainBytes = null;
if (!EnsureDpapi())
{
return false;
}
try
{
plainBytes = s_dpapiUnprotectMethod.Invoke(null, new object[] { protectedBytes, BuildEntropy(category), s_dpapiCurrentUserScope }) as byte[];
return plainBytes != null && plainBytes.Length > 0;
}
catch
{
plainBytes = null;
return false;
}
}
private static bool EnsureDpapi()
{
if (s_dpapiInitialized)
{
return s_dpapiSupported;
}
s_dpapiInitialized = true;
try
{
Type protectedDataType =
Type.GetType("System.Security.Cryptography.ProtectedData, System.Security.Cryptography.ProtectedData") ??
Type.GetType("System.Security.Cryptography.ProtectedData, System.Security");
Type scopeType =
Type.GetType("System.Security.Cryptography.DataProtectionScope, System.Security.Cryptography.ProtectedData") ??
Type.GetType("System.Security.Cryptography.DataProtectionScope, System.Security");
if (protectedDataType == null || scopeType == null)
{
s_dpapiSupported = false;
return false;
}
s_dpapiProtectMethod = protectedDataType.GetMethod("Protect", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(byte[]), typeof(byte[]), scopeType }, null);
s_dpapiUnprotectMethod = protectedDataType.GetMethod("Unprotect", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(byte[]), typeof(byte[]), scopeType }, null);
if (s_dpapiProtectMethod == null || s_dpapiUnprotectMethod == null)
{
s_dpapiSupported = false;
return false;
}
s_dpapiCurrentUserScope = Enum.Parse(scopeType, "CurrentUser");
s_dpapiSupported = s_dpapiCurrentUserScope != null;
return s_dpapiSupported;
}
catch (Exception ex)
{
Debug.LogWarning($"[SecureSaveVault] DPAPI initialize failed, fallback to AES: {ex.Message}");
s_dpapiSupported = false;
return false;
}
}
#endif
private static byte[] BuildEntropy(string category)
{
string seed = Application.identifier + "|" + SystemInfo.deviceUniqueIdentifier + "|" + SecretSeed + "|" + category;
using (var sha = SHA256.Create())
{
return sha.ComputeHash(Encoding.UTF8.GetBytes(seed));
}
}
private static byte[] BuildAesKey(string category, string key)
{
return BuildEntropy(category);
}
private static string ShortHash(string value)
{
using (var sha = SHA256.Create())
{
byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(value + "|" + Application.identifier + "|" + SecretSeed));
return BitConverter.ToString(hash, 0, 12).Replace("-", string.Empty).ToLowerInvariant();
}
}
private static void DeleteLegacyPlainFile(string legacyPlainPath)
{
if (string.IsNullOrEmpty(legacyPlainPath) || !File.Exists(legacyPlainPath))
{
return;
}
DeleteIfExists(legacyPlainPath);
}
private static void DeleteIfExists(string path)
{
if (!string.IsNullOrEmpty(path) && File.Exists(path))
{
File.Delete(path);
}
}
private static void TryHidePath(string path)
{
try
{
if (Application.platform != RuntimePlatform.WindowsEditor && Application.platform != RuntimePlatform.WindowsPlayer)
{
return;
}
var attributes = File.GetAttributes(path);
if ((attributes & FileAttributes.Hidden) == 0)
{
File.SetAttributes(path, attributes | FileAttributes.Hidden);
}
}
catch
{
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 79fb5f18bfcf1674993076d6b5e5bb9d
@@ -32,6 +32,12 @@ public static class StoreExpBottlePurchaseService
return false;
}
if (RequiresSinglePurchase(itemSO) && packageCount > 1)
{
failureMessage = "该物品仅支持单份购买";
return false;
}
if (itemSO.costRequirements == null || itemSO.costRequirements.Count == 0)
{
failureMessage = "不可购买";
@@ -45,13 +51,6 @@ public static class StoreExpBottlePurchaseService
return false;
}
ExpBottleKind kind;
if (!StoreExpBottleGrantResolver.TryResolve(itemSO, out kind))
{
failureMessage = "当前仅支持经验瓶发放";
return false;
}
long longGrantedCount = (long)Mathf.Max(1, itemSO.itemSinglePurchaseQty) * packageCount;
if (longGrantedCount > int.MaxValue)
{
@@ -74,9 +73,13 @@ public static class StoreExpBottlePurchaseService
}
int totalCost = (int)totalCostLong;
if (!ValidateGrantTarget(itemSO, out failureMessage))
{
return false;
}
PlayerEconomyLedger.EnsureInstance().AttachPlayerData(playerData);
ExpBottleLedger.EnsureInstance().AttachPlayerData(playerData);
StoreOwnershipLedger.EnsureInstance().InitializeIfNeeded();
if (!PlayerEconomyLedger.EnsureInstance().HasEnoughCoins(totalCost))
{
@@ -90,14 +93,95 @@ public static class StoreExpBottlePurchaseService
return false;
}
ExpBottleLedger.EnsureInstance().Add(kind, grantedCount);
if (!GrantPurchasedItem(playerData, itemSO, grantedCount, out failureMessage))
{
PlayerEconomyLedger.EnsureInstance().AddCoins(totalCost);
return false;
}
DebugPurchaseSuccess(itemSO, totalCost, grantedCount);
return true;
}
private static bool RequiresSinglePurchase(storeItemSO itemSO)
{
if (itemSO == null)
{
return false;
}
return itemSO.itemType == storeItemSO.ItemType.character
|| itemSO.itemType == storeItemSO.ItemType.song
|| itemSO.itemType == storeItemSO.ItemType.storyPassage;
}
private static bool ValidateGrantTarget(storeItemSO itemSO, out string failureMessage)
{
failureMessage = string.Empty;
if (itemSO == null)
{
failureMessage = "商品数据丢失";
return false;
}
switch (itemSO.itemType)
{
case storeItemSO.ItemType.consumable:
ExpBottleKind bottleKind;
if (!StoreExpBottleGrantResolver.TryResolve(itemSO, out bottleKind))
{
failureMessage = "当前仅支持经验瓶发放";
return false;
}
return true;
case storeItemSO.ItemType.character:
case storeItemSO.ItemType.song:
case storeItemSO.ItemType.storyPassage:
return StoreOwnershipLedger.EnsureInstance().TryGrantOwnershipPreview(itemSO, out failureMessage);
default:
failureMessage = "当前未配置发放逻辑";
return false;
}
}
private static bool GrantPurchasedItem(Player_SO playerData, storeItemSO itemSO, int grantedCount, out string failureMessage)
{
failureMessage = string.Empty;
if (itemSO == null)
{
failureMessage = "商品数据丢失";
return false;
}
switch (itemSO.itemType)
{
case storeItemSO.ItemType.consumable:
ExpBottleKind bottleKind;
if (!StoreExpBottleGrantResolver.TryResolve(itemSO, out bottleKind))
{
failureMessage = "当前仅支持经验瓶发放";
return false;
}
ExpBottleLedger.EnsureInstance().AttachPlayerData(playerData);
ExpBottleLedger.EnsureInstance().Add(bottleKind, grantedCount);
return true;
case storeItemSO.ItemType.character:
case storeItemSO.ItemType.song:
case storeItemSO.ItemType.storyPassage:
return StoreOwnershipLedger.EnsureInstance().TryGrantOwnership(itemSO, out failureMessage);
default:
failureMessage = "当前未配置发放逻辑";
return false;
}
}
private static void DebugPurchaseSuccess(storeItemSO itemSO, int totalCost, int grantedCount)
{
var snapshot = ExpBottleLedger.EnsureInstance().GetSnapshot();
var builder = new StringBuilder();
builder.Append("[StorePurchase] 已成功购买:");
builder.Append(itemSO != null ? itemSO.itemName : "未知物品");
@@ -105,6 +189,15 @@ public static class StoreExpBottlePurchaseService
builder.Append(totalCost);
builder.Append(" | 发放数量=");
builder.Append(grantedCount);
if (itemSO != null && itemSO.itemType != storeItemSO.ItemType.consumable)
{
builder.Append(" | 已写入OwnershipSave");
Debug.Log(builder.ToString());
return;
}
var snapshot = ExpBottleLedger.EnsureInstance().GetSnapshot();
builder.Append(" | 经验瓶库存:");
bool appendedAny = false;
@@ -0,0 +1,656 @@
using System;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public sealed class StoreOwnershipLedger : MonoBehaviour
{
private const string RuntimeStoreItemResourcesPath = "so/storeSO";
public static StoreOwnershipLedger Instance { get; private set; }
private readonly Dictionary<int, StoreOwnershipEntry> entriesByItemId = new Dictionary<int, StoreOwnershipEntry>();
private readonly List<storeItemSO> cachedStoreItems = new List<storeItemSO>();
private bool initialized;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Bootstrap()
{
EnsureInstance();
}
public static StoreOwnershipLedger EnsureInstance()
{
if (Instance != null)
{
return Instance;
}
var host = new GameObject("__runtime_ownership_bridge");
host.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
DontDestroyOnLoad(host);
Instance = host.AddComponent<StoreOwnershipLedger>();
return Instance;
}
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
InitializeIfNeeded();
}
private void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus)
{
SaveNow();
}
}
private void OnApplicationQuit()
{
SaveNow();
}
public void InitializeIfNeeded()
{
if (initialized)
{
return;
}
StoreOwnershipPayload payload;
StoreOwnershipStorage.TryLoad(out payload);
RebuildFromPayload(payload);
LoadStoreItems();
SeedFromCurrentMirrorFlags();
SyncAllMirrorFlags();
initialized = true;
SaveNow();
}
public void ForceSyncMirrorFlags()
{
InitializeIfNeeded();
LoadStoreItems();
SyncAllMirrorFlags();
}
public bool IsOwned(storeItemSO itemSO)
{
InitializeIfNeeded();
if (itemSO == null)
{
return false;
}
StoreOwnershipEntry entry;
entriesByItemId.TryGetValue(itemSO.itemID, out entry);
switch (itemSO.itemType)
{
case storeItemSO.ItemType.character:
return (entry != null && entry.owned) || (itemSO.associatedAllyHero != null && itemSO.associatedAllyHero.isUnlocked);
case storeItemSO.ItemType.song:
return (entry != null && entry.owned) || (itemSO.associatedSong != null && itemSO.associatedSong.isUnlocked);
case storeItemSO.ItemType.storyPassage:
return IsStoryGrantOwned(itemSO, entry);
default:
return false;
}
}
public bool TryGrantOwnership(storeItemSO itemSO, out string failureMessage)
{
InitializeIfNeeded();
failureMessage = string.Empty;
if (!ValidateGrantTarget(itemSO, out failureMessage))
{
return false;
}
var entry = GetOrCreateEntry(itemSO.itemID);
switch (itemSO.itemType)
{
case storeItemSO.ItemType.character:
case storeItemSO.ItemType.song:
entry.owned = true;
break;
case storeItemSO.ItemType.storyPassage:
GrantStoryOwnership(itemSO, entry);
break;
default:
failureMessage = "当前未配置发放逻辑";
return false;
}
ApplyEntryToItem(itemSO, entry);
SaveNow();
return true;
}
public bool TryGrantOwnershipPreview(storeItemSO itemSO, out string failureMessage)
{
InitializeIfNeeded();
return ValidateGrantTarget(itemSO, out failureMessage);
}
public void SaveNow()
{
if (!initialized)
{
return;
}
StoreOwnershipStorage.TrySave(CreatePayload());
}
private void RebuildFromPayload(StoreOwnershipPayload payload)
{
entriesByItemId.Clear();
if (payload == null || payload.entries == null)
{
return;
}
for (int i = 0; i < payload.entries.Count; i++)
{
var entry = payload.entries[i];
if (entry == null)
{
continue;
}
if (entry.unlockedStorySonIds == null)
{
entry.unlockedStorySonIds = new List<int>();
}
entriesByItemId[entry.storeItemId] = entry;
}
}
private StoreOwnershipPayload CreatePayload()
{
var payload = StoreOwnershipStorage.CreateDefaultPayload();
foreach (var pair in entriesByItemId)
{
payload.entries.Add(CloneEntry(pair.Value));
}
return payload;
}
private void LoadStoreItems()
{
cachedStoreItems.Clear();
var loadedItems = Resources.LoadAll<storeItemSO>(RuntimeStoreItemResourcesPath);
var seen = new HashSet<int>();
for (int i = 0; i < loadedItems.Length; i++)
{
var item = loadedItems[i];
if (item == null || !seen.Add(item.itemID))
{
continue;
}
cachedStoreItems.Add(item);
}
}
private void SeedFromCurrentMirrorFlags()
{
bool changed = false;
for (int i = 0; i < cachedStoreItems.Count; i++)
{
var item = cachedStoreItems[i];
if (item == null || entriesByItemId.ContainsKey(item.itemID))
{
continue;
}
StoreOwnershipEntry seededEntry = TrySeedEntry(item);
if (seededEntry == null)
{
continue;
}
entriesByItemId[item.itemID] = seededEntry;
changed = true;
}
if (changed)
{
SaveNow();
}
}
private StoreOwnershipEntry TrySeedEntry(storeItemSO itemSO)
{
if (itemSO == null)
{
return null;
}
switch (itemSO.itemType)
{
case storeItemSO.ItemType.character:
if (itemSO.associatedAllyHero != null && itemSO.associatedAllyHero.isUnlocked)
{
return new StoreOwnershipEntry { storeItemId = itemSO.itemID, owned = true };
}
break;
case storeItemSO.ItemType.song:
if (itemSO.associatedSong != null && itemSO.associatedSong.isUnlocked)
{
return new StoreOwnershipEntry { storeItemId = itemSO.itemID, owned = true };
}
break;
case storeItemSO.ItemType.storyPassage:
return TrySeedStoryEntry(itemSO);
}
return null;
}
private StoreOwnershipEntry TrySeedStoryEntry(storeItemSO itemSO)
{
var story = itemSO != null ? itemSO.associatedStoryPassage : null;
if (story == null)
{
return null;
}
var entry = new StoreOwnershipEntry { storeItemId = itemSO.itemID };
switch (itemSO.storyPassageGrantMode)
{
case storeItemSO.StoryPassageGrantMode.fatherOnly:
entry.owned = story.isUnlocked;
break;
case storeItemSO.StoryPassageGrantMode.specificSonOnly:
if (HasStorySonUnlocked(story, itemSO.associatedStorySonId))
{
entry.unlockedStorySonIds.Add(itemSO.associatedStorySonId);
}
break;
case storeItemSO.StoryPassageGrantMode.fatherAndSpecificSon:
if (story.isUnlocked || HasStorySonUnlocked(story, itemSO.associatedStorySonId))
{
entry.owned = true;
if (itemSO.associatedStorySonId >= 0)
{
entry.unlockedStorySonIds.Add(itemSO.associatedStorySonId);
}
}
break;
case storeItemSO.StoryPassageGrantMode.allSons:
if (story.isUnlocked || AreAllStorySonsUnlocked(story))
{
entry.owned = true;
AddAllStorySonIds(story, entry.unlockedStorySonIds);
}
break;
}
return entry.owned || entry.unlockedStorySonIds.Count > 0 ? entry : null;
}
private void SyncAllMirrorFlags()
{
for (int i = 0; i < cachedStoreItems.Count; i++)
{
var item = cachedStoreItems[i];
if (item == null)
{
continue;
}
StoreOwnershipEntry entry;
if (!entriesByItemId.TryGetValue(item.itemID, out entry))
{
continue;
}
ApplyEntryToItem(item, entry);
}
}
private bool ValidateGrantTarget(storeItemSO itemSO, out string failureMessage)
{
failureMessage = string.Empty;
if (itemSO == null)
{
failureMessage = "商品数据丢失";
return false;
}
if (IsOwned(itemSO))
{
failureMessage = "物品已解锁";
return false;
}
switch (itemSO.itemType)
{
case storeItemSO.ItemType.character:
if (itemSO.associatedAllyHero == null)
{
failureMessage = "角色数据未配置";
return false;
}
return true;
case storeItemSO.ItemType.song:
if (itemSO.associatedSong == null)
{
failureMessage = "歌曲数据未配置";
return false;
}
return true;
case storeItemSO.ItemType.storyPassage:
if (itemSO.associatedStoryPassage == null)
{
failureMessage = "剧情数据未配置";
return false;
}
if (RequiresSpecificStorySon(itemSO.storyPassageGrantMode))
{
if (itemSO.associatedStorySonId < 0 || FindStorySonIndex(itemSO.associatedStoryPassage, itemSO.associatedStorySonId) < 0)
{
failureMessage = "剧情子章节未配置";
return false;
}
}
return true;
default:
failureMessage = "当前未配置发放逻辑";
return false;
}
}
private void GrantStoryOwnership(storeItemSO itemSO, StoreOwnershipEntry entry)
{
if (entry.unlockedStorySonIds == null)
{
entry.unlockedStorySonIds = new List<int>();
}
switch (itemSO.storyPassageGrantMode)
{
case storeItemSO.StoryPassageGrantMode.fatherOnly:
entry.owned = true;
break;
case storeItemSO.StoryPassageGrantMode.specificSonOnly:
AddStorySonId(entry.unlockedStorySonIds, itemSO.associatedStorySonId);
break;
case storeItemSO.StoryPassageGrantMode.fatherAndSpecificSon:
entry.owned = true;
AddStorySonId(entry.unlockedStorySonIds, itemSO.associatedStorySonId);
break;
case storeItemSO.StoryPassageGrantMode.allSons:
entry.owned = true;
AddAllStorySonIds(itemSO.associatedStoryPassage, entry.unlockedStorySonIds);
break;
}
}
private void ApplyEntryToItem(storeItemSO itemSO, StoreOwnershipEntry entry)
{
if (itemSO == null || entry == null)
{
return;
}
switch (itemSO.itemType)
{
case storeItemSO.ItemType.character:
if (entry.owned && itemSO.associatedAllyHero != null)
{
itemSO.associatedAllyHero.isUnlocked = true;
MarkDirty(itemSO.associatedAllyHero);
}
break;
case storeItemSO.ItemType.song:
if (entry.owned && itemSO.associatedSong != null)
{
itemSO.associatedSong.isUnlocked = true;
MarkDirty(itemSO.associatedSong);
}
break;
case storeItemSO.ItemType.storyPassage:
ApplyStoryEntry(itemSO.associatedStoryPassage, entry);
break;
}
}
private void ApplyStoryEntry(notebook_faterType story, StoreOwnershipEntry entry)
{
if (story == null || entry == null)
{
return;
}
if (entry.owned || (entry.unlockedStorySonIds != null && entry.unlockedStorySonIds.Count > 0))
{
story.isUnlocked = true;
MarkDirty(story);
}
if (entry.unlockedStorySonIds == null || story.sonList == null)
{
return;
}
for (int i = 0; i < entry.unlockedStorySonIds.Count; i++)
{
int targetSonId = entry.unlockedStorySonIds[i];
int sonIndex = FindStorySonIndex(story, targetSonId);
if (sonIndex < 0)
{
continue;
}
story.sonList[sonIndex].son_isUnlocked = true;
MarkDirty(story);
}
}
private bool IsStoryGrantOwned(storeItemSO itemSO, StoreOwnershipEntry entry)
{
if (itemSO == null || itemSO.associatedStoryPassage == null)
{
return false;
}
switch (itemSO.storyPassageGrantMode)
{
case storeItemSO.StoryPassageGrantMode.fatherOnly:
return entry != null && entry.owned;
case storeItemSO.StoryPassageGrantMode.specificSonOnly:
return HasEntryStorySon(entry, itemSO.associatedStorySonId)
|| HasStorySonUnlocked(itemSO.associatedStoryPassage, itemSO.associatedStorySonId);
case storeItemSO.StoryPassageGrantMode.fatherAndSpecificSon:
return (entry != null && entry.owned)
|| HasEntryStorySon(entry, itemSO.associatedStorySonId)
|| HasStorySonUnlocked(itemSO.associatedStoryPassage, itemSO.associatedStorySonId);
case storeItemSO.StoryPassageGrantMode.allSons:
return (entry != null && entry.owned) || AreAllStorySonsUnlocked(itemSO.associatedStoryPassage);
default:
return false;
}
}
private static bool HasEntryStorySon(StoreOwnershipEntry entry, int sonId)
{
if (entry == null || entry.unlockedStorySonIds == null || sonId < 0)
{
return false;
}
for (int i = 0; i < entry.unlockedStorySonIds.Count; i++)
{
if (entry.unlockedStorySonIds[i] == sonId)
{
return true;
}
}
return false;
}
private static bool HasStorySonUnlocked(notebook_faterType story, int sonId)
{
int index = FindStorySonIndex(story, sonId);
return index >= 0 && story.sonList[index].son_isUnlocked;
}
private static bool AreAllStorySonsUnlocked(notebook_faterType story)
{
if (story == null || story.sonList == null || story.sonList.Count == 0)
{
return false;
}
for (int i = 0; i < story.sonList.Count; i++)
{
var son = story.sonList[i];
if (son == null || !son.son_isUnlocked)
{
return false;
}
}
return true;
}
private static int FindStorySonIndex(notebook_faterType story, int sonId)
{
if (story == null || story.sonList == null || sonId < 0)
{
return -1;
}
for (int i = 0; i < story.sonList.Count; i++)
{
var son = story.sonList[i];
if (son != null && son.son_id == sonId)
{
return i;
}
}
return -1;
}
private static bool RequiresSpecificStorySon(storeItemSO.StoryPassageGrantMode mode)
{
return mode == storeItemSO.StoryPassageGrantMode.specificSonOnly
|| mode == storeItemSO.StoryPassageGrantMode.fatherAndSpecificSon;
}
private static void AddStorySonId(List<int> sonIds, int sonId)
{
if (sonIds == null || sonId < 0)
{
return;
}
for (int i = 0; i < sonIds.Count; i++)
{
if (sonIds[i] == sonId)
{
return;
}
}
sonIds.Add(sonId);
}
private static void AddAllStorySonIds(notebook_faterType story, List<int> sonIds)
{
if (story == null || story.sonList == null || sonIds == null)
{
return;
}
for (int i = 0; i < story.sonList.Count; i++)
{
var son = story.sonList[i];
if (son == null)
{
continue;
}
AddStorySonId(sonIds, son.son_id);
}
}
private StoreOwnershipEntry GetOrCreateEntry(int storeItemId)
{
StoreOwnershipEntry entry;
if (entriesByItemId.TryGetValue(storeItemId, out entry))
{
return entry;
}
entry = new StoreOwnershipEntry
{
storeItemId = storeItemId,
owned = false,
unlockedStorySonIds = new List<int>()
};
entriesByItemId[storeItemId] = entry;
return entry;
}
private static StoreOwnershipEntry CloneEntry(StoreOwnershipEntry source)
{
return new StoreOwnershipEntry
{
storeItemId = source.storeItemId,
owned = source.owned,
unlockedStorySonIds = source.unlockedStorySonIds != null
? new List<int>(source.unlockedStorySonIds)
: new List<int>()
};
}
private static void MarkDirty(UnityEngine.Object target)
{
#if UNITY_EDITOR
if (target != null)
{
EditorUtility.SetDirty(target);
}
#endif
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 873f2d3326b8e9c42b3ad9757ef2b4b2
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
[Serializable]
public class StoreOwnershipEntry
{
public int storeItemId;
public bool owned;
public List<int> unlockedStorySonIds = new List<int>();
}
[Serializable]
public class StoreOwnershipPayload
{
public int version;
public long lastUpdatedUtcTicks;
public List<StoreOwnershipEntry> entries = new List<StoreOwnershipEntry>();
}
[Serializable]
public class StoreOwnershipEnvelope
{
public string payload;
public string signature;
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: df11f9fb00f4cb949ad6556e341680bc
@@ -0,0 +1,202 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using UnityEngine;
public static class StoreOwnershipStorage
{
private const string SecretSeed = "ban_total.store_ownership.v1";
private const string VaultDirectoryName = ".cache_bridge";
private const string MainFileName = ".own.dat";
private const string BackupFileName = ".own.bak";
private const string TempFileName = ".own.tmp";
private static string VaultDirectoryPath
{
get { return Path.Combine(Application.persistentDataPath, VaultDirectoryName); }
}
private static string MainFilePath
{
get { return Path.Combine(VaultDirectoryPath, MainFileName); }
}
private static string BackupFilePath
{
get { return Path.Combine(VaultDirectoryPath, BackupFileName); }
}
private static string TempFilePath
{
get { return Path.Combine(VaultDirectoryPath, TempFileName); }
}
public static bool TryLoad(out StoreOwnershipPayload payload)
{
payload = CreateDefaultPayload();
if (TryReadPayload(MainFilePath, out payload))
{
return true;
}
if (TryReadPayload(BackupFilePath, out payload))
{
TrySave(payload);
return true;
}
return false;
}
public static bool TrySave(StoreOwnershipPayload payload)
{
try
{
Directory.CreateDirectory(VaultDirectoryPath);
TryHidePath(VaultDirectoryPath);
var envelopeJson = BuildEnvelopeJson(payload);
File.WriteAllText(TempFilePath, envelopeJson, Encoding.UTF8);
TryHidePath(TempFilePath);
if (File.Exists(MainFilePath))
{
File.Copy(MainFilePath, BackupFilePath, true);
TryHidePath(BackupFilePath);
}
File.Copy(TempFilePath, MainFilePath, true);
TryHidePath(MainFilePath);
File.Delete(TempFilePath);
return true;
}
catch (Exception ex)
{
Debug.LogWarning($"[StoreOwnershipStorage] Save failed: {ex.Message}");
return false;
}
}
public static StoreOwnershipPayload CreateDefaultPayload()
{
return new StoreOwnershipPayload
{
version = 1,
lastUpdatedUtcTicks = DateTime.UtcNow.Ticks,
entries = new System.Collections.Generic.List<StoreOwnershipEntry>()
};
}
private static bool TryReadPayload(string path, out StoreOwnershipPayload payload)
{
payload = CreateDefaultPayload();
if (!File.Exists(path))
{
return false;
}
try
{
var envelopeJson = File.ReadAllText(path, Encoding.UTF8);
var envelope = JsonUtility.FromJson<StoreOwnershipEnvelope>(envelopeJson);
if (envelope == null || string.IsNullOrEmpty(envelope.payload) || string.IsNullOrEmpty(envelope.signature))
{
return false;
}
var expectedSignature = ComputeSignature(envelope.payload);
if (!string.Equals(expectedSignature, envelope.signature, StringComparison.Ordinal))
{
Debug.LogWarning("[StoreOwnershipStorage] Save signature mismatch. Possible tampering detected.");
return false;
}
var encryptedBytes = Convert.FromBase64String(envelope.payload);
var plainBytes = XorTransform(encryptedBytes, BuildKeyBytes());
var payloadJson = Encoding.UTF8.GetString(plainBytes);
var loadedPayload = JsonUtility.FromJson<StoreOwnershipPayload>(payloadJson);
if (loadedPayload == null)
{
return false;
}
payload = loadedPayload;
return true;
}
catch (Exception ex)
{
Debug.LogWarning($"[StoreOwnershipStorage] Load failed from '{path}': {ex.Message}");
return false;
}
}
private static string BuildEnvelopeJson(StoreOwnershipPayload payload)
{
payload.lastUpdatedUtcTicks = DateTime.UtcNow.Ticks;
var payloadJson = JsonUtility.ToJson(payload, false);
var plainBytes = Encoding.UTF8.GetBytes(payloadJson);
var encryptedBytes = XorTransform(plainBytes, BuildKeyBytes());
var payloadBase64 = Convert.ToBase64String(encryptedBytes);
var envelope = new StoreOwnershipEnvelope
{
payload = payloadBase64,
signature = ComputeSignature(payloadBase64)
};
return JsonUtility.ToJson(envelope, false);
}
private static string ComputeSignature(string payloadBase64)
{
var signText = payloadBase64 + "|" + Application.identifier + "|" + SecretSeed + "|" + SystemInfo.deviceUniqueIdentifier;
using (var sha = SHA256.Create())
{
var bytes = Encoding.UTF8.GetBytes(signText);
var hash = sha.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
}
private static byte[] BuildKeyBytes()
{
using (var sha = SHA256.Create())
{
var seed = Application.identifier + "|" + SystemInfo.deviceUniqueIdentifier + "|" + SecretSeed;
return sha.ComputeHash(Encoding.UTF8.GetBytes(seed));
}
}
private static byte[] XorTransform(byte[] source, byte[] key)
{
var result = new byte[source.Length];
for (int i = 0; i < source.Length; i++)
{
result[i] = (byte)(source[i] ^ key[i % key.Length]);
}
return result;
}
private static void TryHidePath(string path)
{
try
{
if (Application.platform != RuntimePlatform.WindowsEditor && Application.platform != RuntimePlatform.WindowsPlayer)
{
return;
}
var attributes = File.GetAttributes(path);
if ((attributes & FileAttributes.Hidden) == 0)
{
File.SetAttributes(path, attributes | FileAttributes.Hidden);
}
}
catch
{
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7f902609758920443b9df12a43213dd6
+32 -4
View File
@@ -9,12 +9,13 @@
});
}
public static void ReportPlaySong()
public static void ReportPlaySong(int songID = 0)
{
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
{
taskType = userTasksPool.TaskType.PlaySong,
amount = 1f
amount = 1f,
songID = songID
});
}
@@ -36,6 +37,15 @@
});
}
public static void ReportWatchStory()
{
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
{
taskType = userTasksPool.TaskType.WatchStory,
amount = 1f
});
}
public static void ReportUseItem(int amount)
{
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
@@ -54,11 +64,29 @@
});
}
public static void ReportGameDuration(float durationSeconds)
public static void ReportSingleRunCombo(int combo)
{
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
{
taskType = userTasksPool.TaskType.GameDuration,
taskType = userTasksPool.TaskType.SingleRunCombo,
amount = combo
});
}
public static void ReportSingleRunScore(float totalScore)
{
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
{
taskType = userTasksPool.TaskType.SingleRunScore,
amount = totalScore
});
}
public static void ReportOnlineDuration(float durationSeconds)
{
DailyTaskService.EnsureInstance().ReportEvent(new DailyTaskEventData
{
taskType = userTasksPool.TaskType.OnlineDuration,
amount = durationSeconds
});
}
@@ -14,15 +14,25 @@ public class DailyTaskRuntimeEntry
public class DailyTaskSaveData
{
public string dateKey;
public int dateStamp;
public int refreshUsedCount;
public int trustedDateStamp;
public int lastRefreshDateStamp;
public long lastRefreshLocalTicks;
public long lastRefreshUtcTicks;
public long lastSeenLocalTicks;
public long lastSeenUtcTicks;
public int timeRollbackDetections;
public List<DailyTaskRuntimeEntry> activeTasks = new List<DailyTaskRuntimeEntry>();
public List<DailyTaskAccumulatedProgress> accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
public List<DailyTaskUniqueIntProgress> uniqueIntProgress = new List<DailyTaskUniqueIntProgress>();
}
public struct DailyTaskEventData
{
public userTasksPool.TaskType taskType;
public float amount;
public int songID;
}
[Serializable]
@@ -32,6 +42,13 @@ public class DailyTaskAccumulatedProgress
public float progress;
}
[Serializable]
public class DailyTaskUniqueIntProgress
{
public int taskType;
public List<int> values = new List<int>();
}
public sealed class DailyTaskViewData
{
public userTasksPool.TaskDefinition definition;
@@ -4,24 +4,26 @@ using UnityEngine;
public static class DailyTaskSaveService
{
private static string SaveFilePath
private const string SaveCategory = "daily_task";
private const string SaveKey = "runtime";
private static string LegacySaveFilePath
{
get { return Path.Combine(Application.persistentDataPath, "daily_tasks.json"); }
}
public static DailyTaskSaveData Load()
{
if (!File.Exists(SaveFilePath))
{
return new DailyTaskSaveData();
}
try
{
var json = File.ReadAllText(SaveFilePath);
var data = JsonUtility.FromJson<DailyTaskSaveData>(json);
DailyTaskSaveData data;
if (SecureSaveVault.TryLoadJson(SaveCategory, SaveKey, out data, LegacySaveFilePath))
{
return data ?? new DailyTaskSaveData();
}
return new DailyTaskSaveData();
}
catch (Exception ex)
{
Debug.LogWarning($"[DailyTaskSaveService] Load failed: {ex.Message}");
@@ -38,8 +40,7 @@ public static class DailyTaskSaveService
try
{
var json = JsonUtility.ToJson(data, true);
File.WriteAllText(SaveFilePath, json);
SecureSaveVault.SaveJson(SaveCategory, SaveKey, data, LegacySaveFilePath);
}
catch (Exception ex)
{
+325 -67
View File
@@ -10,6 +10,14 @@ public sealed class DailyTaskService : MonoBehaviour
public event Action OnTasksChanged;
private enum ProgressCombineMode
{
Sum,
Max
}
private const float OnlineDurationFlushStepSeconds = 5f;
private readonly Queue<DailyTaskEventData> pendingEvents = new Queue<DailyTaskEventData>();
private readonly Dictionary<string, userTasksPool.TaskDefinition> definitionById = new Dictionary<string, userTasksPool.TaskDefinition>(StringComparer.Ordinal);
@@ -18,6 +26,8 @@ public sealed class DailyTaskService : MonoBehaviour
private int configuredTaskMaxSize = 5;
private int configuredRefreshMaxTimes = 3;
private bool initialized;
private bool appFocused = true;
private float pendingOnlineDurationSeconds;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Bootstrap()
@@ -54,10 +64,53 @@ public sealed class DailyTaskService : MonoBehaviour
InitializeStorageIfNeeded();
}
private void Update()
{
if (!Application.isPlaying || !appFocused)
{
return;
}
pendingOnlineDurationSeconds += Time.unscaledDeltaTime;
if (pendingOnlineDurationSeconds >= OnlineDurationFlushStepSeconds)
{
float flushSeconds = Mathf.Floor(pendingOnlineDurationSeconds);
pendingOnlineDurationSeconds = Mathf.Max(0f, pendingOnlineDurationSeconds - flushSeconds);
ReportEvent(new DailyTaskEventData
{
taskType = userTasksPool.TaskType.OnlineDuration,
amount = flushSeconds
});
}
}
private void OnApplicationFocus(bool hasFocus)
{
appFocused = hasFocus;
if (!hasFocus)
{
FlushPendingOnlineDuration();
}
}
private void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus)
{
FlushPendingOnlineDuration();
}
}
private void OnApplicationQuit()
{
FlushPendingOnlineDuration();
}
private void OnDestroy()
{
if (Instance == this)
{
FlushPendingOnlineDuration();
SceneManager.sceneLoaded -= OnSceneLoaded;
}
}
@@ -117,15 +170,11 @@ public sealed class DailyTaskService : MonoBehaviour
}
EnsureTodayTasks();
float accumulatedIncrement = GetAccumulatedIncrement(eventData);
if (accumulatedIncrement <= 0f)
bool changed = ApplyEventToTrackedProgress(eventData);
if (saveData != null && saveData.activeTasks != null)
{
return;
}
float accumulatedValue = AddAccumulatedProgress(eventData.taskType, accumulatedIncrement);
bool changed = false;
for (int i = 0; i < saveData.activeTasks.Count; i++)
{
var runtimeEntry = saveData.activeTasks[i];
@@ -145,16 +194,17 @@ public sealed class DailyTaskService : MonoBehaviour
continue;
}
float previousProgress = runtimeEntry.progress;
bool previousCompleted = runtimeEntry.isCompleted;
runtimeEntry.progress = GetInitialProgress(definition, accumulatedValue);
runtimeEntry.isCompleted = runtimeEntry.progress >= Mathf.Max(0.0001f, definition.targetValue);
float nextProgress = CalculateProgressForDefinition(definition);
bool nextCompleted = nextProgress >= Mathf.Max(0.0001f, definition.targetValue);
if (!Mathf.Approximately(previousProgress, runtimeEntry.progress) || previousCompleted != runtimeEntry.isCompleted)
if (!Mathf.Approximately(runtimeEntry.progress, nextProgress) || runtimeEntry.isCompleted != nextCompleted)
{
runtimeEntry.progress = nextProgress;
runtimeEntry.isCompleted = nextCompleted;
changed = true;
}
}
}
if (changed)
{
@@ -177,6 +227,7 @@ public sealed class DailyTaskService : MonoBehaviour
}
saveData.refreshUsedCount += 1;
StampRefreshUsage();
GenerateDailyTasks();
Save();
NotifyTasksChanged();
@@ -278,11 +329,7 @@ public sealed class DailyTaskService : MonoBehaviour
{
if (string.Equals(scene.name, "UI_UI", StringComparison.OrdinalIgnoreCase))
{
ReportEvent(new DailyTaskEventData
{
taskType = userTasksPool.TaskType.Login,
amount = 1f
});
DailyTaskEventHub.ReportLogin();
}
}
@@ -294,6 +341,7 @@ public sealed class DailyTaskService : MonoBehaviour
}
saveData = DailyTaskSaveService.Load() ?? new DailyTaskSaveData();
EnsureRuntimeCollections();
initialized = true;
}
@@ -306,25 +354,36 @@ public sealed class DailyTaskService : MonoBehaviour
return;
}
string todayKey = GetTodayKey();
bool timeStateChanged;
int effectiveTodayStamp = GetEffectiveTodayStamp(out timeStateChanged);
string todayKey = FormatDateKey(effectiveTodayStamp);
if (saveData == null)
{
saveData = new DailyTaskSaveData();
}
bool needRegenerate = !string.Equals(saveData.dateKey, todayKey, StringComparison.Ordinal)
EnsureRuntimeCollections();
bool needRegenerate = saveData.dateStamp != effectiveTodayStamp
|| !string.Equals(saveData.dateKey, todayKey, StringComparison.Ordinal)
|| saveData.activeTasks == null
|| saveData.activeTasks.Count == 0;
if (!needRegenerate)
{
EnsureAccumulatedProgressCompatibility();
if (timeStateChanged)
{
Save();
}
return;
}
saveData.dateKey = todayKey;
saveData.dateStamp = effectiveTodayStamp;
saveData.refreshUsedCount = 0;
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
saveData.uniqueIntProgress = new List<DailyTaskUniqueIntProgress>();
GenerateDailyTasks();
Save();
}
@@ -348,7 +407,8 @@ public sealed class DailyTaskService : MonoBehaviour
}
int pickCount = Mathf.Min(configuredTaskMaxSize, candidates.Count);
var random = new System.Random(unchecked(GetTodayKey().GetHashCode() + saveData.refreshUsedCount * 397));
int dateSeed = saveData.dateStamp > 0 ? saveData.dateStamp : GetLocalDateStamp(DateTime.Now);
var random = new System.Random(unchecked(dateSeed + saveData.refreshUsedCount * 397));
var selectedDefinitions = new List<userTasksPool.TaskDefinition>();
while (selectedDefinitions.Count < pickCount && candidates.Count > 0)
@@ -374,7 +434,7 @@ public sealed class DailyTaskService : MonoBehaviour
for (int i = 0; i < selectedDefinitions.Count; i++)
{
var definition = selectedDefinitions[i];
float currentProgress = GetInitialProgress(definition, GetAccumulatedProgress(definition.taskType));
float currentProgress = CalculateProgressForDefinition(definition);
saveData.activeTasks.Add(new DailyTaskRuntimeEntry
{
taskID = definition.taskID,
@@ -415,15 +475,7 @@ public sealed class DailyTaskService : MonoBehaviour
private void EnsureAccumulatedProgressCompatibility()
{
if (saveData == null)
{
return;
}
if (saveData.accumulatedProgress == null)
{
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
}
EnsureRuntimeCollections();
if (saveData.accumulatedProgress.Count > 0 || saveData.activeTasks == null || saveData.activeTasks.Count == 0)
{
@@ -445,8 +497,12 @@ public sealed class DailyTaskService : MonoBehaviour
continue;
}
AddAccumulatedProgress(definition.taskType, Mathf.Max(0f, runtimeEntry.progress));
changed = true;
if (definition.taskType == userTasksPool.TaskType.DifferentSongs)
{
continue;
}
changed |= AddOrCombineProgress(definition.taskType, Mathf.Max(0f, runtimeEntry.progress), GetCombineMode(definition.taskType));
}
if (changed)
@@ -468,23 +524,74 @@ public sealed class DailyTaskService : MonoBehaviour
}
}
private bool ApplyEventToTrackedProgress(DailyTaskEventData eventData)
{
switch (eventData.taskType)
{
case userTasksPool.TaskType.Login:
case userTasksPool.TaskType.PlaySong:
case userTasksPool.TaskType.FullCombo:
case userTasksPool.TaskType.WatchStory:
case userTasksPool.TaskType.UseItem:
case userTasksPool.TaskType.ShareGame:
{
bool changed = AddOrCombineProgress(eventData.taskType, Mathf.Max(1f, eventData.amount), ProgressCombineMode.Sum);
if (eventData.taskType == userTasksPool.TaskType.PlaySong && eventData.songID > 0)
{
changed |= AddUniqueIntValue(userTasksPool.TaskType.DifferentSongs, eventData.songID);
}
return changed;
}
case userTasksPool.TaskType.TotalScore:
case userTasksPool.TaskType.SpendCoins:
case userTasksPool.TaskType.GameDuration:
case userTasksPool.TaskType.OnlineDuration:
return AddOrCombineProgress(eventData.taskType, Mathf.Max(0f, eventData.amount), ProgressCombineMode.Sum);
case userTasksPool.TaskType.SingleRunCombo:
case userTasksPool.TaskType.SingleRunScore:
return AddOrCombineProgress(eventData.taskType, Mathf.Max(0f, eventData.amount), ProgressCombineMode.Max);
default:
return false;
}
}
private static bool IsEventMatch(userTasksPool.TaskDefinition definition, DailyTaskEventData eventData)
{
return definition != null && definition.taskType == eventData.taskType;
if (definition == null)
{
return false;
}
private float AddAccumulatedProgress(userTasksPool.TaskType taskType, float amount)
if (definition.taskType == eventData.taskType)
{
if (saveData == null)
{
saveData = new DailyTaskSaveData();
return true;
}
if (saveData.accumulatedProgress == null)
{
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
return definition.taskType == userTasksPool.TaskType.DifferentSongs && eventData.taskType == userTasksPool.TaskType.PlaySong;
}
private float CalculateProgressForDefinition(userTasksPool.TaskDefinition definition)
{
if (definition == null)
{
return 0f;
}
float targetValue = Mathf.Max(0.0001f, definition.targetValue);
switch (definition.taskType)
{
case userTasksPool.TaskType.DifferentSongs:
return Mathf.Min(GetUniqueIntCount(userTasksPool.TaskType.DifferentSongs), targetValue);
default:
return Mathf.Min(Mathf.Max(0f, GetAccumulatedProgress(definition.taskType)), targetValue);
}
}
private bool AddOrCombineProgress(userTasksPool.TaskType taskType, float amount, ProgressCombineMode combineMode)
{
EnsureRuntimeCollections();
int taskTypeValue = (int)taskType;
for (int i = 0; i < saveData.accumulatedProgress.Count; i++)
{
@@ -494,17 +601,25 @@ public sealed class DailyTaskService : MonoBehaviour
continue;
}
entry.progress = Mathf.Max(0f, entry.progress + amount);
return entry.progress;
float nextValue = combineMode == ProgressCombineMode.Max
? Mathf.Max(entry.progress, amount)
: Mathf.Max(0f, entry.progress + amount);
if (Mathf.Approximately(entry.progress, nextValue))
{
return false;
}
var newEntry = new DailyTaskAccumulatedProgress
entry.progress = nextValue;
return true;
}
saveData.accumulatedProgress.Add(new DailyTaskAccumulatedProgress
{
taskType = taskTypeValue,
progress = Mathf.Max(0f, amount)
};
saveData.accumulatedProgress.Add(newEntry);
return newEntry.progress;
});
return true;
}
private float GetAccumulatedProgress(userTasksPool.TaskType taskType)
@@ -529,14 +644,151 @@ public sealed class DailyTaskService : MonoBehaviour
return 0f;
}
private static float GetInitialProgress(userTasksPool.TaskDefinition definition, float accumulatedValue)
private bool AddUniqueIntValue(userTasksPool.TaskType taskType, int value)
{
if (definition == null)
if (value <= 0)
{
return 0f;
return false;
}
return Mathf.Min(Mathf.Max(0f, accumulatedValue), Mathf.Max(0.0001f, definition.targetValue));
EnsureRuntimeCollections();
int taskTypeValue = (int)taskType;
for (int i = 0; i < saveData.uniqueIntProgress.Count; i++)
{
var entry = saveData.uniqueIntProgress[i];
if (entry == null || entry.taskType != taskTypeValue)
{
continue;
}
if (entry.values == null)
{
entry.values = new List<int>();
}
if (entry.values.Contains(value))
{
return false;
}
entry.values.Add(value);
return true;
}
saveData.uniqueIntProgress.Add(new DailyTaskUniqueIntProgress
{
taskType = taskTypeValue,
values = new List<int> { value }
});
return true;
}
private int GetUniqueIntCount(userTasksPool.TaskType taskType)
{
if (saveData == null || saveData.uniqueIntProgress == null)
{
return 0;
}
int taskTypeValue = (int)taskType;
for (int i = 0; i < saveData.uniqueIntProgress.Count; i++)
{
var entry = saveData.uniqueIntProgress[i];
if (entry == null || entry.taskType != taskTypeValue || entry.values == null)
{
continue;
}
return entry.values.Count;
}
return 0;
}
private static ProgressCombineMode GetCombineMode(userTasksPool.TaskType taskType)
{
switch (taskType)
{
case userTasksPool.TaskType.SingleRunCombo:
case userTasksPool.TaskType.SingleRunScore:
return ProgressCombineMode.Max;
default:
return ProgressCombineMode.Sum;
}
}
private void EnsureRuntimeCollections()
{
if (saveData == null)
{
saveData = new DailyTaskSaveData();
}
if (saveData.activeTasks == null)
{
saveData.activeTasks = new List<DailyTaskRuntimeEntry>();
}
if (saveData.accumulatedProgress == null)
{
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
}
if (saveData.uniqueIntProgress == null)
{
saveData.uniqueIntProgress = new List<DailyTaskUniqueIntProgress>();
}
}
private int GetEffectiveTodayStamp(out bool stateChanged)
{
stateChanged = false;
if (saveData == null)
{
saveData = new DailyTaskSaveData();
}
int currentStamp = GetLocalDateStamp(DateTime.Now);
long nowLocalTicks = DateTime.Now.Ticks;
long nowUtcTicks = DateTime.UtcNow.Ticks;
if (saveData.trustedDateStamp <= 0)
{
saveData.trustedDateStamp = currentStamp;
stateChanged = true;
}
else if (currentStamp < saveData.trustedDateStamp)
{
saveData.timeRollbackDetections += 1;
currentStamp = saveData.trustedDateStamp;
stateChanged = true;
}
else if (currentStamp > saveData.trustedDateStamp)
{
saveData.trustedDateStamp = currentStamp;
stateChanged = true;
}
if (saveData.lastSeenLocalTicks != nowLocalTicks)
{
saveData.lastSeenLocalTicks = nowLocalTicks;
stateChanged = true;
}
if (saveData.lastSeenUtcTicks != nowUtcTicks)
{
saveData.lastSeenUtcTicks = nowUtcTicks;
stateChanged = true;
}
return Mathf.Max(currentStamp, saveData.trustedDateStamp);
}
private void StampRefreshUsage()
{
saveData.lastRefreshDateStamp = saveData.dateStamp;
saveData.lastRefreshLocalTicks = DateTime.Now.Ticks;
saveData.lastRefreshUtcTicks = DateTime.UtcNow.Ticks;
}
private bool TryGetTaskPair(string taskID, out DailyTaskRuntimeEntry runtimeEntry, out userTasksPool.TaskDefinition definition)
@@ -627,27 +879,33 @@ public sealed class DailyTaskService : MonoBehaviour
}
}
private static float GetAccumulatedIncrement(DailyTaskEventData eventData)
private void FlushPendingOnlineDuration()
{
switch (eventData.taskType)
if (pendingOnlineDurationSeconds <= 0f)
{
case userTasksPool.TaskType.TotalScore:
case userTasksPool.TaskType.SpendCoins:
case userTasksPool.TaskType.GameDuration:
return Mathf.Max(0f, eventData.amount);
case userTasksPool.TaskType.Login:
case userTasksPool.TaskType.PlaySong:
case userTasksPool.TaskType.FullCombo:
case userTasksPool.TaskType.UseItem:
return Mathf.Max(1f, eventData.amount);
default:
return Mathf.Max(0f, eventData.amount);
}
return;
}
private static string GetTodayKey()
float flushSeconds = pendingOnlineDurationSeconds;
pendingOnlineDurationSeconds = 0f;
ReportEvent(new DailyTaskEventData
{
return DateTime.Now.ToString("yyyy-MM-dd");
taskType = userTasksPool.TaskType.OnlineDuration,
amount = flushSeconds
});
}
private static int GetLocalDateStamp(DateTime dateTime)
{
return dateTime.Year * 10000 + dateTime.Month * 100 + dateTime.Day;
}
private static string FormatDateKey(int dateStamp)
{
int year = dateStamp / 10000;
int month = (dateStamp / 100) % 100;
int day = dateStamp % 100;
return year.ToString("D4") + "-" + month.ToString("D2") + "-" + day.ToString("D2");
}
private void Save()
@@ -1,4 +1,4 @@
%YAML 1.1
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
@@ -14,78 +14,111 @@ MonoBehaviour:
m_EditorClassIdentifier:
tasks:
- taskID: 10101
description: "\u767B\u5F55\u6E38\u620Fv50"
description: 登录游戏 1 次
taskType: 0
refreshFrequency: 1
targetValue: 1
rewardType: 0
rewardAmount: 50
rewardAmount: 1
rewardExpBottleKind: 0
rewardDushMaterialKind: 0
addToTaskPool: 1
selectionWeight: 1
- taskID: 10102
description: "\u5B8C\u62103\u9996\u6B4C\u66F2"
description: 完成任意歌曲 3 首
taskType: 1
refreshFrequency: 1
targetValue: 3
rewardType: 0
rewardAmount: 0
rewardAmount: 1
rewardExpBottleKind: 0
rewardDushMaterialKind: 0
addToTaskPool: 1
selectionWeight: 1
- taskID: 10103
description: "\u8FBE\u62101\u6B21Full Combo"
taskType: 2
description: 单局达到 99 连击
taskType: 9
refreshFrequency: 1
targetValue: 1
targetValue: 99
rewardType: 0
rewardAmount: 0
rewardAmount: 1
rewardExpBottleKind: 0
rewardDushMaterialKind: 0
addToTaskPool: 1
selectionWeight: 1
- taskID: 10104
description: "\u7D2F\u8BA1\u83B7\u5F971000000\u5206"
taskType: 3
description: 单局总分达到 1000000
taskType: 10
refreshFrequency: 1
targetValue: 1000000
rewardType: 1
rewardAmount: 4
rewardType: 0
rewardAmount: 1
rewardExpBottleKind: 0
rewardDushMaterialKind: 0
addToTaskPool: 1
selectionWeight: 1
- taskID: 10105
description: "\u4F7F\u7528\u4EFB\u610F\u7C7B\u578B\u7ECF\u9A8C\u74F6\u4E00\u6B21"
taskType: 5
description: 今日累计分数达到 10000000
taskType: 3
refreshFrequency: 1
targetValue: 1
targetValue: 10000000
rewardType: 0
rewardAmount: 0
rewardAmount: 1
rewardExpBottleKind: 0
rewardDushMaterialKind: 0
addToTaskPool: 1
selectionWeight: 1
- taskID: 10106
description: "\u82B1\u8D391000\u91D1\u5E01"
taskType: 7
description: 阅读剧情 1 次
taskType: 4
refreshFrequency: 1
targetValue: 1000
targetValue: 1
rewardType: 0
rewardAmount: 0
rewardAmount: 1
rewardExpBottleKind: 0
rewardDushMaterialKind: 0
addToTaskPool: 1
selectionWeight: 1
- taskID: 10107
description: "\u6E38\u620F\u65F6\u957F\u8FBE\u523030\u5206\u949F"
taskType: 8
description: 使用任意经验提升或突破材料 1 次
taskType: 5
refreshFrequency: 1
targetValue: 1800
targetValue: 1
rewardType: 0
rewardAmount: 0
rewardAmount: 1
rewardExpBottleKind: 0
rewardDushMaterialKind: 0
addToTaskPool: 1
selectionWeight: 1
- taskID: 10108
description: 在线 15 分钟
taskType: 11
refreshFrequency: 1
targetValue: 900
rewardType: 0
rewardAmount: 1
rewardExpBottleKind: 0
rewardDushMaterialKind: 0
addToTaskPool: 1
selectionWeight: 1
- taskID: 10109
description: 消费 2000 coins
taskType: 7
refreshFrequency: 1
targetValue: 2000
rewardType: 0
rewardAmount: 1
rewardExpBottleKind: 0
rewardDushMaterialKind: 0
addToTaskPool: 1
selectionWeight: 1
- taskID: 10110
description: 游玩 3 首不同的歌曲
taskType: 12
refreshFrequency: 1
targetValue: 3
rewardType: 0
rewardAmount: 1
rewardExpBottleKind: 0
rewardDushMaterialKind: 0
addToTaskPool: 1
@@ -55,7 +55,11 @@ public class userTasksPool : ScriptableObject
UseItem,
ShareGame,
SpendCoins,
GameDuration
GameDuration,
SingleRunCombo,
SingleRunScore,
OnlineDuration,
DifferentSongs
}
public enum RewardType
@@ -111,6 +111,11 @@ public class NoteSpawner : MonoBehaviour
// Guard to avoid double-trigger / deadlock
private bool immediateSettlementTriggered = false;
public bool IsImmediateSettlementTriggered
{
get { return immediateSettlementTriggered; }
}
private void Awake()
{
// Load saved visual speed multiplier before any spawning logic uses it
@@ -149,6 +149,7 @@ public class settlementController : MonoBehaviour
private Transform cachedIntroMvpTransform;
private Vector3 cachedIntroMvpBaseLocalPos;
private bool hasCachedIntroMvpBaseLocalPos;
private bool settlementHistoryRecorded;
private void Awake()
{
@@ -183,6 +184,9 @@ public class settlementController : MonoBehaviour
try { gameMusicMixer.SetFloat(lowpassParamName, 22000f); }
catch { }
}
RegisterCurrentLineupDeployCount();
settlementHistoryRecorded = false;
}
private void Update()
@@ -243,6 +247,62 @@ public class settlementController : MonoBehaviour
}
}
private void RegisterCurrentLineupDeployCount()
{
HashSet<int> uniqueHeroIds = new HashSet<int>();
AllyHero_SO[] allHeroes = null;
if (teamUIController.Instance != null && teamUIController.Instance.allySlotIds != null)
{
for (int i = 0; i < teamUIController.Instance.allySlotIds.Count; i++)
{
int heroId = teamUIController.Instance.allySlotIds[i];
if (heroId > 0)
{
uniqueHeroIds.Add(heroId);
}
}
}
if (uniqueHeroIds.Count == 0)
{
for (int slotIndex = 1; slotIndex <= 5; slotIndex++)
{
int heroId = PlayerPrefs.GetInt("selected_heroSlot0" + slotIndex + "_heroID", 0);
if (heroId > 0)
{
uniqueHeroIds.Add(heroId);
}
}
}
if (uniqueHeroIds.Count == 0)
{
return;
}
allHeroes = Resources.LoadAll<AllyHero_SO>(string.Empty);
if (allHeroes == null || allHeroes.Length == 0)
{
return;
}
foreach (int heroId in uniqueHeroIds)
{
for (int i = 0; i < allHeroes.Length; i++)
{
AllyHero_SO hero = allHeroes[i];
if (hero == null || hero.ally_heroID != heroId)
{
continue;
}
hero.IncrementBattleDeployCount();
break;
}
}
}
public void startSettlement_uiUpdate()
{
if (settlementUiInitialized)
@@ -251,6 +311,7 @@ public class settlementController : MonoBehaviour
return;
}
settlementUiInitialized = true;
settlementHistoryRecorded = false;
// Documentation text normalized.
InitializeSettlementCanvas();
@@ -317,18 +378,22 @@ public class settlementController : MonoBehaviour
if (!GameConfig.autoPlayEnabled)
{
DailyTaskEventHub.ReportPlaySong();
DailyTaskEventHub.ReportPlaySong(thisSong_so != null ? thisSong_so.songID : 0);
DailyTaskEventHub.ReportTotalScore(targetTotalScore);
DailyTaskEventHub.ReportSingleRunScore(targetTotalScore);
if (InGamePerformanceManager.Instance != null && InGamePerformanceManager.Instance.HighestComboThisRun > 0)
{
DailyTaskEventHub.ReportSingleRunCombo(InGamePerformanceManager.Instance.HighestComboThisRun);
}
if (targetMissCount <= 0 && noteCountRaw > 0)
{
DailyTaskEventHub.ReportFullCombo();
}
}
if (sessionDurationForTasks > 0f)
{
DailyTaskEventHub.ReportGameDuration(sessionDurationForTasks);
}
}
TryRecordRecentPlayHistory(noteCountRaw);
perfectHitCount_Text.text = targetPerfectCount.ToString();
greatHitCount_Text.text = targetGreatCount.ToString();
@@ -460,6 +525,127 @@ public class settlementController : MonoBehaviour
StartSettlementIntro();
}
private void TryRecordRecentPlayHistory(int noteCountRaw)
{
if (settlementHistoryRecorded)
{
return;
}
settlementHistoryRecorded = true;
var record = new RecentPlayRecord
{
playedAt = System.DateTime.Now.ToString("MM-dd, HH:mm"),
songID = thisSong_so != null ? thisSong_so.songID : 0,
songName = thisSong_so != null && !string.IsNullOrWhiteSpace(thisSong_so.songName)
? thisSong_so.songName
: (bmm != null ? bmm.parsedTitle : "Unknown"),
difficultyDisplay = BuildDifficultyDisplay(),
accuracy = targetAccuracyPercent,
srks = CalculateSongRankingScore(noteCountRaw),
totalScore = targetTotalScore,
scoreReadable = true,
wasEarlySettlement = IsEarlySettlement(noteCountRaw),
wasAllPerfect = IsAllPerfectRun(noteCountRaw)
};
RecentPlayHistoryStore.Push(record);
}
private string BuildDifficultyDisplay()
{
int difficulty = bmm != null ? bmm.assignedDifficulty : -1;
string suffix = GetDifficultyShortName(difficulty);
float level = 0f;
if (thisSong_so != null && thisSong_so.chartFiles != null)
{
for (int i = 0; i < thisSong_so.chartFiles.Count; i++)
{
ChartFileEntry entry = thisSong_so.chartFiles[i];
if (entry == null || entry.difficulty != difficulty)
{
continue;
}
level = entry.difficultyLEVEL;
break;
}
}
string levelText = Mathf.Approximately(level, Mathf.Round(level))
? Mathf.RoundToInt(level).ToString()
: level.ToString("0.#");
if (string.IsNullOrEmpty(levelText) || levelText == "0")
{
return suffix;
}
return levelText + "_" + suffix;
}
private static string GetDifficultyShortName(int difficulty)
{
switch (difficulty)
{
case 0: return "EZ";
case 1: return "HD";
case 2: return "IN";
case 3: return "IM";
default: return "UN";
}
}
private float CalculateSongRankingScore(int noteCountRaw)
{
int totalNotes = Mathf.Max(1, noteCountRaw);
float accuracyScore = Mathf.Clamp01(targetAccuracyPercent / 100f);
float scoreScore = Mathf.Clamp01((float)targetTotalScore / Mathf.Max(1, _1000000));
int highestCombo = InGamePerformanceManager.Instance != null ? InGamePerformanceManager.Instance.HighestComboThisRun : 0;
float comboScore = Mathf.Clamp01((float)highestCombo / totalNotes);
float judgementScore = Mathf.Clamp01(
(targetPerfectCount + targetGreatCount * 0.7f + targetGoodCount * 0.35f) / totalNotes);
float missPenalty = Mathf.Clamp01((float)targetMissCount / totalNotes);
float srks =
accuracyScore * 42f +
scoreScore * 28f +
comboScore * 18f +
judgementScore * 12f;
srks -= missPenalty * 18f;
if (targetMissCount <= 0 && totalNotes > 0)
{
srks += 4f;
}
return Mathf.Clamp(srks, 0f, 100f);
}
private bool IsEarlySettlement(int noteCountRaw)
{
NoteSpawner spawner = FindAnyObjectByType<NoteSpawner>();
if (spawner != null && spawner.IsImmediateSettlementTriggered)
{
return true;
}
int parsedNoteAmount = bmm != null ? bmm.parsedNoteAmount : 0;
return parsedNoteAmount > 0 && noteCountRaw < parsedNoteAmount;
}
private bool IsAllPerfectRun(int noteCountRaw)
{
return noteCountRaw > 0 &&
targetPerfectCount >= noteCountRaw &&
targetGreatCount == 0 &&
targetGoodCount == 0 &&
targetMissCount == 0;
}
private void StartSettlementIntro()
{
skipIntroRequested = false;

Some files were not shown because too many files have changed in this diff Show More