加入用户最近100场战绩记录并实现展示
This commit is contained in:
@@ -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":[]}
|
||||
+2
-3
@@ -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
|
||||
|
||||
+9
-7
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 |
+156
@@ -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:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 713e02abf704c454d8c992e51643195f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01cdae7333ce2284fbd8246a1d450c3e
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00d9c70de03d5584caa4f58f04471b5b
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+43
-67
@@ -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] 开始异步验证所有歌曲存档...");
|
||||
|
||||
// 记录开始时间
|
||||
float startTime = Time.realtimeSinceStartup;
|
||||
ValidateSongRuntimeSaves();
|
||||
}
|
||||
|
||||
int totalFiles = 0;
|
||||
private void ValidateSongRuntimeSaves()
|
||||
{
|
||||
float startTime = Time.realtimeSinceStartup;
|
||||
string persistentPath = Application.persistentDataPath;
|
||||
string legacyRoot = persistentPath;
|
||||
|
||||
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)
|
||||
string json = loadedJsonList[i];
|
||||
SongDataSerializable serializable;
|
||||
if (!SongData.VerifyJsonIntegrity(json, Salt, out serializable))
|
||||
{
|
||||
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
|
||||
{
|
||||
corruptedFiles++;
|
||||
Debug.LogError($"[DataCheck] 存档严重损坏: {Path.GetFileName(file)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
corruptedFiles++;
|
||||
Debug.LogError($"[DataCheck] 读取文件失败 {file}: {e.Message}");
|
||||
}
|
||||
corruptedFiles++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
string GetSavePath()
|
||||
string GetLegacySavePath()
|
||||
{
|
||||
return Path.Combine(Application.persistentDataPath, "mail_state.json");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
if (!SecureSaveVault.SaveRawJson(SaveCategory, file_Name, json, legacyPath))
|
||||
{
|
||||
Debug.LogWarning($"[JsonDataHandler] Save failed: {file_Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
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
|
||||
@@ -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,23 +4,25 @@ 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);
|
||||
return data ?? new DailyTaskSaveData();
|
||||
DailyTaskSaveData data;
|
||||
if (SecureSaveVault.TryLoadJson(SaveCategory, SaveKey, out data, LegacySaveFilePath))
|
||||
{
|
||||
return data ?? new DailyTaskSaveData();
|
||||
}
|
||||
|
||||
return new DailyTaskSaveData();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,42 +170,39 @@ 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];
|
||||
if (runtimeEntry == null || runtimeEntry.isClaimed)
|
||||
for (int i = 0; i < saveData.activeTasks.Count; i++)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var runtimeEntry = saveData.activeTasks[i];
|
||||
if (runtimeEntry == null || runtimeEntry.isClaimed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
userTasksPool.TaskDefinition definition;
|
||||
if (!definitionById.TryGetValue(runtimeEntry.taskID, out definition))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
userTasksPool.TaskDefinition definition;
|
||||
if (!definitionById.TryGetValue(runtimeEntry.taskID, out definition))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsEventMatch(definition, eventData))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!IsEventMatch(definition, eventData))
|
||||
{
|
||||
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)
|
||||
{
|
||||
changed = true;
|
||||
if (!Mathf.Approximately(runtimeEntry.progress, nextProgress) || runtimeEntry.isCompleted != nextCompleted)
|
||||
{
|
||||
runtimeEntry.progress = nextProgress;
|
||||
runtimeEntry.isCompleted = nextCompleted;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 static bool IsEventMatch(userTasksPool.TaskDefinition definition, DailyTaskEventData eventData)
|
||||
private bool ApplyEventToTrackedProgress(DailyTaskEventData eventData)
|
||||
{
|
||||
return definition != null && definition.taskType == eventData.taskType;
|
||||
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 float AddAccumulatedProgress(userTasksPool.TaskType taskType, float amount)
|
||||
private static bool IsEventMatch(userTasksPool.TaskDefinition definition, DailyTaskEventData eventData)
|
||||
{
|
||||
if (saveData == null)
|
||||
if (definition == null)
|
||||
{
|
||||
saveData = new DailyTaskSaveData();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (saveData.accumulatedProgress == null)
|
||||
if (definition.taskType == eventData.taskType)
|
||||
{
|
||||
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
entry.progress = nextValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
var newEntry = new DailyTaskAccumulatedProgress
|
||||
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;
|
||||
}
|
||||
|
||||
float flushSeconds = pendingOnlineDurationSeconds;
|
||||
pendingOnlineDurationSeconds = 0f;
|
||||
ReportEvent(new DailyTaskEventData
|
||||
{
|
||||
taskType = userTasksPool.TaskType.OnlineDuration,
|
||||
amount = flushSeconds
|
||||
});
|
||||
}
|
||||
|
||||
private static string GetTodayKey()
|
||||
private static int GetLocalDateStamp(DateTime dateTime)
|
||||
{
|
||||
return DateTime.Now.ToString("yyyy-MM-dd");
|
||||
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,19 +378,23 @@ 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();
|
||||
goodHitCount_Text.text = targetGoodCount.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
Reference in New Issue
Block a user