diff --git a/Assets/000_assets/material/M_SquareFrame 1.mat b/Assets/000_assets/material/M_SquareFrame 1.mat index 0a1bceb9..ac4a140e 100644 --- a/Assets/000_assets/material/M_SquareFrame 1.mat +++ b/Assets/000_assets/material/M_SquareFrame 1.mat @@ -213,7 +213,7 @@ Material: - _Dst: 10 - _DstBlend: 0 - _DstBlendAlpha: 0 - - _EdgeValue: 0.34460104 + - _EdgeValue: 0.9350733 - _EnvironmentReflections: 1 - _FNLfanxiangkaiguan: 0 - _Face: 1 @@ -258,7 +258,7 @@ Material: - _Mask_scale: 1 - _Metallic: 0 - _OcclusionStrength: 1 - - _Opacity: 0.65539896 + - _Opacity: 0.064926684 - _Parallax: 0.005 - _Pass: 0 - _QueueOffset: 0 diff --git a/Assets/Resources/Story/Chapter0/Dialogs.meta b/Assets/AddressableAssetsData.meta similarity index 77% rename from Assets/Resources/Story/Chapter0/Dialogs.meta rename to Assets/AddressableAssetsData.meta index b2aeb06f..d3fbeab1 100644 --- a/Assets/Resources/Story/Chapter0/Dialogs.meta +++ b/Assets/AddressableAssetsData.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1897c06a1d583324981471707b41cfa5 +guid: da4cd00152a37304cbc6130209a0c422 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset b/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset new file mode 100644 index 00000000..c454b2ae --- /dev/null +++ b/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b846191d28d78ae587fbe21f06f859d640b77e20de439502369029d03e959354 +size 954 diff --git a/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset.meta b/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset.meta new file mode 100644 index 00000000..df4b7640 --- /dev/null +++ b/Assets/AddressableAssetsData/AddressableAssetGroupSortSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f1e90c2a8af58ef4fb1f06587fb0bc17 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AddressableAssetSettings.asset b/Assets/AddressableAssetsData/AddressableAssetSettings.asset new file mode 100644 index 00000000..af051aac --- /dev/null +++ b/Assets/AddressableAssetsData/AddressableAssetSettings.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61471a34a91e7effc6f2db9f3e9aaf4f4bad5f5175dd529068178c642ad8fcd8 +size 4843 diff --git a/Assets/AddressableAssetsData/AddressableAssetSettings.asset.meta b/Assets/AddressableAssetsData/AddressableAssetSettings.asset.meta new file mode 100644 index 00000000..2eec754a --- /dev/null +++ b/Assets/AddressableAssetsData/AddressableAssetSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 333668651d5ae6d46b48444295d78f93 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_A.txt.meta b/Assets/AddressableAssetsData/AssetGroupTemplates.meta similarity index 67% rename from Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_A.txt.meta rename to Assets/AddressableAssetsData/AssetGroupTemplates.meta index 4056eac9..3e80b276 100644 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_A.txt.meta +++ b/Assets/AddressableAssetsData/AssetGroupTemplates.meta @@ -1,5 +1,6 @@ fileFormatVersion: 2 -guid: 4d0a8b411a62bd9449344eb530b3234f +guid: c4c50610fc4c9e34dbadd157785adaeb +folderAsset: yes DefaultImporter: externalObjects: {} userData: diff --git a/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset b/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset new file mode 100644 index 00000000..35b456a5 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5aca6ae20be1cf325053b373d8042e30654ccd81784a990583c4330aaa3d49c7 +size 2525 diff --git a/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset.meta b/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset.meta new file mode 100644 index 00000000..a4112f09 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroupTemplates/Packed Assets.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6c4d2cf29343e034b80084ca5bf93e9f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_B.txt.meta b/Assets/AddressableAssetsData/AssetGroups.meta similarity index 67% rename from Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_B.txt.meta rename to Assets/AddressableAssetsData/AssetGroups.meta index 918f6cc9..0c6c340a 100644 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_B.txt.meta +++ b/Assets/AddressableAssetsData/AssetGroups.meta @@ -1,5 +1,6 @@ fileFormatVersion: 2 -guid: ce2f5ed054e5d4d4082038afac4c472c +guid: 52de1c5cf221d9645b8411aad9bb6921 +folderAsset: yes DefaultImporter: externalObjects: {} userData: diff --git a/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset new file mode 100644 index 00000000..4770d7b9 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cdf2102b2662de2609ac8fb64d7cb9031801e00b8944034d5ef31d7b835397e8 +size 874 diff --git a/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset.meta new file mode 100644 index 00000000..4526d4e1 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Default Local Group.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 79f20a7c8c628df49bdbdf0b8d3ed4da +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-Assets-Shared.asset b/Assets/AddressableAssetsData/AssetGroups/Localization-Assets-Shared.asset new file mode 100644 index 00000000..c3f4bda6 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-Assets-Shared.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04e36419ad0f0bd45377a7d540689ea9ee7ceb05b10dd8bec904d1bce3150eab +size 1288 diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-Assets-Shared.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Localization-Assets-Shared.asset.meta new file mode 100644 index 00000000..657d6153 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-Assets-Shared.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 233a82c0c1f1bfc49b4aff8b5bb11fb0 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-Locales.asset b/Assets/AddressableAssetsData/AssetGroups/Localization-Locales.asset new file mode 100644 index 00000000..42600583 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-Locales.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:593ae32e6ec286505270c2baeaf47ee6ac7b3c6e29b9cded58e19d4f4020388c +size 2130 diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-Locales.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Localization-Locales.asset.meta new file mode 100644 index 00000000..2e6af7e6 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-Locales.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9796adb8713daf140aeb9bf7920fbcd3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified) (zh-CN).asset b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified) (zh-CN).asset new file mode 100644 index 00000000..763389e2 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified) (zh-CN).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dba1f02527c509560b7ac00a0db7163d0dda54d4189255487ddebade4d6298e5 +size 1305 diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified) (zh-CN).asset.meta b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified) (zh-CN).asset.meta new file mode 100644 index 00000000..50a2fd63 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified) (zh-CN).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 785455cfd2345164ca119528773f6f6a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified).asset b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified).asset new file mode 100644 index 00000000..9f5e22df --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb481df58a135a3813127b805793d28edced577b3fe6b94ec562a715d1eda381 +size 930 diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified).asset.meta b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified).asset.meta new file mode 100644 index 00000000..5d9cd019 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Simplified).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: be2ab496482d2f645880aca0f11e0e75 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Traditional) (zh-TW).asset b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Traditional) (zh-TW).asset new file mode 100644 index 00000000..47f4dcff --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Traditional) (zh-TW).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcc985186b987c3def378cdc5b1551dc416ac762f1f4633ce1e196ff34f94b10 +size 1307 diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Traditional) (zh-TW).asset.meta b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Traditional) (zh-TW).asset.meta new file mode 100644 index 00000000..f1702ed9 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Chinese (Traditional) (zh-TW).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b69488a2fc705bd409adeefbf86a5298 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-English (en).asset b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-English (en).asset new file mode 100644 index 00000000..cdbe3687 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-English (en).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d28393d3a6374ad1da1dba7b2af9f29fd87633b6cf23b7d82b8e4f9ed916b11 +size 1261 diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-English (en).asset.meta b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-English (en).asset.meta new file mode 100644 index 00000000..98ae36f8 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-English (en).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ac7ca228f02695d4cb21ae4651afb001 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Japanese (ja).asset b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Japanese (ja).asset new file mode 100644 index 00000000..ebb4623d --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Japanese (ja).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae1741db12f54540b5e5150eefd5d599800d98ef207f64297b2461df0db31d1b +size 1263 diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Japanese (ja).asset.meta b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Japanese (ja).asset.meta new file mode 100644 index 00000000..e87bf823 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Japanese (ja).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1690137dfec1b7d4c82ffa36dc18ef41 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Korean (ko).asset b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Korean (ko).asset new file mode 100644 index 00000000..197e9278 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Korean (ko).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54391b035e5d1d2b6db182d231af4167d5b427834755391cb37ac4753666c27b +size 1259 diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Korean (ko).asset.meta b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Korean (ko).asset.meta new file mode 100644 index 00000000..3ceae53b --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Korean (ko).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 430ce305e6093f645b3f774958b0bb53 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Thai (th).asset b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Thai (th).asset new file mode 100644 index 00000000..06f45973 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Thai (th).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbd67aa0f36cb1491c41ada26e016f6e3a10f274f6bed3874e9a01712438d301 +size 1255 diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Thai (th).asset.meta b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Thai (th).asset.meta new file mode 100644 index 00000000..b715a546 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Thai (th).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 48c6fdd839b5fe5419367a5e805cec59 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN).asset b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN).asset new file mode 100644 index 00000000..4c779cb5 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91bfe4656ee7f65b4afbcacffc9c409ce2a3feb994b805a62c5d6d8d31663cf9 +size 1305 diff --git a/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN).asset.meta b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN).asset.meta new file mode 100644 index 00000000..a462d39a --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 83e9e063640fe6d4ab66036d289845da +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas.meta new file mode 100644 index 00000000..ecdf007d --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 65a0745e5b7bffa41ad00a95a0b57571 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..04a8aafe --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbc2349e8901690d954d55fc84be6eee24132920240a12cf41048e1a37ebacd1 +size 1741 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..2b8a1003 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4e634ba1e28a0ec4da4e595556dc3423 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..7c087497 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:088daf66c827c65c95b66f54e743fa607eef3a064542dd79463e22c179c01355 +size 636 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..6a55f1d9 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Default Local Group_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7155c3d6e85e3c94dbad4531f66aadc1 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..a82ca973 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_BundledAssetGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8287370c254b62e087d238a826b522912ec2332a41ab92594ec0ce889d641b8 +size 1748 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..454e2b6a --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 45bcf3e6b96e8534aab3ae75ced2d49e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..a5d2981c --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_ContentUpdateGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba8f4e148369a882b58715811187a95fe3c64a28ce7480bc5d256d82e62a4d97 +size 643 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..cfbddb08 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Assets-Shared_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 910b284cf03caca4b9509604e42a5340 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..78438cd0 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_BundledAssetGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e037a7d1c5d5655a19d44c5630924aa591f9bbadfe2b4bd4f34b722635caf814 +size 1742 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..2a472efc --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 56b624934c118c2459f861929a669247 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..41880423 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_ContentUpdateGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36e46630af4897d59f79fde7f523be3a4d8645e2d950eeabc85ae52c56178f34 +size 637 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..6c1e6f15 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-Locales_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8eea5c367f9fce644af4adee7fcde1f0 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..661870fe --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_BundledAssetGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd9ec1e2f07e0f7750b3d2cf1f63b54651325b4f46ef57ce59cd5fb24908d201 +size 1777 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..17cd86ca --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ecc495c4ab0827f4f8477c104e9f528f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..27b9e6bb --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_ContentUpdateGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:031794f4dae66354bd6ba9ea10cb58dd662f3d7a493dfbb62161e187245b98b6 +size 672 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..a04c3da2 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified) (zh-CN)_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 66e7f846bf6029f4b9f31c3fb002c88b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..7062b528 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_BundledAssetGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87c6ba35133a998ba59839041906838109472f622d1c10d75bd7ad2d3e1660ca +size 1769 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..828408e9 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8eeeb50cf44bfe44c9f1ef14be3f22c2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..42eae1c7 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_ContentUpdateGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58c4d1241ce119f86747bf5fd2b61032fba62544ef0558288cb61c59159ab6fe +size 664 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..4e999660 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Simplified)_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7c0436c264404c1419f2c2d0448a4077 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..8434969f --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_BundledAssetGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:021a1ae6d62819c28f8528dbe8474da8272df52971d3c2b1336b1f8b24c98d9f +size 1778 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..3dd12e7c --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6fe9e6919ec800340b05afe4e563feae +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..6cbcc3ff --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_ContentUpdateGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80526b3226df88e0357d860d07bfb44e0f69fe16df352fd75d394ca744ec13bb +size 673 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..8f549106 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Chinese (Traditional) (zh-TW)_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: abf5cb6f5146362428ea647cd2b14eaf +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..efaeaa32 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_BundledAssetGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6968af0513bcc80491e7af630c946e64067d240a7072bce7a601c0efcf5784e9 +size 1761 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..76c6f400 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cce204332f2ff7843994ccd4ee0dab47 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..790a0b38 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_ContentUpdateGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edb17dd75f561057f32ebc8c9f675cf809a1c7d29cf0f4ef147702fba884a972 +size 656 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..df5f7bd7 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-English (en)_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 681943814f82c964a9531e0f6f28e40a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..d29e5cef --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_BundledAssetGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53b2d515898950e3771666c46a3ce77609d3a94124668a8363bb530e6ca42bc9 +size 1762 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..2b356040 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 880e7cf31ed06b446b8c6dc1b925498d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..7f0fe16b --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_ContentUpdateGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:feabe6919b83ec6d43666d7b0ebfc24c382bfd25365f4530b3d8ccb5810910e5 +size 657 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..c92dcef9 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Japanese (ja)_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: edab4d7e933020b4e8dd4cab8511467a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..a3be0b81 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_BundledAssetGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b28657691c42910ccd6be1df1487ece24e0b086e0bcb276803aeb2dc408f4f79 +size 1760 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..d8602b8d --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 231274b07de2fd042906d81eab72bcda +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..0d3d9e5f --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_ContentUpdateGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90c3185de56b30a8df6df1cd9b7fa2a65c3436b005190d735afe423b19d98595 +size 655 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..401e6552 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Korean (ko)_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7322a30531aec474689ce214fc0991aa +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..0cac65bd --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_BundledAssetGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3aa5499851629ec74a2fe5a6bb68721694ae809aca4199d262a118ff1d68afe2 +size 1758 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..229956bd --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 22bcbb3cfd8faca4b933e20825535910 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..f4a7a811 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_ContentUpdateGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9566945a837b29bc7467e5378a80eb18833e4a391914fe22dbae99487f7de94a +size 653 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..bda4a92c --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Thai (th)_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 917b311f81d28ac4d9a0bffd40aff26f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_BundledAssetGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_BundledAssetGroupSchema.asset new file mode 100644 index 00000000..835f2483 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_BundledAssetGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19986fc7171a160a5910abaad980f33b19b57f4f2d7ef169ef02abed7ee70c62 +size 1777 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_BundledAssetGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_BundledAssetGroupSchema.asset.meta new file mode 100644 index 00000000..31c49262 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_BundledAssetGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 83c8b825f8f3bf24982718bf239aa92a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_ContentUpdateGroupSchema.asset b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_ContentUpdateGroupSchema.asset new file mode 100644 index 00000000..f9a310b7 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_ContentUpdateGroupSchema.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06d0ebe4a78f9ad8eb42f8a07d39eb245309f731549074f23ead04cc616916e2 +size 672 diff --git a/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_ContentUpdateGroupSchema.asset.meta b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_ContentUpdateGroupSchema.asset.meta new file mode 100644 index 00000000..f31590c0 --- /dev/null +++ b/Assets/AddressableAssetsData/AssetGroups/Schemas/Localization-String-Tables-Vietnamese (Vietnam) (vi-VN)_ContentUpdateGroupSchema.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 925fc92141f019b4db41a0326e711684 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/DataBuilders.meta b/Assets/AddressableAssetsData/DataBuilders.meta new file mode 100644 index 00000000..8dea1a58 --- /dev/null +++ b/Assets/AddressableAssetsData/DataBuilders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e170ad71f455c8a48b46373ee4f7c53c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset b/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset new file mode 100644 index 00000000..9c8bc028 --- /dev/null +++ b/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec8fa6f7f04687b1ea742863ca9312cc48c78708d3bd04f5a80ee1c4289fa550 +size 905 diff --git a/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset.meta b/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset.meta new file mode 100644 index 00000000..4da09b51 --- /dev/null +++ b/Assets/AddressableAssetsData/DataBuilders/BuildScriptFastMode.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f526b0955561d08469fdb8f268194658 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset b/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset new file mode 100644 index 00000000..89776d39 --- /dev/null +++ b/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca98b03b4bd46044981893af422aad5ec947f6b4923d5e7d732074ac0cf2d58c +size 909 diff --git a/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset.meta b/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset.meta new file mode 100644 index 00000000..41135116 --- /dev/null +++ b/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedMode.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 19497de4e6f97c247a9f5df510909c67 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset b/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset new file mode 100644 index 00000000..2fb87b08 --- /dev/null +++ b/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:40aec021cfef83513030f6622569c31b4dc2eed74b6024c9ea8762ee981fd3bd +size 917 diff --git a/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset.meta b/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset.meta new file mode 100644 index 00000000..76d46648 --- /dev/null +++ b/Assets/AddressableAssetsData/DataBuilders/BuildScriptPackedPlayMode.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a19b321c06839f049a800c7cfb0fcfa5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/AddressableAssetsData/DefaultObject.asset b/Assets/AddressableAssetsData/DefaultObject.asset new file mode 100644 index 00000000..ae60a26b --- /dev/null +++ b/Assets/AddressableAssetsData/DefaultObject.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7ac75657db0a5301aac185a48e6de7c2b0eef08783662d09e54fd4b702c2fc95 +size 563 diff --git a/Assets/AddressableAssetsData/DefaultObject.asset.meta b/Assets/AddressableAssetsData/DefaultObject.asset.meta new file mode 100644 index 00000000..a060fbdb --- /dev/null +++ b/Assets/AddressableAssetsData/DefaultObject.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1a3d42a40a064524a8bac5b219463f8b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FR2_Cache.asset b/Assets/FR2_Cache.asset index 9b0720ca..fa4bd840 100644 --- a/Assets/FR2_Cache.asset +++ b/Assets/FR2_Cache.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a8a58ceaa1f70f9dee5021d685c59e9e20b86cbdc5ce069421ca83fc533d4b9 -size 8018231 +oid sha256:910dd8b2eb28a401c1bbf1b1dba1bd1951a9d1f39b72c9a33dd33933b2d45f3b +size 8580991 diff --git a/Assets/Localization.meta b/Assets/Localization.meta new file mode 100644 index 00000000..8b7be303 --- /dev/null +++ b/Assets/Localization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1f208d99037571f439cc35ff0e2f7225 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Chinese (Simplified) (zh-CN).asset b/Assets/Localization/Chinese (Simplified) (zh-CN).asset new file mode 100644 index 00000000..687087cf --- /dev/null +++ b/Assets/Localization/Chinese (Simplified) (zh-CN).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5340aa24a80affc77c39379d729b1cd4d35bf07b98805bbd658ebb065678beb2 +size 1152 diff --git a/Assets/Localization/Chinese (Simplified) (zh-CN).asset.meta b/Assets/Localization/Chinese (Simplified) (zh-CN).asset.meta new file mode 100644 index 00000000..62125135 --- /dev/null +++ b/Assets/Localization/Chinese (Simplified) (zh-CN).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7563d3b3e401bd441aa273b7c8b49d3f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Chinese (Traditional) (zh-TW).asset b/Assets/Localization/Chinese (Traditional) (zh-TW).asset new file mode 100644 index 00000000..3f241ad5 --- /dev/null +++ b/Assets/Localization/Chinese (Traditional) (zh-TW).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ae10ba699d21a55bc8bc3558381d6abf0303c72c04c29a66b1806826bffad5e +size 696 diff --git a/Assets/Localization/Chinese (Traditional) (zh-TW).asset.meta b/Assets/Localization/Chinese (Traditional) (zh-TW).asset.meta new file mode 100644 index 00000000..131b20c5 --- /dev/null +++ b/Assets/Localization/Chinese (Traditional) (zh-TW).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bbfb4a104b666ce4781e516f74979be5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/English (en).asset b/Assets/Localization/English (en).asset new file mode 100644 index 00000000..7c91ab6c --- /dev/null +++ b/Assets/Localization/English (en).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b0330f7f9af0b1c088ae37ea6e4f0a2e3cf79f09f851a42724ca091ed82d023 +size 659 diff --git a/Assets/Localization/English (en).asset.meta b/Assets/Localization/English (en).asset.meta new file mode 100644 index 00000000..39b261ff --- /dev/null +++ b/Assets/Localization/English (en).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cdfd1903d0feab84c987a21a7b66c0ca +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Japanese (ja).asset b/Assets/Localization/Japanese (ja).asset new file mode 100644 index 00000000..74244633 --- /dev/null +++ b/Assets/Localization/Japanese (ja).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba704eb7ae3524a793032dfe379f05aa06132b8db72adf6c37fa91b8363c7805 +size 661 diff --git a/Assets/Localization/Japanese (ja).asset.meta b/Assets/Localization/Japanese (ja).asset.meta new file mode 100644 index 00000000..0f17981a --- /dev/null +++ b/Assets/Localization/Japanese (ja).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 739271c4743cc7d4fa2395da4b5d85fa +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Korean (ko).asset b/Assets/Localization/Korean (ko).asset new file mode 100644 index 00000000..169ebf62 --- /dev/null +++ b/Assets/Localization/Korean (ko).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6033068cc22c0aca15844b1db3ecf5e70a7578fa812935352bf91ee95267062b +size 657 diff --git a/Assets/Localization/Korean (ko).asset.meta b/Assets/Localization/Korean (ko).asset.meta new file mode 100644 index 00000000..1536da12 --- /dev/null +++ b/Assets/Localization/Korean (ko).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aa5c802e0f80da341acf6cc5dc24a154 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Localization Settings.asset b/Assets/Localization/Localization Settings.asset new file mode 100644 index 00000000..ebb1e118 --- /dev/null +++ b/Assets/Localization/Localization Settings.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d3286c54c1371d3529b40a152aba59fb2699a8e9c912732be1c85f405b163f3 +size 7535 diff --git a/Assets/Localization/Localization Settings.asset.meta b/Assets/Localization/Localization Settings.asset.meta new file mode 100644 index 00000000..9330bc4c --- /dev/null +++ b/Assets/Localization/Localization Settings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2fd149bb5fbc64d43b0faefef6dbd374 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Story.meta b/Assets/Localization/Story.meta new file mode 100644 index 00000000..236b0056 --- /dev/null +++ b/Assets/Localization/Story.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f63fe0dd591591a40ac4ad3364e74683 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Story/Chapter0_Lines Shared Data.asset b/Assets/Localization/Story/Chapter0_Lines Shared Data.asset new file mode 100644 index 00000000..96551484 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines Shared Data.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed67bb7472abede7c8fc5973a60e94bac70fea33306594fa328c3a0f4c5a96b2 +size 9524 diff --git a/Assets/Localization/Story/Chapter0_Lines Shared Data.asset.meta b/Assets/Localization/Story/Chapter0_Lines Shared Data.asset.meta new file mode 100644 index 00000000..d2212d8c --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines Shared Data.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d32b03c142a46454480f74d82f927f5e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Story/Chapter0_Lines.asset b/Assets/Localization/Story/Chapter0_Lines.asset new file mode 100644 index 00000000..e07e2913 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:412dd6a72f535748e37b837e4702c24aeb0a32eb62941921993c5401c9bf8cd7 +size 1168 diff --git a/Assets/Localization/Story/Chapter0_Lines.asset.meta b/Assets/Localization/Story/Chapter0_Lines.asset.meta new file mode 100644 index 00000000..b997bfe4 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6f837963dfd99254089cab718c432ee6 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Story/Chapter0_Lines_en.asset b/Assets/Localization/Story/Chapter0_Lines_en.asset new file mode 100644 index 00000000..9755c24b --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_en.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a70b8cad7a827fa0ab8cb116d8bb437eb078d2241c17bd966b113924508d5f1 +size 5699 diff --git a/Assets/Localization/Story/Chapter0_Lines_en.asset.meta b/Assets/Localization/Story/Chapter0_Lines_en.asset.meta new file mode 100644 index 00000000..a2891d1a --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_en.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0d195e6089c0e5a49b0dcb05380a4c2f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Story/Chapter0_Lines_ja.asset b/Assets/Localization/Story/Chapter0_Lines_ja.asset new file mode 100644 index 00000000..3cc47318 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_ja.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3cb3490687bb2ef1bae590a47e21e93e044e1f237bc40fc303df7a38d637701 +size 674 diff --git a/Assets/Localization/Story/Chapter0_Lines_ja.asset.meta b/Assets/Localization/Story/Chapter0_Lines_ja.asset.meta new file mode 100644 index 00000000..3fd3040f --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_ja.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 193739a16e6c7b74dab5ffe8e05f5522 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Story/Chapter0_Lines_ko.asset b/Assets/Localization/Story/Chapter0_Lines_ko.asset new file mode 100644 index 00000000..6bc705c0 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_ko.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41484809666ac5a3ab201cee4885732e9e50be7d819532036ddb44f0cde4e080 +size 674 diff --git a/Assets/Localization/Story/Chapter0_Lines_ko.asset.meta b/Assets/Localization/Story/Chapter0_Lines_ko.asset.meta new file mode 100644 index 00000000..6c429a42 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_ko.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7b729a8a4982320418da5bbacef5bd1e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Story/Chapter0_Lines_th.asset b/Assets/Localization/Story/Chapter0_Lines_th.asset new file mode 100644 index 00000000..23f0b2c1 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_th.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0d4ac5a58a0ebb14ba84ad7d5b4de6a27c335a2607eec08a44ce2ca0d2532ea +size 674 diff --git a/Assets/Localization/Story/Chapter0_Lines_th.asset.meta b/Assets/Localization/Story/Chapter0_Lines_th.asset.meta new file mode 100644 index 00000000..3ac8a56d --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_th.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9aa3adc81c1747744b52daddc8addff4 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Story/Chapter0_Lines_vi-VN.asset b/Assets/Localization/Story/Chapter0_Lines_vi-VN.asset new file mode 100644 index 00000000..19e4092d --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_vi-VN.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f21547715fd9daa0202d82b4a97127d8b6dcfe40ac0948accdb28067b7b9b80 +size 680 diff --git a/Assets/Localization/Story/Chapter0_Lines_vi-VN.asset.meta b/Assets/Localization/Story/Chapter0_Lines_vi-VN.asset.meta new file mode 100644 index 00000000..6ee44037 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_vi-VN.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 74d6e3963f7d80a44ad06df91ac2cd41 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Story/Chapter0_Lines_zh-CN.asset b/Assets/Localization/Story/Chapter0_Lines_zh-CN.asset new file mode 100644 index 00000000..034b0681 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_zh-CN.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea4d0949ffab4bcaec877dd015cf6d9af99637dad8721b5810a9a5ccb110315d +size 977 diff --git a/Assets/Localization/Story/Chapter0_Lines_zh-CN.asset.meta b/Assets/Localization/Story/Chapter0_Lines_zh-CN.asset.meta new file mode 100644 index 00000000..e9825c52 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_zh-CN.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1be8dbf575e99a9428fb49ec8df976e3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Story/Chapter0_Lines_zh-TW.asset b/Assets/Localization/Story/Chapter0_Lines_zh-TW.asset new file mode 100644 index 00000000..51166b13 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_zh-TW.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:daf09567101de036362e3895bebb3b15c3577fbf34b570a97025001b10a6b41f +size 680 diff --git a/Assets/Localization/Story/Chapter0_Lines_zh-TW.asset.meta b/Assets/Localization/Story/Chapter0_Lines_zh-TW.asset.meta new file mode 100644 index 00000000..359232f9 --- /dev/null +++ b/Assets/Localization/Story/Chapter0_Lines_zh-TW.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8897baecfe9a3b6419eea9b4b922fce2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Thai (th).asset b/Assets/Localization/Thai (th).asset new file mode 100644 index 00000000..77565ff9 --- /dev/null +++ b/Assets/Localization/Thai (th).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a5e5a7d39ff1d2782d31d20ce571300e87db0ef028e6210423ba886625f0ac96 +size 653 diff --git a/Assets/Localization/Thai (th).asset.meta b/Assets/Localization/Thai (th).asset.meta new file mode 100644 index 00000000..b33afbe0 --- /dev/null +++ b/Assets/Localization/Thai (th).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ef3e2f756aa6f2c42b0a6c7c93e6fd21 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/UI.meta b/Assets/Localization/UI.meta new file mode 100644 index 00000000..81a6a7bb --- /dev/null +++ b/Assets/Localization/UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3339a8ea7b57f81488dc2c0e67a64397 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/UI/UI Shared Data.asset b/Assets/Localization/UI/UI Shared Data.asset new file mode 100644 index 00000000..9c18adc7 --- /dev/null +++ b/Assets/Localization/UI/UI Shared Data.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a10f28464485c33a466994b94427384cb398860ef257c602ba591ba6b2f1542 +size 1492 diff --git a/Assets/Localization/UI/UI Shared Data.asset.meta b/Assets/Localization/UI/UI Shared Data.asset.meta new file mode 100644 index 00000000..01609c34 --- /dev/null +++ b/Assets/Localization/UI/UI Shared Data.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 971ba9d57d145154daf7371bd0e6b12f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/UI/UI.asset b/Assets/Localization/UI/UI.asset new file mode 100644 index 00000000..b1245e05 --- /dev/null +++ b/Assets/Localization/UI/UI.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:346e37fe37fb459c1bdc9c580a8dea26a2e70f0b02075503ae80d9410eee8778 +size 1156 diff --git a/Assets/Localization/UI/UI.asset.meta b/Assets/Localization/UI/UI.asset.meta new file mode 100644 index 00000000..c098f82e --- /dev/null +++ b/Assets/Localization/UI/UI.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 80f468ac720723a40a345888e6bcb81a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/UI/UI.csv b/Assets/Localization/UI/UI.csv new file mode 100644 index 00000000..ff916de1 --- /dev/null +++ b/Assets/Localization/UI/UI.csv @@ -0,0 +1,8 @@ +Key,Chinese (Simplified)(zh-Hans),English(en) +Confirm,确定,Confirm +Cancel,å–æ¶ˆ,Cancel +PausePage_Resume,继续游æˆ,Resume +PausePage_Settings,游æˆè®¾ç½®,Settings +PausePage_ReturnToMenu,返回标题,Menu +ConfirmReturnToMenu_Title,返回标题,Return to menu +ConfirmReturnToMenu_Desc,你确定è¦è¿”回标题页é¢å—?未ä¿å­˜çš„进度将丢失。,Are you sure you want to return to the main menu? Unsaved progress will be lost. diff --git a/Assets/Localization/UI/UI.csv.meta b/Assets/Localization/UI/UI.csv.meta new file mode 100644 index 00000000..824489f3 --- /dev/null +++ b/Assets/Localization/UI/UI.csv.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 917a09d42c77b4b49bc8ebf15aa280af +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/UI/UI.xlsx b/Assets/Localization/UI/UI.xlsx new file mode 100644 index 00000000..d44ce7db Binary files /dev/null and b/Assets/Localization/UI/UI.xlsx differ diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P1_A.txt.meta b/Assets/Localization/UI/UI.xlsx.meta similarity index 74% rename from Assets/Resources/Story/Chapter0/Dialogs/Departure_P1_A.txt.meta rename to Assets/Localization/UI/UI.xlsx.meta index b9aaa9d8..b1c9395e 100644 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P1_A.txt.meta +++ b/Assets/Localization/UI/UI.xlsx.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: afd46b56ef0cc5c4699710c478ff7c55 +guid: 89332e728ceca7d4bb803d7f4d2f3a0a DefaultImporter: externalObjects: {} userData: diff --git a/Assets/Localization/UI/UI_en.asset b/Assets/Localization/UI/UI_en.asset new file mode 100644 index 00000000..c5fb1809 --- /dev/null +++ b/Assets/Localization/UI/UI_en.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4be974242f1bbe380cdd8474c03358631a09980ef84b971aece12d77777f7eb2 +size 1322 diff --git a/Assets/Localization/UI/UI_en.asset.meta b/Assets/Localization/UI/UI_en.asset.meta new file mode 100644 index 00000000..fc20395f --- /dev/null +++ b/Assets/Localization/UI/UI_en.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3ce86ef4c08bef64d917b95b50e139b5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/UI/UI_ja.asset b/Assets/Localization/UI/UI_ja.asset new file mode 100644 index 00000000..e70e7255 --- /dev/null +++ b/Assets/Localization/UI/UI_ja.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c489c32070ae3eb07f4626280aaa06eb76453b40c952acdf282687a39346c161 +size 662 diff --git a/Assets/Localization/UI/UI_ja.asset.meta b/Assets/Localization/UI/UI_ja.asset.meta new file mode 100644 index 00000000..692193a3 --- /dev/null +++ b/Assets/Localization/UI/UI_ja.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 11dad01f17cea8449afbcee06de45f3b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/UI/UI_ko.asset b/Assets/Localization/UI/UI_ko.asset new file mode 100644 index 00000000..76e93fa7 --- /dev/null +++ b/Assets/Localization/UI/UI_ko.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ce35d93b68580f53a97304c2a440a219c498be98500ce11374cd948b751dffe +size 662 diff --git a/Assets/Localization/UI/UI_ko.asset.meta b/Assets/Localization/UI/UI_ko.asset.meta new file mode 100644 index 00000000..9bedb3e9 --- /dev/null +++ b/Assets/Localization/UI/UI_ko.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9be9853d071065541bdec3a304f65b89 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/UI/UI_th.asset b/Assets/Localization/UI/UI_th.asset new file mode 100644 index 00000000..4de40e77 --- /dev/null +++ b/Assets/Localization/UI/UI_th.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c191d64b5dc4e436952a06a8b789845914ad92c820fb790ea702b1b1d00d015 +size 662 diff --git a/Assets/Localization/UI/UI_th.asset.meta b/Assets/Localization/UI/UI_th.asset.meta new file mode 100644 index 00000000..09960b3c --- /dev/null +++ b/Assets/Localization/UI/UI_th.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c9008654991bb8b46bee5e6f7c144246 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/UI/UI_vi-VN.asset b/Assets/Localization/UI/UI_vi-VN.asset new file mode 100644 index 00000000..5e7f1839 --- /dev/null +++ b/Assets/Localization/UI/UI_vi-VN.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a82efb0bc96f1061b118edf57ab2a7f66b57112bd28fb4de420ad249816c3c5 +size 668 diff --git a/Assets/Localization/UI/UI_vi-VN.asset.meta b/Assets/Localization/UI/UI_vi-VN.asset.meta new file mode 100644 index 00000000..bb60ad3e --- /dev/null +++ b/Assets/Localization/UI/UI_vi-VN.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dba0f9afe1d703c43807f024b68198e4 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/UI/UI_zh-CN.asset b/Assets/Localization/UI/UI_zh-CN.asset new file mode 100644 index 00000000..8ab6c242 --- /dev/null +++ b/Assets/Localization/UI/UI_zh-CN.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5bed6dc7f591c2d4dbbaf27bd7b44314dfde5149a62f06365530708f65d9429a +size 1463 diff --git a/Assets/Localization/UI/UI_zh-CN.asset.meta b/Assets/Localization/UI/UI_zh-CN.asset.meta new file mode 100644 index 00000000..825efcfc --- /dev/null +++ b/Assets/Localization/UI/UI_zh-CN.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 66c49a5b8e4ee9a42af4356d22df6d89 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/UI/UI_zh-TW.asset b/Assets/Localization/UI/UI_zh-TW.asset new file mode 100644 index 00000000..a35b666a --- /dev/null +++ b/Assets/Localization/UI/UI_zh-TW.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69503f726d44084af2bc343785f3f4952e3c98a5c2fe4bfa411aa9ea1abdb8e3 +size 668 diff --git a/Assets/Localization/UI/UI_zh-TW.asset.meta b/Assets/Localization/UI/UI_zh-TW.asset.meta new file mode 100644 index 00000000..002f2ebd --- /dev/null +++ b/Assets/Localization/UI/UI_zh-TW.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c2aab8f11bff161449e7f5265cd4093d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Localization/Vietnamese (Vietnam) (vi-VN).asset b/Assets/Localization/Vietnamese (Vietnam) (vi-VN).asset new file mode 100644 index 00000000..933f4201 --- /dev/null +++ b/Assets/Localization/Vietnamese (Vietnam) (vi-VN).asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a92cfdb5e70f5d9a78cc2b5f2ec3537ce863a34ea9be7fb8d7fde5c7bab1453 +size 694 diff --git a/Assets/Localization/Vietnamese (Vietnam) (vi-VN).asset.meta b/Assets/Localization/Vietnamese (Vietnam) (vi-VN).asset.meta new file mode 100644 index 00000000..abe26f4c --- /dev/null +++ b/Assets/Localization/Vietnamese (Vietnam) (vi-VN).asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bf262d6fbac8c464192fc9778e03a474 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Easy Save 3/Resources/ES3/ES3Defaults.asset b/Assets/Plugins/Easy Save 3/Resources/ES3/ES3Defaults.asset index de44e6d9..6b8cabd0 100644 --- a/Assets/Plugins/Easy Save 3/Resources/ES3/ES3Defaults.asset +++ b/Assets/Plugins/Easy Save 3/Resources/ES3/ES3Defaults.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82d8dac36eccca8d714fc5cd965446b2365b72245f4bddaa29d6df7a2f18095b -size 2098 +oid sha256:4ecf6d7550e13ce547b96dc8a264defc6a882683c53d7cf314678698c5170a54 +size 2238 diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Config/Editor/OdinModuleConfig.asset b/Assets/Plugins/Sirenix/Odin Inspector/Config/Editor/OdinModuleConfig.asset index 25c9fd70..f38d6249 100644 --- a/Assets/Plugins/Sirenix/Odin Inspector/Config/Editor/OdinModuleConfig.asset +++ b/Assets/Plugins/Sirenix/Odin Inspector/Config/Editor/OdinModuleConfig.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af5d0ef5e13f295a655a7edb5150ea6c543cd21feee664e3dcd9f6f531b2f514 -size 531 +oid sha256:0bec1bcf6fc28d7fce3fca13cc17352c5980c66c248777acfbc8705f06a981bc +size 637 diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables.meta new file mode 100644 index 00000000..0d87ff1a --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 962276dbbc452a84f87d12e00ca79a7f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/AddressablesInspectors.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/AddressablesInspectors.cs new file mode 100644 index 00000000..e5aceac9 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/AddressablesInspectors.cs @@ -0,0 +1,1934 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#if !SIRENIX_INTERNAL +#pragma warning disable +#endif + +using System; +using Sirenix.OdinInspector; + +[assembly: RegisterAssetReferenceAttributeForwardToChild(typeof(InlineEditorAttribute))] +[assembly: RegisterAssetReferenceAttributeForwardToChild(typeof(PreviewFieldAttribute))] + +namespace Sirenix.OdinInspector +{ + using System.Diagnostics; + + /// + /// DisallowAddressableSubAssetField is used on AssetReference properties, and disallows and prevents assigned sub-assets to the asset reference. + /// + /// + /// + /// [DisallowAddressableSubAssetField] + /// public AssetReference Reference; + /// + /// + /// + /// + [Conditional("UNITY_EDITOR")] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.ReturnValue | AttributeTargets.Parameter)] + public class DisallowAddressableSubAssetFieldAttribute : Attribute + { + } + + /// + /// Registers an attribute to be applied to an AssetRefenece property, to be forwarded and applied to the AssetReference's child instead. + /// This allows attributes designed for use on UnityEngine.Objects to be used on AssetReference properties as well. + /// By default, InlineEditorAttribute and PreviewFieldAttribute are registered for forwarding. + /// + /// + /// + /// [assembly: Sirenix.OdinInspector.Modules.RegisterAssetReferenceAttributeForwardToChild(typeof(InlineEditorAttribute))] + /// [assembly: Sirenix.OdinInspector.Modules.RegisterAssetReferenceAttributeForwardToChild(typeof(PreviewFieldAttribute))] + /// + /// + /// + [Conditional("UNITY_EDITOR")] + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public class RegisterAssetReferenceAttributeForwardToChildAttribute : Attribute // TODO: Should this be a global attribute? + { + /// + /// The type of the attribute to forward. + /// + public readonly Type AttributeType; + + /// + /// Registers the specified attribute to be copied and applied to the AssetReference's UnityEngine.Object child instead. + /// + /// The attribute type to forward. + public RegisterAssetReferenceAttributeForwardToChildAttribute(Type attributeType) + { + this.AttributeType = attributeType; + } + } +} + +#if UNITY_EDITOR +namespace Sirenix.OdinInspector.Modules.Addressables.Editor +{ + using Sirenix.OdinInspector.Editor; + using Sirenix.Serialization; + using Sirenix.Utilities; + using Sirenix.Utilities.Editor; + using Sirenix.OdinInspector.Modules.Addressables.Editor.Internal; + using Sirenix.Reflection.Editor; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using UnityEditor; + using UnityEditor.AddressableAssets; + using UnityEditor.AddressableAssets.Settings; + using UnityEditor.AddressableAssets.GUI; + using UnityEngine; + using UnityEngine.AddressableAssets; + using System.Runtime.Serialization; + using UnityEngine.U2D; + using UnityEditor.U2D; + using System.IO; + + /// + /// Draws an AssetReference property. + /// + /// The concrete type of AssetReference to be drawn. For example, AssetReferenceTexture. + [DrawerPriority(0, 1, 0)] + public class AssetReferenceDrawer : OdinValueDrawer, IDefinesGenericMenuItems + where T : AssetReference + { + private bool hideAssetReferenceField; + private Type[] validMainAssetTypes; + private Type targetType; + private bool targetTypeIsNotValidMainAsset; + private string NoneSelectedLabel; + //private string[] labelRestrictions; + private bool showSubAssetField; + + private bool updateShowSubAssetField; + + private bool disallowSubAssets_Backing; + + private bool ActuallyDisallowSubAssets => this.disallowSubAssets_Backing && !this.targetTypeIsNotValidMainAsset; + + private List restrictions; + + private bool isSpriteAtlas; + + protected override bool CanDrawValueProperty(InspectorProperty property) + { + return property.GetAttribute() == null; + } + + protected override void Initialize() + { + // If a child exists, we draw that child instead of the AssetReference field. + if (this.Property.Children.Count > 0) + { + this.hideAssetReferenceField = true; + return; + } + + this.EnsureNotRealNull(); + + this.validMainAssetTypes = OdinAddressableUtility.GetAssetReferenceValidMainAssetTypes(typeof(T)); + this.targetType = OdinAddressableUtility.GetAssetReferenceTargetType(typeof(T)); + this.targetTypeIsNotValidMainAsset = this.validMainAssetTypes.Contains(this.targetType) == false; + + this.isSpriteAtlas = this.validMainAssetTypes.Length > 0 && this.validMainAssetTypes[0] == typeof(SpriteAtlas); + + if (this.targetType == typeof(UnityEngine.Object)) + { + this.NoneSelectedLabel = "None (Addressable Asset)"; + } + else if (this.validMainAssetTypes.Length > 1 || this.validMainAssetTypes[0] != this.targetType) + { + this.NoneSelectedLabel = $"None (Addressable [{string.Join("/", this.validMainAssetTypes.Select(n => n.GetNiceName()))}]>{this.targetType.GetNiceName()})"; + } + else + { + this.NoneSelectedLabel = $"None (Addressable {this.targetType.GetNiceName()})"; + } + + this.restrictions = new List(); + foreach (var attr in this.Property.Attributes) + { + if (attr is AssetReferenceUIRestriction r) + { + this.restrictions.Add(r); + } + } + + this.disallowSubAssets_Backing = Property.GetAttribute() != null; + + this.updateShowSubAssetField = true; + } + + private string lastGuid; + + protected override void DrawPropertyLayout(GUIContent label) + { + if (this.disallowSubAssets_Backing && this.targetTypeIsNotValidMainAsset) + { + SirenixEditorGUI.WarningMessageBox($"This {typeof(T).GetNiceName()} field has been marked as not allowing sub assets, but the target type '{this.targetType.GetNiceName()}' is not a valid main asset for {typeof(T).GetNiceName()}, so the target value *must* be a sub asset. Therefore sub assets have been enabled. (Valid main asset types for {typeof(T).GetNiceName()} are: {string.Join(", ", this.validMainAssetTypes.Select(t => t.GetNiceName()))})"); + } + + if (this.hideAssetReferenceField == false) + { + var value = ValueEntry.SmartValue; + + if (this.lastGuid != this.ValueEntry.SmartValue?.AssetGUID) + { + this.updateShowSubAssetField = true; + } + + this.lastGuid = this.ValueEntry.SmartValue?.AssetGUID; + + // Update showSubAssetField. + if (this.updateShowSubAssetField && Event.current.type == EventType.Layout) + { + if (value == null || value.AssetGUID == null || value.editorAsset == null) + { + this.showSubAssetField = false; + } + else if (string.IsNullOrEmpty(value.SubObjectName) == false) + { + this.showSubAssetField = true; + } + else if (this.ActuallyDisallowSubAssets) + { + this.showSubAssetField = false; + } + else + { + var path = AssetDatabase.GUIDToAssetPath(value.AssetGUID); + + if (path == null) + { + this.showSubAssetField = false; + } + else + { + var mainAsset = AssetDatabase.LoadMainAssetAtPath(path); + this.showSubAssetField = OdinAddressableUtility.EnumerateAllActualAndVirtualSubAssets(mainAsset, path).Any(); + } + } + + this.updateShowSubAssetField = false; + } + + var rect = SirenixEditorGUI.GetFeatureRichControlRect(label, out var controlId, out var _, out var valueRect); + + Rect mainRect = valueRect; + Rect subRect = default, subPickerRect = default; + + if (this.showSubAssetField) + { + subRect = mainRect.Split(1, 2).AddX(1); + mainRect = mainRect.Split(0, 2).SubXMax(1); + subPickerRect = subRect.AlignRight(16); + } + + var mainPickerRect = mainRect.AlignRight(16); + + // Cursor + EditorGUIUtility.AddCursorRect(mainPickerRect, MouseCursor.Link); + if (showSubAssetField) + { + EditorGUIUtility.AddCursorRect(subPickerRect, MouseCursor.Link); + } + + // Selector + if (GUI.Button(mainPickerRect, "", SirenixGUIStyles.None)) + { + OpenMainAssetSelector(valueRect); + } + if (showSubAssetField && GUI.Button(subPickerRect, "", SirenixGUIStyles.None)) + { + OpenSubAssetSelector(valueRect); + } + + // Ping + if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && mainRect.Contains(Event.current.mousePosition) && value != null && value.editorAsset != null) + { + EditorGUIUtility.PingObject(value.editorAsset); + } + + // Drag and drop + EditorGUI.BeginChangeCheck(); + var drop = DragAndDropUtilities.DropZone(rect, null, typeof(object), false, controlId); + if (EditorGUI.EndChangeCheck()) + { + this.EnsureNotRealNull(); + + if (this.ConvertToValidAssignment(drop, out Object obj, out bool isSubAssetAssignment)) + { + if (this.isSpriteAtlas && obj is Sprite sprite) + { + foreach (SpriteAtlas spriteAtlas in AssetDatabase_Internals.FindAssets(String.Empty, false, AssetDatabaseSearchArea.AllAssets)) + { + if (!spriteAtlas.CanBindTo(sprite)) + { + continue; + } + + this.SetMainAndSubAsset(spriteAtlas, sprite); + + break; + } + } + else + { + if (isSubAssetAssignment) + { + string path = AssetDatabase.GetAssetPath(obj); + + UnityEngine.Object mainAsset = AssetDatabase.LoadMainAssetAtPath(path); + + if (mainAsset != null) + { + if (mainAsset is Sprite mainAssetSprite) + { + this.SetMainAndSubAsset(mainAssetSprite, obj); + } + else + { + this.SetMainAndSubAsset(mainAsset, obj); + } + } + + this.updateShowSubAssetField = true; + } + else + { + var isSet = false; + + if (string.IsNullOrEmpty(this.ValueEntry.SmartValue.SubObjectName)) + { + if (obj is Sprite) + { + Object[] subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(AssetDatabase.GetAssetPath(obj)); + + if (subAssets.Length > 0) + { + this.SetMainAndSubAsset(obj, subAssets[0]); + + isSet = true; + } + } + } + + if (!isSet) + { + this.SetMainAsset(obj); + } + } + } + + if (this.ActuallyDisallowSubAssets && + !this.targetTypeIsNotValidMainAsset && + !string.IsNullOrEmpty(this.ValueEntry.SmartValue.SubObjectName)) + { + this.SetSubAsset(null); + } + } + else if (drop == null) + { + this.SetMainAsset(null); + } + } + + // Drawing + if (Event.current.type == EventType.Repaint) + { + GUIContent valueLabel; + if (value == null || string.IsNullOrEmpty(value.AssetGUID) || value.editorAsset == null) + { + valueLabel = GUIHelper.TempContent(NoneSelectedLabel); + } + else if (showSubAssetField) + { + var path = AssetDatabase.GUIDToAssetPath(value.AssetGUID); + var assetName = System.IO.Path.GetFileNameWithoutExtension(path); + + + valueLabel = GUIHelper.TempContent(assetName, GetTheDamnPreview(value.editorAsset)); + } + else + { + valueLabel = GUIHelper.TempContent(value.editorAsset.name, GetTheDamnPreview(value.editorAsset)); + } + + GUI.Label(mainRect, valueLabel, EditorStyles.objectField); + SdfIcons.DrawIcon(mainPickerRect.SetWidth(12), SdfIconType.Record2); + + if (this.showSubAssetField) + { + if (string.IsNullOrEmpty(value.SubObjectName) || value.editorAsset == null) + { + valueLabel = GUIHelper.TempContent(""); + } + else + { + valueLabel = GUIHelper.TempContent(value.SubObjectName); + } + + GUI.Label(subRect, valueLabel, EditorStyles.objectField); + SdfIcons.DrawIcon(subPickerRect.SetWidth(12), SdfIconType.Record2); + } + } + } + else + { + this.Property.Children[0].Draw(label); + } + } + + private static Texture2D GetTheDamnPreview(UnityEngine.Object obj) + { + Texture2D img = obj as Texture2D; + + if (img == null) + { + img = (obj as Sprite)?.texture; + } + + if (img == null) + { + img = AssetPreview.GetMiniThumbnail(obj); + } + + return img; + } + + private bool ConvertToValidAssignment(object drop, out UnityEngine.Object converted, out bool isSubAssetAssignment) + { + converted = null; + isSubAssetAssignment = false; + + bool isDefinitelyMainAssetAssignment = false; + + if (object.ReferenceEquals(drop, null)) return false; + + if (!ConvertUtility.TryWeakConvert(drop, this.targetType, out object convertedObj)) + { + for (int i = 0; i < this.validMainAssetTypes.Length; i++) + { + if (ConvertUtility.TryWeakConvert(drop, this.validMainAssetTypes[i], out convertedObj)) + { + isDefinitelyMainAssetAssignment = true; + break; + } + } + } + + if (convertedObj == null || !(convertedObj is UnityEngine.Object unityObj) || unityObj == null) return false; + + converted = unityObj; + + if (isDefinitelyMainAssetAssignment) + { + isSubAssetAssignment = false; + return true; + } + else if (AssetDatabase.IsSubAsset(converted)) + { + if (this.ActuallyDisallowSubAssets) + { + return false; + } + + isSubAssetAssignment = true; + return true; + } + + return true; + } + + private void OpenMainAssetSelector(Rect rect) + { + this.EnsureNotRealNull(); + + var selector = new AddressableSelector("Select", this.validMainAssetTypes, this.restrictions, typeof(T)); + + bool isUnityRoot = this.Property.SerializationRoot?.ValueEntry.WeakSmartValue is UnityEngine.Object; + + if (isUnityRoot) + { + Undo.IncrementCurrentGroup(); + int undoIndex = Undo.GetCurrentGroup(); + + selector.SelectionCancelled += () => { Undo.RevertAllDownToGroup(undoIndex); }; + + selector.SelectionConfirmed += entries => + { + Undo.RevertAllDownToGroup(undoIndex); + + this.OnMainAssetSelect(entries.FirstOrDefault()); + }; + } + else + { + selector.SelectionConfirmed += entries => { this.OnMainAssetSelect(entries.FirstOrDefault()); }; + } + + selector.SelectionChangedWithType += (type, entries) => + { + if (type == SelectionChangedType.SelectionCleared) + { + return; + } + + AddressableAssetEntry entry = entries.FirstOrDefault(); + + this.OnMainAssetSelect(entry); + }; + + selector.ShowInPopup(rect); + } + + private void OpenSubAssetSelector(Rect rect) + { + this.EnsureNotRealNull(); + + if (this.ValueEntry.SmartValue == null || this.ValueEntry.SmartValue.AssetGUID == null) + return; + + var path = AssetDatabase.GUIDToAssetPath(this.ValueEntry.SmartValue.AssetGUID); + if (path == null) + return; + + var mainAsset = AssetDatabase.LoadMainAssetAtPath(path); + + List subAssets; + + if (mainAsset != null && mainAsset is SpriteAtlas) + { + subAssets = OdinAddressableUtility.EnumerateAllActualAndVirtualSubAssets(mainAsset, path) + .Where(val => val != null && (val is Sprite || val is Texture2D)) + .ToList(); + } + else + { + subAssets = OdinAddressableUtility.EnumerateAllActualAndVirtualSubAssets(mainAsset, path) + .Where(val => val != null && this.targetType.IsInstanceOfType(val)) + .ToList(); + } + + var items = new GenericSelectorItem[subAssets.Count + 1]; + + items[0] = new GenericSelectorItem("", null); + for (int i = 0; i < subAssets.Count; i++) + { + var item = new GenericSelectorItem(subAssets[i].name, subAssets[i]); + items[i + 1] = item; + } + + var selector = new GenericSelector("Select Sub Asset", false, items); + + selector.SelectionChanged += OnSubAssetSelect; + selector.SelectionConfirmed += OnSubAssetSelect; + + selector.ShowInPopup(rect); + } + + private void OnMainAssetSelect(AddressableAssetEntry entry) => this.UpdateAssetReference(entry); + + private void OnSubAssetSelect(IEnumerable selection) + { + if (this.ValueEntry == null || this.ValueEntry.SmartValue.AssetGUID == null) + { + return; + } + + UnityEngine.Object selected = selection.FirstOrDefault(); + + this.SetSubAsset(selected); + } + + private void UpdateAssetReference(AddressableAssetEntry entry) + { + if (entry == null) + { + this.SetMainAsset(null); + + return; + } + + if (typeof(T).InheritsFrom()) + { + this.SetMainAsset(entry.MainAsset); + + return; + } + + + if (typeof(T).InheritsFrom()) + { + UnityEngine.Object subObject = null; + + string path = AssetDatabase.GetAssetPath(entry.TargetAsset); + + if (AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(SpriteAtlas)) + { + if (!(entry.TargetAsset is SpriteAtlas)) + { + subObject = entry.TargetAsset; + } + } + + this.SetMainAndSubAsset(entry.MainAsset, subObject); + } + else if (!this.ActuallyDisallowSubAssets && AssetDatabase.IsSubAsset(entry.TargetAsset)) + { + this.SetMainAndSubAsset(entry.MainAsset, entry.TargetAsset); + } + else + { + this.SetMainAsset(entry.MainAsset); + } + } + + private T CreateAssetReferenceFrom(AddressableAssetEntry entry) + { + if (entry != null) + { + return CreateAssetReferenceFrom(entry.TargetAsset); + } + else + { + return null; + } + } + + private T CreateAssetReferenceFrom(UnityEngine.Object mainAsset, UnityEngine.Object subAsset) + { + var path = AssetDatabase.GetAssetPath(mainAsset); + var guid = AssetDatabase.AssetPathToGUID(path); + + if (guid == null) return null; + + var instance = (T)Activator.CreateInstance(typeof(T), guid); + instance.SetEditorSubObject(subAsset); + return instance; + } + + private T CreateAssetReferenceFrom(UnityEngine.Object obj) + { + var path = AssetDatabase.GetAssetPath(obj); + var guid = AssetDatabase.AssetPathToGUID(path); + + if (guid == null) return null; + + var instance = (T)Activator.CreateInstance(typeof(T), guid); + + if (typeof(T).InheritsFrom()) + { + if (AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(SpriteAtlas)) + { + if (!(obj is SpriteAtlas)) + { + instance.SetEditorSubObject(obj); + } + } + } + else if (typeof(T).InheritsFrom()) + { + // No need to do anything here. + // The user will need to choose a sprite + // "sub asset" from the atlas. + } + else if (this.ActuallyDisallowSubAssets == false && AssetDatabase.IsSubAsset(obj)) + { + instance.SetEditorSubObject(obj); + } + + return instance; + } + + public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu) + { + genericMenu.AddItem(new GUIContent("Set To Null"), false, () => + { + this.EnsureNotRealNull(); + + this.SetMainAsset(null); + }); + + if (this.ValueEntry.SmartValue != null && string.IsNullOrEmpty(this.ValueEntry.SmartValue.SubObjectName) == false) + { + genericMenu.AddItem(new GUIContent("Remove Sub Asset"), false, () => + { + this.EnsureNotRealNull(); + + this.SetSubAsset(null); + }); + } + else + { + genericMenu.AddDisabledItem(new GUIContent("Remove Sub Asset")); + } + + genericMenu.AddItem(new GUIContent("Open Groups Window"), false, OdinAddressableUtility.OpenGroupsWindow); + } + + private void SetMainAndSubAsset(UnityEngine.Object mainAsset, UnityEngine.Object subAsset, bool setDirtyIfChanged = true) + { + string subAssetName = subAsset == null ? null : subAsset.name; + + bool isDifferent = this.ValueEntry.SmartValue.editorAsset != mainAsset || + this.ValueEntry.SmartValue.SubObjectName != subAssetName; + + if (!isDifferent) + { + return; + } + + if (this.Property.SerializationRoot?.ValueEntry.WeakSmartValue is UnityEngine.Object) + { + Undo.IncrementCurrentGroup(); + + Undo.SetCurrentGroupName("Main- and Sub Asset Changed"); + + int index = Undo.GetCurrentGroup(); + + this.SetMainAsset(mainAsset, false); + this.SetSubAsset(subAsset, false); + + Undo.CollapseUndoOperations(index); + + if (setDirtyIfChanged) + { + this.Property.MarkSerializationRootDirty(); + } + } + else + { + this.SetMainAsset(mainAsset, false); + this.SetSubAsset(subAsset, false); + } + } + + private void SetMainAsset(UnityEngine.Object asset, bool setDirtyIfChanged = true) + { + if (this.ValueEntry.SmartValue.editorAsset == asset) + { + return; + } + + this.Property.RecordForUndo("Main Asset Changed"); + + this.ValueEntry.SmartValue.SetEditorAsset(asset); + + this.updateShowSubAssetField = true; + + if (setDirtyIfChanged) + { + this.Property.MarkSerializationRootDirty(); + } + } + + private void SetSubAsset(UnityEngine.Object asset, bool setDirtyIfChanged = true) + { +#if SIRENIX_INTERNAL + if (this.ValueEntry.SmartValue.editorAsset == null) + { + Debug.LogError("[SIRENIX INTERNAL] Attempted to assign the Sub Asset on an AssetReference without the Main Asset being assigned first."); + return; + } +#endif + + string assetName = asset == null ? null : asset.name; + + if (this.ValueEntry.SmartValue.SubObjectName == assetName) + { + return; + } + + this.Property.RecordForUndo("Sub Asset Changed"); + + this.ValueEntry.SmartValue.SetEditorSubObject(asset); + + this.updateShowSubAssetField = true; + + if (setDirtyIfChanged) + { + this.Property.MarkSerializationRootDirty(); + } + } + + private void EnsureNotRealNull() + { + if (this.ValueEntry.WeakSmartValue == null) + { + this.ValueEntry.SmartValue = OdinAddressableUtility.CreateAssetReferenceGuid(null); + } + } + } + + /// + /// Draws an AssetLabelReference field. + /// + [DrawerPriority(0, 1, 0)] + public class AssetLabelReferenceDrawer : OdinValueDrawer, IDefinesGenericMenuItems + { + protected override bool CanDrawValueProperty(InspectorProperty property) + { + return property.GetAttribute() == null; + } + + protected override void DrawPropertyLayout(GUIContent label) + { + var rect = SirenixEditorGUI.GetFeatureRichControlRect(label, out var controlId, out var hasKeyboardFocus, out var valueRect); + + string valueLabel; + if (this.ValueEntry.SmartValue == null || string.IsNullOrEmpty(this.ValueEntry.SmartValue.labelString)) + { + valueLabel = ""; + } + else + { + valueLabel = this.ValueEntry.SmartValue.labelString; + } + + if (GUI.Button(valueRect, valueLabel, EditorStyles.popup)) + { + var selector = new AddressableLabelSelector(); + + selector.SelectionChanged += SetLabel; + selector.SelectionConfirmed += SetLabel; + + selector.ShowInPopup(valueRect); + } + } + + private void SetLabel(IEnumerable selection) + { + var selected = selection.FirstOrDefault(); + this.ValueEntry.SmartValue = new AssetLabelReference() + { + labelString = selected, + }; + } + + public void PopulateGenericMenu(InspectorProperty property, GenericMenu genericMenu) + { + genericMenu.AddItem(new GUIContent("Set To Null"), false, () => property.ValueEntry.WeakSmartValue = null); + genericMenu.AddItem(new GUIContent("Open Label Window"), false, () => OdinAddressableUtility.OpenLabelsWindow()); + } + } + + /// + /// Odin Selector for Addressables. + /// + public class AddressableSelector : OdinSelector + { + //private static EditorPrefBool flatten = new EditorPrefBool("AddressablesSelector.Flatten", false); + public event Action> SelectionChangedWithType; + + private static EditorPrefEnum listMode = new EditorPrefEnum("AddressablesSelector.ListMode", SelectorListMode.Group); + + private readonly string title; + private readonly Type[] filterTypes; + private readonly List restrictions; + + internal bool ShowNonAddressables; + + public override string Title => this.title; + + /// + /// Initializes a AddressableSelector. + /// + /// The title of the selector. Set to null for no title. + /// The type of UnityEngine.Object to be selectable. For example, UnityEngine.Texture. For no restriction, pass in UnityEngine.Object. + /// The Addressable labels to restrict the selector to. Set to null for no label restrictions. + /// Throws if the filter type is null. + public AddressableSelector(string title, Type filterType, List restrictions, Type assetReferenceType) + : this(title, new Type[] { filterType }, restrictions, assetReferenceType) + { + } + + /// + /// Initializes a AddressableSelector. + /// + /// The title of the selector. Set to null for no title. + /// The types of UnityEngine.Object to be selectable. For example, UnityEngine.Texture. For no restriction, pass in an array containing UnityEngine.Object. + /// The Addressable labels to restrict the selector to. Set to null for no label restrictions. + /// Throws if the filter type is null. + public AddressableSelector(string title, Type[] filterTypes, List restrictions, Type assetReferenceType) + { + this.title = title; + this.filterTypes = filterTypes ?? throw new ArgumentNullException(nameof(filterTypes)); + this.restrictions = restrictions; + + if (assetReferenceType != null) + { + if (assetReferenceType.InheritsFrom() == false) + { + throw new ArgumentException("Must inherit AssetReference", nameof(assetReferenceType)); + } + else if (assetReferenceType.IsAbstract) + { + throw new ArgumentException("Cannot be abstract type.", nameof(assetReferenceType)); + } + } + } + + protected override void DrawToolbar() + { + bool drawTitle = !string.IsNullOrEmpty(this.Title); + bool drawSearchToolbar = this.SelectionTree.Config.DrawSearchToolbar; + bool drawButton = this.DrawConfirmSelectionButton; + + if (drawTitle || drawSearchToolbar || drawButton) + { + SirenixEditorGUI.BeginHorizontalToolbar(this.SelectionTree.Config.SearchToolbarHeight); + { + DrawToolbarTitle(); + DrawToolbarSearch(); + EditorGUI.DrawRect(GUILayoutUtility.GetLastRect().AlignLeft(1), SirenixGUIStyles.BorderColor); + + SdfIconType icon; + if (listMode.Value == SelectorListMode.Path) + icon = SdfIconType.ListNested; + else if (listMode.Value == SelectorListMode.Group) + icon = SdfIconType.ListStars; + else if (listMode.Value == SelectorListMode.Flat) + icon = SdfIconType.List; + else + icon = SdfIconType.X; + + if (SirenixEditorGUI.ToolbarButton(icon, true)) + { + int m = (int)listMode.Value + 1; + + if (m >= (int)SelectorListMode.Max) + { + m = 0; + } + + listMode.Value = (SelectorListMode)m; + + this.RebuildMenuTree(); + } + + EditorGUI.BeginChangeCheck(); + this.ShowNonAddressables = SirenixEditorGUI.ToolbarToggle(this.ShowNonAddressables, EditorIcons.UnityLogo); + if (EditorGUI.EndChangeCheck()) + { + this.RebuildMenuTree(); + } + + if (SirenixEditorGUI.ToolbarButton(SdfIconType.GearFill, true)) + { + OdinAddressableUtility.OpenGroupsWindow(); + } + + DrawToolbarConfirmButton(); + } + SirenixEditorGUI.EndHorizontalToolbar(); + } + } + + protected override void BuildSelectionTree(OdinMenuTree tree) + { + if (this.SelectionChangedWithType != null) + { + tree.Selection.SelectionChanged += type => + { + IEnumerable selection = this.GetCurrentSelection(); + + if (this.IsValidSelection(selection)) + { + this.SelectionChangedWithType(type, selection); + } + }; + } + + tree.Config.EXPERIMENTAL_INTERNAL_SparseFixedLayouting = true; + + tree.Config.SelectMenuItemsOnMouseDown = true; + + if (AddressableAssetSettingsDefaultObject.SettingsExists) + { + AddressableAssetSettings settings = AddressableAssetSettingsDefaultObject.Settings; + + foreach (AddressableAssetGroup group in settings.groups) + { + if (group == null || group.name == "Built In Data") + { + continue; + } + + foreach (AddressableAssetEntry entry in group.entries) + { + this.AddEntriesToTree(tree, group.name, entry); + } + } + } + + foreach (OdinMenuItem item in tree.EnumerateTree()) + { + if (item.Value == null) + { + item.SdfIcon = SdfIconType.Folder; + } + } + + if (this.ShowNonAddressables) + { + var searchFilter = ""; + + foreach (Type filterType in this.filterTypes) + { + searchFilter += $"t:{filterType.Name} "; + } + + IEnumerator enumerator = AssetDatabase_Internals.EnumerateAllAssets(searchFilter, false, AssetDatabaseSearchArea.InAssetsOnly); + + if (enumerator.MoveNext()) + { + var addedGuids = new HashSet(); + + foreach (OdinMenuItem item in tree.EnumerateTree()) + { + if (item.Value != null) + { + addedGuids.Add((item.Value as AddressableAssetEntry).guid); + } + } + + const string NON_ADDRESSABLES_ITEM_NAME = "Non Addressables"; + + var nonAddressablesItem = new OdinMenuItem(tree, NON_ADDRESSABLES_ITEM_NAME, null) {Icon = EditorIcons.UnityLogo}; + + tree.MenuItems.Add(nonAddressablesItem); + + do + { + HierarchyProperty current = enumerator.Current; + + if (addedGuids.Contains(current.guid) || !current.isMainRepresentation) + { + continue; + } + + AddressableAssetEntry entry = OdinAddressableUtility.CreateFakeAddressableAssetEntry(current.guid); + + if (listMode == SelectorListMode.Flat) + { + var item = new OdinMenuItem(tree, current.name, entry) {Icon = current.icon}; + + nonAddressablesItem.ChildMenuItems.Add(item); + } + else + { + string path = AssetDatabase.GetAssetPath(current.instanceID); + + if (!current.isFolder) + { + int extensionEndingIndex = GetExtensionsEndingIndex(path); + + if (extensionEndingIndex != -1) + { + path = path.Substring(0, extensionEndingIndex); + } + } + + path = RemoveBaseDirectoryFromAssetPath(path); + + tree.Add($"{NON_ADDRESSABLES_ITEM_NAME}/{path}", entry, current.icon); + } + } while (enumerator.MoveNext()); + + nonAddressablesItem.ChildMenuItems.SortMenuItemsByName(); + } + } + + OdinMenuItem noneItem; + + if (this.filterTypes.Contains(typeof(UnityEngine.Object))) + { + noneItem = new OdinMenuItem(tree, " (Addressable Asset)", null); + } + else + { + string filterTypesJoined; + + if (this.filterTypes.Length == 1) + { + filterTypesJoined = this.filterTypes[0].GetNiceName(); + } + else + { + filterTypesJoined = string.Join("/", this.filterTypes.Select(t => t.GetNiceName())); + } + + noneItem = new OdinMenuItem(tree, $" (Addressable {filterTypesJoined})", null); + } + + noneItem.SdfIcon = SdfIconType.X; + tree.MenuItems.Insert(0, noneItem); + } + + private static int GetExtensionsEndingIndex(string path) + { + for (var i = path.Length - 1; i >= 0; i--) + { + if (path[i] == '\\' || path[i] == '/') + { + return -1; + } + + if (path[i] == '.') + { + return i; + } + } + + return -1; + } + + private static string RemoveBaseDirectoryFromAssetPath(string path) + { + if (path.StartsWith("Assets/")) + { + return path.Remove(0, "Assets/".Length); + } + + return path; + } + + private void AddEntriesToTree(OdinMenuTree tree, string groupName, AddressableAssetEntry entry) + { + if (entry == null) + { + return; + } + + bool isFolder = entry.IsFolder || AssetDatabase.IsValidFolder(entry.AssetPath); + + if (isFolder) + { + entry.GatherAllAssets(null, false, false, true, null); + + if (entry.SubAssets != null) + { + foreach (AddressableAssetEntry e in entry.SubAssets) + { + this.AddEntriesToTree(tree, groupName, e); + } + } + } + else + { + UnityEngine.Object asset = entry.TargetAsset; + + if (asset == null) + { + return; + } + + Type assetType = asset.GetType(); + var inheritsFromFilterType = false; + + for (var i = 0; i < this.filterTypes.Length; i++) + { + if (this.filterTypes[i].IsAssignableFrom(assetType)) + { + inheritsFromFilterType = true; + break; + } + } + + if (inheritsFromFilterType && this.PassesRestrictions(entry)) + { + string name; + if (listMode.Value == SelectorListMode.Group) + { + name = entry.address; + } + else if (listMode.Value == SelectorListMode.Path) + { + name = System.IO.Path.GetFileNameWithoutExtension(entry.AssetPath); + } + else if (listMode.Value == SelectorListMode.Flat) + { + name = entry.address; + } + else + { + throw new Exception("Unsupported list mode: " + listMode.Value); + } + + var item = new OdinMenuItem(tree, name, entry) + { + Icon = AssetPreview.GetMiniThumbnail(asset) + }; + + if (listMode.Value == SelectorListMode.Group) + { + OdinMenuItem groupItem = tree.GetMenuItem(groupName); + + if (groupItem == null) + { + groupItem = new OdinMenuItem(tree, groupName, null); + tree.MenuItems.Add(groupItem); + } + + if (entry.ParentEntry != null && entry.ParentEntry.IsFolder) + { + OdinMenuItem folderItem = null; + + for (int i = 0; i < groupItem.ChildMenuItems.Count; i++) + { + if (groupItem.ChildMenuItems[i].Name == entry.ParentEntry.address) + { + folderItem = groupItem.ChildMenuItems[i]; + break; + } + } + + if (folderItem == null) + { + folderItem = new OdinMenuItem(tree, entry.ParentEntry.address, null); + groupItem.ChildMenuItems.Add(folderItem); + } + + folderItem.ChildMenuItems.Add(item); + } + else + { + groupItem.ChildMenuItems.Add(item); + } + } + else if (listMode.Value == SelectorListMode.Path) + { + tree.AddMenuItemAtPath(System.IO.Path.GetDirectoryName(entry.AssetPath), item); + } + else if (listMode.Value == SelectorListMode.Flat) + { + tree.MenuItems.Add(item); + } + } + } + } + + private bool PassesRestrictions(AddressableAssetEntry entry) + { + if (restrictions == null) return true; + + return OdinAddressableUtility.ValidateAssetReferenceRestrictions(restrictions, entry.MainAsset); + + //for (int i = 0; i < this.restrictions.Count; i++) + //{ + // if (this.restrictions[i].ValidateAsset(entry.AssetPath) == false) + // { + // return false; + // } + //} + + //return true; + + /* If for whatever reason Unity haven't actually implemented their restriction methods, then we can use this code to atleast implement label restriction. */ + //if (this.labelRestrictions == null) return true; + + //for (int i = 0; i < labelRestrictions.Length; i++) + //{ + // if (entry.labels.Contains(labelRestrictions[i])) return true; + //} + + //return false; + } + + private enum SelectorListMode + { + Group, + Path, + Flat, + + Max, + } + } + + public class AddressableLabelSelector : OdinSelector + { + protected override void DrawToolbar() + { + bool drawTitle = !string.IsNullOrEmpty(this.Title); + bool drawSearchToolbar = this.SelectionTree.Config.DrawSearchToolbar; + bool drawButton = this.DrawConfirmSelectionButton; + + if (drawTitle || drawSearchToolbar || drawButton) + { + SirenixEditorGUI.BeginHorizontalToolbar(this.SelectionTree.Config.SearchToolbarHeight); + { + DrawToolbarTitle(); + DrawToolbarSearch(); + EditorGUI.DrawRect(GUILayoutUtility.GetLastRect().AlignLeft(1), SirenixGUIStyles.BorderColor); + + if (SirenixEditorGUI.ToolbarButton(SdfIconType.GearFill, true)) + { + OdinAddressableUtility.OpenLabelsWindow(); + } + + DrawToolbarConfirmButton(); + } + SirenixEditorGUI.EndHorizontalToolbar(); + } + } + protected override void BuildSelectionTree(OdinMenuTree tree) + { + IList labels = null; + + if (AddressableAssetSettingsDefaultObject.SettingsExists) + { + var settings = AddressableAssetSettingsDefaultObject.Settings; + labels = settings.GetLabels(); + } + + if (labels == null) labels = Array.Empty(); + + tree.MenuItems.Add(new OdinMenuItem(tree, "", null)); + + for (int i = 0; i < labels.Count; i++) + { + tree.MenuItems.Add(new OdinMenuItem(tree, labels[i], labels[i])); + } + } + } + + /// + /// Resolves children for AssetReference properties, and implements the RegisterAssetReferenceAttributeForwardToChild behaviour. + /// + /// The concrete type of AssetReference to be drawn. For example, AssetReferenceTexture. + public class AssetReferencePropertyResolver : OdinPropertyResolver + where T : AssetReference + { + private static readonly Type[] attributesToForward; + + static AssetReferencePropertyResolver() + { + attributesToForward = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(x => x.GetCustomAttributes()) + .Cast() + .Select(x => x.AttributeType) + .ToArray(); + } + + public override int ChildNameToIndex(string name) + { + return 0; + } + + public override int ChildNameToIndex(ref StringSlice name) + { + return 0; + } + + public override InspectorPropertyInfo GetChildInfo(int childIndex) + { + var targetType = OdinAddressableUtility.GetAssetReferenceTargetType(typeof(T)); + var getterSetterType = typeof(AssetReferenceValueGetterSetter<>).MakeGenericType(typeof(T), targetType); + + var getterSetter = Activator.CreateInstance(getterSetterType) as IValueGetterSetter; + + List attributes = new List + { + new ShowInInspectorAttribute(), + }; + + foreach (var type in attributesToForward) + { + var attr = this.Property.Attributes.FirstOrDefault(x => x.GetType() == type); + if (attr != null) + { + attributes.Add(attr); + } + } + + string label = "Asset"; + + return InspectorPropertyInfo.CreateValue(label, 0, SerializationBackend.None, getterSetter, attributes); + } + + protected override int GetChildCount(T value) + { + foreach (var attr in attributesToForward) + { + if (this.Property.Attributes.Any(x => x.GetType() == attr)) + { + return 1; + } + } + + return 0; + } + + private class AssetReferenceValueGetterSetter : IValueGetterSetter + where TTarget : UnityEngine.Object + { + public bool IsReadonly => false; + + public Type OwnerType => typeof(T); + + public Type ValueType => typeof(TTarget); + + public TTarget GetValue(ref T owner) + { + var v = owner.editorAsset; + return v as TTarget; + } + + public object GetValue(object owner) + { + var v = (owner as T)?.editorAsset; + return v as TTarget; + } + + public void SetValue(ref T owner, TTarget value) + { + owner.SetEditorAsset(value); + } + + public void SetValue(object owner, object value) + { + (owner as T).SetEditorAsset(value as TTarget); + } + } + } + + /// + /// Processes attributes for AssetReference properties. + /// + /// The concrete type of AssetReference to be drawn. For example, AssetReferenceTexture. + public class AssetReferenceAttributeProcessor : OdinAttributeProcessor + where T : AssetReference + { + public override void ProcessSelfAttributes(InspectorProperty property, List attributes) + { + attributes.Add(new DoNotDrawAsReferenceAttribute()); + attributes.Add(new HideReferenceObjectPickerAttribute()); + attributes.Add(new SuppressInvalidAttributeErrorAttribute()); // TODO: Remove this with proper attribute forwarding support. + } + } + + /// + /// Processes attributes for AssetLabelReference properties. + /// + public class AssetLabelReferenceAttributeProcessor : OdinAttributeProcessor + { + public override void ProcessSelfAttributes(InspectorProperty property, List attributes) + { + attributes.Add(new DoNotDrawAsReferenceAttribute()); + attributes.Add(new HideReferenceObjectPickerAttribute()); + } + } + + /// + /// Implements conversion behaviour for addressables. + /// + [InitializeOnLoad] + internal class AssetReferenceConverter : ConvertUtility.ICustomConverter + { + private readonly Type type_AssetEntryTreeViewItem; + private WeakValueGetter get_AssetEntryTreeViewItem_entry; + + static AssetReferenceConverter() + { + ConvertUtility.AddCustomConverter(new AssetReferenceConverter()); + } + + public AssetReferenceConverter() + { + this.type_AssetEntryTreeViewItem = TwoWaySerializationBinder.Default.BindToType("UnityEditor.AddressableAssets.GUI.AssetEntryTreeViewItem") ?? throw new Exception("Failed to find UnityEditor.AddressableAssets.GUI.AddressableAssetEntryTreeViewItem type."); + var field_AssetEntryTreeViewItem_entry = type_AssetEntryTreeViewItem.GetField("entry", Flags.AllMembers) ?? throw new Exception("Failed to find entry field in UnityEditor.AddressableAssets.GUI.AddressableAssetEntryTreeViewItem type."); + this.get_AssetEntryTreeViewItem_entry = EmitUtilities.CreateWeakInstanceFieldGetter(type_AssetEntryTreeViewItem, field_AssetEntryTreeViewItem_entry); + } + + // UnityEngine.Object > AssetReference/T + // AddressableAssetEntry > AssetReference + // AssetReference/T > UnityEngine.Object + // AssetReference/T > AssetReference/T + // AddressableAssetEntry > UnityEngine.Object + + public bool CanConvert(Type from, Type to) + { + var comparer = FastTypeComparer.Instance; + + if (to.InheritsFrom(typeof(AssetReference))) + { + if (comparer.Equals(from, typeof(AddressableAssetEntry)) || comparer.Equals(from, type_AssetEntryTreeViewItem)) + { + return true; + } + else if (from.InheritsFrom()) + { + if (to.InheritsFrom(typeof(AssetReferenceT<>))) + { + var baseType = to.GetGenericBaseType(typeof(AssetReferenceT<>)); + + var targetType = baseType.GetGenericArguments()[0]; + + return from.InheritsFrom(targetType); + } + else + { + return true; + } + } + else if (from.InheritsFrom(typeof(AssetReference))) + { + return to.InheritsFrom(from); + } + else + { + return false; + } + } + else if (from.InheritsFrom(typeof(AssetReference)) && to.InheritsFrom()) + { + return false; + } + else + { + return false; + } + } + + public bool TryConvert(object obj, Type to, out object result) + { + if (obj == null) + { + result = null; + return false; + } + + var comparer = FastTypeComparer.Instance; + + // AssetEntryTreeViewItems is a UI element container for AddressableAssetEntry. + // With this we can just treat AssetEntryTreeViewItems as an AddressableAssetEntry. + if (comparer.Equals(obj.GetType(), type_AssetEntryTreeViewItem)) + { + obj = get_AssetEntryTreeViewItem_entry(ref obj); + } + + if (to.InheritsFrom(typeof(AssetReference))) + { + Type assetType; + if (to.InheritsFrom(typeof(AssetReferenceT<>))) + { + var baseType = to.GetGenericBaseType(typeof(AssetReferenceT<>)); + assetType = baseType.GetGenericArguments()[0]; + } + else + { + assetType = typeof(UnityEngine.Object); + } + + if (obj is UnityEngine.Object uObj) + { + if (obj.GetType().InheritsFrom(assetType)) + { + string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(uObj)); + + if (string.IsNullOrEmpty(guid) == false) + { + + result = CreateReference(to, uObj); + return true; + } + else + { + result = null; + return false; + } + } + else + { + result = null; + return false; + } + } + else if (obj is AddressableAssetEntry entry) + { + if (entry.TargetAsset.GetType().InheritsFrom(assetType)) + { + result = CreateReference(to, entry.TargetAsset); + return true; + + } + else + { + result = null; + return false; + } + } + else if (obj is AssetReference reference) + { + if (TryGetReferencedAsset(reference, assetType, out var asset)) + { + result = CreateReference(to, asset); + return true; + } + else + { + result = null; + return false; + } + } + else + { + result = null; + return false; + } + } + else if (to.InheritsFrom(typeof(UnityEngine.Object)) && obj is AssetReference reference) + { + if (TryGetReferencedAsset(reference, to, out var asset)) + { + result = asset; + return true; + } + else + { + result = null; + return false; + } + } + else if (to.InheritsFrom(typeof(UnityEngine.Object)) && obj is AddressableAssetEntry entry) + { + var target = entry.TargetAsset; + if (target == null) + { + result = null; + return false; + } + else if (target.GetType().InheritsFrom(to)) + { + result = target; + return true; + } + else if (ConvertUtility.TryWeakConvert(target, to, out var converted)) + { + result = converted; + return true; + } + else + { + result = null; + return false; + } + } + else + { + result = null; + return false; + } + } + + private bool TryGetReferencedAsset(AssetReference reference, Type to, out UnityEngine.Object asset) + { + if (reference.AssetGUID == null) + { + asset = null; + return false; + } + + var path = AssetDatabase.GUIDToAssetPath(reference.AssetGUID); + + if (reference.SubObjectName != null) + { + asset = null; + + foreach (var subAsset in OdinAddressableUtility.EnumerateAllActualAndVirtualSubAssets(reference.editorAsset, path)) + { + if (subAsset.name == reference.SubObjectName) + { + asset = subAsset; + break; + } + } + } + else + { + asset = AssetDatabase.LoadAssetAtPath(path); + } + + if (asset != null) + { + if (asset.GetType().InheritsFrom(to)) + { + return true; + } + else if (ConvertUtility.TryWeakConvert(asset, to, out var converted)) + { + asset = (UnityEngine.Object)converted; + return true; + } + else + { + asset = null; + return false; + } + } + else + { + return false; + } + } + + private AssetReference CreateReference(Type type, UnityEngine.Object obj) + { + var reference = (AssetReference)Activator.CreateInstance(type, new string[] { AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(obj)) }); + if (AssetDatabase.IsSubAsset(obj)) + { + reference.SetEditorAsset(obj); + } + + return reference; + } + } + + /// + /// Odin Inspector utility methods for working with addressables. + /// + public static class OdinAddressableUtility + { + private readonly static Action openAddressableWindowAction; + + static OdinAddressableUtility() + { + var type = TwoWaySerializationBinder.Default.BindToType("UnityEditor.AddressableAssets.GUI.AddressableAssetsWindow") ?? throw new Exception(""); + var method = type.GetMethod("Init", Flags.AllMembers) ?? throw new Exception(""); + openAddressableWindowAction = (Action)Delegate.CreateDelegate(typeof(Action), method); + } + + public static IEnumerable EnumerateAllActualAndVirtualSubAssets(UnityEngine.Object mainAsset, string mainAssetPath) + { + if (mainAsset == null) + { + yield break; + } + + Object[] subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(mainAssetPath); + + foreach (Object subAsset in subAssets) + { + yield return subAsset; + } + + // The sprites/textures in a sprite atlas are not sub assets of the atlas, but they are apparently + // still part of the atlas in a way that the addressables system considers a sub asset. + if (mainAsset is UnityEngine.U2D.SpriteAtlas atlas) + { + Object[] packables = atlas.GetPackables(); + + foreach (Object packable in packables) + { + if (packable == null) + { + continue; + } + + if (!(packable is DefaultAsset packableFolder)) + { + yield return packable; + continue; + } + + string packablePath = AssetDatabase.GetAssetPath(packableFolder); + + if (!AssetDatabase.IsValidFolder(packablePath)) + { + continue; + } + + string[] files = Directory.GetFiles(packablePath, "*.*", SearchOption.AllDirectories); + + foreach (string file in files) + { + if (file.EndsWith(".meta")) + { + continue; + } + + Type assetType = AssetDatabase.GetMainAssetTypeAtPath(file); + + if (assetType != typeof(Sprite) && assetType != typeof(Texture2D)) + { + continue; + } + + yield return AssetDatabase.LoadMainAssetAtPath(file); + } + } + } + } + + /// + /// Opens the addressables group settings window. + /// + public static void OpenGroupsWindow() + { + openAddressableWindowAction(); + } + + /// + /// Opens the addressables labels settings window. + /// + public static void OpenLabelsWindow() + { + if (!AddressableAssetSettingsDefaultObject.SettingsExists) return; + EditorWindow.GetWindow().Intialize(AddressableAssetSettingsDefaultObject.Settings); + } + + /// + /// Converts the specified object into an addressable. + /// + /// The object to make addressable. + /// The addressable group to add the object to. + public static void MakeAddressable(UnityEngine.Object obj, AddressableAssetGroup group) + { + if (!AddressableAssetSettingsDefaultObject.SettingsExists) return; + + var settings = AddressableAssetSettingsDefaultObject.Settings; + var guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(obj)); + var entry = settings.CreateOrMoveEntry(guid, group, false, false); + entry.address = AssetDatabase.GUIDToAssetPath(guid); + settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryCreated, entry, false, true); + } + + /// + /// Gets the type targeted by an AssetReference type. For example, returns Texture for AssetReferenceTexture. + /// Returns UnityEngine.Object for AssetReference type. + /// + /// A type of AssetReference, for example, AssetReferenceTexture. + /// + /// If the given type inherits AssetRefernceT<T>, then the method returns the generic T argument. + /// If the given type is AssetReference, then the method returns UnityEngine.Object. + /// + /// Throws if given parameter is null. + /// Throws if the given type does not inherit or is AssetReference. + public static Type GetAssetReferenceTargetType(Type assetReferenceType) + { + if (assetReferenceType == null) throw new ArgumentNullException(nameof(assetReferenceType)); + + if (assetReferenceType.InheritsFrom(typeof(AssetReferenceT<>))) + { + var genericBase = assetReferenceType.GetGenericBaseType(typeof(AssetReferenceT<>)); + return genericBase.GetGenericArguments()[0]; + } + else + { + return typeof(UnityEngine.Object); + } + } + + public static Type[] GetAssetReferenceValidMainAssetTypes(Type assetReferenceType) + { + if (assetReferenceType == null) throw new ArgumentNullException(nameof(assetReferenceType)); + + if (assetReferenceType.InheritsFrom(typeof(AssetReferenceSprite))) + { + return new Type[] + { + typeof(Sprite), + typeof(SpriteAtlas), + typeof(Texture2D) + }; + } + else if (assetReferenceType.InheritsFrom(typeof(AssetReferenceAtlasedSprite))) + { + return new Type[] { typeof(SpriteAtlas) }; + } + + return new Type[] { GetAssetReferenceTargetType(assetReferenceType) }; + } + + /// + /// Validate an asset against a list of AssetReferenceUIRestrictions. + /// + /// The restrictions to apply. + /// The asset to validate. + /// Returns true if the asset passes all restrictions. Otherwise false. + /// Throws if Addressable Settings have not been created. + /// Throws if restrictions or asset is null. + public static bool ValidateAssetReferenceRestrictions(List restrictions, UnityEngine.Object asset) + { + return ValidateAssetReferenceRestrictions(restrictions, asset, out _); + } + + /// + /// Validate an asset against a list of AssetReferenceUIRestrictions. + /// + /// The restrictions to apply. + /// The asset to validate. + /// The first failed restriction. null if no restrictions failed. + /// Returns true if the asset passes all restrictions. Otherwise false. + /// Throws if Addressable Settings have not been created. + /// Throws if restrictions or asset is null. + public static bool ValidateAssetReferenceRestrictions(List restrictions, UnityEngine.Object asset, out AssetReferenceUIRestriction failedRestriction) + { + if (AddressableAssetSettingsDefaultObject.SettingsExists == false) throw new Exception("Addressable Settings have not been created."); + + _ = restrictions ?? throw new ArgumentNullException(nameof(restrictions)); + _ = asset ?? throw new ArgumentNullException(nameof(asset)); + + for (int i = 0; i < restrictions.Count; i++) + { + if (restrictions[i] is AssetReferenceUILabelRestriction labels) + { + /* Unity, in all its wisdom, have apparently decided not to implement their AssetReferenceRestriction attributes in some versions(?) + * So, to compensate, we're going to manually validate the label restriction attribute, so atleast that works. */ + + var guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(asset)); + + var entry = AddressableAssetSettingsDefaultObject.Settings.FindAssetEntry(guid, true); + + if (entry.labels.Any(x => labels.m_AllowedLabels.Contains(x)) == false) + { + failedRestriction = labels; + return false; + } + } + else if (restrictions[i].ValidateAsset(asset) == false) + { + failedRestriction = restrictions[i]; + return false; + } + } + + failedRestriction = null; + return true; + } + + internal static TAssetReference CreateAssetReferenceGuid(string guid) where TAssetReference : AssetReference + { + return (TAssetReference) Activator.CreateInstance(typeof(TAssetReference), guid); + } + + internal static TAssetReference CreateAssetReference(UnityEngine.Object obj) where TAssetReference : AssetReference + { + if (obj == null) + { + return CreateAssetReferenceGuid(null); + } + + string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(obj)); + + return CreateAssetReferenceGuid(guid); + } + + internal static AddressableAssetEntry CreateFakeAddressableAssetEntry(string guid) + { + var entry = (AddressableAssetEntry) FormatterServices.GetUninitializedObject(typeof(AddressableAssetEntry)); + + OdinAddressableReflection.AddressableAssetEntry_mGUID_Field.SetValue(entry, guid); + + return entry; + } + } +} +#endif \ No newline at end of file diff --git a/Assets/Scripts/Saving/StorySave.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/AddressablesInspectors.cs.meta similarity index 83% rename from Assets/Scripts/Saving/StorySave.cs.meta rename to Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/AddressablesInspectors.cs.meta index 4ae1d50b..66d12eec 100644 --- a/Assets/Scripts/Saving/StorySave.cs.meta +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/AddressablesInspectors.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9ff8f51d49d96524fab4c38c7f846368 +guid: ba1664ec1a0467641a742eaadae146d4 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal.meta new file mode 100644 index 00000000..b945d119 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6384593779a7421881db64cdb25db4c5 +timeCreated: 1718984862 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflection.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflection.cs new file mode 100644 index 00000000..2df8f797 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflection.cs @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#if UNITY_EDITOR + +#if !SIRENIX_INTERNAL +#pragma warning disable +#endif + +using System.Reflection; +using UnityEditor.AddressableAssets.Settings; + +namespace Sirenix.OdinInspector.Modules.Addressables.Editor.Internal +{ + internal static class OdinAddressableReflection + { + public static FieldInfo AddressableAssetEntry_mGUID_Field; + + static OdinAddressableReflection() + { + AddressableAssetEntry_mGUID_Field = typeof(AddressableAssetEntry).GetField("m_GUID", BindingFlags.Instance | BindingFlags.NonPublic); + } + + internal static void EnsureConstructed() { } + } +} +#endif \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflection.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflection.cs.meta new file mode 100644 index 00000000..55c0373c --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflection.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c1a8e9ae60bf4edebd0ec6052eb9064e +timeCreated: 1718984883 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflectionValidator.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflectionValidator.cs new file mode 100644 index 00000000..4a287ea0 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflectionValidator.cs @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#if UNITY_EDITOR +#if SIRENIX_INTERNAL +using System.Collections; +using System.Reflection; +using Sirenix.OdinInspector.Editor.Validation; +using Sirenix.OdinInspector.Modules.Addressables.Editor.Internal; + +[assembly: RegisterValidator(typeof(OdinAddressableReflectionValidator))] + +namespace Sirenix.OdinInspector.Modules.Addressables.Editor.Internal +{ + public class OdinAddressableReflectionValidator : GlobalValidator + { + public override IEnumerable RunValidation(ValidationResult result) + { + OdinAddressableReflection.EnsureConstructed(); + + FieldInfo[] fields = typeof(OdinAddressableReflection).GetFields(BindingFlags.Static | BindingFlags.Public); + + for (var i = 0; i < fields.Length; i++) + { + if (fields[i].IsLiteral) + { + continue; + } + + if (fields[i].GetValue(null) != null) + { + continue; + } + + result.AddError($"[Odin Addressable Module]: {fields[i].Name} was not found."); + } + + return null; + } + } +} +#endif +#endif \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflectionValidator.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflectionValidator.cs.meta new file mode 100644 index 00000000..e662bfb3 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Internal/OdinAddressableReflectionValidator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 97bf306f5a4249b192921db9152934f6 +timeCreated: 1718984873 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Sirenix.OdinInspector.Modules.Unity.Addressables.asmdef b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Sirenix.OdinInspector.Modules.Unity.Addressables.asmdef new file mode 100644 index 00000000..7be66dce --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Sirenix.OdinInspector.Modules.Unity.Addressables.asmdef @@ -0,0 +1,29 @@ +{ + "name": "Sirenix.OdinInspector.Modules.Unity.Addressables", + "references": [ + "Unity.Addressables", + "Unity.Addressables.Editor", + "Sirenix.Serialization", + "Sirenix.OdinInspector.Attributes", + "Sirenix.OdinInspector.Editor", + "Sirenix.Utilities.Editor", + "Sirenix.Utilities", + "Sirenix.OdinValidator.Editor" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [ + "Sirenix.Serialization.dll", + "Sirenix.OdinInspector.Attributes.dll", + "Sirenix.OdinInspector.Editor.dll", + "Sirenix.Utilities.Editor.dll", + "Sirenix.Utilities.dll", + "Sirenix.OdinValidator.Editor.dll" + ], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Sirenix.OdinInspector.Modules.Unity.Addressables.asmdef.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Sirenix.OdinInspector.Modules.Unity.Addressables.asmdef.meta new file mode 100644 index 00000000..f7deb1dc --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Sirenix.OdinInspector.Modules.Unity.Addressables.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3b4d8e09c665bfa47849130d8695171e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators.meta new file mode 100644 index 00000000..05862c29 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b568c1d508ce0b74eb0025b8501d1c1e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetLabelReferenceValidator.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetLabelReferenceValidator.cs new file mode 100644 index 00000000..39c1d1fa --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetLabelReferenceValidator.cs @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#if UNITY_EDITOR + +#if !SIRENIX_INTERNAL +#pragma warning disable +#endif + +using UnityEngine; +using UnityEditor.AddressableAssets; +using Sirenix.OdinInspector.Editor.Validation; +using UnityEngine.AddressableAssets; +using Sirenix.OdinInspector.Modules.Addressables.Editor; + +#if ODIN_VALIDATOR_3_1 +[assembly: RegisterValidationRule(typeof(AssetLabelReferenceValidator), Description = + "This validator ensures that AssetLabelReferences marked with the Required attribute display an error " + + "message if they are not set. It can also be configured to require that all AssetLabelReferences be set " + + "by default; the Optional attribute can then be used to exclude specific AssetLabelReferences from " + + "validation.")] +#else +[assembly: RegisterValidator(typeof(AssetLabelReferenceValidator))] +#endif + +namespace Sirenix.OdinInspector.Modules.Addressables.Editor +{ + /// + /// Validator for AssetLabelReference values. + /// + public class AssetLabelReferenceValidator : ValueValidator + { + [Tooltip("If enabled, the validator will display an error message if the AssetLabelReference is not set. " + + "If disabled, the validator will only display an error message if the AssetLabelReference is set, but the " + + "assigned label does not exist.")] + [ToggleLeft] + public bool RequiredByDefault; + + private bool required; + private bool optional; + private string requiredMessage; + + protected override void Initialize() + { + var requiredAttr = this.Property.GetAttribute(); + + this.requiredMessage = requiredAttr?.ErrorMessage ?? $"{this.Property.NiceName} is required."; + + if (this.RequiredByDefault) + { + required = true; + optional = Property.GetAttribute() != null; + } + else + { + required = requiredAttr != null; + optional = false; + } + } + + protected override void Validate(ValidationResult result) + { + // If the Addressables settings have not been created, nothing else is really valid. + if (AddressableAssetSettingsDefaultObject.SettingsExists == false) + { + result.AddError("Addressables Settings have not been created.") + .WithButton("Open Settings Window", () => OdinAddressableUtility.OpenGroupsWindow()); + return; + } + + var value = Value?.labelString; + + if (string.IsNullOrEmpty(value)) + { + if (optional == false && required) // Optional == false & required? Nice. + { + result.AddError(requiredMessage).EnableRichText(); + } + } + else + { + var labels = AddressableAssetSettingsDefaultObject.Settings.GetLabels(); + + if (labels.Contains(value) == false) + { + result.AddError($"Label {value} has not been created as a label.") + .WithButton("Open Label Settings", () => OdinAddressableUtility.OpenLabelsWindow()); + } + } + } + } + +} + +#endif \ No newline at end of file diff --git a/Assets/Scripts/Story/Dialog/DialogManager.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetLabelReferenceValidator.cs.meta similarity index 83% rename from Assets/Scripts/Story/Dialog/DialogManager.cs.meta rename to Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetLabelReferenceValidator.cs.meta index 0e062938..c9b6cfc0 100644 --- a/Assets/Scripts/Story/Dialog/DialogManager.cs.meta +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetLabelReferenceValidator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9e89b9d7eea97734baa166072b050239 +guid: fcaf7dc3b9a98a545b301a1ea175055b MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetReferenceValidator.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetReferenceValidator.cs new file mode 100644 index 00000000..bc755831 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetReferenceValidator.cs @@ -0,0 +1,284 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#if UNITY_EDITOR + +#if !SIRENIX_INTERNAL +#pragma warning disable +#endif + +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEditor; +using UnityEditor.AddressableAssets; +using Sirenix.OdinInspector.Editor.Validation; +using Sirenix.Utilities; +using Sirenix.Utilities.Editor; +using UnityEditor.AddressableAssets.Settings; +using UnityEngine.AddressableAssets; +using Sirenix.OdinInspector.Modules.Addressables.Editor; + +#if ODIN_VALIDATOR_3_1 +[assembly: RegisterValidationRule(typeof(AssetReferenceValidator), Description = + "This validator provides robust integrity checks for your asset references within Unity. " + + "It validates whether an asset reference has been assigned, and if it's missing, raises an error. " + + "It further checks the existence of the main asset at the assigned path, ensuring it hasn't been " + + "inadvertently deleted or moved. The validator also verifies if the assigned asset is addressable " + + "and, if not, offers a fix to make it addressable. Moreover, it ensures the asset adheres to " + + "specific label restrictions set through the AssetReferenceUILabelRestriction attribute. " + + "Lastly, it performs checks on any sub-object linked to the asset, making sure it hasn't gone missing. " + + "This comprehensive validation system prevents hard-to-spot bugs and errors, " + + "fostering a more robust and efficient development workflow.")] +#else +[assembly: RegisterValidator(typeof(AssetReferenceValidator))] +#endif + +namespace Sirenix.OdinInspector.Modules.Addressables.Editor +{ + public class AssetReferenceValidator : ValueValidator + { + [Tooltip("If true and the AssetReference is not marked with the Optional attribute, " + + "the validator will display an error message if the AssetReference is not set. " + + "If false, the validator will only display an error message if the AssetReference is set, " + + "but the assigned asset does not exist.")] + [ToggleLeft] + public bool RequiredByDefault; + + private bool required; + private bool optional; + private string requiredMessage; + + private List restrictions; + + protected override void Initialize() + { + var requiredAttr = this.Property.GetAttribute(); + + this.requiredMessage = requiredAttr?.ErrorMessage ?? $"{this.Property.NiceName} is required."; + + if (this.RequiredByDefault) + { + this.required = true; + this.optional = this.Property.GetAttribute() != null; + } + else + { + this.required = requiredAttr != null; + this.optional = false; + } + + this.restrictions = new List(); + foreach (var attr in this.Property.Attributes) + { + if (attr is AssetReferenceUIRestriction r) + { + this.restrictions.Add(r); + } + } + } + + protected override void Validate(ValidationResult result) + { + // If the Addressables settings have not been created, nothing else is really valid. + if (AddressableAssetSettingsDefaultObject.SettingsExists == false) + { + result.AddError("Addressables Settings have not been created.") + .WithButton("Open Settings Window", () => OdinAddressableUtility.OpenGroupsWindow()); + return; + } + + var assetReference = this.Value; + var assetReferenceHasBeenAssigned = !string.IsNullOrEmpty(assetReference?.AssetGUID); + + // No item has been assigned. + if (!assetReferenceHasBeenAssigned) + { + if (optional == false && required) // Optional == false & required? Nice. + { + result.AddError(this.requiredMessage).EnableRichText(); + } + + return; + } + + var assetPath = AssetDatabase.GUIDToAssetPath(assetReference.AssetGUID); + var mainAsset = AssetDatabase.LoadMainAssetAtPath(assetPath); + + // The item has been assigned, but is now missing. + if (mainAsset == null) + { + result.AddError($"The previously assigned main asset with path '{assetPath}' is missing. GUID '{assetReference.AssetGUID}'"); + return; + } + + var addressableAssetEntry = AddressableAssetSettingsDefaultObject.Settings.FindAssetEntry(assetReference.AssetGUID, true); + var isAddressable = addressableAssetEntry != null; + + // Somehow an item sneaked through all of unity's validation measures and ended up not being addressable + // while still ending up in the asset reference object field. + if (!isAddressable) + { + result.AddError("Assigned item is not addressable.") + .WithFix("Make Addressable", args => OdinAddressableUtility.MakeAddressable(mainAsset, args.Group)); + } + // Check the assigned item against any and all label restrictions. + else + { + if (OdinAddressableUtility.ValidateAssetReferenceRestrictions(restrictions, mainAsset, out var failedRestriction) == false) + { + if (failedRestriction is AssetReferenceUILabelRestriction labelRestriction) + { + result.AddError($"Asset reference is restricted to items with these specific labels '{string.Join(", ", labelRestriction.m_AllowedLabels)}'. The currently assigned item has none of them.") + .WithFix("Add Labels", args => SetLabels(mainAsset, args.AssetLabels)); + } + else + { + result.AddError("Restriction failed: " + failedRestriction.ToString()); + } + } + } + + // The assigned item had a sub object, but it's missing. + if (!string.IsNullOrEmpty(assetReference.SubObjectName)) + { + var subObjects = OdinAddressableUtility.EnumerateAllActualAndVirtualSubAssets(mainAsset, assetPath); + + var hasMissingSubObject = true; + + foreach (var subObject in subObjects) + { + if (subObject.name == assetReference.SubObjectName) + { + hasMissingSubObject = false; + break; + } + } + + if (hasMissingSubObject) + { + result.AddError($"The previously assigned sub asset with name '{assetReference.SubObjectName}' is missing.").EnableRichText(); + } + } + + if (assetReference.ValidateAsset(mainAsset) || assetReference.ValidateAsset(assetPath)) + return; + + if (assetReference is AssetReferenceSprite && assetReference.editorAsset is Sprite) + return; + + result.AddError($"{assetReference.GetType().GetNiceFullName()}.ValidateAsset failed to validate assigned asset."); + } + + private static void SetLabels(UnityEngine.Object obj, List assetLabels) + { + if (!AddressableAssetSettingsDefaultObject.SettingsExists) return; + + var settings = AddressableAssetSettingsDefaultObject.Settings; + var guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(obj)); + var entry = settings.FindAssetEntry(guid, false); + + foreach (var assetLabel in assetLabels.Where(a => a.Toggled)) + { + entry.SetLabel(assetLabel.Label, true, false, false); + } + + settings.SetDirty(AddressableAssetSettings.ModificationEvent.LabelAdded, entry, false, true); + } + + private class MakeAddressableFixArgs + { + [ValueDropdown(nameof(GetGroups))] + [OnInspectorInit(nameof(SelectDefault))] + public AddressableAssetGroup Group; + + private void SelectDefault() + { + this.Group = AddressableAssetSettingsDefaultObject.SettingsExists + ? AddressableAssetSettingsDefaultObject.Settings.DefaultGroup + : null; + } + + private static IEnumerable GetGroups() + { + return !AddressableAssetSettingsDefaultObject.SettingsExists + ? Enumerable.Empty() + : AddressableAssetSettingsDefaultObject.Settings.groups + .Where(group => !group.ReadOnly) + .Select(group => new ValueDropdownItem(group.Name, group)); + } + + [Button(SdfIconType.ListNested), PropertySpace(8f)] + private void OpenAddressablesGroups() => OdinAddressableUtility.OpenGroupsWindow(); + } + + private class AddLabelsFixArgs + { + [HideIf("@true")] + public List AssetLabels + { + get + { + if (!AddressableAssetSettingsDefaultObject.SettingsExists) return this.assetLabels; + + var settings = AddressableAssetSettingsDefaultObject.Settings; + var labels = settings + .GetLabels() + .Select(l => new AssetLabel { Label = l, Toggled = false }) + .ToList(); + + foreach (var assetLabel in this.assetLabels) + { + var label = labels.FirstOrDefault(l => l.Label == assetLabel.Label); + + if (label != null) + { + label.Toggled = assetLabel.Toggled; + } + } + + this.assetLabels = labels; + return this.assetLabels; + } + } + + private List assetLabels = new List(); + + [OnInspectorGUI] + private void Draw() + { + var togglesRect = EditorGUILayout.GetControlRect(false, Mathf.CeilToInt(this.AssetLabels.Count / 2f) * 20f); + + for (var i = 0; i < this.AssetLabels.Count; i++) + { + var assetLabel = this.AssetLabels[i]; + var toggleRect = togglesRect.SplitGrid(togglesRect.width / 2f, 20, i); + assetLabel.Toggled = GUI.Toggle(toggleRect, assetLabel.Toggled, assetLabel.Label); + } + + if (!AddressableAssetSettingsDefaultObject.SettingsExists) return; + + GUILayout.Space(8f); + + var buttonsRect = EditorGUILayout.GetControlRect(false, 20f); + + if (SirenixEditorGUI.SDFIconButton(buttonsRect, "Open Addressables Labels", SdfIconType.TagsFill)) + { + OdinAddressableUtility.OpenLabelsWindow(); + } + } + } + + private class AssetLabel + { + public bool Toggled; + public string Label; + } + } + +} + +#endif \ No newline at end of file diff --git a/Assets/Scripts/Story/Dialog/DialogModule.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetReferenceValidator.cs.meta similarity index 83% rename from Assets/Scripts/Story/Dialog/DialogModule.cs.meta rename to Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetReferenceValidator.cs.meta index 98fc3ee7..44421787 100644 --- a/Assets/Scripts/Story/Dialog/DialogModule.cs.meta +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/AssetReferenceValidator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d32abf19c3d0f4546b2b86f9fcc4e117 +guid: b44b08a1f58a83149988fde5ac600fe4 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckDuplicateBundleDependenciesValidator.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckDuplicateBundleDependenciesValidator.cs new file mode 100644 index 00000000..3a9b8397 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckDuplicateBundleDependenciesValidator.cs @@ -0,0 +1,205 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#if UNITY_EDITOR && ODIN_VALIDATOR_3_1 + +#if !SIRENIX_INTERNAL +#pragma warning disable +#endif + +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEditor; +using UnityEditor.AddressableAssets; +using Sirenix.OdinInspector.Editor.Validation; +using Sirenix.Utilities; +using Sirenix.Utilities.Editor; +using UnityEditor.AddressableAssets.Settings; +using System.Collections; +using System; +using Sirenix.OdinValidator.Editor; +using Sirenix.OdinInspector.Modules.Addressables.Editor; + +[assembly: RegisterValidationRule(typeof(CheckDuplicateBundleDependenciesValidator), + Description = "This validator detects potential duplicate asset dependencies in an addressable group, without the need for a build. " + + "For instance, imagine two prefabs in separate groups, both referencing the same material. Each group would then include the material " + + "and all its associated dependencies. " + + "To address this, the material should be marked as Addressable, either with one of the prefabs or in a distinct group.\n\n" + + "Fixes: Executing the fix will make the dependency addressable and move it to the specified group.\n\n" + + "Exceptions: It's important to note that duplicate assets aren't inherently problematic. For example, if certain assets are " + + "never accessed by the same user group, such as region-specific assets, these duplications might be desired or at least inconsequential. " + + "As every project is unique, decisions concerning duplicate asset dependencies should be considered on a case-by-case basis.")] + +namespace Sirenix.OdinInspector.Modules.Addressables.Editor +{ + public class CheckDuplicateBundleDependenciesValidator : GlobalValidator + { + private static Dictionary> dependencyGroupMap = new Dictionary>(); + + [Tooltip("The severity of the validation result.")] + public ValidatorSeverity ValidatorSeverity = ValidatorSeverity.Warning; + + [Tooltip("Assets to ignore when validating.")] + [LabelText("Ignored GUIDs"), CustomValueDrawer(nameof(DrawGUIDEntry))] + public List IgnoredGUIDs = new List(); + + public override IEnumerable RunValidation(ValidationResult result) + { + dependencyGroupMap.Clear(); + + var addressableAssetSettings = AddressableAssetSettingsDefaultObject.Settings; + + if (addressableAssetSettings == null) yield break; + + foreach (var addressableAssetGroup in addressableAssetSettings.groups) + { + if (addressableAssetGroup == null) continue; + + foreach (var addressableAssetEntry in addressableAssetGroup.entries) + { + var dependencyAssetPaths = AssetDatabase.GetDependencies(addressableAssetEntry.AssetPath) + .Where(assetPath => !assetPath.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) && + !assetPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)); + + foreach (var dependencyAssetPath in dependencyAssetPaths) + { + var dependencyGUID = new GUID(AssetDatabase.AssetPathToGUID(dependencyAssetPath)); + + if (this.IgnoredGUIDs.Contains(dependencyGUID.ToString())) continue; + + var dependencyAddressableAssetEntry = addressableAssetSettings.FindAssetEntry(dependencyGUID.ToString()); + + var isAddressable = dependencyAddressableAssetEntry != null; + if (isAddressable) continue; + + if (!dependencyGroupMap.ContainsKey(dependencyGUID)) + { + dependencyGroupMap.Add(dependencyGUID, new List()); + } + + if (!dependencyGroupMap[dependencyGUID].Contains(addressableAssetGroup.Name)) + { + dependencyGroupMap[dependencyGUID].Add(addressableAssetGroup.Name); + } + } + } + } + + foreach (var kvp in dependencyGroupMap) + { + var dependencyGUID = kvp.Key; + var groups = kvp.Value; + + if (groups.Count > 1) + { + var assetPath = AssetDatabase.GUIDToAssetPath(dependencyGUID.ToString()); + var message = $"{assetPath} is duplicated in these groups: {string.Join(", ", groups)}"; + + result.Add(this.ValidatorSeverity, message).WithFix(args => + { + if (args.FixChoice == FixChoice.Ignore) + { + var sourceType = args.IgnoreForEveryone ? ConfigSourceType.Project : ConfigSourceType.Local; + var data = RuleConfig.Instance.GetRuleData(sourceType); + data.IgnoredGUIDs.Add(dependencyGUID.ToString()); + RuleConfig.Instance.SetAndSaveRuleData(data, sourceType); + return; + } + + var obj = AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object)); + AddressableAssetGroup group; + + if (args.Group == "Create New Group") + { + if (args.GroupName.IsNullOrWhitespace()) return; + + group = addressableAssetSettings.FindGroup(args.GroupName); + + if (group == null) + { + group = addressableAssetSettings.CreateGroup(args.GroupName, false, false, false, null); + } + } + else + { + group = addressableAssetSettings.FindGroup(args.Group); + + if (group == null) + { + group = addressableAssetSettings.CreateGroup(args.Group, false, false, false, null); + } + } + + OdinAddressableUtility.MakeAddressable(obj, group); + }, false).WithModifyRuleDataContextClick("Ignore", data => + { + data.IgnoredGUIDs.Add(dependencyGUID.ToString()); + }).SetSelectionObject(AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(dependencyGUID.ToString()))); + } + } + } + + private string DrawGUIDEntry(string guid) + { + var assetPath = AssetDatabase.GUIDToAssetPath(guid); + EditorGUILayout.TextArea(assetPath, SirenixGUIStyles.MultiLineLabel); + EditorGUILayout.TextField(guid); + return guid; + } + + private enum FixChoice + { + AddToGroup, + Ignore, + } + + private class FixArgs + { + [EnumToggleButtons, HideLabel] + public FixChoice FixChoice; + + [PropertySpace(10)] + [ValueDropdown("Groups")] + //[Title("Group To Add To", TitleAlignment = TitleAlignments.Centered)] + [ShowIf(nameof(FixChoice), FixChoice.AddToGroup, Animate = false)] + public string Group = "Duplicate Asset Isolation"; + + [ValidateInput(nameof(ValidateGroupName), "The group name cannot be empty")] + [ShowIf(nameof(ShowNewGroupName), Animate = false)] + public string GroupName; + + [LabelWidth(120f)] + [PropertySpace(10)] + [ShowIf("FixChoice", FixChoice.Ignore, Animate = false)] + public bool IgnoreForEveryone = true; + + [OnInspectorGUI] + [PropertySpace(10)] + [DetailedInfoBox("Note that duplicate assets may not always be an issue", "Note that duplicate assets may not always be an issue. If assets will never be requested by the same set of users (for example, region-specific assets), then duplicate dependencies may be desired, or at least inconsequential. Each Project is unique, so fixing duplicate asset dependencies should be evaluated on a case by case basis")] + private void Dummy() { } + + private bool ShowNewGroupName => this.FixChoice != FixChoice.Ignore && this.Group == "Create New Group"; + + private bool ValidateGroupName() => !this.GroupName.IsNullOrWhitespace(); + + private IEnumerable Groups() + { + var addressableAssetSettings = AddressableAssetSettingsDefaultObject.Settings; + + return addressableAssetSettings == null + ? Enumerable.Empty() + : addressableAssetSettings.groups + .Where(group => group != null && group.Name != "Built In Data") + .Select(group => group.Name) + .Append("Duplicate Asset Isolation") + .Prepend("Create New Group"); + } + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/Scripts/Story/Dialog/StoryInterpreters.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckDuplicateBundleDependenciesValidator.cs.meta similarity index 83% rename from Assets/Scripts/Story/Dialog/StoryInterpreters.cs.meta rename to Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckDuplicateBundleDependenciesValidator.cs.meta index 9b551691..f7d4e44a 100644 --- a/Assets/Scripts/Story/Dialog/StoryInterpreters.cs.meta +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckDuplicateBundleDependenciesValidator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: dd119fb631aebb548a637407d15c5eea +guid: bdc8ee2cf75a17644a0bd81a965cc2e0 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckResourcesToAddressableDuplicateDependenciesValidator.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckResourcesToAddressableDuplicateDependenciesValidator.cs new file mode 100644 index 00000000..bb21a1ea --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckResourcesToAddressableDuplicateDependenciesValidator.cs @@ -0,0 +1,175 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#if UNITY_EDITOR && ODIN_VALIDATOR_3_1 + +#if !SIRENIX_INTERNAL +#pragma warning disable +#endif + +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEditor; +using UnityEditor.AddressableAssets; +using Sirenix.OdinInspector.Editor.Validation; +using Sirenix.Utilities.Editor; +using System.Collections; +using System; +using System.IO; +using Sirenix.OdinValidator.Editor; +using Sirenix.OdinInspector.Modules.Addressables.Editor; + +[assembly: RegisterValidationRule(typeof(CheckResourcesToAddressableDuplicateDependenciesValidator), + Description = "This validator identifies dependencies that are duplicated in both addressable groups and the \"Resources\" folder.\n\n" + + "These duplications mean that data will be included in both the application build and the addressables build.\n\n" + + "You can decide to simply ignore these duplicated dependencies if this behavior is desired, or use the provided fix " + + "to move the asset outside of the \"Resources\" folder.")] + +namespace Sirenix.OdinInspector.Modules.Addressables.Editor +{ + public class CheckResourcesToAddressableDuplicateDependenciesValidator : GlobalValidator + { + [Tooltip("The severity of the validation result.")] + public ValidatorSeverity ValidatorSeverity = ValidatorSeverity.Warning; + + [Tooltip("Assets to ignore when validating.")] + [LabelText("Ignored GUIDs"), CustomValueDrawer(nameof(DrawGUIDEntry))] + public List IgnoredGUIDs = new List(); + + public override IEnumerable RunValidation(ValidationResult result) + { + var addressableAssetSettings = AddressableAssetSettingsDefaultObject.Settings; + + if (addressableAssetSettings == null) yield break; + + foreach (var addressableAssetGroup in addressableAssetSettings.groups) + { + if (addressableAssetGroup == null) continue; + + foreach (var addressableAssetEntry in addressableAssetGroup.entries) + { + var dependencyAssetPaths = AssetDatabase.GetDependencies(addressableAssetEntry.AssetPath) + .Where(assetPath => !assetPath.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) && + !assetPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)); + + foreach (var dependencyAssetPath in dependencyAssetPaths) + { + var dependencyGUID = new GUID(AssetDatabase.AssetPathToGUID(dependencyAssetPath)); + + if (this.IgnoredGUIDs.Contains(dependencyGUID.ToString())) continue; + + var dependencyAddressableAssetEntry = addressableAssetSettings.FindAssetEntry(dependencyGUID.ToString()); + + var isAddressable = dependencyAddressableAssetEntry != null; + if (isAddressable) continue; + if (!IsInsideResourcesFolder(dependencyAssetPath)) continue; + + result.Add(this.ValidatorSeverity, $"{dependencyAssetPath} is duplicated in addressable data and resource folders.") + .WithFix(args => + { + if (args.FixChoice == FixChoice.Ignore) + { + var sourceType = args.IgnoreForEveryone ? ConfigSourceType.Project : ConfigSourceType.Local; + var data = RuleConfig.Instance.GetRuleData(sourceType); + data.IgnoredGUIDs.Add(dependencyGUID.ToString()); + RuleConfig.Instance.SetAndSaveRuleData(data, sourceType); + return; + } + + if (!ValidNewFolder(args.NewFolder, out _)) return; + + if (!AssetDatabase.IsValidFolder(args.NewFolder)) + { + Directory.CreateDirectory(new DirectoryInfo(args.NewFolder).FullName); + AssetDatabase.Refresh(); + } + + var newPath = $"{args.NewFolder}/{Path.GetFileName(dependencyAssetPath)}"; + AssetDatabase.MoveAsset(dependencyAssetPath, newPath); + }, false).WithModifyRuleDataContextClick("Ignore", data => + { + data.IgnoredGUIDs.Add(dependencyGUID.ToString()); + }).SetSelectionObject(AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(dependencyGUID.ToString()))); + + yield break; + } + } + } + } + + private string DrawGUIDEntry(string guid) + { + var assetPath = AssetDatabase.GUIDToAssetPath(guid); + EditorGUILayout.TextArea(assetPath, SirenixGUIStyles.MultiLineLabel); + EditorGUILayout.TextField(guid); + return guid; + } + + private static bool IsInsideResourcesFolder(string path) + { + var pathElements = path.Split('/'); + + foreach (var pathElement in pathElements) + { + if (pathElement.Equals("Resources", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + + private static bool ValidNewFolder(string path, out string message) + { + if (IsInsideResourcesFolder(path)) + { + message = "The asset cannot be moved into a 'Resources' folder"; + return false; + } + + if (!path.StartsWith("Assets/")) + { + message = "The asset must be inside the 'Assets' folder"; + return false; + } + + message = "The folder is valid"; + return true; + } + + private enum FixChoice + { + MoveAsset, + Ignore, + } + private class FixArgs + { + [HideLabel] + [EnumToggleButtons] + public FixChoice FixChoice; + + [FolderPath] + [PropertySpace(10)] + [ValidateInput(nameof(ValidateFolderPath))] + [ShowIf("FixChoice", FixChoice.MoveAsset, Animate = false)] + public string NewFolder = "Assets/Resources_moved"; + + [LabelWidth(120f)] + [PropertySpace(10)] + [ShowIf("FixChoice", FixChoice.Ignore, Animate = false)] + public bool IgnoreForEveryone = true; + + private bool ValidateFolderPath(string path, ref string message) + { + return ValidNewFolder(path, out message); + } + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckResourcesToAddressableDuplicateDependenciesValidator.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckResourcesToAddressableDuplicateDependenciesValidator.cs.meta new file mode 100644 index 00000000..a2bc61e2 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/CheckResourcesToAddressableDuplicateDependenciesValidator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f75aebe03a9aa4a4b82d2b54dcc34de5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/DisallowAddressableSubAssetFieldAttributeValidator.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/DisallowAddressableSubAssetFieldAttributeValidator.cs new file mode 100644 index 00000000..38c8decb --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/DisallowAddressableSubAssetFieldAttributeValidator.cs @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#if UNITY_EDITOR + +#if !SIRENIX_INTERNAL +#pragma warning disable +#endif + +using Sirenix.OdinInspector.Editor.Validation; +using Sirenix.OdinInspector.Modules.Addressables.Editor; +using UnityEngine.AddressableAssets; + +[assembly: RegisterValidator(typeof(DisallowAddressableSubAssetFieldAttributeValidator))] + +namespace Sirenix.OdinInspector.Modules.Addressables.Editor +{ + /// + /// Validator for the DisallowAddressableSubAssetFieldAttribute. + /// + public class DisallowAddressableSubAssetFieldAttributeValidator : AttributeValidator + { + protected override void Validate(ValidationResult result) + { + if (this.Value != null && string.IsNullOrEmpty(this.Value.SubObjectName) == false) + { + result.AddError("Sub-asset references is not allowed on this field.") + .WithFix("Remove Sub-Asset", () => this.Value.SubObjectName = null, true); + } + } + } + +} + +#endif \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/DisallowAddressableSubAssetFieldAttributeValidator.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/DisallowAddressableSubAssetFieldAttributeValidator.cs.meta new file mode 100644 index 00000000..7a7fd44d --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/DisallowAddressableSubAssetFieldAttributeValidator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5775d33ffc1143149c4f425f693b04b6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/MissingAddressableGroupReferenceValidator.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/MissingAddressableGroupReferenceValidator.cs new file mode 100644 index 00000000..033335b4 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/MissingAddressableGroupReferenceValidator.cs @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#if UNITY_EDITOR && ODIN_VALIDATOR_3_1 + +#if !SIRENIX_INTERNAL +#pragma warning disable +#endif + +using System.Collections.Generic; +using UnityEditor.AddressableAssets; +using Sirenix.OdinInspector.Editor.Validation; +using UnityEditor.AddressableAssets.Settings; +using System.Collections; +using Sirenix.OdinInspector.Modules.Addressables.Editor; + +[assembly: RegisterValidator(typeof(MissingAddressableGroupReferenceValidator))] + +namespace Sirenix.OdinInspector.Modules.Addressables.Editor +{ + public class MissingAddressableGroupReferenceValidator : GlobalValidator + { + public override IEnumerable RunValidation(ValidationResult result) + { + var addressableAssetSettings = AddressableAssetSettingsDefaultObject.Settings; + + if (addressableAssetSettings == null) yield break; + + var missingGroupIndices = new List(); + + for (var i = 0; i < addressableAssetSettings.groups.Count; i++) + { + var group = addressableAssetSettings.groups[i]; + + if (group == null) + { + missingGroupIndices.Add(i); + } + } + + if (missingGroupIndices.Count > 0) + { + result.Add(ValidatorSeverity.Error, "Addressable groups contains missing references").WithFix("Delete missing reference", () => + { + for (var i = missingGroupIndices.Count - 1; i >= 0; i--) + { + addressableAssetSettings.groups.RemoveAt(missingGroupIndices[i]); + addressableAssetSettings.SetDirty(AddressableAssetSettings.ModificationEvent.GroupRemoved, null, true, true); + } + }); + } + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/MissingAddressableGroupReferenceValidator.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/MissingAddressableGroupReferenceValidator.cs.meta new file mode 100644 index 00000000..0f3914fc --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/Validators/MissingAddressableGroupReferenceValidator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4ae55abdaf19ef4498d1a2fbe1bd9f9b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/manifest.txt b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/manifest.txt new file mode 100644 index 00000000..e3fcfc8c --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/manifest.txt @@ -0,0 +1,26 @@ +ManifestVersion: 1 +ModuleID: Unity.Addressables +ModuleVersion: 1.1.0.11 +ModuleFiles: + AddressablesInspectors.cs + AddressablesInspectors.cs.meta + Internal.meta + Sirenix.OdinInspector.Modules.Unity.Addressables.asmdef + Sirenix.OdinInspector.Modules.Unity.Addressables.asmdef.meta + Validators.meta + Internal/OdinAddressableReflection.cs + Internal/OdinAddressableReflection.cs.meta + Internal/OdinAddressableReflectionValidator.cs + Internal/OdinAddressableReflectionValidator.cs.meta + Validators/AssetLabelReferenceValidator.cs + Validators/AssetLabelReferenceValidator.cs.meta + Validators/AssetReferenceValidator.cs + Validators/AssetReferenceValidator.cs.meta + Validators/CheckDuplicateBundleDependenciesValidator.cs + Validators/CheckDuplicateBundleDependenciesValidator.cs.meta + Validators/CheckResourcesToAddressableDuplicateDependenciesValidator.cs + Validators/CheckResourcesToAddressableDuplicateDependenciesValidator.cs.meta + Validators/DisallowAddressableSubAssetFieldAttributeValidator.cs + Validators/DisallowAddressableSubAssetFieldAttributeValidator.cs.meta + Validators/MissingAddressableGroupReferenceValidator.cs + Validators/MissingAddressableGroupReferenceValidator.cs.meta diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/manifest.txt.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/manifest.txt.meta new file mode 100644 index 00000000..3ceaa57c --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Addressables/manifest.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f77cf3f9873b6284ebbe75488e21ae2d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization.meta new file mode 100644 index 00000000..4ef3dd00 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d85db5a799dd0a946a38784bb927ba22 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor.meta new file mode 100644 index 00000000..bb9bcece --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 175cafb710462ef489cbf56cb46c97d5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs.meta new file mode 100644 index 00000000..6d4e3a49 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3459a30abd1a4e979488e906febc27e7 +timeCreated: 1707714315 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.asset b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.asset new file mode 100644 index 00000000..03e48e69 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.asset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54085f3452b5909a614a1a85b17d95210d7f89a203594a3db9cc1375f0b47be3 +size 1003 diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.asset.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.asset.meta new file mode 100644 index 00000000..0fd9bb9b --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c09625914af7d2d4e92c3653d5f15bd7 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.cs new file mode 100644 index 00000000..839a8b2e --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.cs @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using Sirenix.OdinInspector.Editor; +using Sirenix.OdinInspector.Modules.Localization.Editor.Internal; +using Sirenix.Reflection.Editor; +using Sirenix.Utilities; +using Sirenix.Utilities.Editor; +using UnityEditor; +using UnityEngine; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor.Configs +{ + [GlobalConfig("Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs")] + public class OdinLocalizationConfig : GlobalConfig + { + public class ThemeColorDrawer : OdinValueDrawer + { + protected override void Initialize() + { + base.Initialize(); + this.Property.State.Expanded = false; + } + + protected override void DrawPropertyLayout(GUIContent label) + { + SirenixEditorGUI.BeginBox(string.Empty); + { + SirenixEditorGUI.BeginBoxHeader(); + { + GUILayout_Internal.BeginRow(); + { + GUILayout_Internal.BeginColumn(LayoutSize.Pixels(EditorGUIUtility.labelWidth + 6.0f)); + { + this.Property.State.Expanded = EditorGUILayout.Foldout(this.Property.State.Expanded, + $"{label.text} ({(EditorGUIUtility.isProSkin ? "Dark" : "Light")})", + true); + } + GUILayout_Internal.EndColumn(); + + GUILayout_Internal.BeginColumn(LayoutSize.Auto); + { + this.Property.Children[nameof(ThemeColor.Color)].Draw(null); + } + GUILayout_Internal.EndColumn(); + } + GUILayout_Internal.EndRow(); + } + SirenixEditorGUI.EndBoxHeader(); + + bool toggle = this.ValueEntry.ValueState != PropertyValueState.NullReference && this.Property.State.Expanded; + + if (SirenixEditorGUI.BeginFadeGroup(this, toggle)) + { + GUILayout.BeginHorizontal(); + this.Property.Children[nameof(ThemeColor.lightColor)].Draw(); + GUILayout.Space(3.5f); + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + this.Property.Children[nameof(ThemeColor.darkColor)].Draw(); + GUILayout.Space(3.5f); + GUILayout.EndHorizontal(); + } + + SirenixEditorGUI.EndFadeGroup(); + } + SirenixEditorGUI.EndBox(); + + if (this.Property.State.Expanded) + { + GUILayout.Space(4.0f); + } + } + } + + [Serializable] + public class ThemeColor + { + [ShowInInspector] + public Color Color + { + get => EditorGUIUtility.isProSkin ? this.darkColor : this.lightColor; + + set + { + if (EditorGUIUtility.isProSkin) + { + this.darkColor = value; + } + else + { + this.lightColor = value; + } + } + } + + public Color lightColor; + public Color darkColor; + + public ThemeColor(Color lightColor, Color darkColor) + { + this.lightColor = lightColor; + this.darkColor = darkColor; + } + + public static implicit operator Color(ThemeColor color) => color.Color; + } + + [ShowInInspector] + [BoxGroup("User Interface")] + [Range(96, 1024)] + public int assetRowHeight = 128; + + [BoxGroup("Syntax Highlighting")] + public bool useSyntaxHighlighter = true; + + [EnableIf(nameof(useSyntaxHighlighter))] + [BoxGroup("Syntax Highlighting")] + public ThemeColor placeholderColor = new ThemeColor(new Color(0.743147f, 0.9433962f, 0.9242815f), new Color(0, 0.5882353f, 0.5333334f)); + + [EnableIf(nameof(useSyntaxHighlighter))] + [BoxGroup("Syntax Highlighting")] + public ThemeColor selectorColor = new ThemeColor(new Color(1.0f, 0.7727525f, 0.3632075f), new Color(1, 0.6470588f, 0)); + + [EnableIf(nameof(useSyntaxHighlighter))] + [BoxGroup("Syntax Highlighting")] + public ThemeColor formatterColor = new ThemeColor(new Color(0.9921569f, 0.9855571f, 0.8823529f), new Color(0.9607843f, 0.9607843f, 0.8627451f)); + + [BoxGroup("Navigation")] + [Range(1, 1000.0f)] + public float scrollSpeed = 24.0f; + + [BoxGroup("Navigation")] + public bool invertMouseDragNavigation = true; + + [BoxGroup("Navigation")] + [Range(0.5f, 5.0f)] + public float mouseDragSpeed = 1.0f; + + [InfoBox("We couldn't find the necessary methods/classes to perform custom undo operations, therefore this option has been disabled and will be considered false even if true.", + VisibleIf = "@!OdinLocalizationReflectionValues.HasAPIForCustomUndo")] + [EnableIf("@OdinLocalizationReflectionValues.HasAPIForCustomUndo")] + [BoxGroup("Undo")] + public bool useCustomUndoHandlingForAssetCollections = true; + + [Button(ButtonSizes.Large)] + public void Reset() + { + if (!EditorUtility.DisplayDialog("Odin Localization Config", "Are you certain you want to reset your Localization config?", "Yes", "No")) + { + return; + } + + this.useCustomUndoHandlingForAssetCollections = OdinLocalizationReflectionValues.HasAPIForCustomUndo; + + this.assetRowHeight = 128; + + this.useSyntaxHighlighter = true; + this.placeholderColor = new ThemeColor(new Color(0.743147f, 0.9433962f, 0.9242815f), new Color(0, 0.5882353f, 0.5333334f)); + this.selectorColor = new ThemeColor(new Color(1.0f, 0.7727525f, 0.3632075f), new Color(1, 0.6470588f, 0)); + this.formatterColor = new ThemeColor(new Color(0.9921569f, 0.9855571f, 0.8823529f), new Color(0.9607843f, 0.9607843f, 0.8627451f)); + + this.scrollSpeed = 24.0f; + this.invertMouseDragNavigation = true; + this.mouseDragSpeed = 1.0f; + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.cs.meta new file mode 100644 index 00000000..3ec82c50 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs/OdinLocalizationConfig.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2fc91909e6444cb29aacb664f2098c7e +timeCreated: 1707714331 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI.meta new file mode 100644 index 00000000..e206adbf --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bf930ee5d27d4f90ab84f23d337eaaca +timeCreated: 1704811167 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/LocalizationMetadata.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/LocalizationMetadata.cs new file mode 100644 index 00000000..57454a9d --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/LocalizationMetadata.cs @@ -0,0 +1,993 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Sirenix.OdinInspector.Editor; +using Sirenix.OdinInspector.Modules.Localization.Editor.Internal; +using Sirenix.Utilities; +using Sirenix.Utilities.Editor; +using UnityEditor; +using UnityEditor.Localization; +using UnityEngine; +using UnityEngine.Localization.Metadata; +using UnityEngine.Localization.Tables; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ + public class LocalizationMetadata + { + private object lastTarget; + public object Target; + private LocalizationTableCollection localizationTableCollection; + private SearchField searchField = new SearchField(); + private string searchTerm; + private Vector2 scrollPosition; + private IMetadata toBeAdded; + private IMetadata toBeRemoved; + private Type lastMetadataTypeUnderCursor; + private StringBuilder stringBuilder = new StringBuilder(); + private OdinLocalizationEditorWindow.WindowState windowState; + + private List tags = new List + { + typeof(ExcludeEntryFromExport) + }; + + private GUIStyle _contentPadding; + private GUIStyle ContentPadding => + this._contentPadding = this._contentPadding ?? new GUIStyle + { + padding = new RectOffset(10, 10, 10, 10) + }; + + private GUIStyle _platformOverridePadding; + + private GUIStyle PlatformOverridePadding => + this._platformOverridePadding = this._platformOverridePadding ?? new GUIStyle + { + padding = new RectOffset(10, 20, 10, 10) + }; + + public LocalizationMetadata(LocalizationTableCollection localizationTableCollection, OdinLocalizationEditorWindow.WindowState windowState) + { + this.localizationTableCollection = localizationTableCollection; + this.windowState = windowState; + } + + /// + /// Adds the given metadata to the shared data of the . + /// + public void Add(IMetadata metadata) + { + this.localizationTableCollection.SharedData.Metadata.AddMetadata(metadata); + } + + /// + /// Adds the given metadata to the given . + /// + public void Add(LocalizationTable localizationTable, IMetadata metadata) + { + localizationTable.AddMetadata(metadata); + } + + /// + /// Adds the given metadata to the given . + /// + public void Add(SharedTableData.SharedTableEntry sharedTableEntry, IMetadata metadata) + { + sharedTableEntry.Metadata.AddMetadata(metadata); + } + + /// + /// Adds the given metadata to the given . + /// + public void Add(TableEntry tableEntry, IMetadata metadata) + { + tableEntry.AddMetadata(metadata); + } + + /// + /// Removes the given metadata from the shared data of the . + /// + public void Remove(IMetadata metadata) + { + this.localizationTableCollection.SharedData.Metadata.RemoveMetadata(metadata); + } + + /// + /// Removes the given metadata from the given . + /// + public void Remove(LocalizationTable localizationTable, IMetadata metadata) + { + localizationTable.RemoveMetadata(metadata); + } + + /// + /// Removes the given metadata from the given . + /// + public void Remove(SharedTableData.SharedTableEntry sharedTableEntry, IMetadata metadata) + { + sharedTableEntry.Metadata.RemoveMetadata(metadata); + } + + /// + /// Removes the given metadata from the given . + /// + public void Remove(TableEntry tableEntry, IMetadata metadata) + { + tableEntry.RemoveMetadata(metadata); + } + + /// + /// Draws the metadata for the given + /// + public float Draw(Rect rect) + { + var minWidth = 0f; + if (this.Target == null) return minWidth; + + if (Event.current.type != EventType.Repaint) + { + if (this.toBeAdded != null) + { + switch (this.Target) + { + case LocalizationTable localizationTable: + Undo.RecordObject(this.localizationTableCollection, "Localization Tables Metadata Added"); + this.Add(localizationTable, this.toBeAdded); + break; + case SharedTableData.SharedTableEntry sharedTableEntry: + Undo.RecordObject(this.localizationTableCollection.SharedData, "Localization Tables Metadata Added"); + this.Add(sharedTableEntry, this.toBeAdded); + break; + case TableEntry tableEntry: + Undo.RecordObject(tableEntry.Table, "Localization Tables Metadata Added"); + this.Add(tableEntry, this.toBeAdded); + break; + default: + Undo.RecordObject(this.localizationTableCollection.SharedData, "Localization Tables Metadata Added"); + this.Add(this.toBeAdded); + break; + } + + this.toBeAdded = null; + this.windowState.Dispose(); + } + + if (this.toBeRemoved != null) + { + switch (this.Target) + { + case LocalizationTable localizationTable: + Undo.RecordObject(this.localizationTableCollection, "Localization Tables Metadata Removed"); + this.Remove(localizationTable, this.toBeRemoved); + break; + case SharedTableData.SharedTableEntry sharedTableEntry: + Undo.RecordObject(this.localizationTableCollection.SharedData, "Localization Tables Metadata Removed"); + this.Remove(sharedTableEntry, this.toBeRemoved); + break; + case TableEntry tableEntry: + Undo.RecordObject(tableEntry.Table, "Localization Tables Metadata Removed"); + this.Remove(tableEntry, this.toBeRemoved); + break; + default: + Undo.RecordObject(this.localizationTableCollection.SharedData, "Localization Tables Metadata Removed"); + this.Remove(this.toBeRemoved); + break; + } + + this.toBeRemoved = null; + this.windowState.Dispose(); + } + } + + if (this.windowState.MetadataTree == null || this.lastTarget != this.Target) + { + this.windowState.MetadataTree?.Dispose(); + + switch (this.Target) + { + case LocalizationTable localizationTable: + this.windowState.MetadataTree = PropertyTree.Create(localizationTable); + break; + case SharedTableData.SharedTableEntry sharedTableEntry: + this.windowState.MetadataTree = PropertyTree.Create(sharedTableEntry); + break; + case TableEntry tableEntry: + { + var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + var entryData = typeof(TableEntry).GetProperty("Data", bindingFlags).GetValue(tableEntry); + this.windowState.MetadataTree = PropertyTree.Create(entryData); + break; + } + case LocalizationTableCollection localizationTableCollection: + this.windowState.MetadataTree = PropertyTree.Create(localizationTableCollection.SharedData); + break; + } + + this.lastTarget = this.Target; + } + + var metadataCollection = this.windowState.MetadataTree.RootProperty.Children["m_Metadata"]; + var metadataEntries = metadataCollection.Children["m_Items"].Children.ToList(); + + metadataEntries = metadataEntries + .Where(entry => !entry.Attributes.HasAttribute()) + .OrderBy(entry => !this.tags.Contains(entry.ValueEntry.TypeOfValue)) + .ThenBy(entry => entry.Name) + .ToList(); + + this.windowState.MetadataTree.BeginDraw(false); + switch (this.Target) + { + case LocalizationTable localizationTable: + minWidth = this.Draw(rect, localizationTable, "Table", metadataEntries, m => this.Add(localizationTable, m), m => this.Remove(localizationTable, m), () => this.OpenMetadataSelector(localizationTable)); + break; + case SharedTableData.SharedTableEntry sharedTableEntry: + minWidth = this.Draw(rect, this.localizationTableCollection.SharedData, "Shared Entry", metadataEntries, m => this.Add(sharedTableEntry, m), m => this.Remove(sharedTableEntry, m), () => this.OpenMetadataSelector(sharedTableEntry)); + break; + case TableEntry tableEntry: + minWidth = this.Draw(rect, tableEntry.Table, "Entry", metadataEntries, m => this.Add(tableEntry, m), m => this.Remove(tableEntry, m), () => this.OpenMetadataSelector(tableEntry)); + break; + default: + minWidth = this.Draw(rect, this.localizationTableCollection.SharedData, "Shared Table", metadataEntries, m => this.Add(m), m => this.Remove(m), () => this.OpenMetadataSelector(this.localizationTableCollection)); + break; + } + this.windowState.MetadataTree.EndDraw(); + + return minWidth; + } + + private float Draw(Rect rect, UnityEngine.Object target, string title, List metadataEntries, Action add, Action remove, Action openMetadataSelector) + { + const int headerHeight = 22; + const int addButtonAreaHeight = 40; + + EditorGUIUtility.labelWidth = 0f; + + // We only want to process events if the user's mouse is currently + // in the metadata panel so we grab some information for later. + var metadataPanelHasFocus = Event.current.IsMouseOver(rect); + var holdingControl = Event.current.control && metadataPanelHasFocus; + var holdingAlt = Event.current.alt && metadataPanelHasFocus; + + if (Event.current.OnKeyUp(KeyCode.LeftAlt)) + { + this.lastMetadataTypeUnderCursor = null; + } + + var searchAreaRect = rect.TakeFromTop(32); + EditorGUI.DrawRect(searchAreaRect.AlignBottom(1), SirenixGUIStyles.BorderColor); + + // Draw search field. + if (searchAreaRect.width > 125) + { + var titleWidth = SirenixGUIStyles.LabelCentered.CalcWidth(title) + 13; + var titleRect = searchAreaRect.AlignRight(titleWidth); + EditorGUI.DrawRect(titleRect.TakeFromLeft(1), SirenixGUIStyles.BorderColor); + this.searchTerm = this.searchField.Draw(searchAreaRect.SubXMax(titleWidth).Padding(6), this.searchTerm, + "Search for metadata..."); + GUI.Label(titleRect, title, SirenixGUIStyles.LabelCentered); + } + else + { + GUI.Label(searchAreaRect, title, SirenixGUIStyles.LabelCentered); + } + + var filteredMetadataEntries = string.IsNullOrWhiteSpace(this.searchTerm) + ? metadataEntries.ToList() + : metadataEntries.Where(metadataEntry => + { + if (FuzzySearch.Contains(this.searchTerm, metadataEntry.Info.TypeOfOwner.Name)) + { + return true; + } + + if (FuzzySearch.Contains(this.searchTerm, metadataEntry.ValueEntry?.WeakSmartValue?.ToString())) + { + return true; + } + + foreach (var child in metadataEntry.Children.Recurse()) + { + if (FuzzySearch.Contains(this.searchTerm, child.Name)) + { + return true; + } + + if (FuzzySearch.Contains(this.searchTerm, child.ValueEntry?.WeakSmartValue?.ToString())) + { + return true; + } + } + + return false; + }).ToList(); + + var metadataViewHeight = rect.height - addButtonAreaHeight; + GUILayout.BeginArea(rect.AlignTop(metadataViewHeight)); + this.scrollPosition = EditorGUILayout.BeginScrollView(this.scrollPosition, GUIStyle.none, GUI.skin.verticalScrollbar); + GUIHelper.BeginLayoutMeasuring(); + { + for (var i = 0; i < filteredMetadataEntries.Count; i++) + { + var metadataEntry = filteredMetadataEntries[i]; + var metadataHeaderRect = GUILayoutUtility.GetRect(0, headerHeight, GUILayoutOptions.ExpandWidth().ExpandHeight(false)); + var unchangedMetadataHeaderRect = metadataHeaderRect; + + var genericMenu = new GenericMenu(); + genericMenu.AddItem(new GUIContent("Delete"), false, () => + { + var metadata = (IMetadata)metadataEntry.ValueEntry.WeakSmartValue; + this.toBeRemoved = metadata; + }); + + var isTag = this.tags.Contains(metadataEntry.ValueEntry.TypeOfValue); + + if (!isTag) + { + genericMenu.AddItem(new GUIContent("Copy"), false, () => + { + var metadata = (IMetadata)metadataEntry.ValueEntry.WeakSmartValue; + Clipboard.Copy(metadata); + }); + + if (Clipboard.CanPaste()) + genericMenu.AddItem(new GUIContent("Paste"), false, () => + { + var metadata = (IMetadata)Clipboard.Paste(); + this.toBeAdded = metadata; + }); + else + genericMenu.AddDisabledItem(new GUIContent("Paste")); + + genericMenu.AddSeparator(""); + genericMenu.AddItem(new GUIContent($"{(metadataEntry.State.Expanded ? "Collapse" : "Expand")} All (Ctrl-Click)"), false, () => + { + var newState = !metadataEntry.State.Expanded; + foreach (var entry in filteredMetadataEntries) entry.State.Expanded = newState; + }); + genericMenu.AddItem(new GUIContent($"{(metadataEntry.State.Expanded ? "Collapse" : "Expand")} All Of Same Type (Alt-Click)"), false, () => + { + var newState = !metadataEntry.State.Expanded; + foreach (var entry in filteredMetadataEntries.Where(m => m.Info.TypeOfOwner == metadataEntry.Info.TypeOfOwner)) entry.State.Expanded = newState; + }); + } + + Color metadataHeaderBackground = new Color(0.243f, 0.243f, 0.243f); + + if (!isTag && holdingControl) + { + metadataHeaderBackground = new Color(0.278f, 0.278f, 0.278f); + + GUIHelper.RequestRepaint(); + } + else if (!isTag && holdingAlt) + { + if (Event.current.IsMouseOver(unchangedMetadataHeaderRect)) + { + this.lastMetadataTypeUnderCursor = metadataEntry.ValueEntry.TypeOfValue; + GUIHelper.RequestRepaint(); + } + + metadataHeaderBackground = this.lastMetadataTypeUnderCursor == metadataEntry.ValueEntry.TypeOfValue + ? new Color(0.278f, 0.278f, 0.278f) + : new Color(0.243f, 0.243f, 0.243f); + } + + if (Event.current.IsMouseOver(unchangedMetadataHeaderRect)) + { + metadataHeaderBackground = new Color(0.278f, 0.278f, 0.278f); + } + + EditorGUI.DrawRect(unchangedMetadataHeaderRect, metadataHeaderBackground); + + if (i != 0) + { + EditorGUI.DrawRect(unchangedMetadataHeaderRect.AlignTop(1), new Color(0.102f, 0.102f, 0.102f)); + } + + var foldoutTriangleRect = metadataHeaderRect.TakeFromLeft(headerHeight); + + if (!isTag) + SdfIcons.DrawIcon( + foldoutTriangleRect.Padding(6), + metadataEntry.State.Expanded ? SdfIconType.CaretDownFill : SdfIconType.CaretRightFill, + new Color(0.443f, 0.443f, 0.443f)); + + var iconRect = metadataHeaderRect.TakeFromLeft(headerHeight); + SdfIcons.DrawIcon( + iconRect.Padding(0, 4, 4, 4), + isTag ? SdfIconType.TagFill : SdfIconType.GeoAltFill, + new Color(0.933f, 0.933f, 0.933f)); + + var contextMenuIconRect = metadataHeaderRect.TakeFromRight(headerHeight); + + SdfIcons.DrawIcon(contextMenuIconRect.Padding(7), SdfIconType.ThreeDotsVertical); + + var niceName = ObjectNames.NicifyVariableName(metadataEntry.ValueEntry.TypeOfValue.GetNiceName()); + + if (metadataEntry.Info.TypeOfOwner == typeof(Comment)) + { + var headerLabelWidth = EditorStyles.label.CalcWidth(niceName) + 1; + var comment = ((string)metadataEntry.ValueEntry.WeakSmartValue).Split('\n')[0]; + var hintRect = metadataHeaderRect.AlignRight(metadataHeaderRect.width - headerLabelWidth - 10); + + if (SirenixGUIStyles.RightAlignedGreyMiniLabel.CalcWidth(comment) > hintRect.width) + { + if (hintRect.width < 30) + { + comment = ""; + } + else + { + GUI.Label(hintRect.TakeFromLeft(11), "...", SirenixGUIStyles.RightAlignedGreyMiniLabel); + } + } + + GUI.Label(hintRect, comment, SirenixGUIStyles.RightAlignedGreyMiniLabel); + } + else if (metadataEntry.Info.TypeOfOwner == typeof(PreloadAssetTableMetadata)) + { + var headerLabelWidth = EditorStyles.label.CalcWidth(niceName) + 1; + var preloadAssetLabel = ObjectNames.NicifyVariableName(metadataEntry.ValueEntry.WeakSmartValue.ToString()); + var hintRect = metadataHeaderRect.AlignRight(metadataHeaderRect.width - headerLabelWidth - 10); + + if (SirenixGUIStyles.RightAlignedGreyMiniLabel.CalcWidth(preloadAssetLabel) > hintRect.width) + { + if (hintRect.width < 30) + { + preloadAssetLabel = ""; + } + else + { + GUI.Label(hintRect.TakeFromLeft(11), "...", SirenixGUIStyles.RightAlignedGreyMiniLabel); + } + } + + GUI.Label(hintRect, preloadAssetLabel, SirenixGUIStyles.RightAlignedGreyMiniLabel); + } + else if (metadataEntry.Info.TypeOfOwner == typeof(PlatformOverride)) + { + var platformOverrides = metadataEntry.Children; + var headerLabelWidth = EditorStyles.label.CalcWidth(niceName) + 1; + var hintRect = metadataHeaderRect.AlignRight(metadataHeaderRect.width - headerLabelWidth - 10); + + this.stringBuilder.Clear(); + for (var j = 0; j < platformOverrides.Count; j++) + { + var platformOverrideInspectorProperty = platformOverrides[j]; + var platform = (RuntimePlatform)platformOverrideInspectorProperty.Children[0].ValueEntry.WeakSmartValue; + this.stringBuilder.Append(platform); + + if (j != platformOverrides.Count - 1) + { + this.stringBuilder.Append(", "); + } + } + + var platforms = this.stringBuilder.ToString(); + + if (SirenixGUIStyles.RightAlignedGreyMiniLabel.CalcWidth(platforms) > hintRect.width) + { + if (hintRect.width < 30) + { + GUI.Label(hintRect, $"[{platformOverrides.Count}]", SirenixGUIStyles.RightAlignedGreyMiniLabel); + platforms = ""; + } + else if (platformOverrides.Count > 1) + { + GUI.Label(hintRect.TakeFromLeft(11), "...", SirenixGUIStyles.RightAlignedGreyMiniLabel); + } + } + + GUI.Label(hintRect, platforms, SirenixGUIStyles.RightAlignedGreyMiniLabel); + } + + GUI.Label(metadataHeaderRect, niceName); + + var evt = Event.current; + if (evt.OnMouseDown(contextMenuIconRect, 0, false)) + genericMenu.ShowAsContext(); + else if (evt.OnMouseDown(unchangedMetadataHeaderRect, 1, false)) genericMenu.ShowAsContext(); + + if (metadataEntry.ValueEntry.TypeOfValue == typeof(ExcludeEntryFromExport)) continue; + + if (evt.OnMouseDown(unchangedMetadataHeaderRect, 0, false)) + { + if (holdingControl) + { + var newState = !metadataEntry.State.Expanded; + foreach (var entry in filteredMetadataEntries) + { + entry.State.Expanded = newState; + } + } + else if (holdingAlt) + { + var newState = !metadataEntry.State.Expanded; + foreach (var entry in filteredMetadataEntries.Where(m => m.ValueEntry.TypeOfValue == this.lastMetadataTypeUnderCursor)) + { + entry.State.Expanded = newState; + } + } + else + { + metadataEntry.State.Expanded = !metadataEntry.State.Expanded; + } + + GUIHelper.RemoveFocusControl(); + } + + if (SirenixEditorGUI.BeginFadeGroup(metadataEntry, metadataEntry.State.Expanded)) + { + EditorGUI.DrawRect(unchangedMetadataHeaderRect.AlignBottom(1), + new Color(0.188f, 0.188f, 0.188f)); + + var contentRect = EditorGUILayout.BeginVertical(); + + if (metadataEntry.ValueEntry.TypeOfValue == typeof(Comment)) + { + EditorGUILayout.BeginVertical(this.ContentPadding); + foreach (var child in metadataEntry.Children) + { + child.Draw(null); + } + EditorGUILayout.EndVertical(); + } + else if (metadataEntry.ValueEntry.TypeOfValue == typeof(PreloadAssetTableMetadata)) + { + EditorGUILayout.BeginVertical(this.ContentPadding); + + var preloadAssetTableMetadata = (PreloadAssetTableMetadata)metadataEntry.ValueEntry.WeakSmartValue; + var btnRect = EditorGUILayout.GetControlRect(false); + var leftRect = btnRect.Split(0, 2); + var mouseOverLeft = Event.current.IsMouseOver(leftRect); + var leftActive = mouseOverLeft && Event.current.type == EventType.MouseDown && Event.current.button == 0; + var leftOn = preloadAssetTableMetadata.Behaviour == PreloadAssetTableMetadata.PreloadBehaviour.NoPreload; + + var rightRect = btnRect.Split(1, 2); + var mouseOverRight = Event.current.IsMouseOver(rightRect); + var rightActive = mouseOverRight && Event.current.type == EventType.MouseDown && Event.current.button == 0; + var rightOn = preloadAssetTableMetadata.Behaviour == PreloadAssetTableMetadata.PreloadBehaviour.PreloadAll; + + if (Event.current.type == EventType.Repaint) + { + SirenixGUIStyles.ButtonLeft.Draw(leftRect, "No Preload", mouseOverLeft, leftActive, leftOn, false); + SirenixGUIStyles.ButtonRight.Draw(rightRect, "Preload All", mouseOverRight, rightActive, rightOn, false); + } + + if (Event.current.OnMouseDown(leftRect, 0, false)) + { + preloadAssetTableMetadata.Behaviour = PreloadAssetTableMetadata.PreloadBehaviour.NoPreload; + } + else if (Event.current.OnMouseDown(rightRect, 0, false)) + { + preloadAssetTableMetadata.Behaviour = PreloadAssetTableMetadata.PreloadBehaviour.PreloadAll; + } + + EditorGUILayout.EndVertical(); + } + else if (metadataEntry.ValueEntry.TypeOfValue == typeof(PlatformOverride)) + { + this.DrawPlatformOverride(metadataEntry); + } + else + { + EditorGUILayout.BeginVertical(this.ContentPadding); + foreach (var child in metadataEntry.Children) + { + child.Draw(); + } + EditorGUILayout.EndVertical(); + } + + EditorGUILayout.EndVertical(); + } + + SirenixEditorGUI.EndFadeGroup(); + } + } + var measureRect = GUIHelper.EndLayoutMeasuring(); + EditorGUILayout.EndScrollView(); + GUILayout.EndArea(); + + var pinAddMetadataButton = measureRect.height > metadataViewHeight; + rect.TakeFromTop(pinAddMetadataButton ? metadataViewHeight : measureRect.height); + + if (metadataEntries.Count > 1) + { + EditorGUI.DrawRect(rect.TakeFromTop(1), SirenixGUIStyles.BorderColor); + } + + var addMetadataRect = rect.AlignTop(addButtonAreaHeight).AlignCenter(Mathf.Min(200, rect.width - 16)).VerticalPadding(8); + + if (GUI.Button(addMetadataRect, "Add Metadata")) + { + openMetadataSelector(); + } + + // Draw the shadow that appears under the search bar while scrolling through the metadata. + GUI.DrawTexture( + searchAreaRect.AlignBottom(10).AddY(10), + OdinLocalizationGUITextures.TopToBottomFade, + ScaleMode.StretchToFill, + true, + 1.0f, + new Color(0, 0, 0, Mathf.InverseLerp(0f, 30f, this.scrollPosition.y) * 0.6f), + 0, + 0); + + if (measureRect.height > metadataViewHeight) + { + var pos = this.scrollPosition.y + metadataViewHeight; + // Draw the shadow that appears at the bottom of the metadata while scrolling through the metadata + GUI.DrawTexture( + rect.AlignTop(10).SubY(10), + OdinLocalizationGUITextures.BottomToTopFade, + ScaleMode.StretchToFill, + true, + 1.0f, + new Color(0, 0, 0, Mathf.InverseLerp(measureRect.height, measureRect.height - 30, pos) * 0.6f), + 0, + 0); + } + + return measureRect.width; + } + + private void DrawPlatformOverride(InspectorProperty metadataEntry) + { + var platformOverride = (PlatformOverride)metadataEntry.ValueEntry.WeakSmartValue; + var platformOverrideDatas = (IList)metadataEntry.Children["m_PlatformOverrides"]?.ValueEntry.WeakSmartValue; + var platformOverrideDataInspectorProperties = metadataEntry.Children["m_PlatformOverrides"]?.Children; + + if (platformOverrideDatas == null) + { + return; + } + + if (platformOverrideDatas.Count == 0) + { + platformOverride.AddPlatformOverride(RuntimePlatform.WindowsPlayer, null, null, EntryOverrideType.None); + } + + for (var i = 0; i < platformOverrideDataInspectorProperties.Count; i++) + { + var platformOverrideDataInspectorProperty = platformOverrideDataInspectorProperties[i]; + var backgroundRect = EditorGUILayout.BeginVertical(this.PlatformOverridePadding); + var backgroundColor = i % 2 == 0 ? SirenixGUIStyles.ListItemColorEven : SirenixGUIStyles.ListItemColorOdd; + EditorGUI.DrawRect(backgroundRect, backgroundColor); + platformOverrideDataInspectorProperty.Children["platform"].Draw(); + platformOverrideDataInspectorProperty.Children["entryOverrideType"].Draw(); + + var entryOverrideType = (EntryOverrideType)platformOverrideDataInspectorProperty.Children + .FirstOrDefault(c => c.Info.TypeOfValue == typeof(EntryOverrideType))?.ValueEntry.WeakSmartValue; + + var entryOverrideRect = GUILayoutUtility.GetRect(0, EditorGUIUtility.singleLineHeight, GUILayoutOptions.ExpandWidth(true).ExpandHeight(false)); + + switch (entryOverrideType) + { + case EntryOverrideType.Table: + { + var tableReference = (TableReference)platformOverrideDataInspectorProperty.Children.FirstOrDefault(c => c.Info.TypeOfValue == typeof(TableReference))?.ValueEntry.WeakSmartValue; + this.DoTableGUI(entryOverrideRect, tableReference, platformOverrideDataInspectorProperty); + break; + } + case EntryOverrideType.Entry: + { + var tableReference = (TableReference)platformOverrideDataInspectorProperty.Children.FirstOrDefault(c => c.Info.TypeOfValue == typeof(TableReference)) + ?.ValueEntry.WeakSmartValue; + var tableEntryReference = (TableEntryReference)platformOverrideDataInspectorProperty.Children.FirstOrDefault(c => c.Info.TypeOfValue == typeof(TableEntryReference))?.ValueEntry.WeakSmartValue; + this.DoTableAndEntryGUI(entryOverrideRect, tableReference, tableEntryReference, platformOverrideDataInspectorProperty, false); + break; + } + + case EntryOverrideType.TableAndEntry: + { + var tableReference = (TableReference)platformOverrideDataInspectorProperty.Children.FirstOrDefault(c => c.Info.TypeOfValue == typeof(TableReference))?.ValueEntry.WeakSmartValue; + var tableEntryReference = (TableEntryReference)platformOverrideDataInspectorProperty.Children.FirstOrDefault(c => c.Info.TypeOfValue == typeof(TableEntryReference))?.ValueEntry.WeakSmartValue; + this.DoTableAndEntryGUI(entryOverrideRect, tableReference, tableEntryReference, platformOverrideDataInspectorProperty, true); + break; + } + } + + EditorGUILayout.EndVertical(); + + var xIconRect = backgroundRect.AlignRight(10).SubX(4).AlignCenterY(10); + var mouseOverX = Event.current.IsMouseOver(xIconRect); + SdfIcons.DrawIcon(xIconRect, SdfIconType.X, mouseOverX ? Color.white : EditorStyles.label.normal.textColor); + + if (Event.current.OnMouseDown(xIconRect, 0, false)) + { + var platform = (RuntimePlatform)platformOverrideDataInspectorProperty.Children[0].ValueEntry.WeakSmartValue; + platformOverride.RemovePlatformOverride(platform); + } + } + + var addButtonRect = GUILayoutUtility.GetRect(0, 40, GUILayoutOptions.ExpandWidth().ExpandHeight(false)); + EditorGUI.DrawRect(addButtonRect.AlignTop(1), new Color(0.188f, 0.188f, 0.188f)); + + if (GUI.Button(addButtonRect.Padding(8), "Add Platform Override")) + { + var selector = new EnumSelector(); + selector.SelectionConfirmed += platforms => + { + var platform = platforms.FirstOrDefault(); + platformOverride.AddPlatformOverride(platform, null, null, EntryOverrideType.None); + }; + selector.ShowInPopup(); + } + } + + private void OpenMetadataSelector(LocalizationTableCollection localizationTableCollection) + { + this.OpenMetadataSelector(localizationTableCollection.SharedData.Metadata.MetadataEntries, MetadataType.SharedTableData); + } + + private void OpenMetadataSelector(LocalizationTable localizationTable) + { + switch (localizationTable) + { + case StringTable _: + this.OpenMetadataSelector(localizationTable.MetadataEntries, MetadataType.StringTable); + break; + case AssetTable _: + this.OpenMetadataSelector(localizationTable.MetadataEntries, MetadataType.AssetTable); + break; + } + } + + private void OpenMetadataSelector(SharedTableData.SharedTableEntry sharedTableEntry) + { + switch (this.localizationTableCollection) + { + case StringTableCollection _: + this.OpenMetadataSelector(sharedTableEntry.Metadata.MetadataEntries, MetadataType.StringTableEntry, MetadataType.SharedStringTableEntry); + break; + case AssetTableCollection _: + this.OpenMetadataSelector(sharedTableEntry.Metadata.MetadataEntries, MetadataType.AssetTableEntry, MetadataType.SharedAssetTableEntry); + break; + } + } + + private void OpenMetadataSelector(TableEntry tableEntry) + { + switch (tableEntry) + { + case StringTableEntry _: + this.OpenMetadataSelector(tableEntry.MetadataEntries, MetadataType.StringTableEntry); + break; + case AssetTableEntry _: + this.OpenMetadataSelector(tableEntry.MetadataEntries, MetadataType.AssetTableEntry); + break; + } + } + + private void OpenMetadataSelector(IList existingMetadata, params MetadataType[] allowedTypes) + { + var existingMetadataTypes = existingMetadata.Select(m => m?.GetType()).ToList(); + var metadataTypes = TypeCache.GetTypesDerivedFrom(); + var validMetadataTypes = new List(); + + foreach (var metadataType in metadataTypes) + { + var metadataAttribute = metadataType.GetCustomAttribute(); + if (metadataAttribute != null && allowedTypes.Any(allowedType => metadataAttribute.AllowedTypes.HasFlag(allowedType))) + { + if (existingMetadataTypes.Contains(metadataType) && !OdinLocalizationMetadataRegistry.MetadataAllowsMultiple.ContainsKey(metadataType)) continue; + + validMetadataTypes.Add(metadataType); + } + } + + var selector = new GenericSelector("", validMetadataTypes, false, type => ObjectNames.NicifyVariableName(type.Name)); + + foreach (var item in selector.SelectionTree.MenuItems) + { + item.SdfIcon = SdfIconType.Braces; + } + + selector.SelectionConfirmed += types => + { + var selectedType = types.FirstOrDefault(); + + if (selectedType == null) + { + return; + } + + var metadata = (IMetadata)Activator.CreateInstance(selectedType); + this.toBeAdded = metadata; + }; + selector.EnableSingleClickToSelect(); + selector.ShowInPopup(); + } + + public class TableReferenceSelector : OdinSelector + { + private Type tableType; + + public TableReferenceSelector(Type tableType) + { + this.tableType = tableType; + } + + protected override void BuildSelectionTree(OdinMenuTree tree) + { + tree.Config.SelectMenuItemsOnMouseDown = true; + tree.Selection.SupportsMultiSelect = false; + + var collectionGUIDs = AssetDatabase.FindAssets($"t:{nameof(LocalizationTableCollection)}"); + + foreach (var guid in collectionGUIDs) + { + var path = AssetDatabase.GUIDToAssetPath(guid); + var collection = AssetDatabase.LoadAssetAtPath(path); + + if (collection is null) continue; + if (collection.Tables.Count < 1) continue; + if (collection.Tables[0].asset.GetType() != this.tableType) continue; + + var collectionItem = new OdinMenuItem(tree, collection.name, collection) + { + SdfIcon = SdfIconType.Table + }; + + tree.MenuItems.Add(collectionItem); + } + } + } + + public class TableEntryReferenceSelector : OdinSelector + { + private Type tableType; + + public struct TableEntry + { + public LocalizationTableCollection Collection; + public SharedTableData.SharedTableEntry SharedEntry; + } + + public TableEntryReferenceSelector(Type tableType) + { + this.tableType = tableType; + } + + protected override void BuildSelectionTree(OdinMenuTree tree) + { + tree.Config.SelectMenuItemsOnMouseDown = true; + tree.Selection.SupportsMultiSelect = false; + + + var collectionGUIDs = AssetDatabase.FindAssets($"t:{nameof(LocalizationTableCollection)}"); + + foreach (var guid in collectionGUIDs) + { + var path = AssetDatabase.GUIDToAssetPath(guid); + var collection = AssetDatabase.LoadAssetAtPath(path); + + if (collection is null) continue; + if (collection.Tables.Count < 1) continue; + if (collection.Tables[0].asset.GetType() != this.tableType) continue; + + var collectionItem = new OdinMenuItem(tree, collection.name, collection) + { + SdfIcon = SdfIconType.Table + }; + + List entries = collection.SharedData.Entries; + + for (var j = 0; j < entries.Count; j++) + { + SharedTableData.SharedTableEntry entry = entries[j]; + + var tableEntry = new TableEntry {Collection = collection, SharedEntry = entry}; + + var entryItem = new OdinMenuItem(tree, entry.Key, tableEntry) + { + SdfIcon = SdfIconType.KeyFill + }; + + collectionItem.ChildMenuItems.Add(entryItem); + } + + collectionItem.IsSelectable = false; + tree.MenuItems.Add(collectionItem); + } + } + } + + private void DoTableGUI(Rect rect, TableReference tableReference, InspectorProperty platformOverrideData) + { + var dropDownPosition = EditorGUI.PrefixLabel(rect, new GUIContent("Table Collection")); + var label = tableReference.TableCollectionName; + + if (EditorGUI.DropdownButton(dropDownPosition, new GUIContent(label), FocusType.Passive)) + { + TableReferenceSelector tableReferenceSelector; + + tableReferenceSelector = this.localizationTableCollection.GetType() == typeof(AssetTableCollection) + ? new TableReferenceSelector(typeof(AssetTable)) + : new TableReferenceSelector(typeof(StringTable)); + + tableReferenceSelector.GetCurrentSelection(); + + tableReferenceSelector.SelectionConfirmed += selections => + { + var selection = selections.FirstOrDefault(); + + var platformOverrideType = typeof(PlatformOverride); + var platformOverrideDataType = platformOverrideType.GetNestedType("PlatformOverrideData", BindingFlags.NonPublic); + + platformOverrideDataType + .GetField("tableReference") + .SetValue(platformOverrideData.ValueEntry.WeakSmartValue, selection.TableCollectionNameReference); + + platformOverrideDataType + .GetField("tableEntryReference") + .SetValue(platformOverrideData.ValueEntry.WeakSmartValue, (TableEntryReference) string.Empty); + }; + + tableReferenceSelector.ShowInPopup(); + } + } + + private void DoTableAndEntryGUI(Rect rect, + TableReference tableReference, + TableEntryReference tableEntryReference, + InspectorProperty platformOverrideData, + bool displayTableName) + { + var dropDownPosition = EditorGUI.PrefixLabel(rect, new GUIContent("Reference")); + + var entryLabel = tableEntryReference.ReferenceType != TableEntryReference.Type.Empty + ? tableEntryReference.ResolveKeyName(this.localizationTableCollection.SharedData) + : null; + + string label; + if (displayTableName) + { + var tableLabel = tableReference.TableCollectionName ?? ""; + label = string.IsNullOrEmpty(tableLabel) || string.IsNullOrEmpty(entryLabel) ? "None" : $"{tableLabel}/{entryLabel}"; + } + else + { + label = string.IsNullOrWhiteSpace(entryLabel) ? "None" : $"{entryLabel}"; + } + + if (EditorGUI.DropdownButton(dropDownPosition, new GUIContent(label), FocusType.Passive)) + { + Type tableType = this.localizationTableCollection.GetType() == typeof(AssetTableCollection) + ? typeof(AssetTable) + : typeof(StringTable); + + TableEntryReferenceSelector tableEntryReferenceSelector = new TableEntryReferenceSelector(tableType); + tableEntryReferenceSelector.SelectionConfirmed += selections => + { + var selection = selections.FirstOrDefault(); + var platformOverrideType = typeof(PlatformOverride); + var platformOverrideDataType = platformOverrideType.GetNestedType("PlatformOverrideData", BindingFlags.NonPublic); + + platformOverrideDataType + .GetField("tableReference") + .SetValue(platformOverrideData.ValueEntry.WeakSmartValue, selection.Collection.TableCollectionNameReference); + + platformOverrideDataType + .GetField("tableEntryReference") + .SetValue(platformOverrideData.ValueEntry.WeakSmartValue, (TableEntryReference) selection.SharedEntry.Key); + }; + + tableEntryReferenceSelector.ShowInPopup(); + } + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/LocalizationMetadata.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/LocalizationMetadata.cs.meta new file mode 100644 index 00000000..9a868272 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/LocalizationMetadata.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1490cea564e237341b75e0cdba1f374a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinAssetTableCollectionEditor.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinAssetTableCollectionEditor.cs new file mode 100644 index 00000000..06cbdbd7 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinAssetTableCollectionEditor.cs @@ -0,0 +1,1015 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#define USING_WIDTH_NON_PERCENT + +using System; +using System.Collections.ObjectModel; +using System.IO; +using Sirenix.OdinInspector.Editor; +using Sirenix.OdinInspector.Editor.Internal; +using Sirenix.OdinInspector.Modules.Localization.Editor.Configs; +using Sirenix.OdinInspector.Modules.Localization.Editor.Internal; +using Sirenix.Utilities; +using Sirenix.Utilities.Editor; +using UnityEditor; +using UnityEditor.AddressableAssets.Settings; +using UnityEditor.Localization; +using UnityEngine; +using UnityEngine.Localization.Tables; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ + public class OdinAssetTableCollectionEditor : OdinTableCollectionEditor + { + public Type UserSpecifiedCollectionType; + + private AssetTableEntry pickedEntry; + private string pickedEntryOriginalGuid; + + private Action setEntryAssetTypeAction; + private Action removeEntryAssetTypeAction; + + public OdinAssetTableCollectionEditor(AssetTableCollection collection, OdinMenuEditorWindow relatedWindow, + OdinLocalizationEditorWindow.WindowState windowState) : + base(collection, relatedWindow, windowState) { } + + public void SetEntryAssetType(AssetTableEntry entry, Type assetType) + { + if (assetType == null || assetType == typeof(UnityEngine.Object)) + { + this.removeEntryAssetTypeAction(entry.KeyId, entry.Table.LocaleIdentifier.Code); + + return; + } + + this.setEntryAssetTypeAction(entry.KeyId, assetType, entry.Table.LocaleIdentifier.Code); + } + + protected override void OnInitialize() + { + this.setEntryAssetTypeAction = OdinLocalizationReflectionValues.Create_AssetTableCollection_SetEntryAssetType_PrivateMethod_Delegate(this.Collection); + + this.removeEntryAssetTypeAction = + OdinLocalizationReflectionValues.Create_AssetTableCollection_RemoveEntryAssetType_PrivateMethod_Delegate(this.Collection); + + for (var i = 0; i < this.SharedEntries.Length; i++) + { + SharedTableData.SharedTableEntry sharedEntry = this.SharedEntries[i]; + + this.MeasureEntry(sharedEntry); + } + + //this.SharedEntries.OnSharedEntryAdded += (i, sharedEntry) => { this.MeasureEntry(sharedEntry); }; + + //this.SharedEntries.OnSharedEntryRemoved += (i, sharedEntry) => { this.SharedEntryHeights.Remove(sharedEntry.Id); }; + + this.OnTableEntryModified = sharedEntry => + { + if (!this.Collection.SharedData.Contains(sharedEntry.Id)) + { + return; + } + + int index = this.SharedEntries.GetIndex(sharedEntry); + + this.MeasureEntry(sharedEntry); + + this.EntryScrollView.ReallocateRect(index, this.SharedEntryHeights[sharedEntry.Id], sharedEntry); + }; + + this.OnAssetTableEntryAdded = (collection, table, entry) => + { + if (this.Collection != collection) + { + return; + } + + this.MeasureEntry(entry.SharedEntry); + }; + + this.OnAssetTableEntryRemoved = (collection, table, entry, guid) => + { + if (this.Collection != collection) + { + return; + } + + if (entry == null) + { + return; + } + + SharedTableData.SharedTableEntry sharedEntry = table.SharedData.GetEntry(entry.KeyId); + + if (sharedEntry == null) + { + return; + } + + this.MeasureEntry(sharedEntry); + }; + } + + protected override void AllocateItems() + { + for (var i = 0; i < this.SharedEntries.Length; i++) + { + SharedTableData.SharedTableEntry sharedEntry = this.SharedEntries[i]; + + if (!this.SharedEntries.IsVisible(sharedEntry)) + { + continue; + } + + if (!this.SharedEntryHeights.ContainsKey(sharedEntry.Id)) + { + this.MeasureEntry(sharedEntry); + } + + this.EntryScrollView.AllocateRect(this.SharedEntryHeights[sharedEntry.Id], sharedEntry); + + //this.ControlIds[sharedEntry] = GUIUtility.GetControlID(FocusType.Keyboard); + } + } + + protected override void DrawItems(ref OdinGUIScrollView.VisibleItems visibleItems) + { + this.HandleObjectPickerUpdates(); + + this.MeasureVisibleEntries(ref visibleItems); + + float scrollSpeed = OdinLocalizationConfig.Instance.scrollSpeed; + + this.EntryScrollView.BeginScrollView(offset: new Vector2(this.PinnedWidth, OdinLocalizationConstants.COLUMN_HEIGHT), + addViewSize: new Vector2(-this.PinnedWidth, 0), + scrollSpeed: scrollSpeed); + { + this.DrawEntries(ref visibleItems, false); + } + this.EntryScrollView.EndScrollView(); + + this.EntryScrollView.BeginClip(offset: new Vector2(0, OdinLocalizationConstants.COLUMN_HEIGHT), ignoreScrollX: true); + { + this.DrawEntries(ref visibleItems, true); + } + this.EntryScrollView.EndClip(); + } + + protected override void MeasureAllEntries() + { + for (var i = 0; i < this.SharedEntries.Length; i++) + { + this.MeasureEntry(this.SharedEntries[i]); + } + + this.HasGUIChanged = true; + } + + protected override void MeasureVisibleEntries(ref OdinGUIScrollView.VisibleItems visibleItems) + { + int dataOffset = visibleItems.Offset; + + for (var i = 0; i < visibleItems.Length; i++) + { + if (!visibleItems.HasAssociatedData(i)) + { + continue; + } + + var sharedEntry = visibleItems.GetAssociatedData(i); + + this.MeasureEntry(sharedEntry); + + this.EntryScrollView.ReallocateRect(dataOffset + i, this.SharedEntryHeights[sharedEntry.Id], sharedEntry); + } + } + + private void MeasureEntry(SharedTableData.SharedTableEntry sharedEntry) + { + var keyColumnWidth = 0.0f; + + for (var i = 0; i < this.GUITables.Count; i++) + { + OdinGUITable table = this.GUITables[i]; + + if (table.Type != OdinGUITable.GUITableType.Key) + { + continue; + } + +#if USING_WIDTH_NON_PERCENT + keyColumnWidth = table.Width; +#else + keyColumnWidth = this.GUITables[i].Width; +#endif + break; + } + + float keyHeight = MeasureText(sharedEntry.Key, keyColumnWidth); + + Type assetSharedType = this.Collection.GetEntryAssetType(sharedEntry.Id); + float assetHeight; + + if (assetSharedType == null || assetSharedType == typeof(UnityEngine.Object)) + { + assetHeight = OdinLocalizationConstants.EMPTY_ASSET_ROW_HEIGHT; + } + else + { + assetHeight = EditorGUIUtility.HasObjectThumbnail(assetSharedType) + ? OdinLocalizationConstants.AssetPreviewRowHeight + : OdinLocalizationConstants.ASSET_ROW_HEIGHT; + } + + this.SharedEntryHeights[sharedEntry.Id] = Mathf.Max(keyHeight, assetHeight); + } + + private void DrawEntries(ref OdinGUIScrollView.VisibleItems visibleItems, bool pinned) + { + bool isDraggingControls = this.IsDraggingAnything(); + + for (var i = 0; i < visibleItems.Length; i++) + { + if (!visibleItems.HasAssociatedData(i)) + { + continue; + } + + Rect position = visibleItems.GetRect(i); + + int hint = visibleItems.Offset + i + this.ControlIdHint; + + var sharedEntry = visibleItems.GetAssociatedData(i); + + bool isEven = (visibleItems.Offset + i) % 2 == 0; + + for (var j = 0; j < this.GUITables.Count; j++) + { + OdinGUITable table = this.GUITables[j]; + + if (!table.IsVisible) + { + continue; + } + + if (table.IsPinned != pinned) + { + continue; + } + + if (!this.GUITables.TablesWithinVisibleBounds.Contains(table)) + { + position.TakeFromLeft(table.Width).Padding(OdinLocalizationConstants.ENTRY_PADDING); + continue; + } + +#if USING_WIDTH_NON_PERCENT + Rect entryRect = position.TakeFromLeft(table.Width).Padding(OdinLocalizationConstants.ENTRY_PADDING); +#else + Rect entryRect = position.TakeFromLeft(table.Width).Padding(OdinLocalizationConstants.ENTRY_PADDING); +#endif + + bool isCellPressed, isSelected; + + switch (table.Type) + { + case OdinGUITable.GUITableType.Key: + isSelected = this.IsSharedEntrySelected(sharedEntry); + + if (isSelected) + { + this.SelectionAnimFloat.Move(1 / 0.18f, Easing.InSine); + + FancyColor start = FancyColor.Gray; + + var end = OdinLocalizationGUI.Selected; + + FancyColor.PushBlend(start.Lerp(end, this.SelectionAnimFloat), FancyColor.BlendMode.Overlay); + } + + isCellPressed = DrawCell(entryRect, isEven); + + this.DrawKey(entryRect, sharedEntry, GUIUtility.GetControlID(hint, FocusType.Keyboard)); + + if (isSelected) + { + FancyColor.PopBlend(); + } + + if (isCellPressed) + { + this.SelectSharedEntry(sharedEntry); + } + + break; + + case OdinGUITable.GUITableType.Default: + AssetTableEntry entry = table.Asset.GetEntry(sharedEntry.Id); + + isSelected = this.IsEntrySelected(entry); + + if (isSelected) + { + this.SelectionAnimFloat.Move(1 / 0.18f, Easing.InSine); + + FancyColor start = FancyColor.Gray; + + FancyColor end = OdinLocalizationGUI.Selected; + + FancyColor.PushBlend(start.Lerp(end, this.SelectionAnimFloat), FancyColor.BlendMode.Overlay); + } + + isCellPressed = DrawCell(entryRect, isEven); + + this.DrawEntry(entryRect, sharedEntry, entry, table, isDraggingControls); + + if (isSelected) + { + FancyColor.PopBlend(); + } + + if (isCellPressed) + { + if (entry is null) + { + entry = table.Asset.AddEntry(sharedEntry.Id, string.Empty); + } + + GUIUtility.keyboardControl = 0; + + this.SelectEntry(entry); + } + + if (isSelected && Event.current.type == EventType.KeyDown && EditorWindow.focusedWindow == this.RelatedWindow) + { + switch (Event.current.keyCode) + { + case KeyCode.Delete: + case KeyCode.Backspace: + if (entry == null) + { + break; + } + + this.AssignObjectToSharedEntry(sharedEntry, table.Asset, null); + + Event.current.Use(); + break; + + case KeyCode.Space: + case KeyCode.Return: + if (entry == null) + { + entry = table.Asset.AddEntry(sharedEntry.Id, string.Empty); + } + + this.ShowObjectPickerForEntry(entry); + + Event.current.Use(); + break; + } + } + + break; + } + } + } + } + + private void DrawEntry(Rect rect, SharedTableData.SharedTableEntry sharedEntry, AssetTableEntry entry, OdinGUITable table, + bool isDraggingControls) + { + UnityEngine.Object asset = null; + + Type entryAssetType = this.Collection.GetEntryAssetType(sharedEntry.Id); + + if (entry != null && !entry.IsEmpty) + { + asset = OdinLocalizationAssetCache.Get(entry.Guid, entryAssetType); + } + + Rect fullRect = rect; + + Rect rightMenuRect = rect.TakeFromRight(OdinLocalizationConstants.ROW_MENU_WIDTH); + rect.TakeFromLeft(OdinLocalizationConstants.ROW_MENU_WIDTH); + + rightMenuRect = rightMenuRect.Padding(0, 6); + + Rect openInspectorRect = rightMenuRect.AlignTop(14); + Rect openExplorerRect = rightMenuRect.AlignBottom(14); + + bool isMouseOverInspector = Event.current.IsMouseOver(openInspectorRect); + bool isMouseOverExplorer = Event.current.IsMouseOver(openExplorerRect); + + // TODO: some caveats to just adding this +#if true + var dragAndDropId = 0; + + if (!isDraggingControls && !(asset is DefaultAsset)) + { + EditorGUI.BeginChangeCheck(); + + var dropValue = DragAndDropUtilities.DragAndDropZone(fullRect, asset, entryAssetType, true, false, false) as UnityEngine.Object; + + dragAndDropId = DragAndDropUtilities.PrevDragAndDropId; + + if (EditorGUI.EndChangeCheck() && dropValue != asset) + { + if (!(dropValue is DefaultAsset)) + { + entry = this.AssignObjectToSharedEntry(sharedEntry, table.Asset, dropValue); + + asset = dropValue; + + entryAssetType = this.Collection.GetEntryAssetType(sharedEntry.Id); + } + else + { + this.RelatedWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.ExclamationTriangleFill, + "Default Assets (such as Folders) cannot be used for Asset Entries.", + SirenixGUIStyles.RedErrorColor, + 10.0f); + } + } + } +#endif + + bool hasThumbnail = EditorGUIUtility.HasObjectThumbnail(entryAssetType); + + if (asset != null) + { + if (Event.current.OnMouseDown(rect, 0, false) && Event.current.clickCount >= 2) + { + EditorGUIUtility.PingObject(asset); + } + } + + + if (entry == null || asset == null) + { + GUI.Label(rect, $"None ({entryAssetType.Name})", SirenixGUIStyles.LabelCentered); + } + else if (!hasThumbnail) + { + GUI.Label(rect.AlignMiddle(20), + GUIHelper.TempContent($"{asset.name} ({entryAssetType.Name})", AssetPreview.GetMiniThumbnail(asset)), + SirenixGUIStyles.LabelCentered); + + if (entryAssetType != typeof(UnityEngine.Object)) + { + var c = EditorGUIUtility.isProSkin + ? new Color(1, 1, 1, isMouseOverInspector ? 0.8f : 0.3f) + : new Color(0, 0, 0, isMouseOverInspector ? 0.8f : 0.3f); + + var c1 = EditorGUIUtility.isProSkin + ? new Color(1, 1, 1, isMouseOverExplorer ? 0.8f : 0.3f) + : new Color(0, 0, 0, isMouseOverExplorer ? 0.8f : 0.3f); + + SdfIcons.DrawIcon(openInspectorRect, SdfIconType.PencilFill, c); + SdfIcons.DrawIcon(openExplorerRect, SdfIconType.FolderFill, c1); + + GUI.Label(openInspectorRect, GUIHelper.TempContent(string.Empty, "Inspect Object")); + GUI.Label(openExplorerRect, GUIHelper.TempContent(string.Empty, "Show In Explorer")); + + if (Event.current.OnMouseDown(openInspectorRect, 0)) + { + GUIHelper.OpenInspectorWindow(asset); + } + + if (Event.current.OnMouseDown(openExplorerRect, 0)) + { + string assetAbsPath = Path.GetFullPath(AssetDatabase.GetAssetPath(asset)); + + if (Directory.Exists(assetAbsPath) || File.Exists(assetAbsPath)) + { + EditorUtility.RevealInFinder(assetAbsPath); + +#if SIRENIX_INTERNAL + this.RelatedWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.Tools, + $"(DEVELOPER) Opened asset path: {asset.name} ({assetAbsPath})", + Color.black, + 15.0f); +#endif + } + else + { + this.RelatedWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.ExclamationTriangleFill, + $"Failed to find asset: {asset.name}", + SirenixGUIStyles.RedErrorColor, + 5.0f); + } + + FancyColor.PopBlend(); + GUIHelper.ExitGUI(false); + } + } + } + else + { + Rect previewRect = rect.Padding(12); + + Texture preview; + + if (asset is Sprite || asset is Cubemap) + { + preview = AssetPreview.GetAssetPreview(asset); + } + else + { + preview = AssetPreview.GetMiniThumbnail(asset); + } + + if (preview == null) + { + preview = AssetDatabase.GetCachedIcon(AssetDatabase.GetAssetPath(asset)); + } + + float previewHeight = preview.height; + float aspect = (float) preview.width / preview.height; + + if (previewHeight < previewRect.height) + { + previewRect = previewRect.AlignMiddle(previewHeight < 32 ? 32 : previewHeight); + } + else + { + previewHeight = previewRect.height; + } + + float previewWidth = previewHeight * aspect; + + if (previewWidth > previewRect.width) + { + previewWidth = previewRect.width; + previewHeight = previewWidth / aspect; + } + + previewRect = previewRect.AlignCenter(previewWidth, previewHeight); + + previewRect.x = Mathf.Round(previewRect.x); + previewRect.y = Mathf.Round(previewRect.y); + previewRect.width = Mathf.Round(previewRect.width); + previewRect.height = Mathf.Round(previewRect.height); + + if (Event.current.type == EventType.Repaint) + { + if (this.IsEntrySelected(entry)) + { + GUIHelper.PushColor(OdinLocalizationGUI.Selected * new FancyColor(1.05f)); + } + + EditorStyles.objectFieldThumb.Draw(previewRect, GUIContent.none, false, false, false, false); + + if (this.IsEntrySelected(entry)) + { + GUIHelper.PopColor(); + } + } + + GUI.DrawTexture(previewRect, preview, ScaleMode.ScaleToFit); + + GUI.DrawTexture(previewRect, Texture2D.whiteTexture, ScaleMode.StretchToFill, true, 1.0f, new FancyColor(0.17f), 1, 0); + + var c = EditorGUIUtility.isProSkin + ? new Color(1, 1, 1, isMouseOverInspector ? 0.8f : 0.3f) + : new Color(0, 0, 0, isMouseOverInspector ? 0.8f : 0.3f); + + var c1 = EditorGUIUtility.isProSkin + ? new Color(1, 1, 1, isMouseOverExplorer ? 0.8f : 0.3f) + : new Color(0, 0, 0, isMouseOverExplorer ? 0.8f : 0.3f); + + SdfIcons.DrawIcon(openInspectorRect, SdfIconType.PencilFill, c); + SdfIcons.DrawIcon(openExplorerRect, SdfIconType.FolderFill, c1); + + GUI.Label(openInspectorRect, GUIHelper.TempContent(string.Empty, "Inspect Object")); + GUI.Label(openExplorerRect, GUIHelper.TempContent(string.Empty, "Show In Explorer")); + + if (Event.current.OnMouseDown(openInspectorRect, 0)) + { + GUIHelper.OpenInspectorWindow(asset); + } + + if (Event.current.OnMouseDown(openExplorerRect, 0)) + { + string assetAbsPath = Path.GetFullPath(AssetDatabase.GetAssetPath(asset)); + + if (Directory.Exists(assetAbsPath) || File.Exists(assetAbsPath)) + { + EditorUtility.RevealInFinder(assetAbsPath); + +#if SIRENIX_INTERNAL + this.RelatedWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.Tools, + $"(DEVELOPER) Opened asset path: {asset.name} ({assetAbsPath})", + Color.black, + 15.0f); +#endif + } + else + { + this.RelatedWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.ExclamationOctagonFill, + $"Failed to find asset: {asset.name} ({assetAbsPath})", + SirenixGUIStyles.RedErrorColor, + 5.0f); + } + + FancyColor.PopBlend(); + GUIHelper.ExitGUI(false); + } + } + + Rect pickerRect = rightMenuRect.AlignMiddle(14); + + if (OdinLocalizationGUI.ObjectPickerButton(pickerRect)) + { + if (entry == null) + { + entry = table.Asset.AddEntry(sharedEntry.Id, string.Empty); + } + + this.ShowObjectPickerForEntry(entry); + } + + GUI.Label(pickerRect, GUIHelper.TempContent(string.Empty, "Select Object")); + +#if true + if (!isDraggingControls && dragAndDropId != 0 && DragAndDropUtilities.IsDragging && DragAndDropUtilities.HoveringAcceptedDropZone == dragAndDropId) + { + GUI.DrawTexture(fullRect, Texture2D.whiteTexture, ScaleMode.StretchToFill, false, 1, new Color(0, 0.5f, 0.8f, 0.25f), 0, 2.5f); + } +#endif + } + + public override void RemoveKey(SharedTableData.SharedTableEntry sharedEntry) + { + bool hasAssets = this.DoesSharedEntryHaveAssets(sharedEntry); + + if (!hasAssets) + { + base.RemoveKey(sharedEntry); + return; + } + + this.SharedEntryHeights.Remove(sharedEntry.Id); + + for (var i = 0; i < this.GUITables.Count; i++) + { + OdinGUITable table = this.GUITables[i]; + + if (table.Type == OdinGUITable.GUITableType.Key) + { + continue; + } + + AssetTableEntry entry = table.Asset.GetEntry(sharedEntry.Id); + + if (entry == null || entry.IsEmpty) + { + continue; + } + + this.Collection.RemoveAssetFromTable(table.Asset, sharedEntry.Id); + } + + this.Collection.RemoveEntry(sharedEntry.Id); + + this.GUITables.SetDirty(this.Collection.SharedData); + } + + private bool DoesSharedEntryHaveAssets(SharedTableData.SharedTableEntry sharedEntry) + { + Type sharedType = this.Collection.GetEntryAssetType(sharedEntry.Id); + + for (var i = 0; i < this.GUITables.Count; i++) + { + OdinGUITable table = this.GUITables[i]; + + if (table.Type == OdinGUITable.GUITableType.Key) + { + continue; + } + + if (OdinLocalizationAssetCache.Get(sharedEntry, table.Asset, sharedType) != null) + { + return true; + } + } + + return false; + } + + private void ShowObjectPickerForEntry(AssetTableEntry entry) + { + this.pickedEntry = entry; + + this.pickedEntryOriginalGuid = entry.Guid; + + UnityEngine.Object obj = null; + + Type objType = this.UserSpecifiedCollectionType ?? this.Collection.GetEntryAssetType(entry.KeyId); + + UnityEngine.Object asset = OdinLocalizationAssetCache.Get(entry.Guid, objType); + + if (asset != null) + { + obj = asset; + } + + OdinObjectSelector.Show(this.RelatedWindow, OdinObjectSelectorIds.LOCALIZATION_EDITOR, obj, objType, false); + //SirenixObjectPickerUtilities.ShowObjectPicker(obj, objType, false, string.Empty, this.RelatedWindowId); + } + + private void HandleObjectPickerUpdates() + { + if (this.pickedEntry == null) + { + return; + } + +#if true + object selectedObj = OdinObjectSelector.SelectorObject; + + try + { + if (OdinObjectSelector.IsReadyToClaim(this.RelatedWindow, OdinObjectSelectorIds.LOCALIZATION_EDITOR)) + { + object claimedObject = OdinObjectSelector.Claim(); + + if (!(claimedObject is DefaultAsset)) + { + this.AssignObjectToSelectorEntry(claimedObject as UnityEngine.Object); + } + else + { + this.RelatedWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.ExclamationTriangleFill, + "Default Assets (such as Folders) cannot be used for Asset Entries.", + SirenixGUIStyles.RedErrorColor, + 10.0f); + } + + Event.current.Use(); + } + else + { + if (!OdinObjectSelector.IsOpen) + { + this.pickedEntry.Guid = this.pickedEntryOriginalGuid; + this.pickedEntry = null; + this.pickedEntryOriginalGuid = string.Empty; + } + + EditorGUI.BeginChangeCheck(); + var changedValue = OdinObjectSelector.GetChangedObject(null, this.RelatedWindow, OdinObjectSelectorIds.LOCALIZATION_EDITOR); + if (EditorGUI.EndChangeCheck()) + { + if (changedValue == null) + { + this.pickedEntry.Guid = string.Empty; + } + else + { + this.pickedEntry.Guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(changedValue)); + } + } + } + } + catch (NullReferenceException exception) + { + Debug.LogError($"{nameof(NullReferenceException)}: The asset '{selectedObj ?? ""}' is NULL.\n{exception}"); + } +#else + if (Event.current.type != EventType.ExecuteCommand || + EditorGUIUtility.GetObjectPickerControlID() != this.RelatedWindowId || + this.RelatedWindowId == 0) + { + return; + } + + UnityEngine.Object selectedObj = null; + + try + { + if (Event.current.commandName == "ObjectSelectorClosed") + { + Vector2 lastScrollPosition = this.EntryScrollView.Position; + + this.pickedEntry.Guid = this.pickedEntryOriginalGuid; + + selectedObj = EditorGUIUtility.GetObjectPickerObject(); + + var table = (AssetTable) this.pickedEntry.Table; + + SharedTableData.SharedTableEntry sharedEntry = this.pickedEntry.SharedEntry; + + if (selectedObj == null) + { + this.Collection.RemoveAssetFromTable((AssetTable) this.pickedEntry.Table, + this.pickedEntry.SharedEntry.Id, + OdinLocalizationConfig.Instance.recordUndosForAssetTableEntries); + + this.pickedEntry = table.GetEntry(sharedEntry.Id) ?? table.AddEntry(sharedEntry.Id, string.Empty); + } + else + { + this.Collection.AddAssetToTable((AssetTable) this.pickedEntry.Table, + this.pickedEntry.SharedEntry.Id, + selectedObj, + OdinLocalizationConfig.Instance.recordUndosForAssetTableEntries); + + this.pickedEntry = table.GetEntry(sharedEntry.Id); + } + + EditorUtility.SetDirty(this.Collection.SharedData); + EditorUtility.SetDirty(table); + + this.EntryScrollView.Position = lastScrollPosition; + + this.pickedEntry = null; + this.pickedEntryOriginalGuid = string.Empty; + Event.current.Use(); + return; + } + + if (Event.current.commandName != "ObjectSelectorUpdated") + { + return; + } + + selectedObj = EditorGUIUtility.GetObjectPickerObject(); + + this.pickedEntry.Guid = selectedObj == null ? string.Empty : AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(selectedObj)); + + Event.current.Use(); + } + catch (NullReferenceException exception) + { + // NOTE: edge cases for when the user selects assets that appear in the object picker, but in reality are null. + Debug.LogError($"{nameof(NullReferenceException)}: The asset '{(selectedObj == null ? "" : selectedObj.name)}' is NULL.\n{exception}"); + } +#endif + } + + // TODO: implement when drag'n'drop areas are done and keyboard commands +#if true + private AssetTableEntry AssignObjectToSharedEntry(SharedTableData.SharedTableEntry sharedEntry, AssetTable table, UnityEngine.Object obj) + { + Vector2 lastScrollPosition = this.EntryScrollView.Position; + + AssetTableEntry entry; + + if (obj == null) + { + this.CustomRemoveAssetFromTable(table, sharedEntry.Id, true); + + entry = table.GetEntry(sharedEntry.Id) ?? table.AddEntry(sharedEntry.Id, string.Empty); + } + else + { + this.Collection.AddAssetToTable(table, sharedEntry.Id, obj, true); + + entry = table.GetEntry(sharedEntry.Id); + } + + this.EntryScrollView.Position = lastScrollPosition; + + return entry; + } +#endif + + private void AssignObjectToSelectorEntry(UnityEngine.Object obj) + { + object selectedObj = OdinObjectSelector.SelectorObject; + + try + { + Vector2 lastScrollPosition = this.EntryScrollView.Position; + + this.pickedEntry.Guid = this.pickedEntryOriginalGuid; + + var table = (AssetTable) this.pickedEntry.Table; + + SharedTableData.SharedTableEntry sharedEntry = this.pickedEntry.SharedEntry; + + if (obj == null) + { + this.CustomRemoveAssetFromTable((AssetTable) this.pickedEntry.Table, this.pickedEntry.SharedEntry.Id, true); + + this.pickedEntry = table.GetEntry(sharedEntry.Id) ?? table.AddEntry(sharedEntry.Id, string.Empty); + } + else + { + this.Collection.AddAssetToTable((AssetTable) this.pickedEntry.Table, this.pickedEntry.SharedEntry.Id, obj, true); + + this.pickedEntry = table.GetEntry(sharedEntry.Id); + } + + EditorUtility.SetDirty(this.Collection.SharedData); + EditorUtility.SetDirty(table); + + this.EntryScrollView.Position = lastScrollPosition; + + this.pickedEntry = null; + this.pickedEntryOriginalGuid = string.Empty; + } + catch (NullReferenceException exception) + { + Debug.LogError($"{nameof(NullReferenceException)}: The asset '{selectedObj ?? ""}' is NULL.\n{exception}"); + } + } + + public void CustomRemoveAssetFromTable(AssetTable table, TableEntryReference entryReference, bool createUndo = false) + { + if (!OdinLocalizationReflectionValues.HasAPIForCustomUndo || !OdinLocalizationConfig.Instance.useCustomUndoHandlingForAssetCollections) + { + this.Collection.RemoveAssetFromTable(table, entryReference, createUndo); + + return; + } + + var groupIndex = 0; + + if (createUndo) + { + groupIndex = Undo.GetCurrentGroup(); + Undo.IncrementCurrentGroup(); + Undo.SetCurrentGroupName("Remove asset from table"); + } + + if (createUndo) + { + Undo.RecordObjects(new UnityEngine.Object[] {table, table.SharedData}, "Remove asset from table"); + } + + AssetTableEntry tableEntry = table.GetEntryFromReference(entryReference); + + if (tableEntry == null) + { + return; + } + + string removedAssetGuid = tableEntry.Guid; + + tableEntry.Guid = string.Empty; + + AddressableAssetSettings addressableAssetSettings = OdinLocalizationEditorSettings.GetAddressableAssetSettings(false); + + if (addressableAssetSettings == null) + { + return; + } + + EditorUtility.SetDirty(table); + EditorUtility.SetDirty(table.SharedData); + + this.SetEntryAssetType(tableEntry, null); + + if (tableEntry.MetadataEntries.Count == 0) + { + table.RemoveEntry(tableEntry.KeyId); + } + + ReadOnlyCollection assetTableCollections = LocalizationEditorSettings.GetAssetTableCollections(); + + foreach (AssetTableCollection collection in assetTableCollections) + { + if (collection.GetTable(table.LocaleIdentifier) is AssetTable tableWithMatchingLocaleId && + tableWithMatchingLocaleId.ContainsValue(removedAssetGuid)) + { + return; + } + } + + AddressableAssetEntry assetEntry = addressableAssetSettings.FindAssetEntry(removedAssetGuid); + + if (assetEntry != null) + { + if (createUndo) + { + Undo.RecordObject(assetEntry.parentGroup, "Remove asset from table"); + } + + var assetLabel = (string) OdinLocalizationReflectionValues.FormatAssetLabelMethod.Invoke(null, new object[] {table.LocaleIdentifier}); + + assetEntry.SetLabel(assetLabel, false); + + OdinLocalizationReflectionValues.UpdateAssetGroupMethod.Invoke(this.Collection, new object[] {addressableAssetSettings, assetEntry, createUndo}); + } + + OdinLocalizationEvents.RaiseAssetTableEntryRemoved(this.Collection, table, tableEntry, removedAssetGuid); + + if (createUndo) + { + Undo.CollapseUndoOperations(groupIndex); + } + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinAssetTableCollectionEditor.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinAssetTableCollectionEditor.cs.meta new file mode 100644 index 00000000..7568b716 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinAssetTableCollectionEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bc0d9dcd4d4f4e3094085c752e185ef8 +timeCreated: 1704811181 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITable.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITable.cs new file mode 100644 index 00000000..0552ebf8 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITable.cs @@ -0,0 +1,196 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#define USING_WIDTH_NON_PERCENT +//#undef USING_WIDTH_NON_PERCENT + +using System; +using Sirenix.OdinInspector.Editor.Internal; +using Sirenix.Utilities.Editor; +using UnityEditor; +using UnityEngine; +using UnityEngine.Localization; +using UnityEngine.Localization.Tables; +using UnityEngine.Serialization; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ + public class OdinGUITable : IComparable> where TTable : LocalizationTable + { + public const string KEY_DISPLAY_NAME = "Key"; + + public enum GUITableType + { + Default, + Key + } + + public float Width + { + get => this._width; + set + { + if (value < OdinLocalizationConstants.MIN_COLUMN_WIDTH) + { + this._width = OdinLocalizationConstants.MIN_COLUMN_WIDTH; + return; + } + + this._width = Mathf.Round(value); + } + } + + public bool IsDraggingSlider = false; + public bool IsVisible; + public string DisplayName; + public GUITableType Type; + + [FormerlySerializedAs("Table")] + public TTable Asset; + public bool IsPinned; + + private float _width; + +#if USING_WIDTH_NON_PERCENT + //public float VisualWidth; +#else + internal float WidthPercentage; +#endif + +#if USING_WIDTH_NON_PERCENT + public static OdinGUITable CreateKeyTable() +#else + public static OdinGUITable CreateKeyTable(float widthPercent) +#endif + { + return new OdinGUITable + { + IsVisible = true, + DisplayName = KEY_DISPLAY_NAME, + Type = GUITableType.Key, + Asset = null, + IsPinned = false, + Width = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH, +#if USING_WIDTH_NON_PERCENT + // VisualWidth = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH +#else + WidthPercentage = widthPercent +#endif + }; + } + +#if USING_WIDTH_NON_PERCENT + public static OdinGUITable CreateTable(TTable table, Locale locale) +#else + public static OdinGUITable CreateTable(TTable table, float widthPercent) +#endif + { + return new OdinGUITable + { + IsVisible = true, + DisplayName = locale.LocaleName, + Type = GUITableType.Default, + Asset = table, + IsPinned = false, + Width = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH, +#if USING_WIDTH_NON_PERCENT + // VisualWidth = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH +#else + WidthPercentage = widthPercent +#endif + }; + } + + public int CompareTo(OdinGUITable other) + { + if (other == null) + { + return -1; + } + + if (this.IsVisible && !other.IsVisible) + { + return -1; + } + + if (!this.IsVisible && other.IsVisible) + { + return 1; + } + + if (this.IsPinned && !other.IsPinned) + { + return -1; + } + + if (!this.IsPinned && other.IsPinned) + { + return 1; + } + + if (this.DisplayName == KEY_DISPLAY_NAME && other.DisplayName == KEY_DISPLAY_NAME) + { + return 0; + } + + if (other.DisplayName == KEY_DISPLAY_NAME) + { + return 1; + } + + if (this.DisplayName == KEY_DISPLAY_NAME) + { + return -1; + } + + return this.Asset.LocaleIdentifier.CompareTo(other.Asset.LocaleIdentifier); + } + + public Vector2 HandleSlider(Rect position) + { + if (GUIUtility.hotControl == 0) + { + this.IsDraggingSlider = false; + } + + if (!GUI.enabled) + { + return Vector2.zero; + } + + EditorGUIUtility.AddCursorRect(position, MouseCursor.ResizeHorizontal); + + if (GUI.enabled && Event.current.OnMouseDown(position, 0)) + { + this.IsDraggingSlider = true; + SharedUniqueControlId.SetActive(); + EditorGUIUtility.SetWantsMouseJumping(1); + Event.current.Use(); + } + else if (SharedUniqueControlId.IsActive && this.IsDraggingSlider) + { + if (Event.current.type == EventType.MouseDrag) + { + Event.current.Use(); + GUI.changed = true; + return Event.current.delta; + } + + if (Event.current.type != EventType.MouseUp) + { + return Vector2.zero; + } + + this.IsDraggingSlider = false; + SharedUniqueControlId.SetInactive(); + EditorGUIUtility.SetWantsMouseJumping(0); + Event.current.Use(); + } + + return Vector2.zero; + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITable.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITable.cs.meta new file mode 100644 index 00000000..6ad3d192 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c6172b1a79444464bfac84d14344febf +timeCreated: 1706411334 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITableCollection.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITableCollection.cs new file mode 100644 index 00000000..abc61f52 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITableCollection.cs @@ -0,0 +1,483 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#define USING_WIDTH_NON_PERCENT +//#undef USING_WIDTH_NON_PERCENT + +using System; +using System.Collections.Generic; +using Sirenix.OdinInspector.Editor.Internal; +using UnityEditor; +using UnityEngine.Localization.Tables; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ + public class OdinGUITableCollection : List> where TTable : LocalizationTable + { + public readonly struct VisibleTables + { + public readonly int Offset; + public readonly int Length; + public readonly int PinnedLength; + public readonly float StartOffset; + + private readonly OdinGUITableCollection collection; + + public VisibleTables(OdinGUITableCollection collection, int offset, int length, int pinnedLength, float startOffset) + { + this.collection = collection; + this.Offset = offset; + this.Length = length + pinnedLength; + this.PinnedLength = pinnedLength; + this.StartOffset = startOffset; + } + + public OdinGUITable this[int index] => + index >= this.PinnedLength ? this.collection[this.Offset + index - this.PinnedLength] : this.collection[index]; + } + + //public VisibleTables CurrentVisibleTables; + + public HashSet> TablesWithinVisibleBounds = new HashSet>(); + + public OdinGUITableCollection(int capacity) : base(capacity) { } + +#if USING_WIDTH_NON_PERCENT + public void AddKeyTable() + { + for (var i = 0; i < this.Count; i++) + { + if (this[i].Type == OdinGUITable.GUITableType.Key) + { + return; + } + } + + this.Add(OdinGUITable.CreateKeyTable()); + } +#else + public void AddKeyTable(float widthPercent) + { + for (var i = 0; i < this.Count; i++) + { + if (this[i].Type == OdinGUITable.GUITableType.Key) + { + return; + } + } + + this.Add(OdinGUITable.CreateKeyTable(widthPercent)); + } +#endif + +#if USING_WIDTH_NON_PERCENT + public void UpdateVisibleTables(OdinGUIScrollView view, float pinnedWidth) + { +#if true + this.TablesWithinVisibleBounds.Clear(); + + var offset = 0; + var currentVisibleWidth = 0.0f; + var xMin = 0.0f; + var xMax = 0.0f; + + for (var i = 0; i < this.Count; i++) + { + OdinGUITable table = this[i]; + + if (!table.IsVisible) + { + continue; + } + + if (table.IsPinned) + { + this.TablesWithinVisibleBounds.Add(table); + continue; + } + + xMax += table.Width; + + bool isVisible = view.Position.x >= xMin && view.Position.x <= xMax; + + if (!isVisible) + { + xMin = xMax; + continue; + } + + offset = i; + + currentVisibleWidth = xMax - view.Position.x; + this.TablesWithinVisibleBounds.Add(table); + break; + } + + float width = view.Bounds.width - pinnedWidth; + + for (int i = offset + 1; i < this.Count; i++) + { + OdinGUITable table = this[i]; + + if (!table.IsVisible) + { + break; + } + + if (currentVisibleWidth >= width) + { + this.TablesWithinVisibleBounds.Add(table); + break; + } + + this.TablesWithinVisibleBounds.Add(table); + currentVisibleWidth += this[i].Width; + } +#else + if (this.GetVisibleCount() < 1) + { + this.CurrentVisibleTables = new VisibleTables(this, 0, 0, 0, 0.0f); + return; + } + + const int LENGTH_NOT_SET = -1; + + var offset = 0; + int length = LENGTH_NOT_SET; + + var currentVisibleWidth = 0.0f; + + float xMin = 0.0f, xMax = 0.0f; + + float positionX = view.Position.x; + float width = view.Bounds.width - pinnedWidth; + + for (var i = 0; i < this.Count; i++) + { + OdinGUITable table = this[i]; + + if (table.IsPinned) + { + continue; + } + + if (!table.IsVisible) + { + break; + } + + xMax += table.VisualWidth; + + bool isVisible = positionX >= xMin && positionX <= xMax; + + if (!isVisible) + { + xMin = xMax; + continue; + } + + offset = i; + + currentVisibleWidth = xMax - positionX; + break; + } + + float startOffset = xMin; + + for (int i = offset + 1; i < this.Count; i++) + { + if (!this[i].IsVisible) + { + length = i - offset; + break; + } + + if (currentVisibleWidth >= width) + { + length = i - offset + 1; + break; + } + + currentVisibleWidth += this[i].VisualWidth; + } + + int pinnedCount = this.GetPinnedCount(); + + if (length == LENGTH_NOT_SET) + { + length = this.Count - offset; + } + + this.CurrentVisibleTables = new VisibleTables(this, offset, length, pinnedCount, startOffset); +#endif + } + + public float GetVisibleWidth() + { + var result = 0.0f; + + for (var i = 0; i < this.Count; i++) + { + if (this[i].IsVisible) + { + result += this[i].Width; + } + } + + return result; + } + + public void ResizeBy(float factor) + { + for (var i = 0; i < this.Count; i++) + { + if (this[i].IsVisible) + { + this[i].Width *= factor; + } + } + } +#else + /// Calculates the field on beforehand. + /// The to perform calculations on. + public void CalcWidths(OdinGUIScrollView scrollView) + { + float viewWidth = scrollView.ViewRect.width; + + float visibleFactor = this.GetVisibleFactor(); + + for (var i = 0; i < this.Count; i++) + { + this[i].Width = viewWidth * (this[i].WidthPercentage * visibleFactor); + } + } +#endif + + public int GetLastVisibleIndex() + { + for (int i = this.Count - 1; i >= 0; i--) + { + if (this[i].IsVisible) + { + return i; + } + } + + return 0; + } + + public int GetLastVisiblePinnedIndex() + { + for (int i = this.Count - 1; i >= 0; i--) + { + if (this[i].IsVisible && this[i].IsPinned) + { + return i; + } + } + + return 0; + } + + public int GetVisibleCount() + { + var result = 0; + + for (var i = 0; i < this.Count; i++) + { + if (this[i].IsVisible) + { + result++; + } + } + + return result; + } + + public int GetHiddenCount() + { + var result = 0; + + for (var i = 0; i < this.Count; i++) + { + if (!this[i].IsVisible) + { + result++; + } + } + + return result; + } + + public int GetPinnedCount() + { + var result = 0; + + for (var i = 0; i < this.Count; i++) + { + if (this[i].IsVisible && this[i].IsPinned) + { + result++; + } + } + + return result; + } + + public OdinGUITable GetNextVisible(int index) + { + for (int i = index + 1; i < this.Count; i++) + { + if (this[i].IsVisible) + { + return this[i]; + } + } + + return null; + } + + public void UndoRecordCollection(SharedTableData sharedTableData, string name) + { + for (var i = 0; i < this.Count; i++) + { + switch (this[i].Type) + { + case OdinGUITable.GUITableType.Default: + Undo.RecordObject(this[i].Asset, name); + break; + + case OdinGUITable.GUITableType.Key: + if (sharedTableData != null) + { + Undo.RecordObject(sharedTableData, name); + } + + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + } + + public void SetDirty(SharedTableData sharedTableData) + { + for (var i = 0; i < this.Count; i++) + { + switch (this[i].Type) + { + case OdinGUITable.GUITableType.Default: + EditorUtility.SetDirty(this[i].Asset); + break; + + case OdinGUITable.GUITableType.Key: + if (sharedTableData != null) + { + EditorUtility.SetDirty(sharedTableData); + } + + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + } + +#if !USING_WIDTH_NON_PERCENT + private float GetVisibleFactor() + { + var visibleCount = 0; + var sum = 0.0f; + + for (var i = 0; i < this.Count; i++) + { + if (!this[i].IsVisible) + { + continue; + } + + sum += this[i].WidthPercentage; + visibleCount++; + } + + float result = (float) this.Count / visibleCount; + + sum *= result; + + result *= 1.0f / sum; + + return result; + } +#endif + public void ResizeToFit(float targetWidth) + { + var count = 0; + + for (var i = 0; i < this.Count; i++) + { + if (this[i].IsPinned || !this[i].IsVisible) + { + continue; + } + + count++; + } + + if (count < 1) + { + return; + } + + int averageSize = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH * count; + + float scaleFactor = targetWidth / averageSize; + + for (var i = 0; i < this.Count; i++) + { + if (!this[i].IsVisible || this[i].IsPinned) + { + continue; + } + + this[i].Width = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH * scaleFactor; + } + } + + public void ResizePinnedToFit(float targetWidth) + { + var currentTotalWidth = 0.0f; + + for (var i = 0; i < this.Count; i++) + { + if (!this[i].IsPinned || !this[i].IsVisible) + { + continue; + } + + currentTotalWidth += this[i].Width; + } + + if (currentTotalWidth < 1.0f) + { + return; + } + + float scaleFactor = targetWidth / currentTotalWidth; + + for (var i = 0; i < this.Count; i++) + { + if (!this[i].IsVisible || !this[i].IsPinned) + { + continue; + } + + this[i].Width *= scaleFactor; + } + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITableCollection.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITableCollection.cs.meta new file mode 100644 index 00000000..d34ca872 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinGUITableCollection.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3a739bb40d31496a9591094d81fd2d19 +timeCreated: 1706411492 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinSharedEntryCollection.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinSharedEntryCollection.cs new file mode 100644 index 00000000..705c1a1f --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinSharedEntryCollection.cs @@ -0,0 +1,441 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using Sirenix.OdinInspector.Modules.Localization.Editor.Internal; +using Sirenix.Utilities.Editor; +using UnityEditor; +using UnityEditor.Localization; +using UnityEngine.Localization.Tables; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ + public class OdinSharedEntryCollection + { + private class StringComparer : IComparer + { + public bool IsAscending = true; + + public int Compare(string self, string other) + { + if (string.IsNullOrEmpty(self) && string.IsNullOrEmpty(other)) + { + return 0; + } + + if (string.IsNullOrEmpty(self)) + { + return this.IsAscending ? 1 : -1; + } + + if (string.IsNullOrEmpty(other)) + { + return this.IsAscending ? -1 : 1; + } + + return string.Compare(self, other, StringComparison.InvariantCulture); + } + } + + public enum SortOrderState + { + Unsorted, + Ascending, + Descending + } + + public bool IsSearching; + + public bool IsSorted => this.CurrentSortOrderState != SortOrderState.Unsorted; + + public int Length => this.Entries.Count; + + private string _searchTerm = string.Empty; + + private StringComparer stringComparer = new StringComparer(); + + public string SearchTerm + { + get => this._searchTerm; + private set + { + this._searchTerm = value; + + this.IsSearching = !string.IsNullOrEmpty(value); + } + } + + public List Entries => this.IsSorted ? this.SortedEntries : this.Collection.SharedData.Entries; + + public List SortedEntries; + public readonly HashSet FilteredEntries; + + public readonly LocalizationTableCollection Collection; + + public SortOrderState CurrentSortOrderState = SortOrderState.Unsorted; + + public OdinSharedEntryCollection(LocalizationTableCollection collection) + { + this.Collection = collection; + + this.FilteredEntries = new HashSet(); + } + + public SharedTableData.SharedTableEntry this[int index] => this.Entries[index]; + + public bool IsVisible(SharedTableData.SharedTableEntry sharedEntry) + { + return !this.IsSearching || (this.IsSearching && this.FilteredEntries.Contains(sharedEntry)); + } + + public bool UpdateSearchTerm(string value, + OdinGUITableCollection tables, + LocalizationTableCollection collection, + bool forceUpdate = false) where TTable : LocalizationTable + { + if (this.SearchTerm == value && !forceUpdate) + { + return false; + } + + this.SearchTerm = value; + + if (string.IsNullOrEmpty(this.SearchTerm)) + { + return true; + } + + this.FilteredEntries.Clear(); + + for (var i = 0; i < tables.Count; i++) + { + OdinGUITable table = tables[i]; + + switch (table.Type) + { + case OdinGUITable.GUITableType.Default: + switch (table.Asset) + { + case AssetTable assetTable: + var assetCollection = collection as AssetTableCollection; + + for (var j = 0; j < this.Length; j++) + { + SharedTableData.SharedTableEntry sharedEntry = this[j]; + + Type assetType = assetCollection.GetEntryAssetType(sharedEntry.Id); + + UnityEngine.Object asset = OdinLocalizationAssetCache.Get(sharedEntry, assetTable, assetType); + + if (asset == null) + { + continue; + } + + if (FuzzySearch.Contains(this.SearchTerm, asset.name)) + { + this.FilteredEntries.Add(sharedEntry); + } + } + + break; + + case StringTable stringTable: + for (var j = 0; j < this.Length; j++) + { + SharedTableData.SharedTableEntry sharedEntry = this[j]; + + StringTableEntry entry = stringTable.GetEntry(sharedEntry.Id); + + if (entry is null || string.IsNullOrEmpty(entry.Value)) + { + continue; + } + + if (FuzzySearch.Contains(this.SearchTerm, entry.Value)) + { + this.FilteredEntries.Add(sharedEntry); + } + } + + break; + } + + break; + + case OdinGUITable.GUITableType.Key: + for (var j = 0; j < this.Entries.Count; j++) + { + if (FuzzySearch.Contains(this.SearchTerm, this.Entries[j].Key)) + { + this.FilteredEntries.Add(this.Entries[j]); + } + } + + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + return true; + } + + public void SortByKeys(bool preserveCurrentOrder) + { + switch (this.CurrentSortOrderState) + { + case SortOrderState.Ascending: + this.stringComparer.IsAscending = true; + + if (preserveCurrentOrder) + { + List result = this.Collection.SharedData.Entries.OrderBy(entry => entry.Key, this.stringComparer) + .ThenBy(this.GetOrderIndex) + .ToList(); + + this.SortedEntries = result; + } + else + { + this.SortedEntries = this.Collection.SharedData.Entries.OrderBy(entry => entry.Key, this.stringComparer).ToList(); + } + + return; + + case SortOrderState.Descending: + this.stringComparer.IsAscending = false; + + if (preserveCurrentOrder) + { + List result = this.Collection.SharedData.Entries.OrderByDescending(entry => entry.Key, this.stringComparer) + .ThenBy(this.GetOrderIndex) + .ToList(); + + this.SortedEntries = result; + } + else + { + this.SortedEntries = this.Collection.SharedData.Entries.OrderByDescending(entry => entry.Key, this.stringComparer).ToList(); + } + + return; + } + } + + public void SortByAssetTable(AssetTableCollection collection, AssetTable table, bool preserveCurrentOrder) + { + switch (this.CurrentSortOrderState) + { + case SortOrderState.Ascending: + this.stringComparer.IsAscending = true; + + if (preserveCurrentOrder) + { + List result = this.Collection.SharedData.Entries + .OrderBy(entry => GetAssetNameFromEntry(entry, table, collection), this.stringComparer) + .ThenBy(this.GetOrderIndex) + .ToList(); + + this.SortedEntries = result; + } + else + { + this.SortedEntries = this.Collection.SharedData.Entries.OrderBy(entry => GetAssetNameFromEntry(entry, table, collection), this.stringComparer) + .ToList(); + } + + return; + + case SortOrderState.Descending: + this.stringComparer.IsAscending = false; + + if (preserveCurrentOrder) + { + List result = this.Collection.SharedData.Entries + .OrderByDescending(entry => GetAssetNameFromEntry(entry, table, collection), + this.stringComparer) + .ThenBy(this.GetOrderIndex) + .ToList(); + + this.SortedEntries = result; + } + else + { + this.SortedEntries = this.Entries.OrderByDescending(entry => GetAssetNameFromEntry(entry, table, collection), this.stringComparer).ToList(); + } + + return; + } + } + + public void SortByStringTable(StringTable table, bool preserveCurrentOrder) + { + switch (this.CurrentSortOrderState) + { + case SortOrderState.Ascending: + this.stringComparer.IsAscending = true; + + if (preserveCurrentOrder) + { + List result = this.Collection.SharedData.Entries + .OrderBy(entry => GetStringFromEntry(entry, table), this.stringComparer) + .ThenBy(this.GetOrderIndex) + .ToList(); + + this.SortedEntries = result; + } + else + { + this.SortedEntries = this.Collection.SharedData.Entries.OrderBy(entry => GetStringFromEntry(entry, table), this.stringComparer).ToList(); + } + + return; + + case SortOrderState.Descending: + this.stringComparer.IsAscending = false; + + if (preserveCurrentOrder) + { + List result = this.Collection.SharedData.Entries + .OrderByDescending(entry => GetStringFromEntry(entry, table), this.stringComparer) + .ThenBy(this.GetOrderIndex) + .ToList(); + + this.SortedEntries = result; + } + else + { + this.SortedEntries = this.Entries.OrderByDescending(entry => GetStringFromEntry(entry, table), this.stringComparer).ToList(); + } + + return; + } + } + + private static string GetStringFromEntry(SharedTableData.SharedTableEntry sharedEntry, StringTable table) + { + StringTableEntry entry = table.GetEntry(sharedEntry.Id); + + return entry?.Value; + } + + private static string GetAssetNameFromEntry(SharedTableData.SharedTableEntry sharedEntry, AssetTable table, AssetTableCollection collection) + { + AssetTableEntry entry = table.GetEntry(sharedEntry.Id); + + if (entry == null || entry.IsEmpty) + { + return null; + } + + Type type = collection.GetEntryAssetType(sharedEntry.Id); + + UnityEngine.Object asset = OdinLocalizationAssetCache.Get(entry.Guid, type); + + return asset == null ? null : asset.name; + } + + public void MoveEntry(int from, int to) + { + if (from < 0 || from >= this.Entries.Count) + { + return; + } + + if (to < 0 || to > this.Entries.Count) + { + return; + } + + if (from == to) + { + return; + } + + SharedTableData.SharedTableEntry fromEntry = this.Collection.SharedData.Entries[from]; + + if (to > from) + { + to -= 1; + } + + //to = afterTo ? to + 1 : to; + + this.Collection.SharedData.Entries.RemoveAt(from); + + this.Collection.SharedData.Entries.Insert(to, fromEntry); + + OdinLocalizationEvents.RaiseTableEntryModified(this.Collection.SharedData.Entries[from]); + OdinLocalizationEvents.RaiseTableEntryModified(this.Collection.SharedData.Entries[to]); + + EditorUtility.SetDirty(this.Collection.SharedData); + } + + public void GotoNextSortOrderState() + { + switch (this.CurrentSortOrderState) + { + case SortOrderState.Unsorted: + this.CurrentSortOrderState = SortOrderState.Ascending; + break; + + case SortOrderState.Ascending: + this.CurrentSortOrderState = SortOrderState.Descending; + break; + + case SortOrderState.Descending: + this.CurrentSortOrderState = SortOrderState.Unsorted; + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + public int GetIndex(SharedTableData.SharedTableEntry sharedEntry) + { + for (var i = 0; i < this.Length; i++) + { + if (this[i].Id == sharedEntry.Id) + { + return i; + } + } + + return -1; + } + + public int GetOrderIndex(SharedTableData.SharedTableEntry sharedEntry) + { + if (this.IsSorted && this.SortedEntries.Count == this.Length) + { + for (var i = 0; i < this.SortedEntries.Count; i++) + { + if (this.SortedEntries[i].Id == sharedEntry.Id) + { + return i; + } + } + + return -1; + } + + for (var i = 0; i < this.Length; i++) + { + if (this[i].Id == sharedEntry.Id) + { + return i; + } + } + + return -1; + } + } +} diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinSharedEntryCollection.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinSharedEntryCollection.cs.meta new file mode 100644 index 00000000..2bd2fe99 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinSharedEntryCollection.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a543e4ee8d6d4b629befbccf5aa7b5e6 +timeCreated: 1706407967 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinStringTableCollectionEditor.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinStringTableCollectionEditor.cs new file mode 100644 index 00000000..80d9440d --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinStringTableCollectionEditor.cs @@ -0,0 +1,449 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#define USING_WIDTH_NON_PERCENT + +using System; +using Sirenix.OdinInspector.Editor; +using Sirenix.OdinInspector.Editor.Internal; +using Sirenix.OdinInspector.Modules.Localization.Editor.Configs; +using Sirenix.OdinInspector.Modules.Localization.Editor.Internal; +using Sirenix.Utilities; +using Sirenix.Utilities.Editor; +using UnityEditor; +using UnityEditor.Localization; +using UnityEngine; +using UnityEngine.Localization.Tables; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ + public class OdinStringTableCollectionEditor : OdinTableCollectionEditor + { + private string currentSyntaxSource; + private string currentSyntaxHighlightedText; + private string currentSyntaxErrorMessage; + private Exception currentSyntaxException; + private bool currentSyntaxHasErrors; + + public OdinStringTableCollectionEditor(StringTableCollection collection, OdinMenuEditorWindow relatedWindow, + OdinLocalizationEditorWindow.WindowState windowState) : + base(collection, relatedWindow, windowState) { } + + protected override void OnInitialize() + { + for (var i = 0; i < this.SharedEntries.Length; i++) + { + SharedTableData.SharedTableEntry sharedEntry = this.SharedEntries[i]; + + this.MeasureEntry(sharedEntry); + } + + //this.SharedEntries.OnSharedEntryAdded += (i, sharedEntry) => { this.MeasureEntry(sharedEntry); }; + + //this.SharedEntries.OnSharedEntryRemoved += (i, sharedEntry) => { this.SharedEntryHeights.Remove(sharedEntry.Id); }; + + this.OnTableEntryModified = sharedEntry => + { + if (!this.Collection.SharedData.Contains(sharedEntry.Id)) + { + return; + } + + int index = this.SharedEntries.GetIndex(sharedEntry); + + this.MeasureEntry(sharedEntry); + + this.EntryScrollView.ReallocateRect(index, this.SharedEntryHeights[sharedEntry.Id], sharedEntry); + }; + } + + protected override void AllocateItems() + { + for (var i = 0; i < this.SharedEntries.Length; i++) + { + SharedTableData.SharedTableEntry sharedEntry = this.SharedEntries[i]; + + if (!this.SharedEntries.IsVisible(sharedEntry)) + { + continue; + } + + if (!this.SharedEntryHeights.ContainsKey(sharedEntry.Id)) + { + this.MeasureEntry(sharedEntry); + } + + this.EntryScrollView.AllocateRect(this.SharedEntryHeights[sharedEntry.Id], sharedEntry); + +#if false + this.ControlIds[sharedEntry] = GUIUtility.GetControlID(FocusType.Keyboard); + + for (var j = 0; j < this.GUITables.Count; j++) + { + OdinGUITable table = this.GUITables[j]; + + if (table.Type == OdinGUITable.GUITableType.Key) + { + continue; + } + + StringTableEntry entry = table.Table.GetEntry(sharedEntry.Id); + + if (entry is null) + { + table.Table.AddEntry(sharedEntry.Id, string.Empty); + + entry = table.Table.GetEntry(sharedEntry.Id); + } + + this.ControlIds[entry] = GUIUtility.GetControlID(FocusType.Keyboard); + } +#endif + } + } + + protected override void DrawItems(ref OdinGUIScrollView.VisibleItems visibleItems) + { + this.MeasureVisibleEntries(ref visibleItems); + + float scrollSpeed = OdinLocalizationConfig.Instance.scrollSpeed; + + this.EntryScrollView.BeginScrollView(offset: new Vector2(this.PinnedWidth, OdinLocalizationConstants.COLUMN_HEIGHT), + addViewSize: new Vector2(-this.PinnedWidth, 0), + scrollSpeed: scrollSpeed); + { + this.DrawEntries(ref visibleItems, false); + } + this.EntryScrollView.EndScrollView(); + + this.EntryScrollView.BeginClip(offset: new Vector2(0.0f, OdinLocalizationConstants.COLUMN_HEIGHT), ignoreScrollX: true); + { + this.DrawEntries(ref visibleItems, true); + } + this.EntryScrollView.EndClip(); + } + + private void DrawEntries(ref OdinGUIScrollView.VisibleItems visibleItems, bool pinned) + { + for (var i = 0; i < visibleItems.Length; i++) + { + if (!visibleItems.HasAssociatedData(i)) + { + continue; + } + + int hint = visibleItems.Offset + i + this.ControlIdHint; + + Rect position = visibleItems.GetRect(i); + + var sharedEntry = visibleItems.GetAssociatedData(i); + + bool isEven = (visibleItems.Offset + i) % 2 == 0; + + for (var j = 0; j < this.GUITables.Count; j++) + { + OdinGUITable table = this.GUITables[j]; + + if (!table.IsVisible) + { + continue; + } + + if (table.IsPinned != pinned) + { + continue; + } + + if (!this.GUITables.TablesWithinVisibleBounds.Contains(table)) + { + GUIUtility.GetControlID(hint, FocusType.Keyboard); + position.TakeFromLeft(table.Width).Padding(OdinLocalizationConstants.ENTRY_PADDING); + continue; + } + + +#if USING_WIDTH_NON_PERCENT + Rect entryRect = position.TakeFromLeft(table.Width).Padding(OdinLocalizationConstants.ENTRY_PADDING); +#else + Rect entryRect = position.TakeFromLeft(table.Width).Padding(OdinLocalizationConstants.ENTRY_PADDING); +#endif + + bool isCellPressed, isSelected; + + switch (table.Type) + { + case OdinGUITable.GUITableType.Key: + isSelected = this.IsSharedEntrySelected(sharedEntry); + + if (isSelected) + { + this.SelectionAnimFloat.Move(1 / 0.18f, Easing.InSine); + + FancyColor start = FancyColor.Gray; + + var end = OdinLocalizationGUI.Selected; + + FancyColor.PushBlend(start.Lerp(end, this.SelectionAnimFloat), FancyColor.BlendMode.Overlay); + } + + isCellPressed = DrawCell(entryRect, isEven); + + this.DrawKey(entryRect, sharedEntry, GUIUtility.GetControlID(hint, FocusType.Keyboard)); + + if (isSelected) + { + FancyColor.PopBlend(); + } + + if (isCellPressed) + { + this.SelectSharedEntry(sharedEntry); + } + + break; + + case OdinGUITable.GUITableType.Default: + StringTableEntry entry = table.Asset.GetEntry(sharedEntry.Id); + + isSelected = this.IsEntrySelected(entry); + + if (isSelected) + { + this.SelectionAnimFloat.Move(1 / 0.18f, Easing.InSine); + + FancyColor start = FancyColor.Gray; + + FancyColor end = OdinLocalizationGUI.Selected; + + if (entry.IsSmart && OdinLocalizationConfig.Instance.useSyntaxHighlighter) + { + if (this.currentSyntaxSource != entry.Value) + { + this.currentSyntaxHighlightedText = OdinLocalizationSyntaxHighlighter.HighlightAsRichText(entry.Value); + this.currentSyntaxErrorMessage = OdinLocalizationSyntaxHighlighter.GetErrorMessage(entry.Value, out bool foundError, out Exception exception); + this.currentSyntaxHasErrors = foundError; + this.currentSyntaxException = exception; + this.currentSyntaxSource = entry.Value; + } + + if (this.currentSyntaxHasErrors) + { + FancyColor.PushBlend(start.Lerp(new FancyColor(0.68f, 0.2f, 0.2f), this.SelectionAnimFloat), FancyColor.BlendMode.Overlay); + } + else + { + FancyColor.PushBlend(start.Lerp(end, this.SelectionAnimFloat), FancyColor.BlendMode.Overlay); + } + } + else + { + FancyColor.PushBlend(start.Lerp(end, this.SelectionAnimFloat), FancyColor.BlendMode.Overlay); + } + } + + isCellPressed = DrawCell(entryRect, isEven); + + this.DrawEntry(entryRect, entry, GUIUtility.GetControlID(hint, FocusType.Keyboard), table, sharedEntry); + + if (isSelected) + { + if (OdinLocalizationConfig.Instance.useSyntaxHighlighter && entry.IsSmart && this.currentSyntaxHasErrors) + { + Rect errorRect = entryRect.AlignLeft(OdinLocalizationConstants.ROW_MENU_WIDTH).AlignMiddle(16); + + SdfIcons.DrawIcon(errorRect, SdfIconType.ExclamationOctagonFill, + Event.current.IsMouseOver(errorRect) ? new Color(1, 1, 1, 1f) : new Color(1, 1, 1, 0.6f)); + + if (Event.current.OnMouseDown(errorRect, 0)) + { + this.RelatedWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.ExclamationOctagonFill, + this.currentSyntaxErrorMessage, + new Color(0.68f, 0.2f, 0.2f), + 20.0f); + + if (this.currentSyntaxException != null) + { + Debug.LogException(this.currentSyntaxException); + } + } + } + + FancyColor.PopBlend(); + } + + if (isCellPressed) + { + if (entry is null) + { + entry = table.Asset.AddEntry(sharedEntry.Id, string.Empty); + } + + this.SelectEntry(entry); + } + + break; + } + } + } + } + + private void DrawEntry(Rect position, StringTableEntry entry, int id, OdinGUITable table, SharedTableData.SharedTableEntry sharedEntry) + { + bool changed; + string value; + + Rect smartToggleRect = position.TakeFromRight(OdinLocalizationConstants.ROW_MENU_WIDTH); + position.TakeFromLeft(OdinLocalizationConstants.ROW_MENU_WIDTH); + + if (entry?.Value is null) + { + value = OdinLocalizationGUI.TextField(position, string.Empty, out changed, id); + } + else if (OdinLocalizationConfig.Instance.useSyntaxHighlighter && entry.IsSmart && entry == this.CurrentSelectedEntry) + { + value = OdinLocalizationGUI.TextFieldSyntaxHighlighted(position, entry.Value, this.currentSyntaxHighlightedText, out changed, id); + + if (changed) + { + this.currentSyntaxHighlightedText = OdinLocalizationSyntaxHighlighter.HighlightAsRichText(value); + this.currentSyntaxErrorMessage = OdinLocalizationSyntaxHighlighter.GetErrorMessage(value, out bool foundError, out Exception exception); + this.currentSyntaxHasErrors = foundError; + this.currentSyntaxException = exception; + this.currentSyntaxSource = value; + } + } + else + { + value = OdinLocalizationGUI.TextField(position, entry.Value, out changed, id); + } + + if (changed) + { + if (entry == null) + { + entry = table.Asset.AddEntry(sharedEntry.Id, value); + } + + Undo.RecordObject(entry.Table, "Modified String Table Entry Text"); + entry.Value = value; + OdinLocalizationEvents.RaiseTableEntryModified(entry.SharedEntry); + EditorUtility.SetDirty(entry.Table); + } + + smartToggleRect = smartToggleRect.AlignMiddle(16); + + if (entry == null) + { + SdfIcons.DrawIcon(smartToggleRect, SdfIconType.Lightbulb, new Color(1, 1, 1, Event.current.IsMouseOver(smartToggleRect) ? 0.8f : 0.3f)); + + if (Event.current.OnMouseDown(smartToggleRect, 0)) + { + Undo.RecordObject(table.Asset, "Added String Table Entry By Smart Toggle"); + entry = table.Asset.AddEntry(sharedEntry.Id, string.Empty); + + entry.IsSmart = !entry.IsSmart; + EditorUtility.SetDirty(table.Asset); + } + } + else + { + SdfIcons.DrawIcon(smartToggleRect, + entry.IsSmart ? SdfIconType.LightbulbFill : SdfIconType.Lightbulb, + new Color(1, 1, 1, Event.current.IsMouseOver(smartToggleRect) ? 0.8f : 0.3f)); + + if (Event.current.OnMouseDown(smartToggleRect, 0)) + { + Undo.RecordObject(entry.Table, "Toggled Smart Flag On String Entry"); + entry.IsSmart = !entry.IsSmart; + EditorUtility.SetDirty(entry.Table); + } + } + + GUI.Label(smartToggleRect, GUIHelper.TempContent(string.Empty, "Toggle Smart String")); + } + + protected override void MeasureAllEntries() + { + for (var i = 0; i < this.SharedEntries.Length; i++) + { + this.MeasureEntry(this.SharedEntries[i]); + } + + this.HasGUIChanged = true; + } + + protected override void MeasureVisibleEntries(ref OdinGUIScrollView.VisibleItems visibleItems) + { + int dataOffset = visibleItems.Offset; + + for (var i = 0; i < visibleItems.Length; i++) + { + if (!visibleItems.HasAssociatedData(i)) + { + continue; + } + + var sharedEntry = visibleItems.GetAssociatedData(i); + + this.MeasureEntry(sharedEntry); + + this.EntryScrollView.ReallocateRect(dataOffset + i, this.SharedEntryHeights[sharedEntry.Id], sharedEntry); + } + } + + private void MeasureEntry(SharedTableData.SharedTableEntry sharedEntry) + { + float height = OdinLocalizationConstants.ROW_HEIGHT; + + for (var i = 0; i < this.GUITables.Count; i++) + { + OdinGUITable currentTable = this.GUITables[i]; + + switch (currentTable.Type) + { + case OdinGUITable.GUITableType.Default: + StringTableEntry strEntry = currentTable.Asset.GetEntry(sharedEntry.Id); + + if (strEntry is null) + { + continue; + } + +#if USING_WIDTH_NON_PERCENT + float strEntryHeight = MeasureText(strEntry.Value, currentTable.Width); +#else + float strEntryHeight = MeasureText(strEntry.Value, currentTable.Width); +#endif + + if (strEntryHeight > height) + { + height = strEntryHeight; + } + + break; + + case OdinGUITable.GUITableType.Key: +#if USING_WIDTH_NON_PERCENT + float keyHeight = MeasureText(sharedEntry.Key, currentTable.Width); +#else + float keyHeight = MeasureText(sharedEntry.Key, currentTable.Width); +#endif + + if (keyHeight > height) + { + height = keyHeight; + } + + break; + } + } + + this.SharedEntryHeights[sharedEntry.Id] = height; + } + + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinStringTableCollectionEditor.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinStringTableCollectionEditor.cs.meta new file mode 100644 index 00000000..695cbff7 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinStringTableCollectionEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b8803d1557464e9a9d34713c9c9755af +timeCreated: 1704811189 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTableCollectionEditor.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTableCollectionEditor.cs new file mode 100644 index 00000000..c16c87e4 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTableCollectionEditor.cs @@ -0,0 +1,2719 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#define USING_WIDTH_NON_PERCENT +//#undef USING_WIDTH_NON_PERCENT + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; +using Sirenix.OdinInspector.Editor; +using Sirenix.OdinInspector.Editor.Internal; +using Sirenix.OdinInspector.Modules.Localization.Editor.Configs; +using Sirenix.OdinInspector.Modules.Localization.Editor.Internal; +using Sirenix.Serialization; +using Sirenix.Utilities; +using Sirenix.Utilities.Editor; +using UnityEditor; +using UnityEditor.Localization; +using UnityEngine; +using UnityEngine.Localization; +using UnityEngine.Localization.Tables; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ + public enum OdinTableSelectionType + { + None, + SharedEntry, + SharedTable, + Table, + TableEntry + } + + public abstract class OdinTableCollectionEditor where TCollection : LocalizationTableCollection + where TTable : LocalizationTable + where TEntry : TableEntry + { + public const int SELECTOR_ID = Int32.MinValue + 14085; + + internal struct DragInfo + { + public static DragInfo None => new DragInfo {Index = -1}; + + public bool IsNone => this.Index == -1; + + public int Index; + } + + [HideInInspector] + public TCollection Collection; + + [HideInInspector] + public OdinGUITableCollection GUITables; + + [HideInInspector] + public OdinSharedEntryCollection SharedEntries; + + [HideInInspector] + public Dictionary SharedEntryHeights; + + [HideInInspector] + public OdinGUIScrollView EntryScrollView; + + [HideInInspector] + public Dictionary> LocaleTableMap; + + [HideInInspector] + public SearchField SearchField = new SearchField(); + + [HideInInspector] + public OdinTableSelectionType SelectionType = OdinTableSelectionType.None; + + [HideInInspector] + public OdinGUITable CurrentSelectedTable; + + [HideInInspector] + public SharedTableData.SharedTableEntry CurrentSelectedSharedEntry; + + [HideInInspector] + public TEntry CurrentSelectedEntry; + + [HideInInspector] + public OdinMenuItem MenuItem; + + protected OdinMenuEditorWindow RelatedWindow; + protected int RelatedWindowId; + protected OdinLocalizationEditorWindow.WindowState WindowState; + + protected float PinnedWidth; + + protected OdinGUITable KeyTable; + + protected SirenixAnimationUtility.InterpolatedFloat SelectionAnimFloat = 0.0f; + + protected int ControlIdHint = "Odin_LocalizationEditor_Control".GetHashCode(); + protected int DragDropIdHint = "Odin_LocalizationEditor_DropId".GetHashCode(); + + private bool isForceDeleteKey; + private SharedTableData.SharedTableEntry keyToRemove; + //private bool isDraggingNonHandle; + + protected Action OnTableEntryModified; + + protected Action OnAssetTableEntryAdded; + + protected Action OnAssetTableEntryRemoved; + + //protected Dictionary guiIDs; + //protected Dictionary ControlIds; + + protected bool HasHandledCurrentModifiedEntry; + protected TEntry CurrentModifiedEntry; + + private Action OnTableAddedToCollection; + + private Action OnTableRemovedFromCollection; + + private Undo.UndoRedoCallback OnUndoRedoPerformed; + + private float rightMenuWidth; + + private OdinGUITable sortedTable; + + protected readonly List LooseTables = new List(); + + public OdinTableCollectionEditor(TCollection collection, OdinMenuEditorWindow relatedWindow, OdinLocalizationEditorWindow.WindowState windowState) + { + this.Collection = collection; + this.WindowState = windowState; + + this.GUITables = new OdinGUITableCollection(this.Collection.Tables.Count); + +#if USING_WIDTH_NON_PERCENT + this.GUITables.AddKeyTable(); +#else + float averageWidthPercent = 1.0f / (this.Collection.Tables.Count + 1); + + this.GUITables.AddKeyTable(averageWidthPercent); +#endif + this.KeyTable = this.GUITables.Last(); + + this.LocaleTableMap = new Dictionary>(this.Collection.Tables.Count); + + for (var i = 0; i < this.Collection.Tables.Count; i++) + { + var tableAsset = (TTable) this.Collection.Tables[i].asset; + + Locale tableLocale = LocalizationEditorSettings.GetLocale(tableAsset.LocaleIdentifier); + + if (tableLocale == null) + { + Debug.LogWarning($"No locale found for {tableAsset.name} in {this.Collection.name}, searched for: {tableAsset.LocaleIdentifier}"); + continue; + } + +#if USING_WIDTH_NON_PERCENT + OdinGUITable table = OdinGUITable.CreateTable(tableAsset, tableLocale); +#else + OdinGUITable table = OdinGUITable.CreateTable(tableAsset, averageWidthPercent); +#endif + + this.GUITables.Add(table); + + this.LocaleTableMap[LocalizationEditorSettings.GetLocale(tableAsset.LocaleIdentifier)] = table; + } + + this.SharedEntries = new OdinSharedEntryCollection(this.Collection); + + //this.ControlIds = new Dictionary(this.SharedEntries.Length + 64); + + this.EntryScrollView = new OdinGUIScrollView(this.SharedEntries.Length + 64, adjustViewForVerticalScrollBar: false); + + this.RelatedWindow = relatedWindow; + this.RelatedWindowId = this.RelatedWindow.GetInstanceID(); + this.keyToRemove = null; + this.isForceDeleteKey = false; + + this.LooseTables.Clear(); + LocalizationEditorSettings.FindLooseStringTablesUsingSharedTableData(this.Collection.SharedData, this.LooseTables); + this.rightMenuWidth = EditorPrefs.GetFloat("OdinTableCollectionEditor_RightMenuWidth", 300.0f); + + this.OnUndoRedoPerformed += () => + { + this.Collection.RefreshAddressables(); + this.MenuItem.Name = this.Collection.SharedData.TableCollectionName; + }; + } + + protected abstract void OnInitialize(); + + private bool hasInitialized; + + public void Initialize() + { + if (this.hasInitialized) + { + return; + } + + this.SharedEntryHeights = new Dictionary(this.SharedEntries.Length + 128); + + this.OnTableAddedToCollection += (collection, table) => + { + if (this.Collection != collection) + { + return; + } + + Locale locale = LocalizationEditorSettings.GetLocale(table.LocaleIdentifier); + + if (locale == null) + { + Debug.LogWarning($"No locale found for {table.name} in {collection.name}, searched for: {table.LocaleIdentifier}"); + return; + } + + if (this.LocaleTableMap.ContainsKey(locale)) + { + return; + } + +#if USING_WIDTH_NON_PERCENT + OdinGUITable guiTable = OdinGUITable.CreateTable((TTable) table, locale); +#else + float lastAveragePercent = 1.0f / this.GUITables.Count; + + float newAveragePercent = 1.0f / (this.GUITables.Count + 1); + + for (var i = 0; i < this.GUITables.Count; i++) + { + this.GUITables[i].WidthPercentage *= newAveragePercent / lastAveragePercent; + } + + OdinGUITable guiTable = OdinGUITable.CreateTable((TTable) table, newAveragePercent); +#endif + + this.GUITables.Add(guiTable); + + this.LocaleTableMap[locale] = guiTable; + + this.LooseTables.Clear(); + LocalizationEditorSettings.FindLooseStringTablesUsingSharedTableData(this.Collection.SharedData, this.LooseTables); + }; + + this.OnTableRemovedFromCollection += (collection, table) => + { + if (this.Collection != collection) + { + return; + } + +#if !USING_WIDTH_NON_PERCENT + float lastAveragePercent = 1.0f / this.GUITables.Count; +#endif + + Locale locale = LocalizationEditorSettings.GetLocale(table.LocaleIdentifier); + + this.GUITables.Remove(this.LocaleTableMap[locale]); + + this.LocaleTableMap.Remove(locale); + +#if !USING_WIDTH_NON_PERCENT + float newAveragePercent = 1.0f / this.GUITables.Count; + + for (var i = 0; i < this.GUITables.Count; i++) + { + this.GUITables[i].WidthPercentage *= newAveragePercent / lastAveragePercent; + } +#endif + + this.LooseTables.Clear(); + LocalizationEditorSettings.FindLooseStringTablesUsingSharedTableData(this.Collection.SharedData, this.LooseTables); + }; + +#if false + this.UndoHandler = () => + { + switch (Undo.GetCurrentGroupName()) + { + case "Add table to collection": + for (var i = 0; i < this.Collection.Tables.Count; i++) + { + LocalizationTable tableAsset = this.Collection.Tables[i].asset; + Locale locale = LocalizationEditorSettings.GetLocale(tableAsset.LocaleIdentifier); + + if (locale == null) + { + Debug.LogWarning($"No locale found for {tableAsset.name} in {this.Collection.name}, searched for: {tableAsset.LocaleIdentifier}"); + continue; + } + + if (this.LocaleTableMap.ContainsKey(locale)) + { + continue; + } + + OdinGUITable table = OdinGUITable.CreateTable((TTable) tableAsset, locale); + + this.GUITables.Add(table); + + this.LocaleTableMap.Add(locale, table); + + this.Collection.RemoveTable(tableAsset); + this.Collection.AddTable(tableAsset); + } + + var localesToRemove = new Stack(); + + foreach (KeyValuePair> kvp in this.LocaleTableMap) + { + if (!this.Collection.ContainsTable(kvp.Key.Identifier)) + { + localesToRemove.Push(kvp.Key); + } + } + + while (localesToRemove.Count > 0) + { + Locale locale = localesToRemove.Pop(); + OdinGUITable table = this.LocaleTableMap[locale]; + + this.GUITables.Remove(table); + this.LocaleTableMap.Remove(locale); + } + + break; + } + }; +#endif + + this.OnInitialize(); + + this.hasInitialized = true; + } + + private bool needsToCheckForErrors = false; + + public void OnSelectInWindow() + { + this.needsToCheckForErrors = true; + + this.Initialize(); + + this.AttachEvents(); + } + + private bool hasAttachedEvents; + + public void AttachEvents() + { + if (this.hasAttachedEvents) + { + return; + } + + // this.SharedEntries.AttachEvents(); + + if (this.OnTableEntryModified != null) + { + LocalizationEditorSettings.EditorEvents.TableEntryModified += this.OnTableEntryModified; + } + + if (this.OnAssetTableEntryAdded != null) + { + LocalizationEditorSettings.EditorEvents.AssetTableEntryAdded += this.OnAssetTableEntryAdded; + } + + if (this.OnAssetTableEntryRemoved != null) + { + LocalizationEditorSettings.EditorEvents.AssetTableEntryRemoved += this.OnAssetTableEntryRemoved; + } + + if (this.OnTableAddedToCollection != null) + { + LocalizationEditorSettings.EditorEvents.TableAddedToCollection += this.OnTableAddedToCollection; + } + + if (this.OnTableRemovedFromCollection != null) + { + LocalizationEditorSettings.EditorEvents.TableRemovedFromCollection += this.OnTableRemovedFromCollection; + } + + if (this.OnUndoRedoPerformed != null) + { + Undo.undoRedoPerformed += this.OnUndoRedoPerformed; + } + + this.hasAttachedEvents = true; + } + + public void DetachEvents() + { + if (!this.hasAttachedEvents) + { + return; + } + + // this.SharedEntries.DetachEvents(); + + if (this.OnTableEntryModified != null) + { + LocalizationEditorSettings.EditorEvents.TableEntryModified -= this.OnTableEntryModified; + } + + if (this.OnAssetTableEntryAdded != null) + { + LocalizationEditorSettings.EditorEvents.AssetTableEntryAdded -= this.OnAssetTableEntryAdded; + } + + if (this.OnAssetTableEntryRemoved != null) + { + LocalizationEditorSettings.EditorEvents.AssetTableEntryRemoved -= this.OnAssetTableEntryRemoved; + } + + if (this.OnTableAddedToCollection != null) + { + LocalizationEditorSettings.EditorEvents.TableAddedToCollection -= this.OnTableAddedToCollection; + } + + if (this.OnTableRemovedFromCollection != null) + { + LocalizationEditorSettings.EditorEvents.TableRemovedFromCollection -= this.OnTableRemovedFromCollection; + } + + if (this.OnUndoRedoPerformed != null) + { + Undo.undoRedoPerformed -= this.OnUndoRedoPerformed; + } + + this.hasAttachedEvents = false; + } + + public virtual void RemoveKey(SharedTableData.SharedTableEntry sharedEntry) + { + this.SharedEntryHeights.Remove(sharedEntry.Id); + + this.GUITables.UndoRecordCollection(this.Collection.SharedData, "Removed Shared Table Entry from Collection"); + + this.Collection.RemoveEntry(sharedEntry.Id); + + this.GUITables.SetDirty(this.Collection.SharedData); + } + + public void SelectEntry(TEntry entry) + { +#if false + if (this.Collection.SharedData.Metadata.HasMetadata()) + { + var templateMetadata = this.Collection.SharedData.Metadata.GetMetadata(); + + if (templateMetadata.MetadataExpected.Count > 0) + { + for (var i = 0; i < templateMetadata.MetadataExpected.Count; i++) + { + if (this.HasMetadataAmountOfType(entry.MetadataEntries, templateMetadata.MetadataExpected[i], templateMetadata)) + { + continue; + } + + entry.AddMetadata((IMetadata) templateMetadata.MetadataExpected[i].InstantiateDefault(false)); + } + } + } +#endif + + OdinTableSelectionType lastSelectionType = this.SelectionType; + + this.SelectionType = OdinTableSelectionType.TableEntry; + + TEntry lastSelection = this.CurrentSelectedEntry; + + this.CurrentSelectedEntry = entry; + + if (lastSelection == entry && this.SelectionType == lastSelectionType) + { + return; + } + + this.SelectionAnimFloat = 0.0f; + this.SelectionAnimFloat.Destination = 1.0f; + + if (this.WindowState.CurrentTopTab != OdinLocalizationEditorWindow.RightMenuTopTabs.Metadata) + { + return; + } + + this.WindowState.ShowSharedMetadata = false; + + this.UpdateMetadataViewForEntry(entry); + } + + public void SelectSharedEntry(SharedTableData.SharedTableEntry sharedEntry) + { + this.SelectionType = OdinTableSelectionType.SharedEntry; + + if (this.CurrentSelectedSharedEntry != sharedEntry) + { + this.SelectionAnimFloat = 0.0f; + this.SelectionAnimFloat.Destination = 1.0f; + } + + this.CurrentSelectedSharedEntry = sharedEntry; + + if (this.WindowState.CurrentTopTab != OdinLocalizationEditorWindow.RightMenuTopTabs.Metadata) + { + return; + } + + this.WindowState.ShowSharedMetadata = true; + + this.WindowState.MetadataTree?.Dispose(); + + this.WindowState.MetadataTree = PropertyTree.Create(sharedEntry); + } + + public void SelectTable(OdinGUITable table) + { + this.SelectionType = table.Type == OdinGUITable.GUITableType.Key + ? OdinTableSelectionType.SharedTable + : OdinTableSelectionType.Table; + this.CurrentSelectedTable = table; + } + + public void UpdateMetadataViewForEntry(TEntry entry) + { + this.WindowState.MetadataTree?.Dispose(); + + object metadataData = null; + + if (this.WindowState.ShowSharedMetadata) + { + metadataData = entry.SharedEntry; + } + else + { + if (typeof(TEntry) == typeof(AssetTableEntry)) + { + metadataData = OdinLocalizationReflectionValues.AssetTableEntry_Data_Property.GetValue(entry); + } + + if (typeof(TEntry) == typeof(StringTableEntry)) + { + metadataData = OdinLocalizationReflectionValues.StringTableEntry_Data_Property.GetValue(entry); + } + } + + if (metadataData != null) + { + this.WindowState.MetadataTree = PropertyTree.Create(metadataData); + } + } + + public bool IsSharedEntrySelected(SharedTableData.SharedTableEntry sharedEntry) + { + return this.SelectionType == OdinTableSelectionType.SharedEntry && this.CurrentSelectedSharedEntry == sharedEntry; + } + + public bool IsEntrySelected(TEntry entry) + { + if (entry == null) + { + return false; + } + + return this.SelectionType == OdinTableSelectionType.TableEntry && this.CurrentSelectedEntry == entry; + } + + public bool IsTableSelected(OdinGUITable table) + { + + return (this.SelectionType == OdinTableSelectionType.Table || + this.SelectionType == OdinTableSelectionType.SharedTable) && this.CurrentSelectedTable == table; + } + + public void ClearSelection() + { + GUIUtility.hotControl = 0; + GUIUtility.keyboardControl = 0; + this.SelectionType = OdinTableSelectionType.None; + this.WindowState.MetadataTree?.Dispose(); + this.WindowState.MetadataTree = null; + } + + public void ClearFocus() + { + GUIUtility.hotControl = 0; + GUIUtility.keyboardControl = 0; + } + + protected bool HasGUIChanged = true; + private int lastGUIEntryCount; + + + private bool firstTimeSeeingTable = true; + + private AddressableEntryNotFoundException tableAddressableException; + private string exceptionHeaderMsg = string.Empty; + private string exceptionMsg = string.Empty; + + + [OnInspectorGUI] + public void DrawAndHandleExceptions() + { + if (this.tableAddressableException != null) + { + const float SPACING = 10; + + Rect rect = GUILayoutUtility.GetRect(0, 0, GUILayoutOptions.ExpandWidth().ExpandHeight()); + + rect = rect.AlignCenter(520, 200); + + Color shadowColor = EditorGUIUtility.isProSkin ? new Color(1, 0, 0, 0.2588235f) : new Color(1, 0, 0, 0.3137255f); + + OdinLocalizationGUI.DrawRoundBlur20(rect, shadowColor); + + Color backgroundColor = EditorGUIUtility.isProSkin ? new Color(0.6037736f, 0.1566394f, 0.1566394f) : new Color(0.8301887f, 0.238875f, 0.238875f); + + SirenixEditorGUI.DrawRoundRect(rect, backgroundColor, 7.5f); + + rect = rect.Padding(14); + + Rect buttonsArea = rect.TakeFromBottom(32); + + GUI.BeginClip(rect.Expand(14)); + { + Rect watermarkPosition = rect.SetPosition(Vector2.zero).AlignRight(80).Expand(34).AddX(30).SubY(20); + + SdfIcons.DrawIcon(watermarkPosition, SdfIconType.ExclamationDiamondFill, new Color(1, 1, 1, 0.075f)); + } + GUI.EndClip(); + + rect.height -= SPACING; + + float msgHeight = OdinLocalizationGUI.CardTitleWhite.CalcHeight(this.exceptionHeaderMsg, rect.width); + GUI.Label(rect.TakeFromTop(msgHeight), this.exceptionHeaderMsg, OdinLocalizationGUI.CardTitleWhite); + + rect.yMin += SPACING; + + GUI.Label(rect, this.exceptionMsg, SirenixGUIStyles.MultiLineWhiteLabel); + + if (OdinLocalizationGUI.OverlaidButton(buttonsArea.TakeFromRight(120), "Fix All", SdfIconType.Tools)) + { + this.Collection.RefreshAddressables(); + this.tableAddressableException = null; + + this.RelatedWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.Tools, + $"Refreshed Addressables for '{this.Collection.name}'.", + new Color(0.26f, 0.51f, 0.44f), + 12.0f); + } + + buttonsArea.width -= SPACING; + + if (OdinLocalizationGUI.OverlaidButton(buttonsArea.TakeFromRight(160), "Fix And Preload All", SdfIconType.Tools)) + { + this.Collection.RefreshAddressables(); + this.Collection.SetPreloadTableFlag(true); + this.tableAddressableException = null; + + this.RelatedWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.Tools, + $"Refreshed Addressables and Preloaded All Tables for '{this.Collection.name}'.", + new Color(0.26f, 0.51f, 0.44f), + 12.0f); + } + + return; + } + + try + { + this.Draw(); + + if (this.needsToCheckForErrors) + { + // NOTE: this attempts to catch any AddressableEntryNotFoundException errors, by fetching the Addressables for every table. + this.Collection.IsPreloadTableFlagSet(); + + this.needsToCheckForErrors = false; + } + } + catch (AddressableEntryNotFoundException e) + { + this.tableAddressableException = e; + + this.exceptionHeaderMsg = e.Message; + this.exceptionMsg = $"There could be multiple other tables facing the same issue in '{this.Collection.name}', " + + "this can potentially be resolved by refreshing the Addressables."; + + GUIHelper.ExitGUI(false); + } + } + + public void Draw() + { + if (Event.current.type == EventType.MouseUp) + { + SharedUniqueControlId.SetInactive(); + } + + if (this.EntryScrollView.IsDraggingMouse) + { + EditorGUIUtility.AddCursorRect(this.EntryScrollView.InteractRect, MouseCursor.Pan); + } + + if (this.HasHandledCurrentModifiedEntry) + { + this.CurrentModifiedEntry = null; + } + + // if (Event.current.type == EventType.MouseUp) + // { + // this.isDraggingNonHandle = false; + // } + // + bool shouldClearSelection = Event.current.OnKeyDown(KeyCode.Escape, false); + + //this.SharedEntries.UpdateIfChangesArePresent(); + + Rect position = GUILayoutUtility.GetRect(0, 0, GUILayoutOptions.ExpandWidth().ExpandHeight()); + + position = this.RelatedWindow.position.SetPosition(Vector2.zero); + position.TakeFromRight(this.RelatedWindow.MenuWidth); + + var leftMenuSliderRect = position.TakeFromLeft(10).SubX(1); + var rightMenuRect = position.TakeFromRight(this.WindowState.RightMenuWidth); + var rightMenuSliderRect = position.TakeFromRight(11); + + this.RelatedWindow.MenuWidth += this.VerticalSlideRect(leftMenuSliderRect.AddXMax(1), false); + this.RelatedWindow.MenuWidth = Mathf.Max(this.RelatedWindow.MenuWidth, 1); + + if (Event.current.clickCount > 1 && Event.current.IsMouseOver(leftMenuSliderRect)) + { + this.RelatedWindow.MenuWidth = 1; + + if (Event.current.control || Event.current.alt || Event.current.shift) + { + this.WindowState.RightMenuWidth = 0; + } + } + + if (Event.current.clickCount > 1 && Event.current.IsMouseOver(rightMenuSliderRect)) + { + if (Event.current.control || Event.current.alt || Event.current.shift) + { + if (this.WindowState.RightMenuWidth > 0) + { + this.WindowState.RightMenuWidth = 0; + this.WindowState.LeftMenuWidth = 0; + } + else + { + this.WindowState.RightMenuWidth = this.WindowState.LastOpenRightMenuWidth; + this.RelatedWindow.MenuWidth = this.WindowState.LastOpenRightMenuWidth; + } + } + else + { + if (this.WindowState.RightMenuWidth > 0) + { + this.WindowState.RightMenuWidth = 0; + } + else + { + this.WindowState.RightMenuWidth = this.WindowState.LastOpenRightMenuWidth; + } + } + } + + Rect toolbarRect = position.TakeFromTop(OdinLocalizationConstants.TOOLBAR_HEIGHT); + + Rect dragHandleRect = position.TakeFromLeft(OdinLocalizationConstants.DRAG_HANDLE_WIDTH); + + this.DrawToolbar(toolbarRect); + + this.GUITables.Sort(); + +#if USING_WIDTH_NON_PERCENT + for (var i = 0; i < this.GUITables.Count; i++) + { + this.GUITables[i].Width = this.GUITables[i].Width; + } +#endif + + float viewWidth = position.width; + +#if USING_WIDTH_NON_PERCENT + float columnsWidth = this.GUITables.GetVisibleWidth(); + + if (columnsWidth >= viewWidth) + { + viewWidth = columnsWidth; + } +#else + int columnsMinTotalWidth = this.GUITables.GetVisibleCount() * OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH; + + if (columnsMinTotalWidth > viewWidth) + { + viewWidth = columnsMinTotalWidth; + } +#endif + + if (position != this.EntryScrollView.InteractRect) + { + this.HasGUIChanged = true; + } + + bool isEntryCountChanged = this.Collection.SharedData.Entries.Count != this.lastGUIEntryCount; + + if (this.HasGUIChanged || isEntryCountChanged) + { +#if false + if (isEntryCountChanged) + { + if (this.SharedEntries.IsSorted) + { + this.Resort(); + } + + if (this.SharedEntries.IsSearching) + { + this.SharedEntries.UpdateSearchTerm(this.SharedEntries.SearchTerm, this.GUITables, this.Collection); + } + } +#endif + + this.HasGUIChanged = false; + + if (isEntryCountChanged) + { + if (this.SharedEntries.IsSorted) + { + switch (this.sortedTable.Type) + { + case OdinGUITable.GUITableType.Default: + switch (this.sortedTable.Asset) + { + case AssetTable assetTable: + this.SharedEntries.SortByAssetTable(this.Collection as AssetTableCollection, assetTable, false); + break; + + case StringTable stringTable: + this.SharedEntries.SortByStringTable(stringTable, false); + break; + } + + break; + + case OdinGUITable.GUITableType.Key: + this.SharedEntries.SortByKeys(false); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + if (this.SharedEntries.IsSearching) + { + this.SharedEntries.UpdateSearchTerm(this.SharedEntries.SearchTerm, this.GUITables, this.Collection, true); + } + } + + this.lastGUIEntryCount = this.Collection.SharedData.Entries.Count; + + float previousY = this.EntryScrollView.PositionY; + + this.EntryScrollView.SetBounds(position, viewWidth); + + this.EntryScrollView.BeginAllocations(); + { + this.AllocateItems(); + } + this.EntryScrollView.EndAllocations(); + + if (this.adjustViewForSeparatorChange && + this.lastViewHeight != 0.0f && + Math.Abs(this.lastViewHeight - this.EntryScrollView.ViewRect.height) > 0.01f) + { + float newHeight = this.EntryScrollView.ViewRect.height; + + float change = previousY / this.lastViewHeight; + + this.EntryScrollView.PositionY = change * newHeight; + + this.adjustViewForSeparatorChange = false; + } + } + else + { + this.EntryScrollView.SetBoundsForCurrentAllocations(position, viewWidth); + } + +#if !USING_WIDTH_NON_PERCENT + this.GUITables.CalcWidths(this.EntryScrollView); +#endif + + this.PinnedWidth = 0.0f; + + for (var i = 0; i < this.GUITables.Count; i++) + { + OdinGUITable table = this.GUITables[i]; + + if (!table.IsVisible || !table.IsPinned) + { + continue; + } + +#if USING_WIDTH_NON_PERCENT + this.PinnedWidth += table.Width; +#else + this.PinnedWidth += this.GUITables[i].Width; +#endif + } + + if (this.PinnedWidth > this.EntryScrollView.Bounds.width) + { + this.PinnedWidth = this.EntryScrollView.Bounds.width; + this.GUITables.ResizePinnedToFit(this.EntryScrollView.Bounds.width); + } + + this.GUITables.UpdateVisibleTables(this.EntryScrollView, this.PinnedWidth); + + if (this.firstTimeSeeingTable && columnsWidth < viewWidth) + { + this.GUITables.ResizeToFit(this.EntryScrollView.Bounds.width - this.PinnedWidth); + this.firstTimeSeeingTable = false; + } + + + OdinGUIScrollView.VisibleItems visibleItems = this.EntryScrollView.GetVisibleItems(); + + this.DrawRows(ref visibleItems); + + this.DrawPseudoRows(); + + this.DrawItems(ref visibleItems); + + this.DrawColumnsAndSeparators(ref visibleItems); + + this.DrawDragHandles(dragHandleRect, ref visibleItems); + + this.DrawRightMenu(rightMenuRect); + + if (this.keyToRemove != null) + { + if (this.isForceDeleteKey || + EditorUtility.DisplayDialog("Odin Table Collection Editor", $"Are you sure you want to remove entry: {this.keyToRemove.Key}?", "Yes", "No")) + { + this.RemoveKey(this.keyToRemove); + } + + this.keyToRemove = null; + this.isForceDeleteKey = false; + } + + if (shouldClearSelection) + { + this.ClearSelection(); + } + + this.EntryScrollView.HandleMiddleMouseDrag(inverted: OdinLocalizationConfig.Instance.invertMouseDragNavigation, + speed: OdinLocalizationConfig.Instance.mouseDragSpeed); + + + this.WindowState.RightMenuWidth -= this.VerticalSlideRect(rightMenuSliderRect, true); + this.WindowState.RightMenuWidth = Mathf.Max(this.WindowState.RightMenuWidth, 0); + + if (this.WindowState.RightMenuWidth > 338) + { + this.WindowState.LastOpenRightMenuWidth = this.WindowState.RightMenuWidth; + } + } + + protected abstract void AllocateItems(); + + protected abstract void DrawItems(ref OdinGUIScrollView.VisibleItems visibleItems); + + protected abstract void MeasureAllEntries(); + + protected abstract void MeasureVisibleEntries(ref OdinGUIScrollView.VisibleItems visibleItems); + + // NOTE: returns true if pressed, TODO add xml comments later + protected static bool DrawCell(Rect rect, bool isEven) + { + Color background = isEven ? OdinLocalizationGUI.RowEvenBackground2 : OdinLocalizationGUI.RowOddBackground2; + + GUI.DrawTexture(rect, Texture2D.whiteTexture, ScaleMode.StretchToFill, false, 1, background, 0, 2.5f); + + if (Event.current.IsMouseOver(rect) && !DragAndDropUtilities.IsDragging) + { + GUI.DrawTexture(rect, Texture2D.whiteTexture, ScaleMode.StretchToFill, false, 1, OdinLocalizationGUI.RowBorderHover, 1, 2.5f); + } + else + { + GUI.DrawTexture(rect, Texture2D.whiteTexture, ScaleMode.StretchToFill, false, 1, OdinLocalizationGUI.RowBorder, 1, 2.5f); + } + + rect.x += OdinLocalizationConstants.ROW_MENU_WIDTH; + rect.width -= OdinLocalizationConstants.ROW_MENU_WIDTH + OdinLocalizationConstants.ROW_MENU_WIDTH; + bool isPressed = Event.current.OnMouseDown(rect, 0, false); + + return isPressed; + } + + protected void DrawKey(Rect rect, SharedTableData.SharedTableEntry sharedEntry, int id) + { + Rect removeRect = rect.TakeFromLeft(OdinLocalizationConstants.ROW_MENU_WIDTH); + Rect copyKeyIdRect = rect.TakeFromRight(OdinLocalizationConstants.ROW_MENU_WIDTH); + + Color removeBgColor = Event.current.IsMouseOver(removeRect) ? new Color(0.8f, 0.1f, 0.1f, 0.8f) : new Color(0, 0, 0, 0.2f); + + GUI.DrawTexture(removeRect, Texture2D.whiteTexture, ScaleMode.StretchToFill, false, 1.0f, removeBgColor, Vector4.zero, + new Vector4(2.5f, 0.0f, 0.0f, 2.5f)); + + if (Event.current.OnMouseDown(removeRect, 0)) + { + this.keyToRemove = sharedEntry; + + if (Event.current.modifiers == EventModifiers.Shift) + { + this.isForceDeleteKey = true; + } + + this.ClearSelection(); + + return; + } + + Color removeFgColor = Event.current.IsMouseOver(removeRect) ? new Color(1, 1, 1, 0.8f) : new Color(1, 1, 1, 0.5f); + + removeRect = removeRect.AlignCenter(14, 14); + + SdfIcons.DrawIcon(removeRect, SdfIconType.X, removeFgColor); + + // copyKeyIdRect.x -= 20; + + copyKeyIdRect = copyKeyIdRect.AlignCenter(16, 16); + + var isMouseOverKeyRect = Event.current.IsMouseOver(copyKeyIdRect); + + Matrix4x4 m = GUI.matrix; + GUIUtility.RotateAroundPivot(45.0f, copyKeyIdRect.center); + SdfIcons.DrawIcon(copyKeyIdRect, SdfIconType.KeyFill, new Color(1, 1, 1, isMouseOverKeyRect ? 0.8f : 0.3f)); + GUI.matrix = m; + + if (isMouseOverKeyRect) + { + GUI.Label(copyKeyIdRect, GUIHelper.TempContent(string.Empty, "Copy Shared Entry Id")); + } + + if (Event.current.OnMouseDown(copyKeyIdRect, 0)) + { + this.RelatedWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.Clipboard, + $"Copied Shared Entry Id '{sharedEntry.Id}' to the clipboard.", + new Color(0.23f, 0.36f, 0.68f), + 8.0f); + + Clipboard.Copy(sharedEntry.Id.ToString()); + + this.ClearSelection(); + } + + string result = OdinLocalizationGUI.TextField(rect, sharedEntry.Key, out bool changed, id); + + if (!changed) + { + return; + } + + if (this.Collection.SharedData.Contains(result) && result != sharedEntry.Key) + { + this.RelatedWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.ExclamationOctagonFill, + $"Key '{result}' already exists in the collection.", + new Color(0.68f, 0.2f, 0.2f), + 8.0f); + } + else + { + Undo.RecordObject(this.Collection.SharedData, "Renamed Shared Table Entry Key"); + this.Collection.SharedData.RenameKey(sharedEntry.Id, result); + OdinLocalizationEvents.RaiseTableEntryModified(sharedEntry); + EditorUtility.SetDirty(this.Collection.SharedData); + } + } + + protected static float MeasureText(string text, float width) + { + // TODO: get rid of this magic number + width -= 20 + 20 + 8 + 16; + + float rowHeightWithoutText = OdinLocalizationConstants.ROW_HEIGHT - SirenixGUIStyles.MultiLineCenteredLabel.lineHeight; + + float heightOfText = SirenixGUIStyles.MultiLineCenteredLabel.CalcHeight(text, width) - SirenixGUIStyles.MultiLineCenteredLabel.padding.vertical; + + return rowHeightWithoutText + heightOfText; + } + + private void DrawToolbar(Rect position) + { + Rect originalPosition = position; + + Rect resizeToFitButtonRect = position.TakeFromRight(180f); + Rect addButtonRect = position.TakeFromRight(180f); + + position = position.Padding(4); + + if (GUI.Button(resizeToFitButtonRect, "Resize Columns To Fit", SirenixGUIStyles.ToolbarButton)) + { + this.GUITables.ResizeToFit(this.EntryScrollView.Bounds.width - this.PinnedWidth); + this.HasGUIChanged = true; + } + + if (GUI.Button(addButtonRect, "Add Shared Entry", SirenixGUIStyles.ToolbarButton)) + { + this.GUITables.UndoRecordCollection(this.Collection.SharedData, "Added Shared Entry To Collection"); + SharedTableData.SharedTableEntry sharedEntry = this.Collection.SharedData.AddKey(); + + OdinLocalizationEvents.RaiseTableEntryAdded(this.Collection, sharedEntry); + this.GUITables.SetDirty(this.Collection.SharedData); + } + + string searchTerm = this.SearchField.Draw(position, this.SharedEntries.SearchTerm, "Search for item(s)..."); + + if (this.SharedEntries.UpdateSearchTerm(searchTerm, this.GUITables, this.Collection)) + { + this.HasGUIChanged = true; + } + + if (!EditorGUIUtility.isProSkin) + { + EditorGUI.DrawRect(originalPosition, new Color(0, 0, 0, 0.05f)); + } + } + + private void DrawRows(ref OdinGUIScrollView.VisibleItems visibleItems) + { + Rect clipRect = this.EntryScrollView.GetClipRect(); + + clipRect.x -= OdinLocalizationConstants.DRAG_HANDLE_WIDTH; + clipRect.width += OdinLocalizationConstants.DRAG_HANDLE_WIDTH; + + this.EntryScrollView.BeginClip(clipRect, offset: new Vector2(0, OdinLocalizationConstants.COLUMN_HEIGHT)); + { + for (var i = 0; i < visibleItems.Length; i++) + { + Rect rect = visibleItems.GetRect(i); + + Rect dropZoneRect = rect; + + rect.width += OdinLocalizationConstants.DRAG_HANDLE_WIDTH; + + bool isEven = (visibleItems.Offset + i) % 2 == 0; + + EditorGUI.DrawRect(rect, isEven ? OdinLocalizationGUI.RowEvenBackground : OdinLocalizationGUI.RowOddBackground); + + this.HandleDropZone(dropZoneRect, visibleItems.Offset + i); + } + } + this.EntryScrollView.EndClip(); + } + + private void DrawPseudoRows() + { + if (this.EntryScrollView.IsBeyondVerticalBounds) + { + return; + } + + var remainderRect = new Rect(this.EntryScrollView.Bounds.x - OdinLocalizationConstants.DRAG_HANDLE_WIDTH, + this.EntryScrollView.Bounds.y + this.EntryScrollView.ViewRect.height + OdinLocalizationConstants.COLUMN_HEIGHT, + this.EntryScrollView.Bounds.width + OdinLocalizationConstants.DRAG_HANDLE_WIDTH, + this.EntryScrollView.Bounds.height - this.EntryScrollView.ViewRect.height - OdinLocalizationConstants.COLUMN_HEIGHT); + + Rect maintainedRemainderRect = remainderRect; + + bool isNextEven = this.SharedEntries.Length % 2 == 0; + + while (remainderRect.height > 0) + { + Rect rect = remainderRect.TakeFromTop(OdinLocalizationConstants.ROW_HEIGHT); + Color color = isNextEven ? OdinLocalizationGUI.RowEvenBackground : OdinLocalizationGUI.RowOddBackground; + + EditorGUI.DrawRect(rect, color); + + isNextEven = !isNextEven; + } + + EditorGUI.DrawRect(maintainedRemainderRect, new Color(0, 0, 0, 0.25f)); + } + + private void DrawColumnsAndSeparators(ref OdinGUIScrollView.VisibleItems visibleItems) + { + EditorGUI.DrawRect(this.EntryScrollView.Bounds.AlignTop(OdinLocalizationConstants.COLUMN_HEIGHT), OdinLocalizationGUI.ColumnBackground); + + Rect columnArea = this.EntryScrollView.ViewRect; + + if (!this.EntryScrollView.IsBeyondVerticalBounds) + { + columnArea.height = this.EntryScrollView.Bounds.height; + } + + int lastIndex = this.GUITables.GetLastVisibleIndex(); + + this.EntryScrollView.BeginClip(offset: new Vector2(this.PinnedWidth, 0), ignoreScrollY: true); + { + this.DrawColumns(ref visibleItems, columnArea.AlignRight(columnArea.width), false, lastIndex); + } + this.EntryScrollView.EndClip(); + + int lastPinnedIndex = this.GUITables.GetLastVisiblePinnedIndex(); + + this.EntryScrollView.BeginClip(ignoreScrollX: true, ignoreScrollY: true); + { + this.DrawColumns(ref visibleItems, columnArea, true, lastPinnedIndex); + } + this.EntryScrollView.EndClip(); + + if (this.PinnedWidth > 0.0f && this.PinnedWidth + 20 <= this.EntryScrollView.Bounds.width) + { + Rect shadowRect = this.EntryScrollView.Bounds; + + shadowRect.x += this.PinnedWidth; + shadowRect.width = 24; + + GUI.DrawTexture(shadowRect, OdinLocalizationGUITextures.LeftToRightFade, ScaleMode.StretchToFill, true, 1.0f, new Color(0, 0, 0, 0.35f), 0, 0); + } + } + + private void DrawColumns(ref OdinGUIScrollView.VisibleItems visibleItems, Rect columnArea, bool pinned, int lastIndex) + { + for (var i = 0; i < this.GUITables.Count; i++) + { + OdinGUITable table = this.GUITables[i]; + + if (table.IsPinned != pinned) + { + continue; + } + + if (!this.GUITables.TablesWithinVisibleBounds.Contains(table)) + { + columnArea.TakeFromLeft(table.Width); + continue; + } + +#if USING_WIDTH_NON_PERCENT + Rect columnRect = columnArea.TakeFromLeft(table.Width); +#else + Rect columnRect = columnArea.TakeFromLeft(table.Width); +#endif + + Rect columnHeaderRect = columnRect.AlignTop(OdinLocalizationConstants.COLUMN_HEIGHT); + + bool isSelected = this.IsTableSelected(table); + + if (isSelected) + { + FancyColor.PushBlend(FancyColor.Gray.Lerp(OdinLocalizationGUI.Selected, 0.5f), FancyColor.BlendMode.Overlay); + EditorGUI.DrawRect(columnHeaderRect, OdinLocalizationGUI.ColumnBackground); + } + + Rect interactColumnRect = columnHeaderRect.Padding(4, 0); + + if (Event.current.IsMouseOver(interactColumnRect)) + { + EditorGUI.DrawRect(columnHeaderRect, new Color(1.0f, 1.0f, 1.0f, 0.035f)); + } + + Rect pinRect = columnHeaderRect.TakeFromRight(30).SubXMax(10).AlignMiddle(18); + SdfIconType pinIcon = table.IsPinned ? SdfIconType.PinAngleFill : SdfIconType.PinAngle; + + GUI.Label(pinRect, GUIHelper.TempContent(string.Empty, "Pin Table")); + + if (Event.current.IsMouseOver(pinRect)) + { + SdfIcons.DrawIcon(pinRect, pinIcon, Color.white); + } + else + { + SdfIcons.DrawIcon(pinRect, pinIcon); + } + + if (Event.current.OnMouseDown(pinRect, 0)) + { + table.IsPinned = !table.IsPinned; + + this.ClearFocus(); + } + + float columnTextWidth = SirenixGUIStyles.LabelCentered.CalcWidth(table.DisplayName); + + Rect minSortRect = columnHeaderRect.TakeFromLeft(30).AddXMin(10); + + Rect sortRect = columnHeaderRect.AlignCenter(20, 18); + + sortRect.x -= columnTextWidth * 0.5f + 12.0f; + + if (sortRect.x < minSortRect.x) + { + sortRect = minSortRect.AlignMiddle(18); + } + + sortRect = sortRect.AlignCenter(16, 16); + + if (EditorGUIUtility.isProSkin) + { + GUI.Label(columnHeaderRect, table.DisplayName, SirenixGUIStyles.LabelCentered); + } + else + { + var t = SirenixGUIStyles.LabelCentered.normal.textColor; + SirenixGUIStyles.LabelCentered.normal.textColor = new Color(0, 0, 0, 0.7f); + GUI.Label(columnHeaderRect, table.DisplayName, SirenixGUIStyles.LabelCentered); + SirenixGUIStyles.LabelCentered.normal.textColor = t; + } + + SdfIconType sortIcon; + + if (this.sortedTable == table) + { + switch (this.SharedEntries.CurrentSortOrderState) + { + case OdinSharedEntryCollection.SortOrderState.Unsorted: + sortIcon = SdfIconType.ArrowDownUp; + break; + + case OdinSharedEntryCollection.SortOrderState.Ascending: + sortIcon = SdfIconType.ArrowDown; + break; + + case OdinSharedEntryCollection.SortOrderState.Descending: + sortIcon = SdfIconType.ArrowUp; + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + else + { + sortIcon = SdfIconType.ArrowDownUp; + } + + if (Event.current.IsMouseOver(sortRect)) + { + SdfIcons.DrawIcon(sortRect, sortIcon, Color.white); + } + else + { + SdfIcons.DrawIcon(sortRect, sortIcon); + } + + GUI.Label(sortRect, GUIHelper.TempContent(string.Empty, "Sort Table")); + + if (Event.current.OnMouseDown(sortRect, 0)) + { + bool wasSorted = this.SharedEntries.IsSorted && this.sortedTable != table; + + if (this.sortedTable == table || this.SharedEntries.CurrentSortOrderState == OdinSharedEntryCollection.SortOrderState.Unsorted) + { + this.SharedEntries.GotoNextSortOrderState(); + } + + switch (table.Type) + { + case OdinGUITable.GUITableType.Default: + switch (table.Asset) + { + case AssetTable assetTable: + this.SharedEntries.SortByAssetTable(this.Collection as AssetTableCollection, assetTable, wasSorted); + break; + + case StringTable stringTable: + this.SharedEntries.SortByStringTable(stringTable, wasSorted); + break; + } + + break; + + case OdinGUITable.GUITableType.Key: + this.SharedEntries.SortByKeys(wasSorted); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + this.sortedTable = table; + + this.ClearFocus(); + + this.HasGUIChanged = true; + } + + if (Event.current.OnMouseDown(interactColumnRect, 0)) + { + this.SelectTable(table); + } + + if (isSelected) + { + FancyColor.PopBlend(); + } + +#if !USING_WIDTH_NON_PERCENT + if (i == lastIndex) + { + continue; + } +#endif + + this.DrawSeparator(ref visibleItems, columnRect, table, i, lastIndex); + } + } + + private bool hasSeparatorChanged = false; + private bool adjustViewForSeparatorChange = false; + private float lastViewHeight; + + private void DrawSeparator(ref OdinGUIScrollView.VisibleItems visibleItems, Rect columnRect, OdinGUITable table, int index, int lastIndex) + { + Rect separatorRect = columnRect.AlignRight(1); + + EditorGUI.DrawRect(separatorRect, OdinLocalizationGUI.RowBorder); + + Rect separatorMouseRect = separatorRect.Expand(1, 0); + + switch (Event.current.type) + { + case EventType.MouseDown: + if (Event.current.button == 0 && Event.current.IsMouseOver(separatorMouseRect)) + { + //this.isDraggingNonHandle = true; + this.ClearFocus(); + } + + break; + + case EventType.MouseUp: + //if (this.isDraggingNonHandle) + //{ + // NOTE: we only adjust the ones we can see while we drag separators, to avoid unnecessary computations. + if (this.hasSeparatorChanged) + { + this.MeasureAllEntries(); + this.hasSeparatorChanged = false; + + this.adjustViewForSeparatorChange = true; + } + //} + + //this.isDraggingNonHandle = false; + break; + } + + Vector2 slideAmount = table.HandleSlider(separatorMouseRect); + + if (slideAmount.x == 0.0f) + { + return; + } + + if (!this.hasSeparatorChanged) + { + this.lastViewHeight = this.EntryScrollView.ViewRect.height; + } + + + this.hasSeparatorChanged = true; + +#if USING_WIDTH_NON_PERCENT + this.AppendWidth(slideAmount.x, index, lastIndex); +#else + float newWidth = table.Width + slideAmount.x; + + OdinGUITable nextTable = this.GUITables.GetNextVisible(index); + + float nextNewWidth = nextTable.Width - slideAmount.x; + + if (nextNewWidth < OdinLocalizationConstants.MIN_COLUMN_WIDTH) + { + float diff = OdinLocalizationConstants.MIN_COLUMN_WIDTH - nextNewWidth; + + newWidth -= diff; + + nextNewWidth += diff; + } + + if (newWidth < OdinLocalizationConstants.MIN_COLUMN_WIDTH) + { + float diff = OdinLocalizationConstants.MIN_COLUMN_WIDTH - newWidth; + + newWidth += diff; + + nextNewWidth -= diff; + } + + table.WidthPercentage *= newWidth / table.Width; + + nextTable.WidthPercentage *= nextNewWidth / nextTable.Width; +#endif + this.MeasureVisibleEntries(ref visibleItems); + } + + private void AppendWidth(float change, int index, int lastIndex) + { + OdinGUITable table = this.GUITables[index]; + + table.Width += change; + + if (change < 0.0f && table.Width <= OdinLocalizationConstants.MIN_COLUMN_WIDTH) + { + int previousIndex = index - 1; + + while (previousIndex > -1) + { + OdinGUITable previousTable = this.GUITables[previousIndex]; + + if (previousTable.IsPinned != table.IsPinned) + { + break; + } + + previousTable.Width += change; + + if (previousTable.Width <= OdinLocalizationConstants.MIN_COLUMN_WIDTH) + { + previousIndex--; + continue; + } + + break; + } + + if (previousIndex == -1) + { + previousIndex = 0; + } + + if (index != lastIndex && this.GUITables[previousIndex].Width > OdinLocalizationConstants.MIN_COLUMN_WIDTH) + { + this.GUITables[index + 1].Width -= change; + } + } + else if (index != lastIndex) + { + this.GUITables[index + 1].Width -= change; + } + } + + private void DrawDragHandles(Rect position, ref OdinGUIScrollView.VisibleItems visibleItems) + { + if (this.EntryScrollView.IsBeyondHorizontalBounds) + { + OdinGUIScrollView.ScrollBackground(position.AlignBottom(OdinGUIScrollView.SCROLL_BAR_SIZE), false); + } + + Rect clipRect = this.EntryScrollView.GetClipRect(); + + clipRect.x -= position.width; + clipRect.width = position.width; + + this.EntryScrollView.BeginClip(clipRect, offset: new Vector2(0, OdinLocalizationConstants.COLUMN_HEIGHT), ignoreScrollX: true); + { + if (this.SharedEntries.IsSorted || this.SharedEntries.IsSearching) //(this.isDraggingNonHandle) + { + for (var i = 0; i < visibleItems.Length; i++) + { + Rect dragHandleRect = visibleItems.GetRect(i); + + dragHandleRect.width = OdinLocalizationConstants.DRAG_HANDLE_WIDTH; + + dragHandleRect.x += 2; + dragHandleRect.width -= 4; + + if (EditorGUIUtility.isProSkin) + { + SdfIcons.DrawIcon(dragHandleRect.AlignMiddle(16), SdfIconType.GripVertical, new Color(0.35f, 0.35f, 0.35f, 1.0f)); + } + else + { + SdfIcons.DrawIcon(dragHandleRect.AlignMiddle(16), SdfIconType.GripVertical, new FancyColor(0.66f)); + } + } + } + else + { + bool isDraggingAnything = this.IsDraggingAnything(); + + for (var i = 0; i < visibleItems.Length; i++) + { + Rect dragHandleRect = visibleItems.GetRect(i); + + dragHandleRect.width = OdinLocalizationConstants.DRAG_HANDLE_WIDTH; + + bool isMouseOver = Event.current.IsMouseOver(dragHandleRect); + + if (!isDraggingAnything) + { + var dragData = new DragInfo {Index = visibleItems.Offset + i}; + DragAndDropUtilities.DragZone(dragHandleRect, dragData, false, false); + } + + dragHandleRect.x += 2; + dragHandleRect.width -= 4; + + if (EditorGUIUtility.isProSkin) + { + SdfIcons.DrawIcon(dragHandleRect.AlignMiddle(16), SdfIconType.GripVertical, new Color(1, 1, 1, isMouseOver ? 0.8f : 0.6f)); + } + else + { + SdfIcons.DrawIcon(dragHandleRect.AlignMiddle(16), SdfIconType.GripVertical, new Color(0, 0, 0, isMouseOver ? 0.6f : 0.4f)); + } + } + } + } + this.EntryScrollView.EndClip(); + + EditorGUI.DrawRect(position.TakeFromTop(OdinLocalizationConstants.COLUMN_HEIGHT), OdinLocalizationGUI.ColumnBackground); + } + + // NOTE: for now we pass by index, since you can't drag stuff around when you're searching or sorting + private void HandleDropZone(Rect position, int indexTo) + { + position.x += OdinLocalizationConstants.DRAG_HANDLE_WIDTH; + + float halfHeight = position.height * 0.5f; + + Rect topDropRect = position.AlignTop(halfHeight); + Rect bottomDropRect = position.AlignBottom(halfHeight); + + int topId = this.DragDropIdHint + indexTo; + + DragInfo topValue = DragAndDropUtilities.DropZone(topDropRect, DragInfo.None, this.DragDropIdHint + indexTo); + + int bottomId = this.DragDropIdHint + indexTo + this.SharedEntries.Length; + + DragInfo bottomValue = DragAndDropUtilities.DropZone(bottomDropRect, DragInfo.None, bottomId); + + if (DragAndDropUtilities.IsDragging) + { + if (DragAndDropUtilities.HoveringAcceptedDropZone == topId) + { + if (EditorGUIUtility.isProSkin) + { + GUI.DrawTexture(topDropRect.AlignTop(40.0f).SubXMin(OdinLocalizationConstants.DRAG_HANDLE_WIDTH), + OdinLocalizationGUITextures.TopToBottomFade, + ScaleMode.StretchToFill, + true, + 1.0f, + new Color(0.16f, 0.7f, 1f, 0.25f), + Vector4.zero, + Vector4.zero); + } + else + { + GUI.DrawTexture(topDropRect.AlignTop(40.0f).SubXMin(OdinLocalizationConstants.DRAG_HANDLE_WIDTH), + OdinLocalizationGUITextures.TopToBottomFade, + ScaleMode.StretchToFill, + true, + 1.0f, + new Color(0.8f, 0.8f, 1, 0.7f), + Vector4.zero, + Vector4.zero); + } + //EditorGUI.DrawRect(topDropRect.AlignTop(1), new Color(0, 1, 1, 0.5f)); + } + + if (DragAndDropUtilities.HoveringAcceptedDropZone == bottomId) + { + if (EditorGUIUtility.isProSkin) + { + GUI.DrawTexture(bottomDropRect.AlignBottom(40.0f).SubXMin(OdinLocalizationConstants.DRAG_HANDLE_WIDTH), + OdinLocalizationGUITextures.BottomToTopFade, + ScaleMode.StretchToFill, + true, + 1.0f, + new Color(0.16f, 0.7f, 1f, 0.25f), + Vector4.zero, + Vector4.zero); + } + else + { + GUI.DrawTexture(bottomDropRect.AlignBottom(40.0f).SubXMin(OdinLocalizationConstants.DRAG_HANDLE_WIDTH), + OdinLocalizationGUITextures.BottomToTopFade, + ScaleMode.StretchToFill, + true, + 1.0f, + new Color(0.8f, 0.8f, 1, 0.7f), + Vector4.zero, + Vector4.zero); + } + //EditorGUI.DrawRect(bottomDropRect.AlignBottom(1), new Color(0, 1, 1, 0.5f)); + } + } + + if (!topValue.IsNone) + { + this.SharedEntries.MoveEntry(topValue.Index, indexTo); + + this.HasGUIChanged = true; + + return; + } + + if (!bottomValue.IsNone) + { + this.SharedEntries.MoveEntry(bottomValue.Index, indexTo + 1); + + this.HasGUIChanged = true; + } + } + + protected void MoveScrollPositionToTable(OdinGUITable table) + { + var x = 0.0f; + +#if USING_WIDTH_NON_PERCENT + for (var i = 0; i < this.GUITables.Count; i++) + { + if (this.GUITables[i] == table) + { + x += this.GUITables[i].Width * 0.5f; + break; + } + + if (!this.GUITables[i].IsVisible || this.GUITables[i].IsPinned) + { + continue; + } + + x += this.GUITables[i].Width; + } +#else + for (var i = 0; i < this.GUITables.Count; i++) + { + if (this.GUITables[i] == table) + { + x += this.GUITables[i].Width * 0.5f; + break; + } + + if (!this.GUITables[i].IsVisible || this.GUITables[i].IsPinned) + { + continue; + } + + x += this.GUITables[i].Width; + } +#endif + + x -= this.EntryScrollView.Bounds.width * 0.5f; + + x += this.PinnedWidth * 0.5f; + + this.EntryScrollView.ScrollTo(1.0f / 0.35f, xPosition: x, easing: Easing.OutQuad); + } + + private float rightMenuTopPanelHeight; + private Rect topPanelRect = Rect.zero; + private Rect bottomPanelRect = Rect.zero; + + private void DrawRightMenu(Rect position) + { + EditorGUI.DrawRect(position, OdinLocalizationGUI.WindowBackground); + + var topPanelMaxHeight = position.height - 32; + this.topPanelRect = position.TakeFromTop(this.WindowState.RightMenuTopPanelHeight); + var topSlideRect = this.topPanelRect.TakeFromBottom(14); + this.bottomPanelRect = position; + + this.WindowState.RightMenuTopPanelHeight += this.HorizontalSlideRect(topSlideRect); + // 183 is enough height to show exactly 3 collapsed entries. + this.WindowState.RightMenuTopPanelHeight = Mathf.Clamp(this.WindowState.RightMenuTopPanelHeight, 183, topPanelMaxHeight); + + EditorGUI.DrawRect(this.topPanelRect, OdinLocalizationGUI.Panel); + EditorGUI.DrawRect(this.bottomPanelRect, OdinLocalizationGUI.Panel); + + + EditorGUI.DrawRect(this.topPanelRect.AlignTop(32), OdinLocalizationGUI.TabsBackground); + EditorGUI.DrawRect(this.bottomPanelRect.AlignTop(32), OdinLocalizationGUI.TabsBackground); + + this.WindowState.CurrentTopTab = OdinLocalizationGUI.Tabs(this.topPanelRect.TakeFromTop(32), this.WindowState.CurrentTopTab, 115); + + this.WindowState.CurrentBottomTab = OdinLocalizationGUI.Tabs(this.bottomPanelRect.TakeFromTop(32), this.WindowState.CurrentBottomTab, 115); + + switch (this.WindowState.CurrentTopTab) + { + case OdinLocalizationEditorWindow.RightMenuTopTabs.Metadata: + this.DrawTopTabMetadata(this.topPanelRect); + break; + + case OdinLocalizationEditorWindow.RightMenuTopTabs.Settings: + this.DrawTopTabSettings(this.topPanelRect); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + switch (this.WindowState.CurrentBottomTab) + { + case OdinLocalizationEditorWindow.RightMenuBottomTabs.Locale: + this.DrawBottomTabLocale(this.bottomPanelRect); + break; + +#if false + case OdinLocalizationEditorWindow.RightMenuBottomTabs.Template: + this.DrawBottomTabTemplate(this.bottomPanelRect); + break; +#endif + + case OdinLocalizationEditorWindow.RightMenuBottomTabs.Settings: + this.DrawBottomTabSettings(this.bottomPanelRect); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + if (Event.current.type == EventType.MouseUp && Event.current.button == 0) + { + this.dragging = false; + GUIHelper.RequestRepaint(); + } + } + + private string metadataSearchTerm = string.Empty; + private SearchField metadataSearchField = new SearchField(); + + public InspectorProperty[] GetMetadataProperties() + { + InspectorProperty metadataCollection = + this.WindowState?.MetadataTree?.RootProperty?.Children[ + OdinLocalizationReflectionValues.TABLE_ENTRY_DATA__METADATA__PATH]; + + InspectorProperty items = + metadataCollection?.Children[OdinLocalizationReflectionValues.METADATA_COLLECTION__ITEMS__PATH]; + + return items?.Children.OrderBy(c => c.ValueEntry.TypeOfValue.Name).ToArray(); + } + + private LocalizationMetadata localizationMetadata; + + private void DrawTopTabMetadata(Rect rect) + { + if (Event.current.OnMouseDown(rect, 0, false)) + { + GUIHelper.RemoveFocusControl(); + } + + if (this.SelectionType == OdinTableSelectionType.None) + { + return; + } + + if (this.localizationMetadata == null) + { + this.localizationMetadata = new LocalizationMetadata(this.Collection, this.WindowState); + } + + switch (this.SelectionType) + { + case OdinTableSelectionType.None: + break; + case OdinTableSelectionType.SharedEntry: + this.localizationMetadata.Target = this.CurrentSelectedSharedEntry; + break; + case OdinTableSelectionType.SharedTable: + this.localizationMetadata.Target = this.Collection; + break; + case OdinTableSelectionType.Table: + this.localizationMetadata.Target = this.CurrentSelectedTable.Asset; + break; + case OdinTableSelectionType.TableEntry: + this.localizationMetadata.Target = this.CurrentSelectedEntry; + break; + default: + throw new ArgumentOutOfRangeException(); + } + + this.localizationMetadata.Draw(rect); + } + + private void DrawTopTabSettings(Rect position) + { + position = position.Padding(4); + + GUILayout.BeginArea(position); + { + bool value; + + switch (this.SelectionType) + { + case OdinTableSelectionType.Table: + EditorGUI.BeginChangeCheck(); + + value = GUILayout.Toggle(LocalizationEditorSettings.GetPreloadTableFlag(this.CurrentSelectedTable.Asset), "Preload Table"); + + if (EditorGUI.EndChangeCheck()) + { + LocalizationEditorSettings.SetPreloadTableFlag(this.CurrentSelectedTable.Asset, value, true); + } + + break; + + case OdinTableSelectionType.TableEntry: + if (this.CurrentSelectedEntry is StringTableEntry stringTableEntry) + { + EditorGUI.BeginChangeCheck(); + + value = GUILayout.Toggle(stringTableEntry.IsSmart, "Smart"); + + if (EditorGUI.EndChangeCheck()) + { + stringTableEntry.IsSmart = value; + EditorUtility.SetDirty(stringTableEntry.Table); + } + + break; + } + + goto default; + + default: + GUILayout.Label("No item with settings selected.", SirenixGUIStyles.LabelCentered); + break; + } + } + GUILayout.EndArea(); + } + + private Vector2 bottomTabLocaleScrollPosition = Vector2.zero; + + private List toggles; + private static FancyColor ThumbColorGrayscale = FancyColor.White; + private static FancyColor BackgroundColorGrayscale = FancyColor.Gray; + private static FancyColor BorderColorGrayscale = new FancyColor(0.91f, 0.91f, 0.91f); + private static readonly FancyColor EnabledColor = new FancyColor(EditorGUIUtility.isProSkin ? 0.66f : 0.86f); + private static readonly FancyColor DisabledColor = new FancyColor(EditorGUIUtility.isProSkin ? 0.46f : 0.66f); + private bool dragging; + private Toggle lastChangedToggle; + private bool newValue; + private Vector2 localeTabScrollPosition; + private void DrawBottomTabLocale(Rect position) + { + position = position.Padding(4); + + GUILayout.BeginArea(position); + { + ReadOnlyCollection projectLocales = LocalizationEditorSettings.GetLocales(); + + if (this.toggles == null) + { + this.toggles = new List + { + new Toggle + { + Label = "Key", + Toggled = this.KeyTable.IsVisible, + } + }; + this.toggles.AddRange(projectLocales.Select(locale => + { + this.LocaleTableMap.TryGetValue(locale, out var table); + + return new Toggle + { + Label = locale.LocaleName, + Toggled = table?.IsVisible ?? false, + }; + })); + } + + bool hasAllLocales = projectLocales.Count == this.LocaleTableMap.Count; + + if (!hasAllLocales) + { + if (SirenixEditorGUI.Button("Add Missing Locales", ButtonSizes.Large)) + { + for (var i = 0; i < projectLocales.Count; i++) + { + Locale locale = projectLocales[i]; + + if (this.LocaleTableMap.ContainsKey(locale)) + { + continue; + } + + LocalizationTable table = this.Collection.GetTable(locale.Identifier); + + if (table != null) + { + this.Collection.AddTable(table, true); + } + else + { + this.Collection.AddNewTable(locale.Identifier); + } + } + } + + GUILayout.Space(4); + SirenixEditorGUI.HorizontalLineSeparator(); + GUILayout.Space(4); + } + + const float LINE_HEIGHT = 20.0f; + const float LOCALE_SPACING = 2.0f; + + this.localeTabScrollPosition = GUILayout.BeginScrollView(this.localeTabScrollPosition /*, GUILayoutOptions.MaxHeight(MAX_HEIGHT)*/); + { + var keyRect = GUILayoutUtility.GetRect(0, LINE_HEIGHT, GUILayoutOptions.ExpandWidth()); + this.KeyTable.IsVisible = this.DrawLocaleToggle(ref keyRect, this.toggles[0], this.KeyTable); + + if (this.EntryScrollView.IsBeyondHorizontalBounds && this.KeyTable.IsVisible && + !this.KeyTable.IsPinned) + { + if (Event.current.OnMouseDown(keyRect, 0)) + { + this.MoveScrollPositionToTable(this.KeyTable); + } + } + + var lastLocaleIndex = projectLocales.Count - 1; + + for (var i = 0; i < projectLocales.Count; i++) + { + var locale = projectLocales[i]; + var toggle = this.toggles[i + 1]; + + var totalRect = GUILayoutUtility.GetRect( + width: 0, + height: LINE_HEIGHT, + options: GUILayoutOptions.ExpandWidth().ExpandHeight(false)); + + if (locale != null && !this.LocaleTableMap.ContainsKey(locale)) + { + GUIHelper.PushGUIEnabled(false); + { + this.DrawLocaleToggle(ref totalRect, toggle, null); + } + GUIHelper.PopGUIEnabled(); + + LocalizationTable looseTable = null; + + foreach (LocalizationTable localizationTable in this.LooseTables) + { + if (localizationTable.LocaleIdentifier == locale.Identifier) + { + looseTable = localizationTable; + break; + } + } + + var buttonRect = totalRect.TakeFromRight(80); + + if (looseTable != null) + { + if (GUI.Button(buttonRect, "Add")) + { + this.Collection.AddTable(looseTable, createUndo: false); + Undo.ClearUndo(looseTable); + Undo.ClearUndo(this.Collection); + FancyColor.PopBlend(); + GUIHelper.ExitGUI(false); + } + } + else + { + if (GUI.Button(buttonRect, "Create")) + { + this.Collection.AddNewTable(locale.Identifier); + Undo.ClearUndo(this.Collection); + FancyColor.PopBlend(); + GUIHelper.ExitGUI(false); + } + } + + if (i != lastLocaleIndex) + { + GUILayout.Space(LOCALE_SPACING); + } + + continue; + } + + + OdinGUITable table = locale == null ? this.KeyTable : this.LocaleTableMap[locale]; + + table.IsVisible = this.DrawLocaleToggle(ref totalRect, toggle, table); + + if (table.Type != OdinGUITable.GUITableType.Key) + { + Rect removeLocaleRect = totalRect.TakeFromRight(80); + + if (GUI.Button(removeLocaleRect, "Remove")) + { + if (EditorUtility.DisplayDialog("Odin Localization Editor", + $"Are you sure you want to remove the locale '{locale.Identifier.CultureInfo.EnglishName}' from '{this.Collection.name}'?\n" + + "This can have side effects that can't be undone.", + "Yes", + "No")) + { + this.Collection.RemoveTable(table.Asset, createUndo: false); + Undo.ClearUndo(table.Asset); + Undo.ClearUndo(this.Collection); + FancyColor.PopBlend(); + GUIHelper.ExitGUI(false); + } + } + } + + if (this.EntryScrollView.IsBeyondHorizontalBounds && table.IsVisible && !table.IsPinned) + { + if (Event.current.OnMouseDown(totalRect, 0)) + { + this.MoveScrollPositionToTable(table); + } + } + + if (i != lastLocaleIndex) + { + GUILayout.Space(LOCALE_SPACING); + } + } + } + GUILayout.EndScrollView(); + + GUILayout.Space(4); + SirenixEditorGUI.HorizontalLineSeparator(); + GUILayout.Space(4); + + if (SirenixEditorGUI.Button("Manage Locales", ButtonSizes.Medium)) + { + try + { + TwoWaySerializationBinder.Default.BindToType("UnityEditor.Localization.UI.LocaleGeneratorWindow, Unity.Localization.Editor")? + .GetMethod("ShowWindow", BindingFlags.Static | BindingFlags.Public)?.Invoke(null, null); + } + catch (NullReferenceException nullReferenceException) + { + Debug.LogError($"[Odin]: Failed to find LocaleGeneratorWindow.ShowWindow.\n{nullReferenceException.Message}"); + } + } + } + GUILayout.EndArea(); + } + + private bool DrawLocaleToggle(ref Rect rect, Toggle toggle, OdinGUITable table) + { + const int toggleWidth = 35; + + var toggleRect = rect.TakeFromLeft(toggleWidth).SubXMax(4).VerticalPadding(2).AddY(1); + var color = GUI.enabled ? toggle.CurrentColor : new Color(0.35f, 0.35f, 0.35f); + toggle.Enabled = GUI.enabled; + + // Draw toggle background + GUI.DrawTexture( + position: toggleRect, + image: Texture2D.whiteTexture, + scaleMode: ScaleMode.StretchToFill, + alphaBlend: false, + imageAspect: 1f, + color: BackgroundColorGrayscale.Blend(color, FancyColor.BlendMode.Multiply), + borderWidth: 0, + borderRadius: float.MaxValue); + + // Draw toggle thumb + GUI.DrawTexture( + position: toggle.CurrentThumbRect, + image: Texture2D.whiteTexture, + scaleMode: ScaleMode.StretchToFill, + alphaBlend: false, + imageAspect: 1f, + color: ThumbColorGrayscale.Blend(color, FancyColor.BlendMode.Multiply), + borderWidth: 0, + borderRadius: float.MaxValue); + + AnimateThumb(toggleRect, toggle); + + GUI.Label(rect, toggle.Label, + GUI.enabled && Event.current.IsMouseOver(rect) ? SirenixGUIStyles.WhiteLabel : SirenixGUIStyles.Label); + + if (GUI.enabled && Event.current.OnMouseDown(toggleRect, 0)) + { + this.dragging = true; + this.lastChangedToggle = toggle; + this.newValue = !toggle.Toggled; + toggle.Toggled = this.newValue; + + switch (Event.current.modifiers) + { + case EventModifiers.Control: + for (var i = 0; i < this.toggles.Count; i++) + { + if (!this.toggles[i].Enabled) continue; + this.toggles[i].Toggled = this.newValue; + } + + break; + case EventModifiers.Shift: + for (var i = 0; i < this.toggles.Count; i++) + { + if (!this.toggles[i].Enabled) continue; + this.toggles[i].Toggled = table.IsVisible; + } + + break; + case EventModifiers.Alt: + for (var i = 0; i < this.toggles.Count; i++) + { + if (!this.toggles[i].Enabled) continue; + this.toggles[i].Toggled = this.toggles[i] == toggle; + } + + break; + } + } + + if (GUI.enabled && this.dragging) + { + var mp = Event.current.mousePosition; + if (toggle != this.lastChangedToggle && toggleRect.y < mp.y && toggleRect.yMax > mp.y) + { + this.lastChangedToggle = toggle; + toggle.Toggled = this.newValue; + } + + GUIHelper.RequestRepaint(); + } + + if (Event.current.type == EventType.MouseUp && Event.current.button == 0) + { + this.dragging = false; + GUIHelper.RequestRepaint(); + } + + return toggle.Toggled; + } + + private static void AnimateThumb(Rect toggleRect, Toggle toggle) + { + const float thumbAnimationDurationInSeconds = 0.07f; + const float thumbAnimationSpeed = 1f / (thumbAnimationDurationInSeconds / 2f); // divided by 2 since the animation is split into 2 phases. + const float colorAnimationDurationInSeconds = 0.6f; + const float colorAnimationSpeed = 1f / colorAnimationDurationInSeconds; + + var targetRect = toggle.Toggled + ? toggleRect.AlignRight(toggleRect.height).AlignCenterY(toggleRect.height).Padding(2) + : toggleRect.AlignLeft(toggleRect.height).AlignCenterY(toggleRect.height).Padding(2); + + var targetColor = toggle.Toggled ? EnabledColor : DisabledColor; + + if (toggle.CurrentColor != (Color)targetColor) + { + if (toggle.T1.IsDone) + { + toggle.T1.Reset(0f); + } + + toggle.T1.Move(colorAnimationSpeed, Easing.InOutExpo); + toggle.CurrentColor = Color.Lerp(toggle.StartColor, targetColor, toggle.T1.GetValue()); + } + + if (Event.current.type == EventType.Repaint && toggle.CurrentThumbRect == Rect.zero) + { + toggle.CurrentThumbRect = targetRect; + toggle.CurrentColor = targetColor; + GUIHelper.RequestRepaint(); + } + + if (Event.current.type == EventType.Repaint && toggle.CurrentThumbRect != targetRect) + { + if (toggle.Toggled) + { + if (toggle.CurrentThumbRect.xMax < targetRect.xMax) + { + if (toggle.T2.IsDone) + { + toggle.T2.Reset(0f); + } + + toggle.T2.Move(thumbAnimationSpeed); + var xMax = Mathf.Lerp(toggle.StartXMax, targetRect.xMax, toggle.T2.GetValue()); + toggle.CurrentThumbRect.xMax = xMax; + } + else if (toggle.CurrentThumbRect.xMin < targetRect.xMin) + { + if (toggle.T2.IsDone) + { + toggle.T2.Reset(0f); + } + + toggle.T2.Move(thumbAnimationSpeed); + var xMin = Mathf.Lerp(toggle.StartXMin, targetRect.xMin, toggle.T2.GetValue()); + toggle.CurrentThumbRect.xMin = xMin; + } + } + else + { + if (toggle.CurrentThumbRect.xMin > targetRect.xMin) + { + if (toggle.T2.IsDone) + { + toggle.T2.Reset(0f); + } + + toggle.T2.Move(thumbAnimationSpeed); + var xMin = Mathf.Lerp(toggle.StartXMin, targetRect.xMin, toggle.T2.GetValue()); + toggle.CurrentThumbRect.xMin = xMin; + } + else if (toggle.CurrentThumbRect.xMax > targetRect.xMax) + { + if (toggle.T2.IsDone) + { + toggle.T2.Reset(0f); + } + + toggle.T2.Move(thumbAnimationSpeed); + var xMax = Mathf.Lerp(toggle.StartXMax, targetRect.xMax, toggle.T2.GetValue()); + toggle.CurrentThumbRect.xMax = xMax; + } + } + + GUIHelper.RequestRepaint(); + } + } + + private void LocaleToggle(Rect position, Locale locale) + { + if (locale != null && !this.LocaleTableMap.ContainsKey(locale)) + { + Rect createLocaleRect = position.TakeFromRight(80); + + GUIHelper.PushGUIEnabled(false); + { + GUI.Toggle(position.TakeFromLeft(GUI.skin.toggle.padding.left), false, GUIContent.none); + + GUI.Label(position, locale.LocaleName, SirenixGUIStyles.Label); + } + GUIHelper.PopGUIEnabled(); + + LocalizationTable looseTable = null; + + foreach (LocalizationTable localizationTable in this.LooseTables) + { + if (localizationTable.LocaleIdentifier == locale.Identifier) + { + looseTable = localizationTable; + break; + } + } + + if (looseTable != null) + { + if (GUI.Button(createLocaleRect, "Add")) + { + this.Collection.AddTable(looseTable, createUndo: true); + FancyColor.PopBlend(); + GUIHelper.ExitGUI(false); + } + } + else + { + if (GUI.Button(createLocaleRect, "Create")) + { + this.Collection.AddNewTable(locale.Identifier); + FancyColor.PopBlend(); + GUIHelper.ExitGUI(false); + } + } + + return; + } + + OdinGUITable table = locale == null ? this.KeyTable : this.LocaleTableMap[locale]; + + EditorGUI.BeginChangeCheck(); + { + table.IsVisible = GUI.Toggle(position.TakeFromLeft(GUI.skin.toggle.padding.left), table.IsVisible, GUIContent.none); + } + if (EditorGUI.EndChangeCheck()) + { + switch (Event.current.modifiers) + { + case EventModifiers.Shift: + for (var i = 0; i < this.GUITables.Count; i++) + { + this.GUITables[i].IsVisible = table.IsVisible; + } + + break; + + case EventModifiers.Alt: + for (var i = 0; i < this.GUITables.Count; i++) + { + this.GUITables[i].IsVisible = this.GUITables[i] == table; + } + + break; + } + } + + if (table.Type != OdinGUITable.GUITableType.Key) + { + Rect removeLocaleRect = position.TakeFromRight(80); + + if (GUI.Button(removeLocaleRect, "Remove")) + { + this.Collection.RemoveTable(table.Asset, createUndo: true); + FancyColor.PopBlend(); + GUIHelper.ExitGUI(false); + } + } + + if (this.EntryScrollView.IsBeyondHorizontalBounds && table.IsVisible && !table.IsPinned) + { + bool isMouseOver = Event.current.IsMouseOver(position); + + GUI.Label(position, table.DisplayName, isMouseOver ? SirenixGUIStyles.WhiteLabel : SirenixGUIStyles.Label); + + if (Event.current.OnMouseDown(position, 0)) + { + this.MoveScrollPositionToTable(table); + } + } + else + { + GUI.Label(position, table.DisplayName, SirenixGUIStyles.Label); + } + } + + protected bool IsDraggingAnything() + { + if (this.EntryScrollView.IsDraggingMouse || + this.EntryScrollView.IsDraggingHorizontalScrollBar || + this.EntryScrollView.IsDraggingVerticalScrollBar) + { + return true; + } + + for (var i = 0; i < this.GUITables.Count; i++) + { + if (this.GUITables[i].IsDraggingSlider) + { + return true; + } + } + + return false; + } + +#if false + private void DrawBottomTabTemplate(Rect position) + { + if (!this.Collection.SharedData.Metadata.HasMetadata()) + { + this.Collection.SharedData.Metadata.AddMetadata(new OdinTemplateMetadata()); + EditorUtility.SetDirty(this.Collection.SharedData); + } + + var templateMetadata = this.Collection.SharedData.Metadata.GetMetadata(); + + GUILayout.BeginArea(position); + { + GUILayout.BeginScrollView(Vector2.zero); + + int removedItemIndex = -1; + + for (var i = 0; i < templateMetadata.MetadataExpected.Count; i++) + { + if (OdinLocalizationStyles.Metadata(templateMetadata.MetadataExpected[i], i == 0)) + { + removedItemIndex = i; + } + } + + GUILayout.EndScrollView(); + + if (removedItemIndex != -1) + { + templateMetadata.MetadataExpected.RemoveAt(removedItemIndex); + EditorUtility.SetDirty(this.Collection.SharedData); + } + + Rect addMetadataRect = GUILayoutUtility.GetRect(0, (int) ButtonSizes.Large); + + if (GUI.Button(addMetadataRect, "Add Metadata")) + { + this.ShowAddMetadataTemplateSelector(addMetadataRect, templateMetadata); + } + + GUILayoutUtility.GetRect(0, 5); + } + GUILayout.EndArea(); + } + + private void ShowAddMetadataTemplateSelector(Rect rect, OdinTemplateMetadata templateMetadata) + { + TypeSelector selector = this.MakeMetadataSelector(); + + selector.SelectionConfirmed += types => + { + foreach (Type type in types) + { + if (templateMetadata.MetadataExpected.Contains(type) && !OdinLocalizationMetadataRegistry.MetadataAllowsMultiple[type]) + { + continue; + } + + templateMetadata.MetadataExpected.Add(type); + + EditorUtility.SetDirty(this.Collection.SharedData); + } + }; + + selector.ShowInPopup(rect); + } + + private TypeSelector MakeMetadataSelector() + { + TypeSelector selector; + + switch (this.Collection) + { + case AssetTableCollection _: + selector = new TypeSelector(OdinLocalizationMetadataRegistry.AssetEntryMetadataTypes, excludeInheritors: true); + break; + + case StringTableCollection _: + selector = new TypeSelector(OdinLocalizationMetadataRegistry.StringEntryMetadataTypes, excludeInheritors: true); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + return selector; + } + + protected bool HasMetadataAmountOfType(IList metadata, Type metadataType, OdinTemplateMetadata templateMetadata) + { + int count = CountMetadataType(metadata, metadataType); + + var expectedCount = 0; + + for (var i = 0; i < templateMetadata.MetadataExpected.Count; i++) + { + if (templateMetadata.MetadataExpected[i] == metadataType) + { + expectedCount++; + } + } + + return count >= expectedCount; + } + + protected static int CountMetadataType(IList metadata, Type metadataType) + { + var result = 0; + + for (var i = 0; i < metadata.Count; i++) + { + if (metadata[i].GetType() == metadataType) + { + result++; + } + } + + return result; + } +#endif + + + private void DrawBottomTabSettings(Rect position) + { + position = position.Padding(6); + + GUILayout.BeginArea(position); + { + // Table Collection Name + { + Rect namePosition = EditorGUILayout.GetControlRect(); + + GUI.Label(namePosition.TakeFromLeft(130), "Collection Name"); + + EditorGUI.BeginChangeCheck(); + + string value = SirenixEditorFields.DelayedTextField(namePosition, this.Collection.SharedData.TableCollectionName); + + if (EditorGUI.EndChangeCheck()) + { + if (!string.IsNullOrEmpty(value) && OdinLocalizationEditorSettings.IsTableNameValid(this.Collection.GetType(), value)) + { + this.Collection.SetTableCollectionName(value, true); + this.MenuItem.Name = this.Collection.SharedData.TableCollectionName; + this.MenuItem.Select(); + } + } + } + + // Preload All Tables + { + EditorGUI.BeginChangeCheck(); + + bool value = GUILayout.Toggle(this.Collection.IsPreloadTableFlagSet(), "Preload All Tables"); + + if (EditorGUI.EndChangeCheck()) + { + this.Collection.SetPreloadTableFlag(value, true); + } + } + + GUILayout.Space(4); + SirenixEditorGUI.HorizontalLineSeparator(); + GUILayout.Space(4); + + if (SirenixEditorGUI.Button("Manage Collection", ButtonSizes.Large)) + { + GUIHelper.OpenInspectorWindow(this.Collection); + } + } + GUILayout.EndArea(); + } + + private float VerticalSlideRect(Rect rect, bool connect) + { + var offset = SirenixEditorGUI.SlideRect(rect, MouseCursor.SplitResizeLeftRight).x; + + var slideThumbColor = Event.current.IsMouseOver(rect) + ? EditorGUIUtility.isProSkin ? new Color(0.5f, 0.5f, 0.5f) : new Color(1f, 1f, 1f) + : this.WindowState.RightMenuWidth > 0 + ? EditorGUIUtility.isProSkin ? new Color(0.25f, 0.25f, 0.25f) : new Color(0.8f, 0.8f, 0.8f) + : EditorGUIUtility.isProSkin + ? new Color(0.4f, 0.4f, 0.4f) + : new Color(1f, 1f, 1f); + + EditorGUI.DrawRect(rect, Event.current.IsMouseOver(rect) + ? EditorGUIUtility.isProSkin ? new Color(0.26f, 0.26f, 0.26f) : new Color(0.7f, 0.7f, 0.7f) + : EditorGUIUtility.isProSkin + ? new Color(0.2f, 0.2f, 0.2f) + : new Color(0.6f, 0.6f, 0.6f)); + + var h2 = connect ? this.WindowState.RightMenuTopPanelHeight : rect.height / 2f - 40; + + EditorGUI.DrawRect(rect.AlignLeft(1), new Color(0, 0, 0, 0.4f)); + var left = new Rect(rect.center.x - 1, 0, 1, rect.height); + EditorGUI.DrawRect(left, slideThumbColor); + + if (!connect) + { + var right = new Rect(rect.center.x + 1, rect.y, 1, rect.height); + EditorGUI.DrawRect(right, slideThumbColor); + EditorGUI.DrawRect(rect.AlignRight(1), new Color(0, 0, 0, 0.4f)); + } + else if (this.WindowState.RightMenuWidth > 0) + { + var crossTop = new Rect(rect.AlignCenterX(1).AddX(2).x, this.WindowState.RightMenuTopPanelHeight - (14 / 2 + 1), 4, 1); + var crossBottom = crossTop.AddY(2); + var rightTop = new Rect(rect.center.x + 1, 0, 1, crossTop.y + 1); + var rightBottom = new Rect(rect.center.x + 1, crossBottom.y, 1, rect.height - crossBottom.y); + EditorGUI.DrawRect(crossTop, slideThumbColor); + EditorGUI.DrawRect(crossBottom, slideThumbColor); + EditorGUI.DrawRect(rightTop, slideThumbColor); + EditorGUI.DrawRect(rightBottom, slideThumbColor); + EditorGUI.DrawRect(rect.AlignRight(1).SetHeight(connect ? h2 - 13 : rect.height), new Color(0, 0, 0, 0.4f)); + EditorGUI.DrawRect(rect.AlignRight(1).AddY(connect ? h2 - 1 : 0), new Color(0, 0, 0, 0.4f)); + } + else + { + var right = new Rect(rect.center.x + 1, 0, 1, rect.height); + EditorGUI.DrawRect(right, slideThumbColor); + EditorGUI.DrawRect(rect.AlignRight(1), new Color(0, 0, 0, 0.4f)); + } + + return offset; + } + + private float HorizontalSlideRect(Rect rect) + { + var offset = SirenixEditorGUI.SlideRect(rect, MouseCursor.SplitResizeUpDown).y; + + var slideThumbColor = Event.current.IsMouseOver(rect) + ? EditorGUIUtility.isProSkin ? new Color(0.5f, 0.5f, 0.5f) : new Color(1f, 1f, 1f) + : this.WindowState.RightMenuWidth > 0 + ? EditorGUIUtility.isProSkin ? new Color(0.25f, 0.25f, 0.25f) : new Color(0.8f, 0.8f, 0.8f) + : EditorGUIUtility.isProSkin + ? new Color(0.4f, 0.4f, 0.4f) + : new Color(1f, 1f, 1f); + + EditorGUI.DrawRect(rect, Event.current.IsMouseOver(rect) + ? EditorGUIUtility.isProSkin ? new Color(0.26f, 0.26f, 0.26f) : new Color(0.7f, 0.7f, 0.7f) + : EditorGUIUtility.isProSkin + ? new Color(0.2f, 0.2f, 0.2f) + : new Color(0.6f, 0.6f, 0.6f)); + + var top = new Rect(rect.x, rect.center.y - 1, rect.width, 1); + var bottom = new Rect(rect.x, rect.center.y + 1, rect.width, 1); + + EditorGUI.DrawRect(top, slideThumbColor); + EditorGUI.DrawRect(bottom, slideThumbColor); + EditorGUI.DrawRect(rect.AlignTop(1), new Color(0, 0, 0, 0.4f)); + EditorGUI.DrawRect(rect.AlignBottom(1), new Color(0, 0, 0, 0.4f)); + + return offset; + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTableCollectionEditor.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTableCollectionEditor.cs.meta new file mode 100644 index 00000000..dc9f6bde --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTableCollectionEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b8f34a530ab34e11b148c6c45d14fd12 +timeCreated: 1704797131 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTemplateMetadata.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTemplateMetadata.cs new file mode 100644 index 00000000..f7d091f8 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTemplateMetadata.cs @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Localization.Metadata; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ +#if false + [HideInInspector] // ReadOnly + [Metadata(AllowedTypes = MetadataType.SharedTableData, AllowMultiple = false)] + public class OdinTemplateMetadata : IMetadata + { + public Type TableAssetType; + public List MetadataExpected = new List(); + } +#endif +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTemplateMetadata.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTemplateMetadata.cs.meta new file mode 100644 index 00000000..1c71ccfd --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/OdinTemplateMetadata.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 50bcb1c3eb1c42b3b6a84c57b1c240dd +timeCreated: 1707729097 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/Toggle.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/Toggle.cs new file mode 100644 index 00000000..ef3a34da --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/Toggle.cs @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using Sirenix.OdinInspector.Editor.Internal; +using UnityEngine; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ + public class Toggle + { + public string Label; + public float StartXMin; + public float StartXMax; + public Color StartColor; + public Rect CurrentThumbRect; + public Color CurrentColor; + public bool Enabled; + public SirenixAnimationUtility.InterpolatedFloat T1 = new SirenixAnimationUtility.InterpolatedFloat {Destination = 1f}; + public SirenixAnimationUtility.InterpolatedFloat T2 = new SirenixAnimationUtility.InterpolatedFloat {Destination = 1f}; + + private bool _toggled; + + public bool Toggled + { + get => this._toggled; + set + { + this._toggled = value; + this.StartXMin = this.CurrentThumbRect.xMin; + this.StartXMax = this.CurrentThumbRect.xMax; + this.StartColor = this.CurrentColor; + } + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/Toggle.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/Toggle.cs.meta new file mode 100644 index 00000000..352c7047 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/GUI/Toggle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e4e8615bf94265a4a96bf4c23380de7a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal.meta new file mode 100644 index 00000000..52a89ad7 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7cf3a6e0f9c8495d843c313b4437232b +timeCreated: 1704797609 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationAssetCache.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationAssetCache.cs new file mode 100644 index 00000000..f31f9bba --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationAssetCache.cs @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using Sirenix.Config; +using UnityEditor; +using UnityEngine.Localization.Tables; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal +{ + public static class OdinLocalizationAssetCache + { + private readonly struct AssetIdentifier + { + public readonly Type AssetType; + public readonly string Guid; + + public AssetIdentifier(Type assetType, string guid) + { + this.AssetType = assetType; + this.Guid = guid; + } + } + + private static readonly Dictionary Assets = new Dictionary(); + + public static UnityEngine.Object Get(string guid, Type assetType) + { + if (string.IsNullOrEmpty(guid)) + { + return null; + } + + var identifier = new AssetIdentifier(assetType, guid); + + if (Assets.TryGetValue(identifier, out UnityEngine.Object result)) + { + return result; + } + + string path = AssetDatabase.GUIDToAssetPath(guid); + + result = AssetDatabase.LoadAssetAtPath(path, assetType); + + Assets.Add(identifier, result); + + return result; + } + + public static UnityEngine.Object Get(SharedTableData.SharedTableEntry sharedEntry, AssetTable assetTable, Type assetType) + { + AssetTableEntry entry = assetTable.GetEntry(sharedEntry.Id); + + if (entry == null || entry.IsEmpty) + { + return null; + } + + return Get(entry.Guid, assetType); + } + + public static void Clear() => Assets.Clear(); + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationAssetCache.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationAssetCache.cs.meta new file mode 100644 index 00000000..d1fed8a0 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationAssetCache.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f5b97d1f7b884ac99cc8ebcdeb3363a7 +timeCreated: 1704809501 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEditorSettings.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEditorSettings.cs new file mode 100644 index 00000000..f3aef061 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEditorSettings.cs @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using UnityEditor; +using UnityEditor.AddressableAssets.Settings; +using UnityEditor.Localization; +using UnityEngine; +using UnityEngine.Localization.Settings; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal +{ + public static class OdinLocalizationEditorSettings + { + public static LocalizationEditorSettings Instance => + (LocalizationEditorSettings) OdinLocalizationReflectionValues.LocalizationEditorSettings_Instance.GetValue(null); + + public static AddressableAssetSettings GetAddressableAssetSettings(bool create) + { + return OdinLocalizationReflectionValues.LocalizationEditorSettings_GetAddressableAssetSettingsFunc(create); + } + + public static bool IsTableNameValid(Type collectionType, string name, out string errorMessage) + { + errorMessage = (string) OdinLocalizationReflectionValues.LocalizationEditorSettings_IsTableNameValid.Invoke(Instance, + new object[] {collectionType, name}); + + return string.IsNullOrEmpty(errorMessage); + } + + public static bool IsTableNameValid(Type collectionType, string name) + { + return IsTableNameValid(collectionType, name, out string _); + } + + public static bool CreateDefaultLocalizationSettingsAsset() + { + if (LocalizationEditorSettings.ActiveLocalizationSettings != null) + { + return false; + } + + string localizationSettingsPath = EditorUtility.SaveFilePanelInProject("Create Localization Settings", + "Localization Settings", + "asset", + "Create the Localization Settings asset"); + + if (string.IsNullOrEmpty(localizationSettingsPath)) + { + return false; + } + + var settings = ScriptableObject.CreateInstance(); + settings.name = "Default Localization Settings"; + + + AssetDatabase.CreateAsset(settings, localizationSettingsPath); + AssetDatabase.SaveAssets(); + + LocalizationEditorSettings.ActiveLocalizationSettings = settings; + + return true; + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEditorSettings.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEditorSettings.cs.meta new file mode 100644 index 00000000..5f1e3a86 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEditorSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 645f3592e85448d7a963218911547f3c +timeCreated: 1706732179 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEvents.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEvents.cs new file mode 100644 index 00000000..f0e27ca4 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEvents.cs @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using UnityEditor.Localization; +using UnityEngine.Localization.Tables; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal +{ + public static class OdinLocalizationEvents + { + private static readonly Action RaiseTableEntryModifiedAction; + + private static readonly Action RaiseTableEntryAddedAction; + + private static readonly Action RaiseTableEntryRemovedAction; + + private static readonly Action RaiseAssetTableEntryAddedAction; + + private static readonly Action RaiseAssetTableEntryRemovedAction; + + static OdinLocalizationEvents() + { + RaiseTableEntryModifiedAction = OdinLocalizationReflectionValues.Create_RaiseTableEntryModified_Method_Delegate(LocalizationEditorSettings.EditorEvents); + RaiseTableEntryAddedAction = OdinLocalizationReflectionValues.Create_RaiseTableEntryAdded_Method_Delegate(LocalizationEditorSettings.EditorEvents); + RaiseTableEntryRemovedAction = OdinLocalizationReflectionValues.Create_RaiseTableEntryRemoved_Method_Delegate(LocalizationEditorSettings.EditorEvents); + RaiseAssetTableEntryAddedAction = + OdinLocalizationReflectionValues.Create_AssetTableEntryAdded_Method_Delegate(LocalizationEditorSettings.EditorEvents); + RaiseAssetTableEntryRemovedAction = + OdinLocalizationReflectionValues.Create_AssetTableEntryRemoved_Method_Delegate(LocalizationEditorSettings.EditorEvents); + } + + public static void RaiseTableEntryModified(SharedTableData.SharedTableEntry sharedEntry) => RaiseTableEntryModifiedAction(sharedEntry); + + public static void RaiseTableEntryAdded(LocalizationTableCollection collection, SharedTableData.SharedTableEntry sharedEntry) => + RaiseTableEntryAddedAction(collection, sharedEntry); + + public static void RaiseTableEntryRemoved(LocalizationTableCollection collection, SharedTableData.SharedTableEntry sharedEntry) => + RaiseTableEntryRemovedAction(collection, sharedEntry); + + public static void RaiseAssetTableEntryAdded(AssetTableCollection assetCollection, AssetTable table, AssetTableEntry entry) => + RaiseAssetTableEntryAddedAction(assetCollection, table, entry); + + public static void RaiseAssetTableEntryRemoved(AssetTableCollection assetCollection, AssetTable table, AssetTableEntry entry, string guid) => + RaiseAssetTableEntryRemovedAction(assetCollection, table, entry, guid); + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEvents.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEvents.cs.meta new file mode 100644 index 00000000..e7bebbb7 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationEvents.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a7cf7883289c4de29b6e2ae8a072c3bd +timeCreated: 1705050052 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUI.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUI.cs new file mode 100644 index 00000000..46b43266 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUI.cs @@ -0,0 +1,438 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using Sirenix.OdinInspector.Editor; +using Sirenix.OdinInspector.Editor.Internal; +using Sirenix.Reflection.Editor; +using Sirenix.Utilities; +using Sirenix.Utilities.Editor; +using UnityEditor; +using UnityEngine; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal +{ + // TODO: update name + public static class OdinLocalizationGUI + { + public static GUIStyle RichLabelTextCenteredMultiLine + { + get + { + if (_richLabelTextCenteredMultiLine == null) + { + _richLabelTextCenteredMultiLine = new GUIStyle(SirenixGUIStyles.MultiLineCenteredLabel) + { + richText = true + }; + } + + return _richLabelTextCenteredMultiLine; + } + } + + public static GUIStyle CardTitleWhite + { + get + { + if (_cardTitleWhite == null) + { + _cardTitleWhite = new GUIStyle(SirenixGUIStyles.MultiLineWhiteLabel) + { + fontSize = 12, + fontStyle = FontStyle.Bold + }; + } + + return _cardTitleWhite; + } + } + + public static FancyColor Background => EditorGUIUtility.isProSkin ? Color.black : Color.white; + + // TODO: LIGHT MODE + public static FancyColor MenuBackground => EditorGUIUtility.isProSkin ? DarkMenuBackground : LightMenuBackground; + public static FancyColor WindowBackground => EditorGUIUtility.isProSkin ? DarkWindowBackground : LightWindowBackground; + public static FancyColor Panel => EditorGUIUtility.isProSkin ? DarkPanel : LightPanel; + public static FancyColor PanelGap => EditorGUIUtility.isProSkin ? DarkPanelGap : LightPanelGap; + + public static FancyColor Selected => EditorGUIUtility.isProSkin ? DarkSelected : LightSelected; + + public static FancyColor TabsBackground => EditorGUIUtility.isProSkin ? DarkTabsBackground : LightTabsBackground; + + public static FancyColor ColumnBackground => EditorGUIUtility.isProSkin ? DarkColumnBackground : LightColumnBackground; + + public static FancyColor RowEvenBackground => EditorGUIUtility.isProSkin ? DarkRowEvenBackground : LightRowEvenBackground; + public static FancyColor RowEvenBackground2 => EditorGUIUtility.isProSkin ? DarkRowEvenBackground2 : LightRowEvenBackground2; + public static FancyColor RowOddBackground => EditorGUIUtility.isProSkin ? DarkRowOddBackground : LightRowOddBackground; + public static FancyColor RowOddBackground2 => EditorGUIUtility.isProSkin ? DarkRowOddBackground2 : LightRowOddBackground2; + public static FancyColor RowBorder => EditorGUIUtility.isProSkin ? DarkRowBorder : LightRowBorder; + public static FancyColor RowBorderHover => EditorGUIUtility.isProSkin ? DarkRowBorderHover : LightRowBorderHover; + + public static FancyColor Tab => EditorGUIUtility.isProSkin ? DarkTab : LightTab; + public static FancyColor TabHover => EditorGUIUtility.isProSkin ? DarkTabHover : LightTabHover; + public static FancyColor TabSelected => EditorGUIUtility.isProSkin ? DarkTabSelected : LightTabSelected; + + public static FancyColor Checkerboard => DarkCheckerboard; + public static FancyColor CheckerboardBorder => DarkCheckerboardBorder; + + public static FancyColor DarkMenuBackground; + + public static FancyColor DarkWindowBackground; + public static FancyColor DarkPanel; + public static FancyColor DarkPanelGap; + + public static FancyColor DarkSelected; + + public static FancyColor DarkTabsBackground; + + public static FancyColor DarkColumnBackground; + + public static FancyColor DarkRowEvenBackground; + public static FancyColor DarkRowEvenBackground2; + public static FancyColor DarkRowOddBackground; + public static FancyColor DarkRowOddBackground2; + public static FancyColor DarkRowBorder; + public static FancyColor DarkRowBorderHover; + + public static FancyColor DarkTab; + public static FancyColor DarkTabHover; + public static FancyColor DarkTabSelected; + + public static FancyColor LightMenuBackground; + + public static FancyColor LightWindowBackground; + public static FancyColor LightPanel; + public static FancyColor LightPanelGap; + + public static FancyColor LightSelected; + + public static FancyColor LightTabsBackground; + + public static FancyColor LightColumnBackground; + + public static FancyColor LightRowEvenBackground; + public static FancyColor LightRowEvenBackground2; + public static FancyColor LightRowOddBackground; + public static FancyColor LightRowOddBackground2; + public static FancyColor LightRowBorder; + public static FancyColor LightRowBorderHover; + + public static FancyColor LightTab; + public static FancyColor LightTabHover; + public static FancyColor LightTabSelected; + + + public static FancyColor DarkCheckerboard; + public static FancyColor DarkCheckerboardBorder; + + private static GUIStyle DefaultTextStyle; + + private static GUIStyle _richLabelTextCenteredMultiLine; + private static GUIStyle _cardTitleWhite; + + static OdinLocalizationGUI() + { + DarkWindowBackground = new Color(0.15f, 0.15f, 0.15f, 1.0f); + LightWindowBackground = new Color(0.358f, 0.358f, 0.358f, 1.0f); + + DarkPanel = new Color(0.2156863f, 0.2156863f, 0.2156863f, 1.0f); + DarkPanelGap = new Color(0.0856863f, 0.0856863f, 0.0856863f, 1.0f); + + LightPanel = new Color(0.761f, 0.761f, 0.761f, 1.0f); + LightPanelGap = new Color(0.532f, 0.532f, 0.532f, 1.0f); + + DarkSelected = new Color(0.7076807f, 0.8213042f, 0.8773585f, 1.0f); + LightSelected = new Color(0.358f, 0.446f, 0.509f, 1.0f); + + DarkTabsBackground = new Color(0.1886792f, 0.1886792f, 0.1886792f, 1.0f); + LightTabsBackground = new Color(0.538f, 0.538f, 0.538f, 1.0f); + + DarkColumnBackground = new Color(0.099f, 0.099f, 0.099f, 1.0f); + LightColumnBackground = new FancyColor(0.84f); + + var rowBaseBlend = new FancyColor(0.42f); + +#region DARK_ROW + + var darkRowEvenBackground = new FancyColor(0.2f); + var darkRowOddBackground = new FancyColor(0.16f); + + DarkRowEvenBackground = darkRowEvenBackground; + DarkRowOddBackground = darkRowOddBackground; + + DarkRowEvenBackground2 = darkRowEvenBackground.Blend(rowBaseBlend, FancyColor.BlendMode.Overlay); + DarkRowOddBackground2 = darkRowOddBackground.Blend(rowBaseBlend, FancyColor.BlendMode.Overlay); + + DarkRowBorder = new Color(0, 0, 0, 0.3607843f); + DarkRowBorderHover = new Color(1, 1, 1, 0.2470588f); + +#endregion + +#region LIGHT_ROW + + var lightRowEvenBackground = new FancyColor(0.711f); + var lightRowOddBackground = new FancyColor(0.611f); + + LightRowEvenBackground = lightRowEvenBackground; + LightRowOddBackground = lightRowOddBackground; + + LightRowEvenBackground2 = lightRowEvenBackground.Blend(rowBaseBlend, FancyColor.BlendMode.Overlay); + LightRowOddBackground2 = lightRowOddBackground.Blend(rowBaseBlend, FancyColor.BlendMode.Overlay); + + LightRowBorder = new Color(0, 0, 0, 0.2f); + LightRowBorderHover = new Color(1, 1, 1, 0.4f); + +#endregion + + DarkTab = new Color(0.254717f, 0.254717f, 0.254717f, 1.0f); + DarkTabHover = new Color(0.277f, 0.277f, 0.277f, 1.0f); + DarkTabSelected = new Color(0.312f, 0.312f, 0.312f, 1.0f); + + float scalar = 0.925f; + LightTab = new FancyColor(0.85f * scalar); + LightTabHover = new FancyColor(0.915f * scalar); + LightTabSelected = new FancyColor(0.95f * scalar); + + DarkCheckerboard = new Color(0.298f, 0.298f, 0.298f, 1.0f); + DarkCheckerboardBorder = new Color(0, 0, 0, 0.5529412f); + + DefaultTextStyle = new GUIStyle(SirenixGUIStyles.MultiLineCenteredLabel); + DefaultTextStyle.focused.textColor = DefaultTextStyle.normal.textColor; + + DarkMenuBackground = DarkPanel; + + LightMenuBackground = new FancyColor(0.84f); + } + + public static string TextField(Rect position, string text, out bool changed, int id) + { + if (id == 0) + { + GUI.Label(position, text, SirenixGUIStyles.MultiLineCenteredLabel); + changed = false; + return text; + } + + Color lastCursorColor = GUI.skin.settings.cursorColor; + + bool isMouseDown = Event.current.type == EventType.MouseDown; + + if (isMouseDown) + { + GUI.skin.settings.cursorColor = Color.clear; + } + + text = EditorGUI_Internals.DoTextField(id, position, text, DefaultTextStyle, null, out changed, false, true, false); + + if (isMouseDown) + { + GUI.skin.settings.cursorColor = lastCursorColor; + } + + return text; + } + + public static string TextFieldSyntaxHighlighted(Rect position, string text, string syntaxRichText, out bool changed, int id) + { + if (string.IsNullOrEmpty(syntaxRichText)) + { + return TextField(position, text, out changed, id); + } + + GUIHelper.PushContentColor(Color.clear); + text = EditorGUI_Internals.DoTextField(id, position, text, SirenixGUIStyles.MultiLineCenteredLabel, null, out changed, false, true, false); + GUIHelper.PopContentColor(); + + GUI.Label(position, syntaxRichText, RichLabelTextCenteredMultiLine); + + return text; + } + + public static bool ObjectPickerButton(Rect position) + { + bool isMouseOver = Event.current.IsMouseOver(position); + + var c1 = EditorGUIUtility.isProSkin + ? new Color(1, 1, 1, isMouseOver ? 0.8f : 0.3f) + : new Color(0, 0, 0, isMouseOver ? 0.8f : 0.3f); + + SdfIcons.DrawIcon(position, SdfIconType.StopCircle, c1); + + return Event.current.OnMouseDown(position, 0); + } + + public static TEnum Tabs(Rect rect, TEnum value, float width) where TEnum : Enum + { + EnumTypeUtilities.EnumMember[] infos = EnumTypeUtilities.VisibleEnumMemberInfos; + + for (var i = 0; i < infos.Length; i++) + { + Rect itemRect = rect.TakeFromLeft(width); + + bool isMouseOver = Event.current.IsMouseOver(itemRect); + bool isSelected = value.Equals(infos[i].Value); + bool isPressed = Event.current.OnMouseDown(itemRect, 0); + + Color tabColor; + + if (isSelected) + { + tabColor = TabSelected; + } + else if (isMouseOver) + { + tabColor = TabHover; + } + else + { + tabColor = Tab; + } + + SirenixEditorGUI.DrawSolidRect(itemRect, tabColor); + + Vector2 textSize = EditorStyles.label.CalcSize(GUIHelper.TempContent(infos[i].NiceName)); + float iconSize = textSize.y; + float size = iconSize + 2.0f + textSize.x + 1.0f; + + if (size > itemRect.width) + { + if (iconSize > itemRect.width) + { + continue; + } + + SdfIcons.DrawIcon(itemRect.AlignCenter(iconSize), infos[i].Icon); + } + else + { + Rect contentRect = itemRect.AlignCenter(size); + + Rect iconRect = contentRect.TakeFromLeft(iconSize).AlignMiddle(iconSize); + SdfIcons.DrawIcon(iconRect, infos[i].Icon); + + contentRect.TakeFromLeft(2); + + GUI.Label(contentRect, infos[i].NiceName); + } + + if (isPressed) + { + value = infos[i].Value; + } + } + + return value; + } + + public static bool OverlaidButton(Rect position, string text, SdfIconType icon = SdfIconType.None, GUIStyle labelStyle = null, bool invert = false) + { + const float ICON_SIZE = 18; + const float SPACE = 4; + + bool isMouseOver = Event.current.IsMouseOver(position); + + ref SirenixAnimationUtility.InterpolatedFloat t = ref SirenixAnimationUtility.GetTemporaryFloat(position, isMouseOver ? 1.0f : 0.0f); + + t.ChangeDestination(isMouseOver ? 1.0f : 0.0f); + + t.Move(1.0f / 0.15f, Easing.OutQuad); + + float value = t.GetValue(); + float inverseValue = 1.0f - value; + + if (invert) + { + SirenixEditorGUI.DrawRoundRect(position, new Color(0, 0, 0, 0.2f * inverseValue), 5.0f, new Color(0, 0, 0, 0.05f * inverseValue), 1); + SirenixEditorGUI.DrawRoundRect(position, new Color(0, 0, 0, 0.4f * value), 5.0f, new Color(1.0f, 1.0f, 1.0f, 0.05f * value), 1); + } + else + { + SirenixEditorGUI.DrawRoundRect(position, new Color(1, 1, 1, 0.2f * inverseValue), 5.0f, new Color(0, 0, 0, 0.05f * inverseValue), 1); + SirenixEditorGUI.DrawRoundRect(position, new Color(1, 1, 1, 0.4f * value), 5.0f, new Color(1.0f, 1.0f, 1.0f, 0.05f * value), 1); + } + + labelStyle = labelStyle ?? SirenixGUIStyles.WhiteLabelCentered; + + if (icon == SdfIconType.None) + { + GUI.Label(position, text, labelStyle); + } + else + { + float textWidth = SirenixGUIStyles.WhiteLabelCentered.CalcWidth(text); + + Rect contentPosition = position.AlignCenter(ICON_SIZE + SPACE + textWidth); + + SdfIcons.DrawIcon(contentPosition.AlignLeft(ICON_SIZE), icon, Color.white); + + GUI.Label(contentPosition.AlignRight(textWidth), text, labelStyle); + } + + return Event.current.OnMouseDown(position, 0); + } + + public static bool Metadata(Type metadataType, bool isFirst) + { + var isRemoved = false; + + if (isFirst) + { + EditorGUI.DrawRect(GUILayoutUtility.GetRect(0, 1), new FancyColor(0.06603771f)); + } + + Vector2 padding = new Vector2(4, 4); + Rect rect = GUILayoutUtility.GetRect(0, 24); + + if (Event.current.IsMouseOver(rect)) + { + EditorGUI.DrawRect(rect, new FancyColor(0.2784314f)); + } + else + { + EditorGUI.DrawRect(rect, new FancyColor(0.2431373f)); + } + + + EditorGUI.DrawRect(GUILayoutUtility.GetRect(0, 1), new FancyColor(0.06603771f)); + + var iconWidth = 16.0f; + var iconPadding = new Vector2(2, 2); + + SdfIcons.DrawIcon(rect.TakeFromLeft(iconWidth).Padding(iconPadding.x, iconPadding.y), SdfIconType.GripVertical); + + rect = rect.Padding(padding.x, 0); + + SdfIcons.DrawIcon(rect.TakeFromLeft(iconWidth).Padding(iconPadding.x, iconPadding.y), SdfIconType.Braces); + + rect.TakeFromLeft(5); + GUI.Label(rect, metadataType.GetNiceName(), SirenixGUIStyles.BoldLabel); + + var removeRect = rect.TakeFromRight(iconWidth).Padding(iconPadding.x, iconPadding.y); + + SdfIcons.DrawIcon(removeRect, SdfIconType.X); + + if (GUI.Button(removeRect, GUIContent.none, GUIStyle.none)) + { + isRemoved = true; + } + + return isRemoved; + } + + public static void DrawRoundBlur20(Rect position, Color color) + { + position = position.Expand(63); + SirenixEditorGUI.DrawTextureSliced(position, OdinLocalizationGUITextures.RoundBlur20, color, 63); + } + + public static void DrawRoundBlur6(Rect position, Color color) + { + position = position.Expand(10); + SirenixEditorGUI.DrawTextureSliced(position, OdinLocalizationGUITextures.RoundBlur6, color, 10); + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUI.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUI.cs.meta new file mode 100644 index 00000000..e85c5679 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUI.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 12ea4fb9c8db4471801a31e2e0b353de +timeCreated: 1704803170 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUITextures.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUITextures.cs new file mode 100644 index 00000000..882f4c72 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUITextures.cs @@ -0,0 +1,310 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using Sirenix.Utilities.Editor; +using UnityEngine; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal +{ + public static class OdinLocalizationGUITextures + { + private const float FADE_STRENGTH = 0.4f; + + public static Texture2D LeftToRightFade + { + get + { + if (leftToRightFade != null) + { + return leftToRightFade; + } + + leftToRightFade = new Texture2D(32, 32) + { + hideFlags = HideFlags.HideAndDontSave + }; + + var pixels = new Color[32 * 32]; + + var index = 0; + + for (var x = 0; x < 32; x++) + { + for (var y = 0; y < 32; y++) + { + pixels[index++] = Color.Lerp(Color.white, Color.clear, Mathf.Pow(y / 31.0f, FADE_STRENGTH)); + } + } + + leftToRightFade.SetPixels(pixels); + + leftToRightFade.Apply(); + + CleanupUtility.DestroyObjectOnAssemblyReload(leftToRightFade); + + return leftToRightFade; + } + } + + public static Texture2D TopToBottomFade + { + get + { + if (topToBottomFade != null) + { + return topToBottomFade; + } + + topToBottomFade = new Texture2D(32, 32) + { + hideFlags = HideFlags.HideAndDontSave + }; + + var pixels = new Color[32 * 32]; + + var index = 0; + + for (var x = 0; x < 32; x++) + { + for (var y = 0; y < 32; y++) + { + pixels[index++] = Color.Lerp(Color.white, Color.clear, Mathf.Pow(1.0f - x / 31.0f, FADE_STRENGTH)); + } + } + + topToBottomFade.SetPixels(pixels); + + topToBottomFade.Apply(); + + CleanupUtility.DestroyObjectOnAssemblyReload(topToBottomFade); + + return topToBottomFade; + } + } + + public static Texture2D BottomToTopFade + { + get + { + if (bottomToTopFade != null) + { + return bottomToTopFade; + } + + bottomToTopFade = new Texture2D(32, 32) + { + hideFlags = HideFlags.HideAndDontSave + }; + + var pixels = new Color[32 * 32]; + + var index = 0; + + for (var x = 0; x < 32; x++) + { + for (var y = 0; y < 32; y++) + { + pixels[index++] = Color.Lerp(Color.white, Color.clear, Mathf.Pow(x / 31.0f, FADE_STRENGTH)); + } + } + + bottomToTopFade.SetPixels(pixels); + + bottomToTopFade.Apply(); + + CleanupUtility.DestroyObjectOnAssemblyReload(bottomToTopFade); + + return bottomToTopFade; + } + } + + public static Texture2D RoundBlur6 + { + get + { + if (roundBlur6 == null) + { + const string BASE64 = + "iVBORw0KGgoAAAANSUhEUgAAAGYAAABmCAYAAAA53+RiAAAFDElEQVR4Ae2dzZIURRSFaQFBDGBBsHGB76Ju0BfxkXwRZaXvIgs3BhEiIT/DYHu+pG+R3XN6mDa6uZuTEacr61ZWVcb3kVXFaq5dSwuBELg6gVUN" + + "Xa/X1a1tHWO7G8bU8RqfrSdQYNnuhjPq+Dh7tXqP9cbY2/4p4J+pTK5PqRrbGqdu2iUEAP/vlHfqV6rO6VuCdsUAm5SQm+p/vgl9wjklSN3RIqlIvN/OkAv+uQ693eRMW8I+khhDW87bFcNBoFO/pdxW7ihfbML+" + + "LCdCBOSSVqulpLzW2FebvNSW/TcKxxG0tFnMvFKQgpC7yn3l3qZPreTkcSYYH2klhpWBBGS8UJ4r/AOHIY1xNZbtWBlsqzGQE4CPlAfKQ+Vb5XvlkcKjLe1wAjy6nio/K78pcKbxGKvHWT3SPrzA9VWGFKCzKlgl" + + "SPlK+VF5rKQdj8ATXeon5Q/lmcIKYjWd6atsyKmlpNqQxBcYcninIOcbJVIE4cgNpjyFYAxrmMN+eWc7MSwxBvMo+0FJOw0BXg0whjXMLxWDqHrH8Eh7pKSdhgBsYWw/pnZXDPt8qZUclljaaQjAtqTAHPb2Ucbt" + + "OTjLoZZ2OgIsgJIC96XNO9iq/8uUoGVgOichUJzZFv9xo1nMfOetQfOB9I9KYC/nfWKOevdc7HACTgwW0z4tgQvMnZhPO6XczRKIGIulvxgx/Q7sDCLGYukvRky/AzuDiLFY+osR0+/AziBiLJb+YsT0O7AziBiL" + + "pb8YMf0O7AwixmLpL0ZMvwM7g4ixWPqLEdPvwM4gYiyW/mLE9DuwM4gYi6W/GDH9DuwMIsZi6S9GTL8DO4OIsVj6ixHT78DOIGIslv5ixPQ7sDOIGIulvxgx/Q7sDCLGYukvRky/AzuDiLFY+osR0+/AziBiLJb+" + + "YsT0O7AziBiLpb8YMf0O7AwixmLpL0ZMvwM7g4ixWPqLEdPvwM4gYiyW/mLE9DuwM4gYi6W/GDH9DuwMIsZi6S9GTL8DO4OIsVj6ixHT78DOIGIslv5ixPQ7sDOIGIulvxgx/Q7sDCLGYukvRky/AzuDiLFY+osR" + + "0+/AziBiLJb+YsT0O7AziBiLpb8YMf0O7AwixmLpL0ZMvwM7g4ixWPqLEdPvwM4gYiyW/mLE9DuwM4gYi6W/GDH9DuwMnJjxZ8vt6BRPReACcyfmVDfPdQ8gsE8MBi9YPOC6GXo1Ans5z2JqEH9PvnK1y2fU/yVQ" + + "nNkW/3GtWQyFGniu/tsxIj+nJABjWBf35V6zGIwxoKS8Vv9sGZnOsQnAFsazHByM5sQwkBNeKk/HqPycggBsYVxy6nE27rUr5p2qiHmlvFB+UdJOQwC2MIY1zGG/d8VwkCXG4L+VX5UnStpxCcAUtjCGNcy3xCx/" + + "fHm9XtO/odxSvlTuKw+Uh8p3ymPla+WmknY4AVbF7worBSl/Ks+U58o/yhvlfLVajVWzK4ZHG3JuK3eUuwqC7m361DiGHMYu56ufdpEAkHl3zO9tHl+sFITQr/fM+DorMUioVhdhSWGPVhfF6F9KSeG8iBGEj7Ri" + + "On/p8ugiJQTWMN96+c9idGw0BnAhLsoJ9THAKiElZf5wyMoRmKnBrho8iyksCe8UQr+kqPuhLUD1jqlq1QBPrk+pGtsap27aJQRq1ZQgRFSqxulDgB5l41IL3EnMOKCfOsZ2N4yp4/TT9hOof/Fsd8NZdXxcocSM" + + "nfyEQAhckcB/Z2rwteNGzXsAAAAASUVORK5CYII="; + + byte[] bytes = Convert.FromBase64String(BASE64); + + roundBlur6 = TextureUtilities.LoadImage(102, 102, bytes); + + roundBlur6.hideFlags = HideFlags.HideAndDontSave; + + CleanupUtility.DestroyObjectOnAssemblyReload(roundBlur6); + } + + return roundBlur6; + } + } + + public static Texture2D RoundBlur20 + { + get + { + if (roundBlur20 == null) + { + const string BASE64 = + "iVBORw0KGgoAAAANSUhEUgAAAMEAAADBCAYAAAB2QtScAAAgAElEQVR4Ae2di5bjOI5Ep3f3//94t1fXruuMhEGKsmVnVlXiHDkCgQdBiazqeZ35z39+7OcN/LyBnzfw8wZ+3sDPG/h5Az9v4OcN/LyBnzfw8wZ+" + + "3sBf/Ab+6fb+77//dvKP9vwbaN/3pO3Ph5i8nEdD//zz+TP8z6ON/tK6z2/v9S/h2fV+LtHCN/q5BJ9f0rOH7nO3r/f29vNzSbZv9Ddfgr0D8vVH+PUTdO/gr7sYf8sl6D7264/Y77lC967+6IvxJ1+C7mP+nsfy" + + "66fOd/nHXYg/6RLkh/rKY/OuOb7qMNb9fdUcp33jP+ES1I9y2suJRu9YI5ZbokdmeuVBdY5XrrH0Qh5N+l0vgS/+0X2P6l7Vd7Teu/TRvs48uLnGmX1f/o5+t0uQL/rZl3NmrzrLK3uz1lmHrJvzjN72PaNXfben" + + "+7/LJfClPvsCnu3zbP2z81u/OscjhzB7P1LvjKC9nu2TPU/n3/0S+BIf3fij9Y/WzeZ8tOczB2i05mrPWr9aV9+DfR6tr/1O9b/rJfClPbLZo7VH85npkZpH9vLoWnuHrZt/r6bOspJf9+y6j9TWXqf53/ES+KKO" + + "bnK1bjWP9Y/k1nmfqa290l85QKO1Z7W1ZpbLPObv5eXscmofqbP+VPxOl8CXenSDK3Vn5TDbSq+jeziSv7p+d8i62i6v7nOUk3mznG5/znK0ruv1lPZdLoEvZHUzK/l7Oc/GnXWvj3mvxnqYRnPt5dU4c2evLr6a" + + "070Deo96dvmna7m5W/M3/u8J2vVvg/Rkr2YWf0Usp5z1z7yz+OrhmeW9Isb+Zn1H+3+kZtRrqNf/PUH70d50Cdq1h5N//tOops16jWJHddYc1RyZp+Y+4q8ellneKHZUZ/5RzV6s2/usV5d/WPsul2D1MLHBWe4o" + + "dkQ/krs3T36QUd/MeYSvHpJR3hH9SC57GeXvxep7mPWpuYf973AJjhyOWW4Xe4fGS+/WyY+xF8/cR/jeIRnFO/0dmnvs1jJW8UhurZ36X30JVg/HLK+LrWg1p/q8uKpV35c70rse1pyNs0MyilW9+sxYtT2/qxlp" + + "6FjteVXvf1fz7isnyldegtnByZFHeZ2+omVOctY86nc1zl57qXe4l3vk449yO71qR/y9XPZZc0baTCeW1vXM+GH+VZdg76O7kVFe1atPfWrJz4rVPvhYXeuqXn9nscxb5bMD0cWqNvPPirGXWa/ca83LWPLVvKwZ" + + "8q+4BKsHoctb0TInOS8h/aN8Vu8Lzp4zzVjFWn/kY3e5e1qNp3+Us5dRTY2578yfacYSu9qML/N3X4L6kbtBRzlVn/kZezVnD7lG57vPmqf+KI4OQtVnfsZezdlnrtH5vouap564kpP5LX/nJVg5AKOcqqefnE2m" + + "LxePxPdqaq8Vnxwt+6ut4OzD19jMz1jHVzVmNldMrfIVnxws+12V+9+VnPuqUN51CVY++Cin6unv8S4+02YxXptxMTVfa8a6uHl7scyTzz54jc18YyL95WKnzWJdfmozXmP4Wq6pVnElp9bc/O9yCerBccDUkxNP" + + "Xy5mXK3iSs6spqtHw6y7evf+nm684uhjV33kp95xtT1krkdyss692aeLdTlqidkj9SX+jktQD0Q3WM2Z+RnruNqrkPlr79Qq7/yRhr5n3QevWvodn2nGzkb2Zc8Zr7HOR6uWvWts6r/6EnhYZkN0Oant8S6ulpic" + + "eTq/00a5qa9y8jDXuXrHf7sPntoeN76CZ+Sww9ontRknhll/9frflZy7ynoJ3v1fpe4OQ2p73PgKdjmrGi+O3C7fl1pj1hjv/JXY7MPWWPpykbXkiXBm75CaGkM7anu96ec6lbtWxtVegix0Zw/+t0jbXtG8i6e2" + + "x413uKdlfMQZlVjGZxoxrOandkn49WNeaivcA5y5qclF8uSJyc3Z0zI+4rNexhJXOXmY6169+9+9+F3Fq/4m2PvAXTy1PW48MTkbxU+t8upnzSxm3gpmTuX4mGtdvfFv93FT67jaDDPWceZTTz6alNy6J7UO6ZN9" + + "R9z1Mq6WuBfP3Ja/+x+HHILBtT1uHExOfdUybqzTsnYvnrnyGWascnzNdfVFD6C+mHrH1TpMrXJ8ZgF90mf96qN1Zq+MqSUSt+eMZ5+X8TMuAZuZWY1X39rU5YmrvObh+7CWPLHqM9/YCmYOPI31O+OwdJa6fAUz" + + "B77im8OMWeNc6vodUuce5YnUjPpUvfp1vb14zf/kn3EJPjUsDsPNzLhIrjxxxmsM38d++h3OcmosffkMMwbXmGNmHJZqqclnmDF456uDzJS+POfInNST2wut42ogZk/xqt7/7sXvKxaVZy8Bgx2xzE9uD7XEGSdW" + + "42pHkPVn+TWevrzD1OCY81698a+HxIz05R2mBu98dZE15HvoPDOkh/vsuBqYRo1a8swZ8aP5tz7PXoJbo4b4Egyl33G1xBknlg/rpN/x/1rIsW7Ur+r6MySG0fuIeSCs0Z8hsYzri/SS7+H/ufAE2RN9qqG5346r" + + "JdIj+yWvMfxT7JlL4AafGcQeHaZWOf4zz5HLUNdhv6mlL0+svPPR0jgYaenLEyvvfLTVx8PPe1qpyVmTU8u7wjqulnjNfuyXteh1yJ65BLOF3Lg56cvFmpM6XF9+BEeH/aieazJv+nAsUZ565fh7lh+0cv1EuA+9" + + "5SPksHexUa2Xg/iq0d/3Acfw1dUugV8/xnGTd/6vksfh0UvgprqVj8YyXw76sIZ8BesBP+q7Rq1TB7H0q2b8kvgrt+NqHebh6Diaulykn7xiHnzmNl719MnB551k/uYuGTW+Iwr0Qcw5xNQuCeUn80ro1qvqQ//R" + + "SzBs2ARy84bVKhJHU09ffYT10KZ/lLtG1qlVrDPqd4iG0WNmHg5z9DtES12/Qw4ya2fMw44ur/3yAsAx3g15+mgzs6d7Tx/uXGL26rSMP8VffQncMEMmz6HVQbn5ajOsBzV9uUgfecWMuZ45+nuYc8tBjFotuVqi" + + "BwSt42jq8hVk3czjAKvJwdp7ky4aMd4JcThW/as6/rW37wDfGWpV6slr3lP+I5fA4buFZzHzzRHRK8dfefKQdlxtFVnT3Mo73xndQ+cTw4hpydUSPSholeuDPubpix5ofdaVE9OXd0g+lkge7wkNjuHLL8LOj/2c" + + "gXS5OGsxy5nF7no+cgnumgwEBtGSo+mLavipoWvGOuwOLpp6h51G76qn1q3dacysLgcx9BXzkJArBytX65C9oHM4QXw5cxiDo4sb/bRO+uTZBz15tzfWqJZ5xtHgojXpJzf+NB69BAwxslnMmi4HrepqifRIn5eP" + + "v4KZs8K7vqnlHB3PWeUgRv6KeTjIlYOVq3XogWfPctYnN7FeAGJomOvh0wdTA9UTL0m/8rr9Wk+e8dTUq4ae5h5Sk89i5lzw6CX4VLzouEnT9UE5Mf1ncHbAa2zmMwNxc/SdLXW1xNyPPLFyfC0/vDyxcvz6cCDR" + + "mLNy5iSWCCfPPcAx9av3cfiJ25scrGJql4TBD7Ng1uds18g1Zp7aaXjWJXADDlZ99NQqT98e1hCrj4cwsfLOR/Ohp1xMTd5havDu2eSbLgcx8meWH1wOVq6W6AFFk4P4zo0PV3N+9fS3tJuRr9kbX56ITp+0rFc3" + + "xxh+xzPfOFrmm3MIj1wChz2yQK1JPzk98esz0jOPA6zvYRbR5StY89OXg5WrVcz55aBGflp+XHR9sHK1ihxENTmYB9Q5yZOL5I2M/DRyqePdyomjYSLcteBp2ZN8/eTkVz97PMWPXILVhXLj1nQaMfRHH168tSsH" + + "fDWHnjVXrUNnGOHW7jYnHCN3Zh4EcuD68hlyGImD7CPRg+o+0h/Nnzr5ndnH9dyfSE3yrgea+8w4dVXvtKyRL+WdcQlWNudQIPkrNZknB3nR+nlY1cSMjfh//+o3iruecf3EXE8OYp1/jVx/zVOrH1sf9CFXDnIA" + + "O18drIc0tTrjlj411soa3o1W13F/1pAHn1nWzPIyRs1e38z/xM+4BJ8ahuNmKpqC/ujDi6+1HtQZdoe+07oerKcudwZ0ecUtdIvBMXJmlh8Uri+vyOFTk3vQmU0O1vn0t9DF9EczOov5tafr2Ye87JXcHonZn1z8" + + "ipn/NF+9BHuDO8he3l6cPuSYJ+/QA7mKedhHfLUX85Cb2M2Y2pZ+21fl+FoeAjm493AYyUmE10OaMyXfUj+9e/zOnKmLodX10FgHozb5RRz8kDdbay9u29281Utgw0fRjVMPTz81dXOO4ugQe+hF8uSitdVXT2Qu" + + "/MTKnT33Jwc18rD6wfXBytU88PjyRPjo+d8t5ozOIG6hu2+EhjmLPHt0nDz7ivSQ2wfE1F0HX35JOPvn2UvgwHUudZF4cn201NNXV/PQ6Yvd4UzNQ72H1JhjffXRWde4PNG5Km5lt70Sm5kfPRHePXnoiXvomVEu" + + "1oOfPvM4l4hWLWdiDQ297tkZ0I2L1Nkr11Mjbl3y1NC1kW58iM9egmycG0k9+SgH3Zh8hh5C0LzURtxDvYr2Md/11NPPOeQgpi9PhKd5CBIrx/fwJ/ewG9PPw46mz7rOCO8s1ybe+fTgXWjk2Ne9p09e5tjTepD8" + + "Tj+ak/ktP/MStAtsops3ni9FnjG4NcZFD58+mJq8Yh5iOTji1hvX79AZEnM+ufvCXzEOgIdAXjEPOzEPvejaYB5+fC8CiHVzuT5xeSKcd4LBXU+NNdVAcxI3eXdta8k93VYuAQN0tqdnPPmolzmJ8NnDyyaeh7P6" + + "xrpD7yGvMWoyZo8RumbOSi4+lvpV+YjpixwQDe6DBudgqaXvwR9hHvqci76Y2tX7/Ot6qHL2B8dA/dxrx8lFFzd66wOvZi66XJzlZmyUf8lZuQTZ7BnOINXQ1OWr6CHrEI3Hg6yv5qE33vnmJmaf5MyMn1j3sYUv" + + "cXQtOZqHSq4P1sfLkLh6AfJCsBZWZ0HLNfGx1NgzfsW69+rTx/WoT05MQyf+UjvrEriJOmzV8fMx37xE88iRi/XApQ/PJw+4h34WN2eEWStnLniis1bc0m4fHd6ZHx4cPXn4yXnmAjhDrqWWmHE5+4ZXzH3TI33y" + + "8RPNATXimvn64kg3votnXYLdhSYJbAJLlHc6sXzy8MH3nnopRj59Hr0IzOdczrpJt7nhGLG0/OhwfTno4Zfrd5cg/8Rnrdk//+cc8lx3xtkrcfeaSC984pgx8xOvGW/+PfsSsEFMrPwS/PXjy8icWmdOh3nI4Eee" + + "0cGvOj1XLwIzOpNY5x7tFV3zsCTC88mDj756AZzHy+CaFXOtVe7+yXedDoljxMwV0Y3DNXPx5aI5D+PeJWChzkZ6l4tGfn3MtVdizU0/D1jyzIETq4+HXF1/huSuXATXTKwz4WPi1bv/9SCA3bN3Cbq/AVhz9fCz" + + "X2y0drcv3hP5xJJ3uWjmJm5y+27IWTV71/yR/p+9S1AbHfFZdM/MSYQfeTzQFemRmgcZzUOfXK3DzOt65lry0R54J8Qw8epdDwbcjw52T14CODOBHn76yhM3eWisw94x18SXg66VWvLRnkc6tcQSN3do5g4THgmc" + + "cQkY7BGzrmLtRfzIk4e043xYdTm491CT+faoyKxos5m38MXISeMwaHm4Kq+XAN/18jKsXAB7s7eOsxf15Lmma8/QfSWST++KmbPCrV/Jvcs54xLUpgykJVcDq64Pjh7riNdDhu9jXH+E9UDvXYIuv+vt+qN9oGPi" + + "1fv45VBgHrwOu0uAdvQCuI6YF4G9sbaYnHVSx3e/6u5PvUN6ooNa9Tt9lGPuITzzEjBYZ+j1MS9rzCEmN56+nJftUzV8YqJ5o4OcOnz20Mv85LmWHOyeTb7oYDUPBNg9HkAPHjPA/VOf9eTgzOjPXmZIf+K5bmru" + + "NWete97KP72H9KnTT/5LvoGxm7AR1un0zNnlZ16C3cVKAhvAxKv32a8vc+bzYYzD9x4Pcodos4fe1nXrMAe68yRu8m3P6Gl+0ER4PhxGfNDH/vhnXABmZw33JhdZhzWJy51hhlv6xcihl6Yvqr8FH7kEDLpneznG" + + "E+H1YZ29HOJ54Pxw6vpiHl55h2ijh17WJM814fryTbrtET4yDoiHRM5hg7OeB0/uwWcdTLx697/0Yf4O3Y9r4ctZF9913Z/zuM/ELf02T+rJ6Y+fSF1n5nUxtZUcc1/67w7dFimEAVeMvKMPH8caPxa+vEMPc4do" + + "o8de1unnes5ScWt7OxjwNA4CBuZDf3wPHL6c/nkRZv8YRA9m7tA9EJO7jr5rqte97flb612jBzO8xWZ/EzDIns1yulhqcrB7WHsvhzgfo6Kauh9whB7kPPDkpl+58a6n6ybCMVCuD+ZHh+vLOXxwe3oY7Qd6ETba" + + "GvXso8PcB/H04a6njq8u5izJt9TbnlNP7t5AjJj8Igy0ldg0Z3YJLDyCDJ6WfnJzOq3GyKl5ajP0YyWan5qcwyEH9evhr3rWJGctfNcUN+m2H7Q0P3oinIdeefDsl5i9KqcHs3eYc7sOfauesVy38lzbGBp8ZMSY" + + "LS215ORUP+sO8bMuAQONjFiNp5+cHpnfcfONJfKR0q+8+6ip5QFH15frg8mzh5y14XUGfEy8eh+/HgSwPtSgjXp+dPlg9mBeeEXn7dA//bv1yO/irJz56VeOj7mvyvVBZu8sa7v4rnbWJdhdaCehvjTS0bSM7/Hu" + + "Yz6i5SGX0wfuo9/1Z070bt5N/rQ/fM2P7eFNpBf+ak9ymRWTg8wldrOvaN0F6OZC0yo3n1m+zL7iEvgixG7zxnxJZ+LeB84DX3Pz8BPL3OTMSzwx98Ce8TvzQID5eOhE+9Ue1qNnPfPgO7ccv3vsfyZuSw33bYy5" + + "WBN8i73iErCBkdVY56uJtRe6Mfke5kc2N7URz4Ndub7Y9ci15OwHjolX7+PDewBAHg8+nBr8tNqHGLnMZg85c6J186LRiyfjanu4ld3q4dWox+xz9a6/aMylVV8dnMUyb4m/4hKsLMwmNLhPanD1xKpXP3Ph+WH1" + + "R5p6HgD47KCbmzmjdXI25h6ZB1ekDu7h7/p4gKwRPfz4zkofefaCdzpazUt/C3+KV99cdRBTB50/+SXp1T/vvARsbsXME7MGTV0+w/rx0vdjp0avqqdfuQdfzLi9EuFYxav6cRA8EB5kDq0atV4G6xKtSWSuPPj6" + + "zlbnVgd9yJHPkFmMw9PQMfHqjX/Jc9/jrCcjr7oEK5vscqqGn4/bVcOX72F+xOTU5SHo+ErOrK7O5txgNT96HmLqOcTiSo31efjRRnM6I/GOq42QmYzBMX3xql5/0aqhuf8a01/JMXcJj16CbvCVhbLuKM/+1Pqg" + + "y49g/cjp1z55YIilv8KtqX2dHazmIfAQi+QZW63xAlDHvF6kOo97UceXH0Hmynz8asS1yt0fesetW8HsMc0/egmyWW4gdblxUb0i8cyRqydSW+Ojflm3wj0Iidap4ctBfTFjNW4vEBOv3sevHx/MhwxjH9lXjbWI" + + "ifC8AB7+Op8z5myrPGeAWycX1UV1EFPv9nbNuOYQJ3clz7olfOYS1AUYsFqn1Zwjvv1EauH5pJY8c+B5AJJnnofG/JpnXL3zrbVvzgSvxkfODw73QIvWEGNNLOtcM2vJ8zI4i3n67kM/kTX0k6emngg/w1iH/aR1" + + "WsaX+JmXYGVBhsbEq/fxqy5+RO5ryBk91I1iz+r1oNNPLXt7oMSMwTHx6t0ffnV6eAHyIKNjHA564XsZMq+u/Yy/LTF9t8S1uj90NdFcEd391ENvzqn47ktwZHheRj7Wjl5exs1ZwVxjxD3Ionmdj6Ze0TpmhVdD" + + "ywOQB5peXgTrzKWOePXNX70Q9HWuGRojv7OMw/Pp8r9U+06XIF/c6KWY40s1T7/GU38VHx30kZ4zOv8e5gXIA+3BF1+1x9qXeVNzfjXj6iMk/8vtjEvw6EaybsT3XhB1WUu+/iNov3ege3NO/hTH8OUX4dePeWfP" + + "RvvsfcT/Ndql3h5qM8zcyru9z3oRo8cjdZe+o0uQg10SX/TDOqtrmSd2IxlbQXLyoV/6yY2JGXuU0wujfsXMW12Pnns15tSe6itITrVct8Y63/UfPshd04HGWp/WGV2CQf3bZF+iC674Ncda0TiYnHhq6Wde5sgz" + + "vlc3qqEOsxcfSH4J/PpBM2YvQuaqrfrWZv5IQ+/MNTNmP7U937wvw3degvoyVjeddZXjp0ZP/YoZg2vm4cPTr9osPqtb6UOO5mHH908t19YnpjZb2xzytarVevJSk1es/YyP6s1fRfrlflfrDuXxL6i+k9WXmP7K" + + "nKP81OWifTs/tcrxU6OPmpgaHMvYUd9a8dLw148aqFWePjkjP/Xk9u1qM9Zx+mSv5F3+27SvuATv2LxriKMXalwkD179rM+Y+Ymz3IzVmtp3lmvMGrHqnU9u5stFayoaF2v8TP8da9zm/YpLcFv8zeSMF/tMD2qz" + + "PrmvIrWab84qZq/Vmpp3Ro/a89v5f9Ml+HYv/2eg7/EG/qZLcMa/wHqmB7VZn9zTkFrNN2cVs9dqTc07o0ft+e38r7gE73ixriGOXrxxkTx49bM+Y+YnznIzVmtq31muMWvEqnc+uZkvF62paFys8TP9d6xxm/cr" + + "LsFt8Ybk5uHpN+l30ig/dblok85PrXL81OijJqYGxzJ21LdWvDT89aMGapWnT87ITz25fbvajHWcPtkreZf/Nu2d/zkBm37kX2jly6ocPzVenH7FjME18/Dh6VdtFh/VuWfj+HL6VzMGysmpfmqZl3rl+FjtVevN" + + "uST/yk+t5tsv9RG35wpmj5X8h3LeeQmODFg3v+LXnLqecTA5eamln3mZI894rfOwm0NcM4ZfLwla9k9eY/qiuSt+5shFZxbRO8v1jNeaPd+6L8PRJWBwP84rh+te4mg9X6bY5RlbQXLyoV/6yY2JGZtx88E9o482" + + "67kao5c9RzXm1Lj6CpJTLdetsc53/S52tuZst76jS3BLWCA0feTCZF0OlnxveXJrvv4jaL+zkPmdI/eC5jurcX3wHQ9z5ZpHfHIx57x6+7+uZ60Vqaut4KN1l95nXIKVIVdyVjZiDiint75a+q/m/vf7RdfD5994" + + "EJlzxbo90ANdzDU63firkH1kb3xMTX4RJz/kf7l9p0tQX4YvVDS+9+Iy39wZmj9DD7JoLj5/oqfvnHkBzCPXhxr/NrAGDbNfIj3wQZ8urpb5anu4tb6ssYf0mVnG65qzui+JvfsS8EI8NPUA8AJ8eWK+lKrhjx57" + + "jeLP6BwuD7J7QcP06Y+WF4E4Rsx6eDVnQ5ePDjS6MXL1U7PHWZhzdT2Ja8SrqYlH4zX/af/MS8Cm+LhpnZbxo9wXJ1IPzye15JkDz4OSPPM8VB5m9gc3h7h7BvE1a0TiPtRbZ75IDHMN0RmdaeRnfs0xlmgOWvLM" + + "gWNqyVNTT4SfYc6QvTot40v8mUvAAKMPyeLGxdFAxHk0uXoiOTVunZj5RzgHoD4eeHT22h1oY2A13w+YD3nGak3dn3ugPxzMR000lj48fXNAY0dxK/1k1iPCMbVE9UtC5Oh3mP26uJp5+kt49BKwyOjjzRbMuhm3" + + "x2gz6D7kyo9gfngPw6jeOHvuLoDvQmSmNHUwH3KMZT6cWbA6k3M70wrSI/NqT31z0pcfwTo3fjX6aUe5dSuYvaf5Ry/BtFkEGWD0kU3rcurg+PlkrbkZn3E+tPHkaB6CiuwBbXQB3KO4pd6Mvv5NApJjXkWLqMES" + + "na9inXXkWzeKo5Pjk35y4yN0buJazVUHM0+904yJKznmLuGrLkG3OMP78bu4mpsU1UE0dfkM60dMH56PfVLz8KN5iBM3uTV6UYuRj5918Jk5S0VnQ5fzf9YHF9UrZg0xe3d55prT+RlLvrW+9YankYeJV2/8u5o3" + + "7rAQeeclyHHYnAcB7mOOm1dPJGfmZwyeH1y/aqnnwSePOf1/hewOsvvY0m5zeQHoSxzf2o3e9p616ORjic4GyuvBnfnd5cg+9hXtpW9u+pU7s3r1q04cUwe15GovxVdcAjZRP66bqLHO9yWI1oroxuR76IcFzU2t" + + "cuZHy8OffAt9MudRdA36cAHw4T4bnb4j4vYQnT3RGUF5HvoRN7/DXM+42h7m3PBq1GP2uXrXX2Nq1VcHZ7HMW+KvuAR7C7MBDoPY5btJ8OzHDztCDztx5hThxDT8as7qwRfJzYe6rh59tndmId7NXg98+jV/FjPX" + + "vZyJuT94tdx7jb3M/4pL0G0mX7RxXwh+xve4H/FR5HBSy0GBg5iH+Opdf52xm4l8dOsSqcbvbNaTuYh3e/Ngg3L3oS929Ue1bs8jzX26N/zMNf4leNYlYEN7HzXjmZ+cl+DLGXHiGTMf5EP6p2/q8r0PzSFhzopq" + + "W6g1+ydSgw/mQwN8TLx613w4dVj2gzO/2O2FuT3oyclNPf2uj1pdP31y0pdv8ic9/crxMWq15GjZ15zEmp+xJX7WJXAxBsoPm37yUb466OZAuXG1GfohE70gqck5JMzeoet22M1AT9YS6et7SU4/9dyjPYnL6QVP" + + "hOeTBx2eD3kZTz97wHPNUcycDrcWNzOOAB9ZF0stOT2qP+q7q88uAYv4gUaNZjldLDU52D2siY51cTU+Uh44P9rswLMv80AOB1qHm9ya6+f/OySa88BdB8yHhvidUYfZv6Jzo8vdg34e/uSZh56+tSN0DuOun2hO" + + "Rfcj1ji+sV/09u31azz1vZi5rqN/wdkl+JR4osMgowOQy3Qvak/jA9XD7wXx44Gsr98dfOLoI2OOPPzyuj59WB/kwZJflftf90lETu/k+qAHOjla99Sc9OHdw7rqOYP8CG6tdo1+b7NHLgED+kFHg+7lGE+E14f+" + + "aFiNpc8Hmh1+5vUjcjD05R1uaUPrZmL9nMHDz7oefN9bRRfKvmi5Rzn94O6nIntBA0eP8Q5Tg+eT69Z59CtuLXa/Yc3BHxn993Q2/NkAABGMSURBVGwl59bjkUtwK36SMCiHQbRdbgC++vCx8hDi0z8xeXfwyUef" + + "GfP4Jz/ryV3fwy/Ssz70R+vM/Y/2zTrEwOTMrSYHRw+5mZe+fUbobMTlK7ilX4zcNH0xYy/nZ14CNtB92G5j5mUNXF8OYunL+QAerjz8aPjERTR8Pjp8hlt4aKztoRfRWCdnyHWdMZEF8DujH+Y+O/TwgfXpDjZa" + + "9/hOZmh/5oCL8vRTy7m3sk/7SR+Oka9lrdxYYtakfoifeQlcmMH8wMmNg1XXB0ePdcR52axhrj5aHsDU5WJehK3sNjO8M9by4Ise/kRnAEeP/YmnsYbm3jpkD+hg9xy5CF4O+ljX9Uytrp0zkqe/0RtXSzQOasQ7" + + "Sz15l3tIO+MSMFD9mCtDWFex1hI/8vARPHwd9/CTU3ldW5/1PfgiB1/OOl4E+sKdoeIWur0vYmmsgyV2e/eggcnzEMsT4fWxh3n4yY1X7OaaaVvbO8t9EtS/S9wRHq27tD3jEozmY7D6kWuuOYnwIw8fpx40fA5i" + + "xviw5sExfPlFaH6YxcNekZiHn7U8/KLrJbIE/szoi43eA2sRS4T7eIg7ROseatVrH33X1E8czTrTtyVvB5+8PVvJ2etxF9+7BCzafbCRfrfAL6Eb3r72SoSPHl48tcST13xj5PrwkbHqX9X7X3rWg6/v4Rfp6eEX" + + "XUdkBTgmXr2PX9bEEru9obFHEZ4Pe8Xv0MNe0Xp1/cQ6iz45HVeruKV/2iM+VvPwj9gof6T/Z+8SHFmcXBbi44qpwdMcKvNrHf7o4aVbK8dfefjIM3NND3xFD77Imsn1cxbW03dt/DTW1ZwBXw7Ww6YP1od9oiVW" + + "jl+f2mfPdz7y5CPcUi5GHEuUXyMfv6nLxY+sB9nZl+CRMdgMhyFRTj83C3YPL5560YM2wi1113LNvQvgn/oVZ+sTm1mu3+0ZzQOXCK9PHnpi6dfDn37tM/KdhXg36ybfvqGcPHniVX3z71mXgE11H7bqbt5tWmNe" + + "Irzz0Xjh1I6QmM9G78wYWI3+9eDr+6d9RfrsXQLWcV04VtdnbQ2uL0/00CXCuycPPvH0Pfi1Tl2scXzmEZ2t+uiY8fTVLwklx7yMyRNrXsaW+FmXYGUxhh19dHQ3kwgfPbxs6kaYa8F9NvrJXA8R7oGvWA9++vT2" + + "EriOPn3VQC25Gljncf/G6iHTB2dPHnzy0q9cP/tVjbmId+jMK7i1uH1jeBr1L7eVS8Ag3Qfb0zOevNsUcYx1zAX3Hj4CNSAG1wcxtat3/+vaROSu60XIA185/T3womsm0l8fvmfOQJ68YncI0UZPHmQ5uMdrP+ZI" + + "Tb/O1/lb6d17RhsZPTS5qC4e1S91K5fABR5FBuPja92gxs1NhPvw4jHyO424vSpaB6bRBwM99CIaBzt91siLwDp5+JPnDMm3ktuc8LScBx0/NX3mqBzNh5jcQ1599RGSb8zaxFzDWUYaOlZRLevNuRREjf6peOYl" + + "YHA/9GjIUY6bpl6eCE+fD6FZo+YMI7SuYvaH18Pvwadv5Wrg6GE9Yph49e5/cxai+KOHfRNLrByfpx7o9OUVrR2hc2VcbYbbOLc9watRu2crOXs9nv63SBmi+6DqIoMk1wcxe5CD4ZsP8vCSOXz6IrpGnb49jSVS" + + "iyXaL//UR/PAJzoLa6CDlattoVtcDmrkYc5y9T585zJHH2QOfXkifPR42I3ri+hy0VzQ9dWcI9E8NCxRrm7dJfHXT80xN3Pkmau2hGf+TTBbkAHrx9anLjeArt8h2ujhpWvZXw20Z/LaLw98x+nt4RfRZg/r5UzJ" + + "iWkr89V58T1wifDVJw/6iNde3RwrGnvNPHxNPX35S3D1EjDY6KPlYHt5e3F6kYOxnjwRzsMHmdnevPahh3yGrOeFoHce/uTE8tncmw/HVma7Zn7Mhj+aj9mMycHK1TrMg2+804yN0Dn2kP3MjPqZ7cWt3c1bvQQ2" + + "PIIszseuaI+94TIuB3n4ABw8fbVNutnooGWNyakldx0Pvz6968GvPr3J89EHtToja6fp50zE02emzlcHfciTd3jGoadvzpN8Cw1j5pGjoWEVr+pJv2dcAgasH3M2nhvaq8k8OchL9gLANTVyzDM2Q/NF+9PPnmrM" + + "jJYInz1b+BaHY+TPjFk058KXz5BZiXeI5mOO/irWtbPOGJocxPSv3vh3NS87uEZqy/yMS1AXY6D6kTuNupXhM0cO8qLzkNKvGnlHnuyZa7COMZD9eRmOXID6XqrPmmn67oGYfITMZ0wOVt755o2Qvl2srqd/FLf2" + + "d0aPap1Wc/CX8o5cAhrWj9YtnFqtST85NfjVWC91eSIfZXQZZvPSoz6uh25fkVge/OTE6rNJN00OYuTOzP2RI3dWNf2KzKsm7zA1uA+18oo1VtfR73A0N7pGnZYcrfrmPY1HLsFsMQbMD1t9alOrnHjW42Pkpenz" + + "cTiEieSpwbHak3ofcuUi+XD7wtH0QXzXgXfPJt90eWLl+BpravLEyvHrw5xqlesnwo/69K91aq5dcSu5M3MM4GvJR1qXY+4SnnUJZosxJAdF03d4Y/rmdZg58kQ+igeUenvDySOGwfXlIPmJ9kOvHG30bKFbTA5i" + + "1KwYc2hysHK1DplZXd5haiucnubZ/1HMPdIDE6/eva9+Ch69BAw3+oizmMN2OW44+6pZBxJPnY/ggRftIapbD9IjLwMcjcc1El0HDQ7uPVvKLUcOYtSuWO5V7pzUy1cwD6x8hkdirF/znWk0J7qWuWog+p7Ncmax" + + "T32PXoJPxTsOQ/jBk1OmL6qBmHVX7/pLbjU1PoIHWw5i9qpIrRdAJMeZ7IMGB8kD67NJn7T04Rg1WnK1RPeFVrk+6GOevsjccjD9jqutYu2Za8mZrZoxUJOLVcevMXOewkcuAYOMPuIs5qDmiOiVm7uHfKw8/MyF" + + "j+6Miazjga9orbPgJ8e3L7x7Nvmmy0GMfC25WiLrah1HU5evoIfbXH0RXS7uabWX/h7u7c/4COk/slnsruaRS3DXZCIwjB88eZaoO3jmZ96I87GoWbkM9gZZz4tgvbPgmyMf4ZZ6yTWu3yEaRu7MfBfm6HeIlrp+" + + "h3mwjad2lNMja+w5w9wTeZh49T5+U0/+kXECe/UlYESGrx9draL5oDWrm+djUDO7DPYEeehNvlwdzAuSOhybacYvib9yO67WYe6742jqcpF+8oqjQ1v1o35dp/ruUT195000PtIy/hR/9BKwEQ9DHeBoLPPloMY6" + + "6auP0MtAHQc5fXt58PVB8+XOoi+yrlxUE9G1ETc+w9x35fqJcB/6ykdYD7p5R3XrRlhn0Qcx6jCx8kswfjIv5AudxWruxX/0ErTNQmSQ/Pjpy0XL9EV0OEYv+UVY/KkXgD7OJU907dQqZ+nU0pcnVt75aGl1r+nL" + + "EyvvfLTVZ3QJVuszj33pu0d8TLx6H7+pJ5/VfFQfZM9cAobjMDxj9uiQvvT3JSTv1rRHHtDK+RsAG12OzCcvfXnV9WdIDKPHEXPv1ujPkFjG9UV6yfdw5TLM+mWs42qJ8EeN/Ry2Zy7B3mIMlB89/Y6rJbIGPdCw" + + "5Ffl+mtNah0fHX5y6T16ajx9eYepwTHWWDH3bG768g5Tg3e+usga8kewq0+t42qJM15j+KfYs5eAF7b6URk485O7GbVEYqyBhiW/KuNf+1AzeqhejWWufIYZg2usNzP3mjmpyWeYMXjnq+/FzGMeeWLV0++4Gpjm" + + "HGjJM2fEj+bf+jx7CW6NBoTBZh/cuEgbeSI6fdCw5Ffl86+1de3UiflUnW7G7KFvbAUzB55m39Tg7nGmm7OCmQNf8Ud5I925a+9OVwOxrLkq/a95ffQJ9YxLwHCjD8poNV59x09dnkge66DNzJrMUXPO9NVAH2pn" + + "3HiHqVWOr7muvjjaX+odV+swtcpX/L0cZicn82YaMcz8q/fxW/Xqf2Re2V685n/yz7gEnxouOgztIdjjxutGqa9aXd7aupa1oE/NVacnHNvDzKkcH7PH1Rv/dntLreNqM8xY5dVnOjSf9Ds+04yBmGvN+CXx1T9n" + + "XQI2NPu4XTy1PW48Md8NaxObmbXkjDh93Edicur1k3cacS3jaivY7Ss1uUhPeWJyc/a0jI/4rJexxFVOHua6V+/+dy9+X1GU9sP8++/Dfdt+sWYXT22PG1/BLmdVY2Ryu3y3U2PWGO/8ldjs5ddY+nKRteSJyWtO" + + "F1vVaq/OT23GiWGuffXuf/fi9xWb8s8/frpr+Ky/CdrFGpGhP09w3ahaxjuuBj5iWV/XrDF91jFXTG3GiWFZd1WO/Xb7TW2PG1/BM3LYXe2T2owTw6y/ei/8bT/OE38TMGrbs+yh5sz8jHVc7VWYe3KN1Crv/JGG" + + "vmfdYaha+h2facbORvZlzxmvsc5Hq5a9a2zq178J8qPeCp+8BPRp+94WGMezLnntaUzMuFrFlZxZTVePhll39e79Pd14xdGHrvrIT73janvIXI/kZJ17s08X63LUErNH6kv8u1wChq0Hxw1UPf093sVn2iyWM3Z5" + + "K/OaI2YftRnOPnaNzXxjImvKxU6bxbr81Ga8xvC1XFOt4kpOrbn577oELLjywUc5VZ/5GZOLdQ51MeOdlvHKV3xytOyvtoKzD15jMz9jHV/VmNlcMbXKV3xysOx3Ve5/V3Luq0J55yVg2ZUPP8qp+szP2Kt5t69c" + + "M1730v4zf4+PDkDVZ37GXs3ZT67R+e655qknruRkfsvffQkYYnRA6oBd3oqWOcnr2hlb4bN6Z88+M81YxVp/5CN3uXtajad/lLOXUU2Nue/Mn2nGErvajC/zr7gEDFc/9mjgUV7Vq1/XqPH0kx+pq7nuofZTH+Vn" + + "/CifHYQuVrWZf1aMPc165Z5rXsaSr+ZlzZB/1SVgoNlhyYFHeZ2+omVO8m6mvXhX4+y1Vr3DvdwjH32U2+lVO+Lv5bLPmjPSZjqxtK5nxg/zr7wEDLv38d3QLK+LrWg1p/rdfF1Ol+fce7HMe5bPDscoVvXqM1PV" + + "9vyuZqShY7XnVb3/Xc27r5woX30JGG10sLqxZ7ld7B3ayh66Obr9PartHY5RvNPfobnPbi1jFY/k1tqp/x0uAQMeOSSz3FHsiH4k98jso77TD7QQXD0co7wj+pFcRh/l78Xqtmd9au5h/7tcAgY/ekhm+Y/ERjUj" + + "/cjMsx6HP1pTsHpIZnmj2FGd8UY1e7Fma9NeXf5h7TtdAoZ/5LDs1czir4jlR5j1z7yz+Ozw5RqzvFfEWHvWN2dL/khN1i/x73YJHPro4VnJ38t5Nv7o7NadjasHaC/v2Tj72utR9340v9Yf8r/rJWATe4dytNGV" + + "urNynplzNP+r9NWDtZJ3Vk6315XeXd3DWr0E7/7fE8wG92WsHNjss1JnDnWj/plj/y63yzM/savN+KN8df2u/5Ha1dzVvDrPo3W1z9P+d7oEboaX88gB8qXu1ZrnerP8mrtSY86o1vgr8ZG1j9Yczc/9PlObfU7h" + + "3/ESsDFf0uyAjl6AtcRX6jPfnnt1XY21I9zrOap7ZK1RL/VHez5a9+y61r8Ev+slcLO+9LMO0Gof13UOcbXe/MRRz8w5mz+75rP17uesPvY7Fb/7JXCzvsRnDiG97AN/pFfW02Nkj/Qe9er01Tm62j3tzN5n9tqb" + + "++H473IJ3KAv9YxDZi97g2f0pU/XG/272avmfFXfl7y/3+0S+BLyJZ91cOmdfV0LPHON7PsuPtrXmeu/Y40z5731+l0vwW0DG/Hlv/KgukauO+KvnCPXPDJT1p3Nv8scD+/rT7gEbr5+jHcdRtcX6xzqfwr+cfv7" + + "ky5BPWT5sb7qQtSZflc/3+Xvuofh3H/yJchNdx/x52LkG/rg3bv6iP6B7G+5BN2n6z7233YxunfQvas/WvubL0H3YfcOxe92Sfb2072Dv077uQTHPvmzh+roJXp2vWO7+8n+eQM/b+DnDfy8gZ838PMGft7Azxv4" + + "eQM/b+DnDfyFb+D/AdGPZ94O5ww1AAAAAElFTkSuQmCC"; + + byte[] bytes = Convert.FromBase64String(BASE64); + + roundBlur20 = TextureUtilities.LoadImage(193, 193, bytes); + + roundBlur20.hideFlags = HideFlags.HideAndDontSave; + + CleanupUtility.DestroyObjectOnAssemblyReload(roundBlur20); + } + + return roundBlur20; + } + } + + private static Texture2D leftToRightFade; + private static Texture2D topToBottomFade; + private static Texture2D bottomToTopFade; + private static Texture2D roundBlur6; + private static Texture2D roundBlur20; + + public static void Clear() + { + if (leftToRightFade != null) + { + UnityEngine.Object.Destroy(leftToRightFade); + } + + if (topToBottomFade != null) + { + UnityEngine.Object.Destroy(topToBottomFade); + } + + if (bottomToTopFade != null) + { + UnityEngine.Object.Destroy(bottomToTopFade); + } + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUITextures.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUITextures.cs.meta new file mode 100644 index 00000000..1fb902d6 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationGUITextures.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 78842ff15d254d9b8994383880b759f1 +timeCreated: 1704903394 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationMetadataRegistry.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationMetadataRegistry.cs new file mode 100644 index 00000000..9a70db77 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationMetadataRegistry.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEditor; +using UnityEngine.Localization.Metadata; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal +{ + public class OdinLocalizationMetadataRegistry + { + public static readonly List AssetEntryMetadataTypes = new List(); + + public static readonly List StringEntryMetadataTypes = new List(); + + public static readonly Dictionary MetadataAllowsMultiple = new Dictionary(); + + static OdinLocalizationMetadataRegistry() + { + TypeCache.TypeCollection metadataTypes = TypeCache.GetTypesDerivedFrom(typeof(IMetadata)); + + for (var i = 0; i < metadataTypes.Count; i++) + { + Type currentType = metadataTypes[i]; + + var attr = currentType.GetCustomAttribute(); + + if (attr is null) + { + MetadataAllowsMultiple[currentType] = true; + continue; + } + + MetadataAllowsMultiple[currentType] = attr.AllowMultiple; + + MetadataType currentAllowedTypes = attr.AllowedTypes; + + if (currentAllowedTypes.HasFlag(MetadataType.StringTableEntry)) + { + StringEntryMetadataTypes.Add(currentType); + } + + if (currentAllowedTypes.HasFlag(MetadataType.AssetTableEntry)) + { + AssetEntryMetadataTypes.Add(currentType); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationMetadataRegistry.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationMetadataRegistry.cs.meta new file mode 100644 index 00000000..e4701807 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationMetadataRegistry.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7deb6e0b5f6c4579ba2ead2af5620d3e +timeCreated: 1706509329 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValidator.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValidator.cs new file mode 100644 index 00000000..54e3fc26 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValidator.cs @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#if SIRENIX_INTERNAL +using System.Collections; +using System.Reflection; +using Sirenix.OdinInspector.Editor.Validation; +using Sirenix.OdinInspector.Modules.Localization.Editor.Internal; + +[assembly: RegisterValidator(typeof(OdinLocalizationReflectionValidator))] +namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal +{ + public class OdinLocalizationReflectionValidator : GlobalValidator + { + public override IEnumerable RunValidation(ValidationResult result) + { + OdinLocalizationReflectionValues.EnsureInit(); + + FieldInfo[] fields = typeof(OdinLocalizationReflectionValues).GetFields(BindingFlags.Static | BindingFlags.Public); + + for (var i = 0; i < fields.Length; i++) + { + if (fields[i].IsLiteral) + { + continue; + } + + if (fields[i].GetValue(null) != null) + { + continue; + } + + result.AddError($"[Odin Localization Module]: {fields[i].Name} was not found."); + } + + return null; + } + } +} +#endif \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValidator.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValidator.cs.meta new file mode 100644 index 00000000..bb30c80b --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValidator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c12d37dc91fe4285bd661d45717bf34a +timeCreated: 1704797628 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValues.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValues.cs new file mode 100644 index 00000000..62426c16 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValues.cs @@ -0,0 +1,174 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using System.Reflection; +using Sirenix.Serialization; +using UnityEditor.AddressableAssets.Settings; +using UnityEditor.Localization; +using UnityEngine.Localization; +using UnityEngine.Localization.Metadata; +using UnityEngine.Localization.Tables; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal +{ + public static class OdinLocalizationReflectionValues + { + public const string TABLE_ENTRY_DATA__METADATA__PATH = "m_Metadata"; + public const string METADATA_COLLECTION__ITEMS__PATH = "m_Items"; + public const string TABLE_ENTRY__DATA__PATH = "Data"; + + public static readonly bool HasAPIForCustomUndo = false; + + public static readonly PropertyInfo TableEntry_Data_Property; + public static readonly PropertyInfo AssetTableEntry_Data_Property; + public static readonly PropertyInfo StringTableEntry_Data_Property; + + public static readonly FieldInfo TableEntryData_m_Metadata_Field; + public static readonly FieldInfo MetadataCollection_m_Items_Field; + + public static readonly PropertyInfo LocalizationEditorSettings_Instance; + public static readonly MethodInfo LocalizationEditorSettings_GetAddressableAssetSettings; + public static readonly MethodInfo LocalizationEditorSettings_IsTableNameValid; + + public static readonly MethodInfo RaiseTableEntryModified_Method; + public static readonly MethodInfo RaiseTableEntryAdded_Method; + public static readonly MethodInfo RaiseTableEntryRemoved_Method; + public static readonly MethodInfo RaiseAssetTableEntryAdded_Method; + public static readonly MethodInfo RaiseAssetTableEntryRemoved_Method; + public static readonly MethodInfo AssetTableCollection_SetEntryAssetType_PrivateMethod; + public static readonly MethodInfo AssetTableCollection_RemoveEntryAssetType_PrivateMethod; + + public static readonly Type AddressHelper; + public static readonly MethodInfo FormatAssetLabelMethod; + public static readonly MethodInfo UpdateAssetGroupMethod; + + private const BindingFlags INSTANCE_ALL = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + private const BindingFlags INSTANCE_NON_PUBLIC = BindingFlags.Instance | BindingFlags.NonPublic; + private const BindingFlags STATIC_NON_PUBLIC = BindingFlags.Static | BindingFlags.NonPublic; + + internal static Func LocalizationEditorSettings_GetAddressableAssetSettingsFunc; + + static OdinLocalizationReflectionValues() + { + TableEntry_Data_Property = typeof(TableEntry).GetProperty(TABLE_ENTRY__DATA__PATH, INSTANCE_ALL); + AssetTableEntry_Data_Property = typeof(AssetTableEntry).BaseType.GetProperty(TABLE_ENTRY__DATA__PATH, INSTANCE_ALL); + StringTableEntry_Data_Property = typeof(StringTableEntry).BaseType.GetProperty(TABLE_ENTRY__DATA__PATH, INSTANCE_ALL); + + Type tableEntryDataType = TwoWaySerializationBinder.Default.BindToType("UnityEngine.Localization.Tables.TableEntryData, Unity.Localization"); + + TableEntryData_m_Metadata_Field = tableEntryDataType?.GetField(TABLE_ENTRY_DATA__METADATA__PATH, INSTANCE_ALL); + MetadataCollection_m_Items_Field = typeof(MetadataCollection).GetField(METADATA_COLLECTION__ITEMS__PATH, INSTANCE_ALL); + + LocalizationEditorSettings_Instance = typeof(LocalizationEditorSettings).GetProperty("Instance", STATIC_NON_PUBLIC); + LocalizationEditorSettings_GetAddressableAssetSettings = typeof(LocalizationEditorSettings).GetMethod("GetAddressableAssetSettings", + INSTANCE_NON_PUBLIC); + LocalizationEditorSettings_IsTableNameValid = typeof(LocalizationEditorSettings).GetMethod("IsTableNameValid", INSTANCE_NON_PUBLIC); + + + RaiseTableEntryModified_Method = LocalizationEditorSettings.EditorEvents.GetType().GetMethod("RaiseTableEntryModified", INSTANCE_ALL); + RaiseTableEntryAdded_Method = LocalizationEditorSettings.EditorEvents.GetType().GetMethod("RaiseTableEntryAdded", INSTANCE_ALL); + RaiseTableEntryRemoved_Method = LocalizationEditorSettings.EditorEvents.GetType().GetMethod("RaiseTableEntryRemoved", INSTANCE_ALL); + RaiseAssetTableEntryAdded_Method = LocalizationEditorSettings.EditorEvents.GetType().GetMethod("RaiseAssetTableEntryAdded", INSTANCE_ALL); + RaiseAssetTableEntryRemoved_Method = LocalizationEditorSettings.EditorEvents.GetType().GetMethod("RaiseAssetTableEntryRemoved", INSTANCE_ALL); + AssetTableCollection_SetEntryAssetType_PrivateMethod = typeof(AssetTableCollection).GetMethod("SetEntryAssetType", INSTANCE_NON_PUBLIC); + AssetTableCollection_RemoveEntryAssetType_PrivateMethod = typeof(AssetTableCollection).GetMethod("RemoveEntryAssetType", INSTANCE_NON_PUBLIC); + + if (LocalizationEditorSettings_Instance != null && LocalizationEditorSettings_GetAddressableAssetSettings != null) + { + LocalizationEditorSettings_GetAddressableAssetSettingsFunc = + (Func) Delegate.CreateDelegate(typeof(Func), + LocalizationEditorSettings_Instance.GetValue(null), + LocalizationEditorSettings_GetAddressableAssetSettings); + } + + AddressHelper = TwoWaySerializationBinder.Default.BindToType("UnityEngine.Localization.AddressHelper"); + + FormatAssetLabelMethod = AddressHelper?.GetMethod("FormatAssetLabel", + BindingFlags.Static | BindingFlags.Public, + null, + new[] {typeof(LocaleIdentifier)}, + null); + + UpdateAssetGroupMethod = typeof(AssetTableCollection).GetMethod("UpdateAssetGroup", + BindingFlags.Instance | BindingFlags.NonPublic, + null, + new[] + { + typeof(AddressableAssetSettings), + typeof(AddressableAssetEntry), + typeof(bool) + }, + null); + + HasAPIForCustomUndo = AddressHelper != null && + FormatAssetLabelMethod != null && + UpdateAssetGroupMethod != null && + LocalizationEditorSettings_GetAddressableAssetSettingsFunc != null; + } + + public static Action Create_RaiseTableEntryModified_Method_Delegate(LocalizationEditorEvents events) + { + return (Action) Delegate.CreateDelegate(typeof(Action), + events, + RaiseTableEntryModified_Method); + } + + public static Action Create_RaiseTableEntryAdded_Method_Delegate( + LocalizationEditorEvents events) + { + return (Action) + Delegate.CreateDelegate(typeof(Action), + events, + RaiseTableEntryAdded_Method); + } + + public static Action Create_RaiseTableEntryRemoved_Method_Delegate( + LocalizationEditorEvents events) + { + return (Action) + Delegate.CreateDelegate(typeof(Action), + events, + RaiseTableEntryRemoved_Method); + } + + public static Action Create_AssetTableEntryAdded_Method_Delegate( + LocalizationEditorEvents events) + { + return (Action) + Delegate.CreateDelegate(typeof(Action), + events, + RaiseAssetTableEntryAdded_Method); + } + + public static Action Create_AssetTableEntryRemoved_Method_Delegate( + LocalizationEditorEvents events) + { + return (Action) + Delegate.CreateDelegate(typeof(Action), + events, + RaiseAssetTableEntryRemoved_Method); + } + + public static Action Create_AssetTableCollection_SetEntryAssetType_PrivateMethod_Delegate(AssetTableCollection collection) + { + return (Action) Delegate.CreateDelegate(typeof(Action), + collection, + AssetTableCollection_SetEntryAssetType_PrivateMethod); + } + + public static Action Create_AssetTableCollection_RemoveEntryAssetType_PrivateMethod_Delegate(AssetTableCollection collection) + { + return (Action) Delegate.CreateDelegate(typeof(Action), + collection, + AssetTableCollection_RemoveEntryAssetType_PrivateMethod); + } + +#if SIRENIX_INTERNAL + public static void EnsureInit() { } +#endif + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValues.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValues.cs.meta new file mode 100644 index 00000000..c6267e25 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationReflectionValues.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b54903c48e344164a8ef89c56ba21f22 +timeCreated: 1704797678 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationSyntaxHighlighter.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationSyntaxHighlighter.cs new file mode 100644 index 00000000..8ccbfade --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationSyntaxHighlighter.cs @@ -0,0 +1,223 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using System.Text; +using Sirenix.OdinInspector.Modules.Localization.Editor.Configs; +using Sirenix.Utilities; +using UnityEditor; +using UnityEngine; +using UnityEngine.Localization.SmartFormat; +using UnityEngine.Localization.SmartFormat.Core.Parsing; +using UnityEngine.Localization.SmartFormat.Core.Settings; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal +{ + public static class OdinLocalizationSyntaxHighlighter + { + private const string END_RICH_COLOR = ""; + + private static readonly SmartFormatter Formatter; + + private static readonly StringBuilder Buffer; + + // TODO: if this works nicely, maybe implement a custom solution that doesn't rely on SmartFormatter + + static OdinLocalizationSyntaxHighlighter() + { + Formatter = Smart.CreateDefaultSmartFormat(); + Formatter.Settings.ParseErrorAction = ErrorAction.MaintainTokens; + Formatter.Settings.FormatErrorAction = ErrorAction.MaintainTokens; + + Buffer = new StringBuilder(); + } + + public static string HighlightAsRichText(string source) + { + Format format; + + try + { + format = Formatter.Parser.ParseFormat(source, Formatter.GetNotEmptyFormatterExtensionNames()); + } + catch (Exception) + { + return source; + } + + Buffer.Clear(); + + int expectedSize = source.Length; + AppendToBuffer(format, source, ref expectedSize); + + // NOTE: we fallback to source in-case any obvious discrepancies are happening. + return Buffer.Length != expectedSize ? source : Buffer.ToString(); + } + + public static string GetErrorMessage(string source, out bool foundError, out Exception exception) + { + Formatter.Settings.ParseErrorAction = ErrorAction.OutputErrorInResult; + Formatter.Settings.FormatErrorAction = ErrorAction.OutputErrorInResult; + + Format format; + + try + { + format = Formatter.Parser.ParseFormat(source, Formatter.GetNotEmptyFormatterExtensionNames()); + } + catch (Exception e) + { + foundError = true; + + exception = e; + + Debug.LogException(e); + + return $"Unity Formatter threw {ObjectNames.NicifyVariableName(e.GetType().GetNiceName())}: '{e.Message}'\nCheck the console for more information."; + } + + exception = null; + + Buffer.Clear(); + + int expectedSize = source.Length; + AppendToBuffer(format, source, ref expectedSize); + + foundError = expectedSize != Buffer.Length; + + Formatter.Settings.ParseErrorAction = ErrorAction.MaintainTokens; + Formatter.Settings.FormatErrorAction = ErrorAction.MaintainTokens; + + return Buffer.ToString(); + } + + private static void AppendColorAsRichTag(Color color, ref int expectedSize) + { + var color32 = (Color32) color; + + var richTag = $""; + expectedSize += richTag.Length; + + Buffer.Append(richTag); + } + + private static bool IsLastBufferEqual(string expected) + { + if (expected.Length > Buffer.Length) + { + return false; + } + + int bufferIndex = Buffer.Length - 1; + + for (int i1 = expected.Length - 1; i1 >= 0; i1--, bufferIndex--) + { + if (Buffer[bufferIndex] != expected[i1]) + { + return false; + } + } + + return true; + } + + private static void AppendToBuffer(Format format, string source, ref int expectedSize) + { + for (var i = 0; i < format.Items.Count; i++) + { + if (Buffer.Length == source.Length) + { + break; + } + + FormatItem item = format.Items[i]; + + if (!(item is Placeholder placeholder)) + { + Buffer.Append(item.RawText); + continue; + } + + AppendColorAsRichTag(OdinLocalizationConfig.Instance.placeholderColor, ref expectedSize); + + Buffer.Append('{'); + + for (var j = 0; j < placeholder.Selectors.Count; j++) + { + Selector selector = placeholder.Selectors[j]; + + AppendColorAsRichTag(OdinLocalizationConfig.Instance.selectorColor, ref expectedSize); + + string op = selector.Operator; + + if (!string.IsNullOrEmpty(op)) + { + Buffer.Append(selector.Operator); + } + + Buffer.Append(selector.RawText); + + Buffer.Append(END_RICH_COLOR); + expectedSize += END_RICH_COLOR.Length; + } + + if (placeholder.Alignment != 0) + { + Buffer.Append(','); + + Buffer.Append(placeholder.Alignment); + } + + if (!string.IsNullOrEmpty(placeholder.FormatterName)) + { + Buffer.Append(':'); + + AppendColorAsRichTag(OdinLocalizationConfig.Instance.formatterColor, ref expectedSize); + + Buffer.Append(placeholder.FormatterName); + + if (!string.IsNullOrEmpty(placeholder.FormatterOptions)) + { + Buffer.Append('('); + Buffer.Append(placeholder.FormatterOptions); + Buffer.Append(')'); + } + + Buffer.Append(END_RICH_COLOR); + expectedSize += END_RICH_COLOR.Length; + } + + if (placeholder.Format != null) + { + Format nextFormat = placeholder.Format; + + bool showColon; + + if (nextFormat.startIndex > 0) + { + showColon = nextFormat.baseString[nextFormat.startIndex - 1] == ':'; + } + else + { + showColon = true; + } + + if (showColon) + { + Buffer.Append(':'); + } + + AppendToBuffer(nextFormat, source, ref expectedSize); + } + + Buffer.Append('}'); + + Buffer.Append(END_RICH_COLOR); + expectedSize += END_RICH_COLOR.Length; + } + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationSyntaxHighlighter.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationSyntaxHighlighter.cs.meta new file mode 100644 index 00000000..b75002cd --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Internal/OdinLocalizationSyntaxHighlighter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: edfba57bc4284142a39f658cc44f70c6 +timeCreated: 1704797761 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/LocalizationSupport.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/LocalizationSupport.cs new file mode 100644 index 00000000..825c069f --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/LocalizationSupport.cs @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +#if UNITY_EDITOR + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ + using UnityEngine.Localization; + using Sirenix.OdinInspector.Editor; + using System; + using Sirenix.Utilities.Editor; + + public class LocalizedReferenceResolver : OdinPropertyResolver + { + public override int ChildNameToIndex(string name) + { + throw new NotSupportedException(); + } + + public override int ChildNameToIndex(ref StringSlice name) + { + throw new NotSupportedException(); + } + + public override InspectorPropertyInfo GetChildInfo(int childIndex) + { + throw new NotSupportedException(); + } + + protected override int GetChildCount(LocalizedReference value) + { + return 0; + } + } +} +#endif \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/LocalizationSupport.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/LocalizationSupport.cs.meta new file mode 100644 index 00000000..347fefe2 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/LocalizationSupport.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 46db7b2a049564544ad19a2e480a1333 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/OdinLocalizationConstants.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/OdinLocalizationConstants.cs new file mode 100644 index 00000000..b5c22081 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/OdinLocalizationConstants.cs @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using Sirenix.OdinInspector.Modules.Localization.Editor.Configs; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ + public static class OdinLocalizationConstants + { + public static int AssetPreviewRowHeight => OdinLocalizationConfig.Instance.assetRowHeight; + public const int DEFAULT_COLUMN_WIDTH = 300; + public const int MIN_COLUMN_WIDTH = 200; + public const int COLUMN_HEIGHT = 38; + public const int ROW_MENU_WIDTH = 28; + public const int ROW_HEIGHT = 30; + public const int EMPTY_ASSET_ROW_HEIGHT = ROW_HEIGHT; //96; + public const int ASSET_ROW_HEIGHT = 104; //96; + public const int ENTRY_PADDING = 4; + public const int TOOLBAR_HEIGHT = 28; + public const int DRAG_HANDLE_WIDTH = 27; + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/OdinLocalizationConstants.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/OdinLocalizationConstants.cs.meta new file mode 100644 index 00000000..b314d14f --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/OdinLocalizationConstants.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cd2728f4c9e842db8d6b5e8c18cce611 +timeCreated: 1706411619 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Sirenix.OdinInspector.Modules.UnityLocalization.Editor.asmdef b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Sirenix.OdinInspector.Modules.UnityLocalization.Editor.asmdef new file mode 100644 index 00000000..a33eec9c --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Sirenix.OdinInspector.Modules.UnityLocalization.Editor.asmdef @@ -0,0 +1,29 @@ +{ + "name": "Sirenix.OdinInspector.Modules.UnityLocalization.Editor", + "references": [ + "Unity.Localization", + "Sirenix.Serialization", + "Sirenix.OdinInspector.Attributes", + "Sirenix.OdinInspector.Editor", + "Sirenix.Utilities.Editor", + "Unity.Localization.Editor", + "Sirenix.Utilities", + "Unity.Addressables.Editor" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [ + "Sirenix.Serialization.dll", + "Sirenix.OdinInspector.Attributes.dll", + "Sirenix.OdinInspector.Editor.dll", + "Sirenix.Utilities.Editor.dll" + ], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Sirenix.OdinInspector.Modules.UnityLocalization.Editor.asmdef.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Sirenix.OdinInspector.Modules.UnityLocalization.Editor.asmdef.meta new file mode 100644 index 00000000..fd0f6383 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Sirenix.OdinInspector.Modules.UnityLocalization.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3656feb4b010c5941972fb7b3b9eec3e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Windows.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Windows.meta new file mode 100644 index 00000000..03c97b2a --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Windows.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 835e118525d8475f939f4105af066578 +timeCreated: 1704796852 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Windows/OdinLocalizationEditorWindow.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Windows/OdinLocalizationEditorWindow.cs new file mode 100644 index 00000000..015789d6 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Windows/OdinLocalizationEditorWindow.cs @@ -0,0 +1,663 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Sirenix.OdinInspector.Editor; +using Sirenix.OdinInspector.Editor.Internal; +using Sirenix.OdinInspector.Modules.Localization.Editor.Configs; +using Sirenix.OdinInspector.Modules.Localization.Editor.Internal; +using Sirenix.Serialization; +using Sirenix.Utilities; +using Sirenix.Utilities.Editor; +using UnityEditor; +using UnityEditor.Localization; +using UnityEngine; +using UnityEngine.Localization; + +namespace Sirenix.OdinInspector.Modules.Localization.Editor +{ + public class OdinLocalizationCreateTableMenu + { + [Serializable] + public class LocaleItem + { + [HideInInspector] + public Locale Locale; + + [HideLabel] + public bool Enabled; + } + + public enum TableCollectionType + { + StringTableCollection, + AssetTableCollection + } + + private string FolderPath => string.IsNullOrEmpty(this.Folder) ? "Assets" : $"Assets/{this.Folder}"; + + [ValidateInput(nameof(ValidateName), "@this." + nameof(nameErrorMessage))] + [VerticalGroup("Split/Left")] + [PropertySpace(SpaceAfter = 2, SpaceBefore = 2)] + public string Name; + + [EnableIf("@this." + nameof(EnableFolder))] + [InfoBox("The directory is not found, this will create a new directory on creation.", visibleIfMemberName: nameof(ShowFolderInfoBox))] + [HorizontalGroup("Split")] + [VerticalGroup("Split/Left")] + [FolderPath(ParentFolder = "Assets")] + public string Folder; + + [VerticalGroup("Split/Left")] + [PropertySpace(SpaceAfter = 2, SpaceBefore = 2)] + [HideLabel] + [EnumToggleButtons] + public TableCollectionType Type; + + [EnableIf(nameof(EnableCreateIf))] + [VerticalGroup("Split/Left")] + [PropertySpace(SpaceBefore = 4)] + [Button(ButtonSizes.Large)] + public void Create() + { + var localizationWindow = EditorWindow.focusedWindow as OdinLocalizationEditorWindow; + + if (!this.HasAnyLocaleSelected()) + { + if (localizationWindow) + { + localizationWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.ExclamationOctagonFill, + "At least 1 Locale must be selected.", + new Color(0.68f, 0.2f, 0.2f), + 5.0f); + } + + return; + } + + if (!Directory.Exists(this.FolderPath)) + { + Directory.CreateDirectory(this.FolderPath); + } + + var collectionLocales = new List(this.Locales.Count); + + foreach (LocaleItem localeItem in this.Locales) + { + if (localeItem.Enabled) + { + collectionLocales.Add(localeItem.Locale); + } + } + + switch (this.Type) + { + case TableCollectionType.StringTableCollection: + LocalizationEditorSettings.CreateStringTableCollection(this.Name, this.FolderPath, collectionLocales); + break; + + case TableCollectionType.AssetTableCollection: + LocalizationEditorSettings.CreateAssetTableCollection(this.Name, this.FolderPath, collectionLocales); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + if (localizationWindow) + { + string typeNiceName; + + switch (this.Type) + { + case TableCollectionType.StringTableCollection: + typeNiceName = "String Table Collection"; + break; + + case TableCollectionType.AssetTableCollection: + typeNiceName = "Asset Table Collection"; + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + localizationWindow.ShowToast(ToastPosition.BottomLeft, + SdfIconType.Check2, + $"{typeNiceName} '{this.Name}' created at: {this.FolderPath}.", + new Color(0.29f, 0.57f, 0.42f), + 16.0f); + } + } + + [HorizontalGroup("Split")] + [VerticalGroup("Split/Right")] + [InlineProperty] + [ListDrawerSettings(ListElementLabelName = "@this.Locale.LocaleName", + HideAddButton = true, + HideRemoveButton = true, + DefaultExpandedState = true, + ShowFoldout = false, + ShowItemCount = false, + DraggableItems = false)] + public List Locales = new List(); + + [HorizontalGroup("Split/Right/Split")] + [Button] + public void LocaleGenerator() + { + try + { + TwoWaySerializationBinder.Default.BindToType("UnityEditor.Localization.UI.LocaleGeneratorWindow, Unity.Localization.Editor") + .GetMethod("ShowWindow", BindingFlags.Static | BindingFlags.Public) + .Invoke(null, null); + } + catch (NullReferenceException nullReferenceException) + { + Debug.LogError($"[Odin]: Failed to find LocaleGeneratorWindow.ShowWindow.\n{nullReferenceException.Message}"); + } + } + + [HorizontalGroup("Split/Right/Split")] + [Button] + public void SelectNone() + { + for (var i = 0; i < this.Locales.Count; i++) + { + this.Locales[i].Enabled = false; + } + } + + [HorizontalGroup("Split/Right/Split")] + [Button] + public void SelectAll() + { + for (var i = 0; i < this.Locales.Count; i++) + { + this.Locales[i].Enabled = true; + } + } + + [HideInInspector] + internal bool EnableFolder = true; + + private string nameErrorMessage = string.Empty; + + private bool ValidateName(string name) + { + if (string.IsNullOrEmpty(name)) + { + this.nameErrorMessage = $"{nameof(this.Name)} can't be empty."; + return false; + } + + Type collectionType; + + switch (this.Type) + { + case TableCollectionType.StringTableCollection: + collectionType = typeof(StringTableCollection); + break; + + case TableCollectionType.AssetTableCollection: + collectionType = typeof(AssetTableCollection); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + + bool isTableNameValid = OdinLocalizationEditorSettings.IsTableNameValid(collectionType, name, out string localizationErrorMsg); + + if (isTableNameValid) + { + return true; + } + + this.nameErrorMessage = localizationErrorMsg; + + return false; + } + + private bool ShowFolderInfoBox() + { + if (string.IsNullOrEmpty(this.Folder)) + { + return false; + } + + return !Directory.Exists(this.FolderPath); + } + + private bool EnableCreateIf() => this.Locales.Count > 0 && this.ValidateName(this.Name); + + private bool HasAnyLocaleSelected() + { + for (var i = 0; i < this.Locales.Count; i++) + { + if (this.Locales[i].Enabled) + { + return true; + } + } + + return false; + } + } + + public class OdinLocalizationEditorWindow : OdinMenuEditorWindow, IDisposable + { + public enum RightMenuTopTabs + { + [LabelText(SdfIconType.Braces)] + Metadata, + + [LabelText(SdfIconType.GearFill)] + Settings + } + + public enum RightMenuBottomTabs + { + [LabelText(SdfIconType.FlagFill)] + Locale, + +#if false + [LabelText(SdfIconType.BorderWidth)] + Template, +#endif + + [LabelText(SdfIconType.GearFill)] + Settings + } + + public class WindowState : IDisposable + { + public static string EditorPrefsKey = "OdinLocalizationEditorWindow_EditorPrefs"; + + public RightMenuTopTabs CurrentTopTab; + public RightMenuBottomTabs CurrentBottomTab; + public PropertyTree MetadataTree = null; + public bool ShowSharedMetadata = true; + + public float LeftMenuWidth; + public float RightMenuWidth; + public float RightMenuTopPanelHeight; + public float LastOpenRightMenuWidth; + + public void Save() + { + EditorPrefs.SetFloat($"{EditorPrefsKey}_LeftMenuWidth", this.LeftMenuWidth); + EditorPrefs.SetFloat($"{EditorPrefsKey}_RightMenuWidth", this.RightMenuWidth); + EditorPrefs.SetFloat($"{EditorPrefsKey}_RightMenuTopHeight", this.RightMenuTopPanelHeight); + EditorPrefs.SetFloat($"{EditorPrefsKey}_LastOpenRightMenuWidth", this.LastOpenRightMenuWidth); + } + + public void Load() + { + this.LeftMenuWidth = EditorPrefs.GetFloat($"{EditorPrefsKey}_LeftMenuWidth", 300); + this.RightMenuWidth = EditorPrefs.GetFloat($"{EditorPrefsKey}_RightMenuWidth", 300); + this.RightMenuTopPanelHeight = EditorPrefs.GetFloat($"{EditorPrefsKey}_RightMenuTopHeight"); + this.LastOpenRightMenuWidth = EditorPrefs.GetFloat($"{EditorPrefsKey}_LastOpenRightMenuWidth"); + } + + public void Dispose() + { + this.MetadataTree?.Dispose(); + this.MetadataTree = null; + } + } + + [MenuItem("Tools/Odin/Localization Editor", priority = 10_100)] + public static void OpenFromMenu() + { + var wnd = GetWindow(); + wnd.MenuWidth = 300.0f; + } + + public WindowState State; + + private object lastSelection; + + protected override void Initialize() + { + this.State = new WindowState(); + this.State.Load(); + } + + protected override void OnDestroy() + { + base.OnDestroy(); + + this.DisposeActiveCollection(); + this.State.Dispose(); + } + + protected override void OnDisable() + { + base.OnDisable(); + this.State.Save(); + + this.DisposeActiveCollection(); + this.State.Dispose(); + } + + protected override void OnImGUI() + { + if (LocalizationEditorSettings.ActiveLocalizationSettings == null) + { + Rect popupPosition = this.position.SetPosition(Vector2.zero).AlignCenter(360, 160); + + if (EditorGUIUtility.isProSkin) + { + //OdinLocalizationGUI.DrawRoundGlowRect(popupPosition.Expand(54), FancyColor.CreateHex(0x323232)); + OdinLocalizationGUI.DrawRoundBlur6(popupPosition, new Color(0, 0, 0, 0.025f)); + SirenixEditorGUI.DrawRoundRect(popupPosition, FancyColor.CreateHex(0x383838), 5.0f); + } + else + { + OdinLocalizationGUI.DrawRoundBlur6(popupPosition, new Color(0, 0, 0, 0.02f)); + SirenixEditorGUI.DrawRoundRect(popupPosition, new FancyColor(0.84f), 5.0f); //, new Color(0, 0, 0, 0.2f), 1); + + SirenixEditorGUI.DrawRoundRect(popupPosition.AlignBottom(32 + 12 + 8 + 6), new Color(1, 1, 1, 0.2f), 0.0f, 0.0f, 5.0f, 5.0f); + } + + popupPosition = popupPosition.Padding(12); + + Rect buttonsArea = popupPosition.TakeFromBottom(32); + + popupPosition.height -= 16; + + GUIStyle labelStyle = EditorGUIUtility.isProSkin ? SirenixGUIStyles.WhiteLabelCentered : SirenixGUIStyles.BlackLabelCentered; + + if (EditorGUIUtility.isProSkin) + { + GUI.Label(popupPosition, "No Localization Settings found in project.", labelStyle); + } + else + { + GUIHelper.PushColor(new Color(1, 1, 1, 0.75f)); + GUI.Label(popupPosition, "No Localization Settings found in project.", SirenixGUIStyles.BlackLabelCentered); + } + + if (OdinLocalizationGUI.OverlaidButton(buttonsArea.AlignCenter(120), "Create", labelStyle: labelStyle, invert: true)) + { + if (OdinLocalizationEditorSettings.CreateDefaultLocalizationSettingsAsset()) + { + this.ShowToast(ToastPosition.BottomLeft, + SdfIconType.GearWide, + "Default Localization Settings created.", + new Color(0.13f, 0.26f, 0.39f), + 8.0f); + } + } + + if (!EditorGUIUtility.isProSkin) + { + GUIHelper.PopColor(); + } + + this.Repaint(); + return; + } + + base.OnImGUI(); + } + + protected override OdinMenuTree BuildMenuTree() + { + var tree = new OdinMenuTree + { + Config = + { + AutoHandleKeyboardNavigation = false, + DrawSearchToolbar = true + }, + DefaultMenuStyle = + { + Height = 28, + AlignTriangleLeft = true, + TrianglePadding = 0.0f + } + }; + + this.MenuBackgroundColor = OdinLocalizationGUI.MenuBackground; + + if (LocalizationEditorSettings.ActiveLocalizationSettings == null) + { + return tree; + } + + var createMenu = new OdinLocalizationCreateTableMenu(); + + tree.Add("Create Table", createMenu, SdfIconType.Plus); + tree.Add("User Config", OdinLocalizationConfig.Instance, SdfIconType.GearFill); + +#if true + tree.Selection.SelectionChanged += type => + { + switch (type) + { + case SelectionChangedType.ItemAdded: + if (this.lastSelection != null) + { + switch (this.lastSelection) + { + case OdinAssetTableCollectionEditor assetCollection: + { + assetCollection.DetachEvents(); + break; + } + + case OdinStringTableCollectionEditor stringCollection: + { + stringCollection.DetachEvents(); + break; + } + } + + this.State.MetadataTree?.Dispose(); + this.State.MetadataTree = null; + } + + switch (tree.Selection.SelectedValue) + { + case OdinAssetTableCollectionEditor assetCollection: + { + assetCollection.OnSelectInWindow(); + + if (assetCollection.SelectionType == OdinTableSelectionType.TableEntry && this.State.CurrentTopTab == RightMenuTopTabs.Metadata) + { + assetCollection.UpdateMetadataViewForEntry(assetCollection.CurrentSelectedEntry); + } + + break; + } + + case OdinStringTableCollectionEditor stringCollection: + { + stringCollection.OnSelectInWindow(); + + if (stringCollection.SelectionType == OdinTableSelectionType.TableEntry && this.State.CurrentTopTab == RightMenuTopTabs.Metadata) + { + stringCollection.UpdateMetadataViewForEntry(stringCollection.CurrentSelectedEntry); + } + + break; + } + + case OdinLocalizationCreateTableMenu createTableMenu: + createTableMenu.Locales.Clear(); + + foreach (Locale locale in LocalizationEditorSettings.GetLocales()) + { + createTableMenu.Locales.Add(new OdinLocalizationCreateTableMenu.LocaleItem {Locale = locale, Enabled = true}); + } + + break; + } + + this.lastSelection = this.MenuTree.Selection.SelectedValue; + + break; + } + }; +#endif + + string[] collectionGUIDs = AssetDatabase.FindAssets($"t:{nameof(LocalizationTableCollection)}"); + + for (var i = 0; i < collectionGUIDs.Length; i++) + { + string assetPath = AssetDatabase.GUIDToAssetPath(collectionGUIDs[i]); + + var collection = AssetDatabase.LoadAssetAtPath(assetPath); + + AssetTableCollection assetTableCollection = LocalizationEditorSettings.GetAssetTableCollection(collection.TableCollectionNameReference); + + if (assetTableCollection != null) + { + var guiCollection = new OdinAssetTableCollectionEditor(assetTableCollection, this, this.State); + + assetPath = assetPath.Replace(".asset", string.Empty); + + if (assetPath.StartsWith("Assets/")) + { + assetPath = assetPath.Remove(0, "Assets/".Length); + } + + tree.Add(assetPath, guiCollection, SdfIconType.Table); + + continue; + } + + StringTableCollection stringTableCollection = LocalizationEditorSettings.GetStringTableCollection(collection.TableCollectionNameReference); + + if (stringTableCollection != null) + { + var guiCollection = new OdinStringTableCollectionEditor(stringTableCollection, this, this.State); + + assetPath = assetPath.Replace(".asset", string.Empty); + + if (assetPath.StartsWith("Assets/")) + { + assetPath = assetPath.Remove(0, "Assets/".Length); + } + + tree.Add(assetPath, guiCollection, SdfIconType.LayoutTextWindow); + + continue; + } + } + + foreach (OdinMenuItem treeMenuItem in tree.EnumerateTree()) + { + if (treeMenuItem.Value != null) + { + if (treeMenuItem.Value is OdinAssetTableCollectionEditor assetEditor) + { + treeMenuItem.Name = assetEditor.Collection.SharedData.TableCollectionName; + + assetEditor.MenuItem = treeMenuItem; + + treeMenuItem.OnDrawItem += item => + { + if (Event.current.OnMouseDown(item.Rect, 0, false)) + { + if (Event.current.clickCount > 1) + { + EditorGUIUtility.PingObject(assetEditor.Collection); + } + } + }; + + continue; + } + + if (treeMenuItem.Value is OdinStringTableCollectionEditor stringEditor) + { + treeMenuItem.Name = stringEditor.Collection.SharedData.TableCollectionName; + + stringEditor.MenuItem = treeMenuItem; + + treeMenuItem.OnDrawItem += item => + { + if (Event.current.OnMouseDown(item.Rect, 0, false)) + { + if (Event.current.clickCount > 1) + { + EditorGUIUtility.PingObject(stringEditor.Collection); + } + } + }; + + continue; + } + + continue; + } + + treeMenuItem.Value = createMenu; + + treeMenuItem.SdfIcon = SdfIconType.FolderFill; + + treeMenuItem.OnDrawItem += item => + { + Rect addTableRect = item.Rect.AlignRight(20).SubX(14); + + bool isMouseOver = Event.current.IsMouseOver(addTableRect); + + if (EditorGUIUtility.isProSkin) + { + SdfIcons.DrawIcon(addTableRect.AlignCenter(16, 16), + SdfIconType.Plus, + isMouseOver ? new Color(1, 1, 1, 0.8f) : new Color(1, 1, 1, 0.4f)); + } + else + { + SdfIcons.DrawIcon(addTableRect.AlignCenter(16, 16), + SdfIconType.Plus, + isMouseOver ? new Color(0, 0, 0, 0.8f) : new Color(0, 0, 0, 0.4f)); + } + + if (Event.current.OnMouseDown(item.Rect, 0, false)) + { + createMenu.Folder = treeMenuItem.GetFullPath(); + } + }; + } + + return tree; + } + + public void Dispose() + { + this.DisposeActiveCollection(); + this.State.Dispose(); + } + + private void DisposeActiveCollection() + { + if (this.MenuTree == null) + { + return; + } + + switch (this.MenuTree.Selection.SelectedValue) + { + case OdinAssetTableCollectionEditor assetCollection: + assetCollection.DetachEvents(); + break; + + case OdinStringTableCollectionEditor stringCollection: + stringCollection.DetachEvents(); + break; + } + } + } +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Windows/OdinLocalizationEditorWindow.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Windows/OdinLocalizationEditorWindow.cs.meta new file mode 100644 index 00000000..009df3f5 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Windows/OdinLocalizationEditorWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a7cd6c96ce4b4c2c98fae9f89eedc690 +timeCreated: 1704796865 \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime.meta new file mode 100644 index 00000000..650a9338 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b2477452e56429547b05666cbaaf3502 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/LocalizedStringFormatter.cs b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/LocalizedStringFormatter.cs new file mode 100644 index 00000000..6bdeb3c0 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/LocalizedStringFormatter.cs @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Sirenix ApS. All rights reserved. +// +//----------------------------------------------------------------------- + +using Sirenix.Serialization; +using System.Reflection; +using UnityEngine.Localization; +using System; + +#if !ODIN_INSPECTOR_EDITOR_ONLY +[assembly: RegisterFormatter(typeof(Sirenix.OdinInspector.Modules.Localization.LocalizedStringFormatter))] +#endif + +namespace Sirenix.OdinInspector.Modules.Localization +{ + #if !ODIN_INSPECTOR_EDITOR_ONLY + public class LocalizedStringFormatter : ReflectionOrEmittedBaseFormatter + { + private static readonly FieldInfo m_LocalVariables_Field; + + static LocalizedStringFormatter() + { + m_LocalVariables_Field = typeof(LocalizedString).GetField("m_LocalVariables", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + if (m_LocalVariables_Field == null) + { + DefaultLoggers.DefaultLogger.LogError("Could not find field 'UnityEngine.LocalizedString.m_LocalVariables'" + + " - the internals of the Localization package have changed, and deserialization of Odin-serialized" + + " LocalizedString instances may be broken in some cases."); + } + } + + protected override LocalizedString GetUninitializedObject() + { + return new LocalizedString(); + } + + protected override void DeserializeImplementation(ref LocalizedString value, IDataReader reader) + { + base.DeserializeImplementation(ref value, reader); + + if (m_LocalVariables_Field != null && value != null) + { + var localVariablesList = m_LocalVariables_Field.GetValue(value); + + // This list is not allowed to be null! + if (localVariablesList == null) + { + localVariablesList = Activator.CreateInstance(m_LocalVariables_Field.FieldType); + m_LocalVariables_Field.SetValue(value, localVariablesList); + } + } + } + } + #endif +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/LocalizedStringFormatter.cs.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/LocalizedStringFormatter.cs.meta new file mode 100644 index 00000000..f0c54924 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/LocalizedStringFormatter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 93ff93294e9339b40b68190c779d0009 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/Sirenix.OdinInspector.Modules.UnityLocalization.asmdef b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/Sirenix.OdinInspector.Modules.UnityLocalization.asmdef new file mode 100644 index 00000000..661c1e80 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/Sirenix.OdinInspector.Modules.UnityLocalization.asmdef @@ -0,0 +1,11 @@ +{ + "name": "Sirenix.OdinInspector.Modules.UnityLocalization", + "references": [ "Unity.Localization", "Sirenix.Serialization", "Sirenix.Serialization.Config" ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "autoReferenced": true, + "overrideReferences": false, + "precompiledReferences": [ "Sirenix.Serialization.dll", "Sirenix.Serialization.Config.dll" ], + "defineConstraints": [ "!ODIN_INSPECTOR_EDITOR_ONLY" ] +} \ No newline at end of file diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/Sirenix.OdinInspector.Modules.UnityLocalization.asmdef.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/Sirenix.OdinInspector.Modules.UnityLocalization.asmdef.meta new file mode 100644 index 00000000..95310884 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Runtime/Sirenix.OdinInspector.Modules.UnityLocalization.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 09320d9ddd505f84d9a6e5203cf75192 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/manifest.txt b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/manifest.txt new file mode 100644 index 00000000..4c63d536 --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/manifest.txt @@ -0,0 +1,62 @@ +ManifestVersion: 1 +ModuleID: Unity.Localization +ModuleVersion: 2.0.0.5 +ModuleFiles: + Editor.meta + Runtime.meta + Editor/Configs.meta + Editor/GUI.meta + Editor/Internal.meta + Editor/LocalizationSupport.cs + Editor/LocalizationSupport.cs.meta + Editor/OdinLocalizationConstants.cs + Editor/OdinLocalizationConstants.cs.meta + Editor/Sirenix.OdinInspector.Modules.UnityLocalization.Editor.asmdef + Editor/Sirenix.OdinInspector.Modules.UnityLocalization.Editor.asmdef.meta + Editor/Windows.meta + Editor/Configs/OdinLocalizationConfig.asset + Editor/Configs/OdinLocalizationConfig.asset.meta + Editor/Configs/OdinLocalizationConfig.cs + Editor/Configs/OdinLocalizationConfig.cs.meta + Editor/GUI/LocalizationMetadata.cs + Editor/GUI/LocalizationMetadata.cs.meta + Editor/GUI/OdinAssetTableCollectionEditor.cs + Editor/GUI/OdinAssetTableCollectionEditor.cs.meta + Editor/GUI/OdinGUITable.cs + Editor/GUI/OdinGUITable.cs.meta + Editor/GUI/OdinGUITableCollection.cs + Editor/GUI/OdinGUITableCollection.cs.meta + Editor/GUI/OdinSharedEntryCollection.cs + Editor/GUI/OdinSharedEntryCollection.cs.meta + Editor/GUI/OdinStringTableCollectionEditor.cs + Editor/GUI/OdinStringTableCollectionEditor.cs.meta + Editor/GUI/OdinTableCollectionEditor.cs + Editor/GUI/OdinTableCollectionEditor.cs.meta + Editor/GUI/OdinTemplateMetadata.cs + Editor/GUI/OdinTemplateMetadata.cs.meta + Editor/GUI/Toggle.cs + Editor/GUI/Toggle.cs.meta + Editor/Internal/OdinLocalizationAssetCache.cs + Editor/Internal/OdinLocalizationAssetCache.cs.meta + Editor/Internal/OdinLocalizationEditorSettings.cs + Editor/Internal/OdinLocalizationEditorSettings.cs.meta + Editor/Internal/OdinLocalizationEvents.cs + Editor/Internal/OdinLocalizationEvents.cs.meta + Editor/Internal/OdinLocalizationGUI.cs + Editor/Internal/OdinLocalizationGUI.cs.meta + Editor/Internal/OdinLocalizationGUITextures.cs + Editor/Internal/OdinLocalizationGUITextures.cs.meta + Editor/Internal/OdinLocalizationMetadataRegistry.cs + Editor/Internal/OdinLocalizationMetadataRegistry.cs.meta + Editor/Internal/OdinLocalizationReflectionValidator.cs + Editor/Internal/OdinLocalizationReflectionValidator.cs.meta + Editor/Internal/OdinLocalizationReflectionValues.cs + Editor/Internal/OdinLocalizationReflectionValues.cs.meta + Editor/Internal/OdinLocalizationSyntaxHighlighter.cs + Editor/Internal/OdinLocalizationSyntaxHighlighter.cs.meta + Editor/Windows/OdinLocalizationEditorWindow.cs + Editor/Windows/OdinLocalizationEditorWindow.cs.meta + Runtime/LocalizedStringFormatter.cs + Runtime/LocalizedStringFormatter.cs.meta + Runtime/Sirenix.OdinInspector.Modules.UnityLocalization.asmdef + Runtime/Sirenix.OdinInspector.Modules.UnityLocalization.asmdef.meta diff --git a/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/manifest.txt.meta b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/manifest.txt.meta new file mode 100644 index 00000000..cebc4f9a --- /dev/null +++ b/Assets/Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/manifest.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ad7e9006721a58d47aa9555af183b6b1 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Prefabs/Story/BlockConnectorUI.prefab b/Assets/Prefabs/Story/BlockConnectorUI.prefab index fb83bdde..687225b0 100644 --- a/Assets/Prefabs/Story/BlockConnectorUI.prefab +++ b/Assets/Prefabs/Story/BlockConnectorUI.prefab @@ -11,7 +11,7 @@ GameObject: - component: {fileID: 3170048441224470995} - component: {fileID: 6458159057691837353} - component: {fileID: 7035782240384533237} - - component: {fileID: -805279860356291106} + - component: {fileID: 3598816098124740053} m_Layer: 5 m_Name: BlockConnectorUI m_TagString: Untagged @@ -82,7 +82,7 @@ MonoBehaviour: LineJoins: 1 BezierMode: 0 drivenExternally: 1 ---- !u!114 &-805279860356291106 +--- !u!114 &3598816098124740053 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -91,7 +91,9 @@ MonoBehaviour: m_GameObject: {fileID: 2558107630079687234} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 98bbf463dca50cc43961c0bb2b8d4f22, type: 3} + m_Script: {fileID: 11500000, guid: 9d179a524aa28d74f9ae667fd78aed20, type: 3} m_Name: - m_EditorClassIdentifier: + m_EditorClassIdentifier: Assembly-CSharp::Ichni.Story.UI.BlockConnectorView curve: {fileID: 7035782240384533237} + startBlock: {fileID: 0} + endBlock: {fileID: 0} diff --git a/Assets/Prefabs/Story/CurrentTextBlock.prefab b/Assets/Prefabs/Story/CurrentTextBlock.prefab new file mode 100644 index 00000000..d9985f6a --- /dev/null +++ b/Assets/Prefabs/Story/CurrentTextBlock.prefab @@ -0,0 +1,167 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2947357181438598616 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 808855937929311006} + - component: {fileID: 3938143345111524688} + - component: {fileID: 1269722336407503250} + - component: {fileID: 3931523710101296998} + m_Layer: 5 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &808855937929311006 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2947357181438598616} + 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: 5350638854831129010} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &3938143345111524688 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2947357181438598616} + m_CullTransparentMesh: 1 +--- !u!114 &1269722336407503250 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2947357181438598616} + 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: a53caee9af6d16b44a78a926722a32f5, 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!114 &3931523710101296998 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2947357181438598616} + 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: 1269722336407503250} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &8279096577214194803 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5350638854831129010} + - component: {fileID: 6895756204793606274} + m_Layer: 5 + m_Name: CurrentTextBlock + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5350638854831129010 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8279096577214194803} + 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: 808855937929311006} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 400, y: 200} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6895756204793606274 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8279096577214194803} + m_CullTransparentMesh: 1 diff --git a/Assets/Prefabs/Story/CurrentTextBlockUI.prefab.meta b/Assets/Prefabs/Story/CurrentTextBlock.prefab.meta similarity index 100% rename from Assets/Prefabs/Story/CurrentTextBlockUI.prefab.meta rename to Assets/Prefabs/Story/CurrentTextBlock.prefab.meta diff --git a/Assets/Prefabs/Story/CurrentTextBlockUI.prefab b/Assets/Prefabs/Story/CurrentTextBlockUI.prefab deleted file mode 100644 index aed92e21..00000000 --- a/Assets/Prefabs/Story/CurrentTextBlockUI.prefab +++ /dev/null @@ -1,487 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!1 &147050084074733619 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 5120337310990590007} - - component: {fileID: 501271540936040954} - - component: {fileID: 4219007272173467644} - m_Layer: 5 - m_Name: OutPort - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &5120337310990590007 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 147050084074733619} - 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: 5350638854831129010} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 1, y: 0.5} - m_AnchorMax: {x: 1, y: 0.5} - m_AnchoredPosition: {x: 0, y: -30} - m_SizeDelta: {x: 25, y: 25} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &501271540936040954 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 147050084074733619} - m_CullTransparentMesh: 1 ---- !u!114 &4219007272173467644 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 147050084074733619} - m_Enabled: 0 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 1, g: 0, b: 0, 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 &1093014837187042595 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 8088187650405042001} - - component: {fileID: 8483913691444022236} - - component: {fileID: 8300599191905768772} - m_Layer: 5 - m_Name: InPort - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &8088187650405042001 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1093014837187042595} - 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: 5350638854831129010} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0.5} - m_AnchorMax: {x: 0, y: 0.5} - m_AnchoredPosition: {x: 0, y: -30} - m_SizeDelta: {x: 25, y: 25} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &8483913691444022236 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1093014837187042595} - m_CullTransparentMesh: 1 ---- !u!114 &8300599191905768772 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1093014837187042595} - m_Enabled: 0 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 0, g: 0, 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 &2947357181438598616 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 808855937929311006} - - component: {fileID: 3938143345111524688} - - component: {fileID: 1269722336407503250} - - component: {fileID: 3931523710101296998} - m_Layer: 5 - m_Name: Image - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &808855937929311006 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2947357181438598616} - 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: 5350638854831129010} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &3938143345111524688 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2947357181438598616} - m_CullTransparentMesh: 1 ---- !u!114 &1269722336407503250 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2947357181438598616} - 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: a53caee9af6d16b44a78a926722a32f5, 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!114 &3931523710101296998 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2947357181438598616} - 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: 1269722336407503250} - m_OnClick: - m_PersistentCalls: - m_Calls: [] ---- !u!1 &7991069327751894070 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 9095470796084918386} - - component: {fileID: 606259940963463531} - - component: {fileID: 8391808794720387191} - - component: {fileID: 6909263626923910701} - m_Layer: 5 - m_Name: Text (TMP) - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &9095470796084918386 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7991069327751894070} - 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: 5350638854831129010} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: -20.000002} - m_SizeDelta: {x: 0, y: -39.999996} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &606259940963463531 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7991069327751894070} - m_CullTransparentMesh: 1 ---- !u!114 &8391808794720387191 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7991069327751894070} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, 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_text: Title - m_isRightToLeft: 0 - m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} - m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} - m_fontSharedMaterials: [] - m_fontMaterial: {fileID: 0} - m_fontMaterials: [] - m_fontColor32: - serializedVersion: 2 - rgba: 4294967295 - m_fontColor: {r: 1, g: 1, b: 1, a: 1} - m_enableVertexGradient: 0 - m_colorMode: 3 - m_fontColorGradient: - topLeft: {r: 1, g: 1, b: 1, a: 1} - topRight: {r: 1, g: 1, b: 1, a: 1} - bottomLeft: {r: 1, g: 1, b: 1, a: 1} - bottomRight: {r: 1, g: 1, b: 1, a: 1} - m_fontColorGradientPreset: {fileID: 0} - m_spriteAsset: {fileID: 0} - m_tintAllSprites: 0 - m_StyleSheet: {fileID: 0} - m_TextStyleHashCode: -1183493901 - m_overrideHtmlColors: 0 - m_faceColor: - serializedVersion: 2 - rgba: 4294967295 - m_fontSize: 48 - m_fontSizeBase: 48 - m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 - m_fontSizeMax: 72 - m_fontStyle: 0 - m_HorizontalAlignment: 2 - m_VerticalAlignment: 512 - m_textAlignment: 65535 - m_characterSpacing: 0 - m_wordSpacing: 0 - m_lineSpacing: 0 - m_lineSpacingMax: 0 - m_paragraphSpacing: 0 - m_charWidthMaxAdj: 0 - m_enableWordWrapping: 1 - m_wordWrappingRatios: 0.4 - m_overflowMode: 0 - m_linkedTextComponent: {fileID: 0} - parentLinkedComponent: {fileID: 0} - m_enableKerning: 1 - m_enableExtraPadding: 0 - checkPaddingRequired: 0 - m_isRichText: 1 - m_parseCtrlCharacters: 1 - m_isOrthographic: 1 - m_isCullingEnabled: 0 - m_horizontalMapping: 0 - m_verticalMapping: 0 - m_uvLineOffset: 0 - m_geometrySortingOrder: 0 - m_IsTextObjectScaleStatic: 0 - m_VertexBufferAutoSizeReduction: 0 - m_useMaxVisibleDescender: 1 - m_pageToDisplay: 1 - m_margin: {x: 0, y: 0, z: 0, w: 0} - m_isUsingLegacyAnimationComponent: 0 - m_isVolumetricText: 0 - m_hasFontAssetChanged: 0 - m_baseMaterial: {fileID: 0} - m_maskOffset: {x: 0, y: 0, z: 0, w: 0} ---- !u!225 &6909263626923910701 -CanvasGroup: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7991069327751894070} - m_Enabled: 1 - m_Alpha: 1 - m_Interactable: 0 - m_BlocksRaycasts: 0 - m_IgnoreParentGroups: 0 ---- !u!1 &8279096577214194803 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 5350638854831129010} - - component: {fileID: 6895756204793606274} - - component: {fileID: 7871082850049165166} - m_Layer: 5 - m_Name: CurrentTextBlockUI - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &5350638854831129010 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8279096577214194803} - 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: 808855937929311006} - - {fileID: 9095470796084918386} - - {fileID: 8088187650405042001} - - {fileID: 5120337310990590007} - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 400, y: 200} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &6895756204793606274 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8279096577214194803} - m_CullTransparentMesh: 1 ---- !u!114 &7871082850049165166 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8279096577214194803} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b976128eb3ec59e4e866dbb610441706, type: 3} - m_Name: - m_EditorClassIdentifier: - blockRect: {fileID: 5350638854831129010} - inPort: {fileID: 8088187650405042001} - outPort: {fileID: 5120337310990590007} - blockName: - blockTitle: - button: {fileID: 3931523710101296998} - titleText: {fileID: 8391808794720387191} diff --git a/Assets/Prefabs/Story/FinishedTextBlock.prefab b/Assets/Prefabs/Story/FinishedTextBlock.prefab new file mode 100644 index 00000000..ff5453dc --- /dev/null +++ b/Assets/Prefabs/Story/FinishedTextBlock.prefab @@ -0,0 +1,167 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2947357181438598616 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 808855937929311006} + - component: {fileID: 3938143345111524688} + - component: {fileID: 1269722336407503250} + - component: {fileID: 3931523710101296998} + m_Layer: 5 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &808855937929311006 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2947357181438598616} + 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: 5350638854831129010} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &3938143345111524688 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2947357181438598616} + m_CullTransparentMesh: 1 +--- !u!114 &1269722336407503250 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2947357181438598616} + 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: a53caee9af6d16b44a78a926722a32f5, 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!114 &3931523710101296998 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2947357181438598616} + 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: 1269722336407503250} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &8279096577214194803 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5350638854831129010} + - component: {fileID: 6895756204793606274} + m_Layer: 5 + m_Name: FinishedTextBlock + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5350638854831129010 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8279096577214194803} + 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: 808855937929311006} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 400, y: 200} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6895756204793606274 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8279096577214194803} + m_CullTransparentMesh: 1 diff --git a/Assets/Prefabs/Story/FinishedTextBlockUI.prefab.meta b/Assets/Prefabs/Story/FinishedTextBlock.prefab.meta similarity index 100% rename from Assets/Prefabs/Story/FinishedTextBlockUI.prefab.meta rename to Assets/Prefabs/Story/FinishedTextBlock.prefab.meta diff --git a/Assets/Prefabs/Story/LockedTextBlock.prefab b/Assets/Prefabs/Story/LockedTextBlock.prefab new file mode 100644 index 00000000..2dfcd80c --- /dev/null +++ b/Assets/Prefabs/Story/LockedTextBlock.prefab @@ -0,0 +1,189 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &4395100195316225510 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2559973671531178526} + - component: {fileID: 7314817520828819316} + - component: {fileID: 40060167445544855} + - component: {fileID: 7807195957997196324} + m_Layer: 5 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2559973671531178526 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4395100195316225510} + 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: 5350638854831129010} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &7314817520828819316 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4395100195316225510} + m_CullTransparentMesh: 1 +--- !u!114 &40060167445544855 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4395100195316225510} + 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: 7ddd15eec542d5f43a4e8a2e01322627, 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!114 &7807195957997196324 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4395100195316225510} + 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: 40060167445544855} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!1 &8279096577214194803 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5350638854831129010} + - component: {fileID: 6895756204793606274} + - component: {fileID: 8428017859984400476} + m_Layer: 5 + m_Name: LockedTextBlock + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5350638854831129010 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8279096577214194803} + 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: 2559973671531178526} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 400, y: 200} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6895756204793606274 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8279096577214194803} + m_CullTransparentMesh: 1 +--- !u!114 &8428017859984400476 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8279096577214194803} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 28d84946aa995484dbcbe24b7aab1652, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Ichni.Story.UI.TextBlockView + blockId: + blockPosition: {x: 0, y: 0} + state: 0 + blockRect: {fileID: 5350638854831129010} + inPort: {fileID: 0} + outPort: {fileID: 0} + button: {fileID: 0} + storyIdText: {fileID: 0} + titleText: {fileID: 0} diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P3_A.txt.meta b/Assets/Prefabs/Story/LockedTextBlock.prefab.meta similarity index 63% rename from Assets/Resources/Story/Chapter0/Dialogs/Departure_P3_A.txt.meta rename to Assets/Prefabs/Story/LockedTextBlock.prefab.meta index d8e38229..266f47c8 100644 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P3_A.txt.meta +++ b/Assets/Prefabs/Story/LockedTextBlock.prefab.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: b661de85b293b7247830ee85f2182ccf -DefaultImporter: +guid: 82ffc6b4beba3f6438d1fd97f2e45719 +PrefabImporter: externalObjects: {} userData: assetBundleName: diff --git a/Assets/Prefabs/Story/LockedTextBlockUI.prefab b/Assets/Prefabs/Story/LockedTextBlockUI.prefab deleted file mode 100644 index 66da9442..00000000 --- a/Assets/Prefabs/Story/LockedTextBlockUI.prefab +++ /dev/null @@ -1,474 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!1 &147050084074733619 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 5120337310990590007} - - component: {fileID: 501271540936040954} - - component: {fileID: 4219007272173467644} - m_Layer: 5 - m_Name: OutPort - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &5120337310990590007 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 147050084074733619} - 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: 5350638854831129010} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 1, y: 0.5} - m_AnchorMax: {x: 1, y: 0.5} - m_AnchoredPosition: {x: 0, y: -30} - m_SizeDelta: {x: 25, y: 25} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &501271540936040954 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 147050084074733619} - m_CullTransparentMesh: 1 ---- !u!114 &4219007272173467644 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 147050084074733619} - m_Enabled: 0 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 1, g: 0, b: 0, 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 &1093014837187042595 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 8088187650405042001} - - component: {fileID: 8483913691444022236} - - component: {fileID: 8300599191905768772} - m_Layer: 5 - m_Name: InPort - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &8088187650405042001 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1093014837187042595} - 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: 5350638854831129010} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0.5} - m_AnchorMax: {x: 0, y: 0.5} - m_AnchoredPosition: {x: 0, y: -30} - m_SizeDelta: {x: 25, y: 25} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &8483913691444022236 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1093014837187042595} - m_CullTransparentMesh: 1 ---- !u!114 &8300599191905768772 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1093014837187042595} - m_Enabled: 0 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 0, g: 0, 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 &2947357181438598616 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 808855937929311006} - - component: {fileID: 3938143345111524688} - - component: {fileID: 1269722336407503250} - - component: {fileID: 3931523710101296998} - m_Layer: 5 - m_Name: Image - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &808855937929311006 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2947357181438598616} - 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: 5350638854831129010} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &3938143345111524688 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2947357181438598616} - m_CullTransparentMesh: 1 ---- !u!114 &1269722336407503250 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2947357181438598616} - 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: 7ddd15eec542d5f43a4e8a2e01322627, 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!114 &3931523710101296998 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2947357181438598616} - 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: 1269722336407503250} - m_OnClick: - m_PersistentCalls: - m_Calls: [] ---- !u!1 &7991069327751894070 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 9095470796084918386} - - component: {fileID: 606259940963463531} - - component: {fileID: 8391808794720387191} - m_Layer: 5 - m_Name: Text (TMP) - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &9095470796084918386 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7991069327751894070} - 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: 5350638854831129010} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 1, y: 1} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: -72, y: -20} - m_SizeDelta: {x: 135, y: 25} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &606259940963463531 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7991069327751894070} - m_CullTransparentMesh: 1 ---- !u!114 &8391808794720387191 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 7991069327751894070} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, 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_text: Title - m_isRightToLeft: 0 - m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} - m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} - m_fontSharedMaterials: [] - m_fontMaterial: {fileID: 0} - m_fontMaterials: [] - m_fontColor32: - serializedVersion: 2 - rgba: 4294967295 - m_fontColor: {r: 1, g: 1, b: 1, a: 1} - m_enableVertexGradient: 0 - m_colorMode: 3 - m_fontColorGradient: - topLeft: {r: 1, g: 1, b: 1, a: 1} - topRight: {r: 1, g: 1, b: 1, a: 1} - bottomLeft: {r: 1, g: 1, b: 1, a: 1} - bottomRight: {r: 1, g: 1, b: 1, a: 1} - m_fontColorGradientPreset: {fileID: 0} - m_spriteAsset: {fileID: 0} - m_tintAllSprites: 0 - m_StyleSheet: {fileID: 0} - m_TextStyleHashCode: -1183493901 - m_overrideHtmlColors: 0 - m_faceColor: - serializedVersion: 2 - rgba: 4294967295 - m_fontSize: 24 - m_fontSizeBase: 24 - m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 - m_fontSizeMax: 72 - m_fontStyle: 0 - m_HorizontalAlignment: 2 - m_VerticalAlignment: 512 - m_textAlignment: 65535 - m_characterSpacing: 0 - m_wordSpacing: 0 - m_lineSpacing: 0 - m_lineSpacingMax: 0 - m_paragraphSpacing: 0 - m_charWidthMaxAdj: 0 - m_enableWordWrapping: 1 - m_wordWrappingRatios: 0.4 - m_overflowMode: 0 - m_linkedTextComponent: {fileID: 0} - parentLinkedComponent: {fileID: 0} - m_enableKerning: 1 - m_enableExtraPadding: 0 - checkPaddingRequired: 0 - m_isRichText: 1 - m_parseCtrlCharacters: 1 - m_isOrthographic: 1 - m_isCullingEnabled: 0 - m_horizontalMapping: 0 - m_verticalMapping: 0 - m_uvLineOffset: 0 - m_geometrySortingOrder: 0 - m_IsTextObjectScaleStatic: 0 - m_VertexBufferAutoSizeReduction: 0 - m_useMaxVisibleDescender: 1 - m_pageToDisplay: 1 - m_margin: {x: 0, y: 0, z: 0, w: 0} - m_isUsingLegacyAnimationComponent: 0 - m_isVolumetricText: 0 - m_hasFontAssetChanged: 0 - m_baseMaterial: {fileID: 0} - m_maskOffset: {x: 0, y: 0, z: 0, w: 0} ---- !u!1 &8279096577214194803 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 5350638854831129010} - - component: {fileID: 6895756204793606274} - - component: {fileID: 7871082850049165166} - m_Layer: 5 - m_Name: LockedTextBlockUI - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &5350638854831129010 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8279096577214194803} - 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: 808855937929311006} - - {fileID: 9095470796084918386} - - {fileID: 8088187650405042001} - - {fileID: 5120337310990590007} - m_Father: {fileID: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0.5} - m_AnchorMax: {x: 0, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 400, y: 200} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &6895756204793606274 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8279096577214194803} - m_CullTransparentMesh: 1 ---- !u!114 &7871082850049165166 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 8279096577214194803} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b976128eb3ec59e4e866dbb610441706, type: 3} - m_Name: - m_EditorClassIdentifier: - blockRect: {fileID: 5350638854831129010} - inPort: {fileID: 8088187650405042001} - outPort: {fileID: 5120337310990590007} - blockName: - blockTitle: - button: {fileID: 3931523710101296998} - titleText: {fileID: 8391808794720387191} diff --git a/Assets/Prefabs/Story/FinishedTextBlockUI.prefab b/Assets/Prefabs/Story/TextBlockUI.prefab similarity index 80% rename from Assets/Prefabs/Story/FinishedTextBlockUI.prefab rename to Assets/Prefabs/Story/TextBlockUI.prefab index 14e3932f..86f52187 100644 --- a/Assets/Prefabs/Story/FinishedTextBlockUI.prefab +++ b/Assets/Prefabs/Story/TextBlockUI.prefab @@ -34,7 +34,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 0.5} m_AnchorMax: {x: 1, y: 0.5} - m_AnchoredPosition: {x: 0, y: -30} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 25, y: 25} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &501271540936040954 @@ -109,7 +109,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0.5} m_AnchorMax: {x: 0, y: 0.5} - m_AnchoredPosition: {x: 0, y: -30} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 25, y: 25} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &8483913691444022236 @@ -159,11 +159,11 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 808855937929311006} - - component: {fileID: 3938143345111524688} - - component: {fileID: 1269722336407503250} + - component: {fileID: 464408401004507889} + - component: {fileID: 5684991247721594449} - component: {fileID: 3931523710101296998} m_Layer: 5 - m_Name: Image + m_Name: Button m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -188,7 +188,7 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &3938143345111524688 +--- !u!222 &464408401004507889 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -196,7 +196,7 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2947357181438598616} m_CullTransparentMesh: 1 ---- !u!114 &1269722336407503250 +--- !u!114 &5684991247721594449 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -207,16 +207,16 @@ MonoBehaviour: m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} m_Name: - m_EditorClassIdentifier: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image m_Material: {fileID: 0} - m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Color: {r: 1, g: 1, b: 1, a: 0} 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: a53caee9af6d16b44a78a926722a32f5, type: 3} + m_Sprite: {fileID: 0} m_Type: 0 m_PreserveAspect: 0 m_FillCenter: 1 @@ -266,11 +266,11 @@ MonoBehaviour: m_SelectedTrigger: Selected m_DisabledTrigger: Disabled m_Interactable: 1 - m_TargetGraphic: {fileID: 1269722336407503250} + m_TargetGraphic: {fileID: 5684991247721594449} m_OnClick: m_PersistentCalls: m_Calls: [] ---- !u!1 &5487477990142788049 +--- !u!1 &3472041800732464321 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -278,23 +278,23 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 1412140060069810010} - - component: {fileID: 7700833151999098032} - - component: {fileID: 2834471624508731075} + - component: {fileID: 6776097071409445873} + - component: {fileID: 5860881451225714455} + - component: {fileID: 4081607830298877796} m_Layer: 5 - m_Name: Text (TMP) (1) + m_Name: TitleText m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &1412140060069810010 +--- !u!224 &6776097071409445873 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5487477990142788049} + m_GameObject: {fileID: 3472041800732464321} 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} @@ -304,24 +304,24 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: -3, y: 80} - m_SizeDelta: {x: -6, y: -160} + m_AnchoredPosition: {x: 0, y: -15} + m_SizeDelta: {x: -20, y: -70} m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &7700833151999098032 +--- !u!222 &5860881451225714455 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5487477990142788049} + m_GameObject: {fileID: 3472041800732464321} m_CullTransparentMesh: 1 ---- !u!114 &2834471624508731075 +--- !u!114 &4081607830298877796 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 5487477990142788049} + m_GameObject: {fileID: 3472041800732464321} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} @@ -335,17 +335,18 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: FINISHED + m_text: Block Title m_isRightToLeft: 0 - m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} - m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_fontAsset: {fileID: 11400000, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} + m_sharedMaterial: {fileID: -5812992765432063453, guid: 87c1b020037c81841b3d98dd497cbdeb, + type: 2} m_fontSharedMaterials: [] m_fontMaterial: {fileID: 0} m_fontMaterials: [] m_fontColor32: serializedVersion: 2 - rgba: 4278190080 - m_fontColor: {r: 0, g: 0, b: 0, a: 1} + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} m_enableVertexGradient: 0 m_colorMode: 3 m_fontColorGradient: @@ -362,31 +363,34 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 24 - m_fontSizeBase: 24 + m_fontSize: 28 + m_fontSizeBase: 20 m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 - m_fontSizeMax: 72 + m_enableAutoSizing: 1 + m_fontSizeMin: 12 + m_fontSizeMax: 28 m_fontStyle: 0 m_HorizontalAlignment: 2 m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 0 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 m_paragraphSpacing: 0 m_charWidthMaxAdj: 0 - m_enableWordWrapping: 1 + m_TextWrappingMode: 1 m_wordWrappingRatios: 0.4 m_overflowMode: 0 m_linkedTextComponent: {fileID: 0} parentLinkedComponent: {fileID: 0} m_enableKerning: 1 + m_ActiveFontFeatures: 6e72656b m_enableExtraPadding: 0 checkPaddingRequired: 0 m_isRichText: 1 + m_EmojiFallbackSupport: 1 m_parseCtrlCharacters: 1 m_isOrthographic: 1 m_isCullingEnabled: 0 @@ -416,7 +420,7 @@ GameObject: - component: {fileID: 606259940963463531} - component: {fileID: 8391808794720387191} m_Layer: 5 - m_Name: Text (TMP) + m_Name: StoryIdText m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -438,8 +442,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 1} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: -72, y: -20} - m_SizeDelta: {x: 135, y: 25} + m_AnchoredPosition: {x: -73, y: -19} + m_SizeDelta: {x: 120, y: 25} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &606259940963463531 CanvasRenderer: @@ -469,10 +473,11 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: Title + m_text: A-1 m_isRightToLeft: 0 - m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} - m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2} + m_fontAsset: {fileID: 11400000, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} + m_sharedMaterial: {fileID: -5812992765432063453, guid: 87c1b020037c81841b3d98dd497cbdeb, + type: 2} m_fontSharedMaterials: [] m_fontMaterial: {fileID: 0} m_fontMaterials: [] @@ -496,31 +501,34 @@ MonoBehaviour: m_faceColor: serializedVersion: 2 rgba: 4294967295 - m_fontSize: 24 - m_fontSizeBase: 24 + m_fontSize: 20 + m_fontSizeBase: 20 m_fontWeight: 400 - m_enableAutoSizing: 0 - m_fontSizeMin: 18 - m_fontSizeMax: 72 + m_enableAutoSizing: 1 + m_fontSizeMin: 12 + m_fontSizeMax: 20 m_fontStyle: 0 m_HorizontalAlignment: 2 m_VerticalAlignment: 512 m_textAlignment: 65535 m_characterSpacing: 0 + m_characterHorizontalScale: 1 m_wordSpacing: 0 m_lineSpacing: 0 m_lineSpacingMax: 0 m_paragraphSpacing: 0 m_charWidthMaxAdj: 0 - m_enableWordWrapping: 1 + m_TextWrappingMode: 1 m_wordWrappingRatios: 0.4 m_overflowMode: 0 m_linkedTextComponent: {fileID: 0} parentLinkedComponent: {fileID: 0} m_enableKerning: 1 + m_ActiveFontFeatures: 6e72656b m_enableExtraPadding: 0 checkPaddingRequired: 0 m_isRichText: 1 + m_EmojiFallbackSupport: 1 m_parseCtrlCharacters: 1 m_isOrthographic: 1 m_isCullingEnabled: 0 @@ -548,9 +556,9 @@ GameObject: m_Component: - component: {fileID: 5350638854831129010} - component: {fileID: 6895756204793606274} - - component: {fileID: 7871082850049165166} + - component: {fileID: 8428017859984400476} m_Layer: 5 - m_Name: FinishedTextBlockUI + m_Name: TextBlockUI m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -568,11 +576,11 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 808855937929311006} - {fileID: 9095470796084918386} - - {fileID: 1412140060069810010} + - {fileID: 6776097071409445873} - {fileID: 8088187650405042001} - {fileID: 5120337310990590007} + - {fileID: 808855937929311006} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0.5} @@ -588,7 +596,7 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 8279096577214194803} m_CullTransparentMesh: 1 ---- !u!114 &7871082850049165166 +--- !u!114 &8428017859984400476 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -597,15 +605,88 @@ MonoBehaviour: m_GameObject: {fileID: 8279096577214194803} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: b976128eb3ec59e4e866dbb610441706, type: 3} + m_Script: {fileID: 11500000, guid: 28d84946aa995484dbcbe24b7aab1652, type: 3} m_Name: - m_EditorClassIdentifier: - blockName: - position: {x: 0, y: 0} + m_EditorClassIdentifier: Assembly-CSharp::Ichni.Story.UI.TextBlockView + serializationData: + SerializedFormat: 2 + SerializedBytes: + ReferencedUnityObjects: + - {fileID: 8279096577214194803, guid: 82ffc6b4beba3f6438d1fd97f2e45719, type: 3} + - {fileID: 8279096577214194803, guid: e145c2ee4ead96e49ae0c8774d6128df, type: 3} + - {fileID: 8279096577214194803, guid: 44de36b0d423e9c418251e354d843dc4, type: 3} + SerializedBytesString: + Prefab: {fileID: 0} + PrefabModificationsReferencedUnityObjects: [] + PrefabModifications: [] + SerializationNodes: + - Name: blockVisuals + Entry: 7 + Data: 0|System.Collections.Generic.Dictionary`2[[Ichni.Story.StoryBlockState, + Assembly-CSharp],[UnityEngine.GameObject, UnityEngine.CoreModule]], mscorlib + - Name: comparer + Entry: 7 + Data: 1|System.Collections.Generic.EnumEqualityComparer`1[[Ichni.Story.StoryBlockState, + Assembly-CSharp]], mscorlib + - Name: + Entry: 12 + Data: 0 + - Name: + Entry: 13 + Data: + - Name: + Entry: 8 + Data: + - Name: + Entry: 12 + Data: 3 + - Name: + Entry: 7 + Data: + - Name: $k + Entry: 3 + Data: 0 + - Name: $v + Entry: 10 + Data: 0 + - Name: + Entry: 8 + Data: + - Name: + Entry: 7 + Data: + - Name: $k + Entry: 3 + Data: 1 + - Name: $v + Entry: 10 + Data: 1 + - Name: + Entry: 8 + Data: + - Name: + Entry: 7 + Data: + - Name: $k + Entry: 3 + Data: 2 + - Name: $v + Entry: 10 + Data: 2 + - Name: + Entry: 8 + Data: + - Name: + Entry: 13 + Data: + - Name: + Entry: 8 + Data: + blockId: + state: 0 blockRect: {fileID: 5350638854831129010} inPort: {fileID: 8088187650405042001} outPort: {fileID: 5120337310990590007} - blockTitle: button: {fileID: 3931523710101296998} - titleText: {fileID: 8391808794720387191} - state: 0 + storyIdText: {fileID: 8391808794720387191} + titleText: {fileID: 4081607830298877796} diff --git a/Assets/Prefabs/Story/LockedTextBlockUI.prefab.meta b/Assets/Prefabs/Story/TextBlockUI.prefab.meta similarity index 100% rename from Assets/Prefabs/Story/LockedTextBlockUI.prefab.meta rename to Assets/Prefabs/Story/TextBlockUI.prefab.meta diff --git a/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard.meta b/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard.meta new file mode 100644 index 00000000..3ff1462f --- /dev/null +++ b/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 176404abacea74341bd2c5c11439610c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard/Beatmap.bytes b/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard/Beatmap.bytes new file mode 100644 index 00000000..ce2a819a Binary files /dev/null and b/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard/Beatmap.bytes differ diff --git a/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard/Beatmap.bytes.meta b/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard/Beatmap.bytes.meta new file mode 100644 index 00000000..9eed9ac2 --- /dev/null +++ b/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard/Beatmap.bytes.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9075bc4b469d0a84d80add0871c48725 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard/CommandScripts.bytes b/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard/CommandScripts.bytes new file mode 100644 index 00000000..673c8399 --- /dev/null +++ b/Assets/Resources/Beatmaps/Chapter0/Drowning in a Moment/Hard/CommandScripts.bytes @@ -0,0 +1,2 @@ +2ô*Ô5‹)s³#A)!(ªò-;3#23ê+‚ø9{Uþ’ùÔ?ó ŽL,./ˆç/Q£qlÌF>‘l$ˆ*ݹškÅ.¹kîªF‘=‹Ëß Ô“\ðümâ(>!°-sârv(´ -1(:<Ìö~Þüz:ß î¬Œ  ic‹-3Ð ÍKÛÐñ‡šˆÏ^<Ýäs -~ÖÁU{¹ïàq•ð D\¯pp&±FÜPçRQ\èêȨ ý*|†U™o‰¥‰ Õ™)üüìÊÆìG<_nãé!¸þ]–N…•UÏì¦&²„ëÉC3!Ó$f­ö…®fýŠa•3ýÑ!³Ö}í‘Ð.jKЭ?] -;c¿"YL€Ô•-{?ê;[rãäK€á· ìЇ†òü ŒgJãçm… GyöL±ò@‚UÐ’SšêÛŸÑœîÔ£Ah+°è8qõÞ„™î‹¥·kDŠ×ö€r4•›m ?Á«ªÓ›ÿk3KÌtN \ No newline at end of file +?EÇ$Ö'V„Ë_ô'ñþû€b«þš¯Ilàô´«CTÓyK’‡FEß‹j~ïˆçƒ1‡GŸ‘Œt"CÊË#EËî– @i"ïK|èN˜|õCÍÀTö3:¾%§HDL§$›dª¶ÊX¦ +pN”WƒE¬q¬ Š5}l£˜×:!i¤íÒrxX³»«ÉÔï¡õ6n5×÷Mºn6|PzUœ*n%}@¢Ÿ¨àyb¦ÕL4–ç;Ȫ3ä æ©×8©ÇNóYv›#º &éûc9~…ìOÊÄû4Bè+…¨ÇÜË>놕ޞH‰Ž ûlÀô­B—™`˜Ü õVÜÚÁa¹sd¾²òï¹Fp»•æ´É7I@˜g¯š»ï£ÎGÔ†¯Ù^œ4'GÝŽ£KBzVøa»„âc_ƒIf÷ž ƒ¥œMS H/’åH«¥¬ù?}sÀUɹ RÄ“TêëŠÆS5uF™‰“„ŸŠjà \ No newline at end of file diff --git a/Assets/Resources/Beatmaps/Chapter0/Space Rain/Special/SongInfo.bytes b/Assets/Resources/Beatmaps/Chapter0/Space Rain/Special/SongInfo.bytes index 4cab8b1f..ffc9556b 100644 Binary files a/Assets/Resources/Beatmaps/Chapter0/Space Rain/Special/SongInfo.bytes and b/Assets/Resources/Beatmaps/Chapter0/Space Rain/Special/SongInfo.bytes differ diff --git a/Assets/Resources/Chapter0.asset b/Assets/Resources/Chapter0.asset index b4541fc3..f0913dc0 100644 --- a/Assets/Resources/Chapter0.asset +++ b/Assets/Resources/Chapter0.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f59be91981810dd5d5a9d6051a932feace3b5f9cb1a35e961bfde75a66fb87b +oid sha256:a6036a84ccf5fd4c015d7fa2883b61d0aa8fbc075c60be7d40e84783ba10ef81 size 14835 diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P1_A.txt b/Assets/Resources/Story/Chapter0/Dialogs/Departure_P1_A.txt deleted file mode 100644 index d0854431..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P1_A.txt +++ /dev/null @@ -1,19 +0,0 @@ -$[Start] -$Soullies: C0_Departure_P1_A_D1_S1 -$Soullies: C0_Departure_P1_A_D1_S2 -$Choice(Departure_Path_Choose_1){ - C0_Departure_P1_A_C1_S1 -> [ChooseP2_A]; - C0_Departure_P1_A_C1_S2 -> [ChooseP2_B]; -} - -$[ChooseP2_A]{ - SetVariable("ChooseResult", 1); - GenerateDialogBlock("Departure_P2_A"); -} -$Soullies: C0_Departure_P1_A_D2_S1 - -$[ChooseP2_B]{ - SetVariable("ChooseResult", 2); - GenerateDialogBlock("Departure_P2_B"); -} -$Soullies: C0_Departure_P1_A_D3_S1 \ No newline at end of file diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_A.txt b/Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_A.txt deleted file mode 100644 index 55085aaa..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_A.txt +++ /dev/null @@ -1,5 +0,0 @@ -$[Entry]{ - GenerateDialogBlock("Departure_P3_A"); -} -$Soullies: C0_Departure_P2_A_D1_S1 -$Soullies: C0_Departure_P2_A_D1_S2 \ No newline at end of file diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_B.txt b/Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_B.txt deleted file mode 100644 index 9f6d37d9..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P2_B.txt +++ /dev/null @@ -1,19 +0,0 @@ -$[Entry] -$Condition{ - GetGlobalVariable("IsCursed") == 1 -> [Cursed-Start]; - GetGlobalVariable("IsCursed") == 0 -> [NotCursed-Start]; -} - -#这是一个注释。 - -$[Cursed-Start] -$Zako-1(Calm): You are cursed! You are cursed! -$Zako-1(Calm): Go find Zako 0 to get rid of the curse! -$Zako-1(Calm): I won't lie to you! - -#这是å¦ä¸€ä¸ªæ³¨é‡Šã€‚ --这个注释有两行。 - -$[NotCursed-Start] -$Zako-1(Happy): You are not cursed! -$Zako-1(Happy): Enjoy your adventure! \ No newline at end of file diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P3_A.txt b/Assets/Resources/Story/Chapter0/Dialogs/Departure_P3_A.txt deleted file mode 100644 index 93383c38..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P3_A.txt +++ /dev/null @@ -1,6 +0,0 @@ -$[Entry]{ - SetUnlockKey("Key_Alunite_Cas"); - GenerateDialogBlock("Departure_P4_A"); -} -$Soullies: C0_Departure_P3_A_D1_S1 -$Soullies: C0_Departure_P3_A_D1_S2 \ No newline at end of file diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P4_A.txt b/Assets/Resources/Story/Chapter0/Dialogs/Departure_P4_A.txt deleted file mode 100644 index eb417ad1..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P4_A.txt +++ /dev/null @@ -1,5 +0,0 @@ -$[Entry]{ - GenerateDialogBlock("Departure_P5_A"); -} -$Soullies: C0_Departure_P4_A_D1_S1 -$Soullies: C0_Departure_P4_A_D1_S2 \ No newline at end of file diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P4_A.txt.meta b/Assets/Resources/Story/Chapter0/Dialogs/Departure_P4_A.txt.meta deleted file mode 100644 index 14f0d2fb..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P4_A.txt.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: d5e8bc27afb79744f8a76da9bd0717e1 -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P5_A.txt b/Assets/Resources/Story/Chapter0/Dialogs/Departure_P5_A.txt deleted file mode 100644 index 05a99415..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P5_A.txt +++ /dev/null @@ -1,3 +0,0 @@ -$[Entry] -$Soullies: C0_Departure_P5_A_D1_S1 -$Soullies: C0_Departure_P5_A_D1_S2 \ No newline at end of file diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P5_A.txt.meta b/Assets/Resources/Story/Chapter0/Dialogs/Departure_P5_A.txt.meta deleted file mode 100644 index 00ee3a73..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/Departure_P5_A.txt.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 6b0b540f826ad54469f7373b29428d62 -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Example.txt b/Assets/Resources/Story/Chapter0/Dialogs/Example.txt deleted file mode 100644 index c805e25b..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/Example.txt +++ /dev/null @@ -1,49 +0,0 @@ -$[开始] -$Condition{ - GetVariable("Branch") == 1 -> [标题1]; - GetVariable("Branch") == 2 -> [标题2]; -} - -#ä¸Šé¢æ˜¯æ¡ä»¶åˆ¤æ–­ï¼ŒèŽ·å–å˜é‡Branch的值,决定进入哪个标题。 -#这是一个注释。 -#----------- - - -$[标题1] -$角色åå­—1: 说è¯å†…容1 - -#Dialog结æŸäº† -#----------- - - -$[标题2] -$角色åå­—2: 说è¯å†…容2 -$Choice(选项åå­—){ - 选项内容A -> [选择A]; - 选项内容B -> [选择B]; -} -#ä¸Šé¢æ˜¯é€‰é¡¹ -#----------- - - -$[选择A]{ - SetVariable("ChooseResult", 1); - GenerateDialogBlock("Departure_P2_A"); -} -$角色åå­—2: 说è¯å†…容3-1 -$角色åå­—2: 说è¯å†…容3-2 - -#标题åŽé¢çš„{}内为自定义函数。 -#SetVariable("ChooseResult", 1) 设置å˜é‡ChooseResult的值为1。 -#GenerateTextBlock("Departure_P2_A") 生æˆå字为Departure_P2_A的文本å—。 -#----------- - - -$[选择B]{ - SetVariable("ChooseResult", 2); - GenerateDialogBlock("Departure_P2_B"); -} -$角色åå­—2: 说è¯å†…容4-1 -$角色åå­—2: 说è¯å†…容4-2 -$角色åå­—2: 说è¯å†…容4-3 -#----------- \ No newline at end of file diff --git a/Assets/Resources/Story/Chapter0/Dialogs/Example.txt.meta b/Assets/Resources/Story/Chapter0/Dialogs/Example.txt.meta deleted file mode 100644 index 8cfded7e..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/Example.txt.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 8b05c7949169cc048afaeb7635fd7638 -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Story/Chapter0/Dialogs/ZakoCurse 0.txt b/Assets/Resources/Story/Chapter0/Dialogs/ZakoCurse 0.txt deleted file mode 100644 index 9f6d37d9..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/ZakoCurse 0.txt +++ /dev/null @@ -1,19 +0,0 @@ -$[Entry] -$Condition{ - GetGlobalVariable("IsCursed") == 1 -> [Cursed-Start]; - GetGlobalVariable("IsCursed") == 0 -> [NotCursed-Start]; -} - -#这是一个注释。 - -$[Cursed-Start] -$Zako-1(Calm): You are cursed! You are cursed! -$Zako-1(Calm): Go find Zako 0 to get rid of the curse! -$Zako-1(Calm): I won't lie to you! - -#这是å¦ä¸€ä¸ªæ³¨é‡Šã€‚ --这个注释有两行。 - -$[NotCursed-Start] -$Zako-1(Happy): You are not cursed! -$Zako-1(Happy): Enjoy your adventure! \ No newline at end of file diff --git a/Assets/Resources/Story/Chapter0/Dialogs/ZakoCurse 0.txt.meta b/Assets/Resources/Story/Chapter0/Dialogs/ZakoCurse 0.txt.meta deleted file mode 100644 index ac7a9fe9..00000000 --- a/Assets/Resources/Story/Chapter0/Dialogs/ZakoCurse 0.txt.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: abedef5a950c819489df91598deb196f -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Resources/Story/Chapter0/StoryData_Chapter0.asset b/Assets/Resources/Story/Chapter0/StoryData_Chapter0.asset deleted file mode 100644 index fa3cc47c..00000000 --- a/Assets/Resources/Story/Chapter0/StoryData_Chapter0.asset +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c2d42167ed94a4a02525775035f9b8867f1c490324c1d1ee2acb44e587be0984 -size 1825 diff --git a/Assets/Scenes/MenuScene.unity b/Assets/Scenes/MenuScene.unity index 40b22278..2d2d4e6d 100644 --- a/Assets/Scenes/MenuScene.unity +++ b/Assets/Scenes/MenuScene.unity @@ -307,7 +307,7 @@ GameObject: - component: {fileID: 8360189} - component: {fileID: 8360188} m_Layer: 5 - m_Name: BlockContainer + m_Name: Content m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -327,11 +327,11 @@ RectTransform: m_Children: [] m_Father: {fileID: 1091682062} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 0, y: 0.5} m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} - m_Pivot: {x: 0, y: 1} + m_Pivot: {x: 0, y: 0.5} --- !u!114 &8360188 MonoBehaviour: m_ObjectHideFlags: 0 @@ -1948,6 +1948,7 @@ GameObject: m_Component: - component: {fileID: 78737088} - component: {fileID: 78737089} + - component: {fileID: 78737090} m_Layer: 5 m_Name: StoryPage m_TagString: Untagged @@ -2004,6 +2005,113 @@ MonoBehaviour: mainCanvasGroup: {fileID: 859038606} messageBox: {fileID: 1211674719} backButton: {fileID: 1519945293} +--- !u!114 &78737090 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 78737087} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7b00483ab980b3248a62618eeb0bcb4b, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Ichni.Story.StoryTreeController + content: {fileID: 8360186} + textBlockPrefab: {fileID: 8279096577214194803, guid: f6a037648752e004ba8846f352a8f421, + type: 3} + songBlockPrefab: {fileID: 8279096577214194803, guid: f0a5abea137f9b446b0408c9dc2d66a8, + type: 3} + tutorialBlockPrefab: {fileID: 8279096577214194803, guid: dc867cd06ddd0684997016b16a5eff2a, + type: 3} + connectorPrefab: {fileID: 2558107630079687234, guid: 1767c8901d2d45a49817babbcbf3cdf1, + type: 3} + cellSize: {x: 400, y: 200} + cellSpacing: {x: 120, y: 150} + marginLeft: 50 + marginRight: 50 + marginTop: 50 + marginBottom: 50 + minContentWidth: 2560 + minContentHeight: 1440 + currentBlock: {fileID: 0} + blocks: [] + connectors: [] +--- !u!1 &82938573 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 82938574} + - component: {fileID: 82938576} + - component: {fileID: 82938575} + m_Layer: 5 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &82938574 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 82938573} + 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: 1340448062} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &82938575 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 82938573} + 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: 439f6117a35abbf4e8c4b847c040f025, 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!222 &82938576 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 82938573} + m_CullTransparentMesh: 1 --- !u!1 &89248691 GameObject: m_ObjectHideFlags: 0 @@ -2718,6 +2826,97 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 143173586} m_CullTransparentMesh: 0 +--- !u!1 &145321077 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 145321078} + - component: {fileID: 145321081} + - component: {fileID: 145321080} + m_Layer: 5 + m_Name: Choice2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &145321078 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 145321077} + 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: 1008658487} + - {fileID: 867819402} + m_Father: {fileID: 762655493} + 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: 1188, y: 150} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &145321080 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 145321077} + m_CullTransparentMesh: 1 +--- !u!114 &145321081 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 145321077} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button + 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: 1008658488} + m_OnClick: + m_PersistentCalls: + m_Calls: [] --- !u!1 &153881444 GameObject: m_ObjectHideFlags: 0 @@ -2973,8 +3172,10 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 190695850} + - component: {fileID: 190695853} + - component: {fileID: 190695852} m_Layer: 5 - m_Name: GameObject + m_Name: Choice0 m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -2993,13 +3194,66 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 736709067} + - {fileID: 1228626495} m_Father: {fileID: 762655493} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 400, y: -80.5} - m_SizeDelta: {x: 768, y: 97} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 1188, y: 150} m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &190695852 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 190695849} + m_CullTransparentMesh: 1 +--- !u!114 &190695853 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 190695849} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button + 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: 736709068} + m_OnClick: + m_PersistentCalls: + m_Calls: [] --- !u!1 &193194204 GameObject: m_ObjectHideFlags: 0 @@ -3131,42 +3385,6 @@ MonoBehaviour: - Name: songSelectionRecords Entry: 6 Data: ---- !u!1 &208016886 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 208016887} - m_Layer: 5 - m_Name: GameObject (1) - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &208016887 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 208016886} - 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: 2144823341} - m_Father: {fileID: 762655493} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 400, y: -227.5} - m_SizeDelta: {x: 768, y: 97} - m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &212689266 GameObject: m_ObjectHideFlags: 0 @@ -3357,7 +3575,7 @@ GameObject: - component: {fileID: 213267829} - component: {fileID: 213267828} m_Layer: 5 - m_Name: OutsideClose (1) + m_Name: SampleImage m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -3459,6 +3677,43 @@ RectTransform: m_AnchoredPosition: {x: 0, y: -210.00002} m_SizeDelta: {x: 600, y: 100} m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &234096306 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 234096307} + m_Layer: 5 + m_Name: ChoiceFrame + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &234096307 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 234096306} + 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: 1426347161} + - {fileID: 762655493} + m_Father: {fileID: 6347218673911890593} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &242387626 GameObject: m_ObjectHideFlags: 0 @@ -3621,6 +3876,97 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &269231009 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 269231010} + - component: {fileID: 269231013} + - component: {fileID: 269231012} + m_Layer: 5 + m_Name: Choice3 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &269231010 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 269231009} + 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: 403017937} + - {fileID: 463634754} + m_Father: {fileID: 762655493} + 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: 1188, y: 150} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &269231012 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 269231009} + m_CullTransparentMesh: 1 +--- !u!114 &269231013 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 269231009} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button + 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: 403017938} + m_OnClick: + m_PersistentCalls: + m_Calls: [] --- !u!1 &272824270 GameObject: m_ObjectHideFlags: 0 @@ -4807,6 +5153,88 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 353400712} m_CullTransparentMesh: 1 +--- !u!1 &361065186 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 361065187} + - component: {fileID: 361065189} + - component: {fileID: 361065188} + m_Layer: 5 + m_Name: UI LineRenderer + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &361065187 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 361065186} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.10691672, y: 0.10691672, z: 0.10691672} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 512486723} + 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: -22.218994, y: 32.871887} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &361065188 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 361065186} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5b33b2e663e78774c9f0c9af55018725, type: 3} + m_Name: + m_EditorClassIdentifier: UnityUIExtensions::UnityEngine.UI.Extensions.UILineRenderer + 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_improveResolution: 0 + m_Resolution: 0 + m_useNativeSize: 0 + m_points: + - {x: 0, y: 0} + - {x: -123.26, y: 125.88} + - {x: -225.07, y: 51.09} + - {x: -129.6, y: -103.11} + lineThickness: 5 + relativeSize: 0 + lineList: 0 + lineCaps: 0 + bezierSegmentsPerCurve: 16 + LineJoins: 0 + BezierMode: 3 + drivenExternally: 0 +--- !u!222 &361065189 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 361065186} + m_CullTransparentMesh: 1 --- !u!1 &361532780 GameObject: m_ObjectHideFlags: 0 @@ -5376,6 +5804,81 @@ MonoBehaviour: Entry: 6 Data: settingsUIPage: {fileID: 1385276032} +--- !u!1 &403017936 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 403017937} + - component: {fileID: 403017939} + - component: {fileID: 403017938} + m_Layer: 5 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &403017937 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 403017936} + 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: 269231010} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &403017938 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 403017936} + 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: 439f6117a35abbf4e8c4b847c040f025, 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!222 &403017939 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 403017936} + m_CullTransparentMesh: 1 --- !u!1 &403492121 GameObject: m_ObjectHideFlags: 0 @@ -6219,6 +6722,169 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 445894608} m_CullTransparentMesh: 1 +--- !u!1 &449802386 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 449802387} + - component: {fileID: 449802388} + - component: {fileID: 449802389} + - component: {fileID: 449802390} + - component: {fileID: 449802391} + - component: {fileID: 449802392} + - component: {fileID: 449802393} + m_Layer: 0 + m_Name: DialogManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &449802387 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 449802386} + serializedVersion: 2 + 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: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &449802388 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 449802386} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7dc7dc14d9f97c043bf6fb67c3a70877, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Ichni.Story.Dialogue.StoryVariableStorage +--- !u!114 &449802389 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 449802386} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4bec29c0a230741bdac901dba8da47ee, type: 3} + m_Name: + m_EditorClassIdentifier: YarnSpinner.Unity::Yarn.Unity.DialogueRunner + yarnProject: {fileID: 7545569452688597077, guid: 8ce10d75c4d583b40a11257d0da22825, + type: 3} + variableStorage: {fileID: 449802388} + lineProvider: {fileID: 449802391} + saliencyStrategy: 0 + dialoguePresenters: + - {fileID: 449802393} + verboseLogging: 0 + autoStart: 0 + startNode: Start + runSelectedOptionAsLine: 0 + allowOptionFallthrough: 1 + onNodeStart: + m_PersistentCalls: + m_Calls: [] + onNodeComplete: + m_PersistentCalls: + m_Calls: [] + onDialogueStart: + m_PersistentCalls: + m_Calls: [] + onDialogueComplete: + m_PersistentCalls: + m_Calls: [] + onUnhandledCommand: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &449802390 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 449802386} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3c5da6d59dd1b3b4594e01d045b271c8, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Ichni.Story.Dialogue.StoryDialogueController + dialogueRunner: {fileID: 449802389} + storyUIPage: {fileID: 78737089} +--- !u!114 &449802391 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 449802386} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f9c1f487b798d402db42bc11cee5b810, type: 3} + m_Name: + m_EditorClassIdentifier: YarnSpinner.Unity::Yarn.Unity.UnityLocalization.UnityLocalisedLineProvider + stringsTable: + m_TableReference: + m_TableCollectionName: Chapter0_Lines + assetTable: + m_TableReference: + m_TableCollectionName: +--- !u!114 &449802392 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 449802386} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7f38c46a2f0519249b2b9efb27734c13, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Ichni.Story.Dialogue.ConsoleLinePresenter +--- !u!114 &449802393 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 449802386} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b7b08c1a67d3d65478d9d9b4e275393f, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::Ichni.Story.Dialogue.VNDialoguePresenter + panelGroup: {fileID: 6759614698927231795} + dialogueText: {fileID: 1834199647} + speakerContainer: {fileID: 993868748} + speakerText: {fileID: 1870072472} + advanceButton: {fileID: 1876757696} + choiceFrame: {fileID: 762655492} + choiceButtons: + - {fileID: 190695853} + - {fileID: 1340448065} + - {fileID: 145321081} + - {fileID: 269231013} + choiceTexts: + - {fileID: 1228626496} + - {fileID: 2043234284} + - {fileID: 867819403} + - {fileID: 463634755} + lettersPerSecond: 40 + fadeInDuration: 0.2 + fadeOutDuration: 0.15 --- !u!1 &454823864 GameObject: m_ObjectHideFlags: 0 @@ -6414,6 +7080,144 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 461162347} m_CullTransparentMesh: 1 +--- !u!1 &463634753 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 463634754} + - component: {fileID: 463634756} + - component: {fileID: 463634755} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &463634754 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 463634753} + 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: 269231010} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -300, y: -10} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &463634755 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 463634753} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Choice 0 + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} + m_sharedMaterial: {fileID: -5812992765432063453, guid: 87c1b020037c81841b3d98dd497cbdeb, + type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 56 + m_fontSizeBase: 56 + m_fontWeight: 400 + m_enableAutoSizing: 1 + m_fontSizeMin: 36 + m_fontSizeMax: 56 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 4096 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_characterHorizontalScale: 1 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_TextWrappingMode: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_EmojiFallbackSupport: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &463634756 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 463634753} + m_CullTransparentMesh: 1 --- !u!1 &463854108 GameObject: m_ObjectHideFlags: 0 @@ -7367,6 +8171,7 @@ Transform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1296688833} + - {fileID: 361065187} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &512486724 @@ -10802,6 +11607,7 @@ GameObject: m_Component: - component: {fileID: 762655493} - component: {fileID: 762655494} + - component: {fileID: 762655495} m_Layer: 5 m_Name: ChoiceFrame m_TagString: Untagged @@ -10816,21 +11622,21 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 762655492} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + 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: 190695850} - - {fileID: 208016887} - - {fileID: 1675131296} - - {fileID: 1724035089} - m_Father: {fileID: 6347218673911890593} + - {fileID: 1340448062} + - {fileID: 145321078} + - {fileID: 269231010} + m_Father: {fileID: 234096307} 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: 780, y: 80} - m_SizeDelta: {x: 800, y: 580} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 1200, y: 1100} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &762655494 MonoBehaviour: @@ -10845,11 +11651,11 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: m_Padding: - m_Left: 20 - m_Right: 20 - m_Top: 10 - m_Bottom: 10 - m_ChildAlignment: 7 + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 1 m_Spacing: 50 m_ChildForceExpandWidth: 0 m_ChildForceExpandHeight: 0 @@ -10858,6 +11664,14 @@ MonoBehaviour: m_ChildScaleWidth: 0 m_ChildScaleHeight: 0 m_ReverseArrangement: 0 +--- !u!222 &762655495 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 762655492} + m_CullTransparentMesh: 1 --- !u!114 &771714481 MonoBehaviour: m_ObjectHideFlags: 0 @@ -11365,7 +12179,6 @@ MonoBehaviour: loginPage: {fileID: 0} chapterSelectionUIPage: {fileID: 512486724} storyUIPage: {fileID: 78737089} - dialogUIPage: {fileID: 2860688340790966187} songSelectionUIPage: {fileID: 1506872041} transitionUIPage: {fileID: 2081546316} settingsUIPage: {fileID: 1385276032} @@ -12166,6 +12979,144 @@ MonoBehaviour: mAlignment_LTR: 514 mAlignmentWasRTL: 0 mInitializeAlignment: 0 +--- !u!1 &867819401 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 867819402} + - component: {fileID: 867819404} + - component: {fileID: 867819403} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &867819402 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 867819401} + 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: 145321078} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -300, y: -10} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &867819403 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 867819401} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Choice 0 + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} + m_sharedMaterial: {fileID: -5812992765432063453, guid: 87c1b020037c81841b3d98dd497cbdeb, + type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 56 + m_fontSizeBase: 56 + m_fontWeight: 400 + m_enableAutoSizing: 1 + m_fontSizeMin: 36 + m_fontSizeMax: 56 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 4096 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_characterHorizontalScale: 1 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_TextWrappingMode: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_EmojiFallbackSupport: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &867819404 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 867819401} + m_CullTransparentMesh: 1 --- !u!1 &869340148 GameObject: m_ObjectHideFlags: 0 @@ -12176,7 +13127,7 @@ GameObject: m_Component: - component: {fileID: 869340149} m_Layer: 5 - m_Name: Content + m_Name: Story m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -12218,7 +13169,6 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 871906516} - - component: {fileID: 871906517} m_Layer: 5 m_Name: DialogContentFrame m_TagString: Untagged @@ -12247,25 +13197,6 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 207.5} m_SizeDelta: {x: 0, y: 415} m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &871906517 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 871906515} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 14874d8a3e4a31941879415479892501, type: 3} - m_Name: - m_EditorClassIdentifier: - textPrefab: {fileID: 8033017966764082997, guid: 628c2093a25cf564cb34323b3de35b5e, - type: 3} - choiceGroupPrefab: {fileID: 8033017966764082997, guid: 66fde4a2944f4334e86cbd6220ad1880, - type: 3} - dialogContentContainer: {fileID: 0} - dialogTexts: [] - choiceGroups: [] --- !u!114 &872451634 MonoBehaviour: m_ObjectHideFlags: 0 @@ -12721,156 +13652,6 @@ RectTransform: m_AnchoredPosition: {x: 32.5, y: -27.5} m_SizeDelta: {x: 25, y: 45} m_Pivot: {x: 0.5, y: 0.5} ---- !u!1 &901926229 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 901926230} - - component: {fileID: 901926232} - - component: {fileID: 901926231} - m_Layer: 5 - m_Name: Image - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &901926230 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 901926229} - 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: 1675131296} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &901926231 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 901926229} - 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: 439f6117a35abbf4e8c4b847c040f025, 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!222 &901926232 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 901926229} - m_CullTransparentMesh: 1 ---- !u!1 &904216262 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 904216263} - - component: {fileID: 904216265} - - component: {fileID: 904216264} - m_Layer: 5 - m_Name: Image - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &904216263 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 904216262} - 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: 1724035089} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &904216264 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 904216262} - 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: 439f6117a35abbf4e8c4b847c040f025, 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!222 &904216265 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 904216262} - m_CullTransparentMesh: 1 --- !u!1 &909563966 GameObject: m_ObjectHideFlags: 0 @@ -13735,6 +14516,81 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 993868748} m_CullTransparentMesh: 1 +--- !u!1 &1008658486 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1008658487} + - component: {fileID: 1008658489} + - component: {fileID: 1008658488} + m_Layer: 5 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1008658487 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1008658486} + 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: 145321078} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1008658488 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1008658486} + 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: 439f6117a35abbf4e8c4b847c040f025, 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!222 &1008658489 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1008658486} + m_CullTransparentMesh: 1 --- !u!1 &1028956952 GameObject: m_ObjectHideFlags: 0 @@ -14148,7 +15004,8 @@ RectTransform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: [] + m_Children: + - {fileID: 1492554505} m_Father: {fileID: 6347218673911890593} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} @@ -14746,7 +15603,6 @@ GameObject: m_Component: - component: {fileID: 1091682062} - component: {fileID: 1091682063} - - component: {fileID: 1091682064} - component: {fileID: 1091682067} - component: {fileID: 1091682066} - component: {fileID: 1091682065} @@ -14769,13 +15625,12 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 1132659036} - {fileID: 8360186} m_Father: {fileID: 869340149} 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: 500, y: 0} + m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 2560, y: 1440} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1091682063 @@ -14808,38 +15663,6 @@ MonoBehaviour: m_OnValueChanged: m_PersistentCalls: m_Calls: [] ---- !u!114 &1091682064 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1091682061} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 42d37c15aeeaf1d4abbdc13962bb8b70, type: 3} - m_Name: - m_EditorClassIdentifier: - content: {fileID: 8360186} - dialogBlockPrefab: {fileID: 8279096577214194803, guid: e145c2ee4ead96e49ae0c8774d6128df, - type: 3} - musicBlockPrefab: {fileID: 8279096577214194803, guid: f0a5abea137f9b446b0408c9dc2d66a8, - type: 3} - tutorialBlockPrefab: {fileID: 8279096577214194803, guid: dc867cd06ddd0684997016b16a5eff2a, - type: 3} - connectionCurvePrefab: {fileID: 2558107630079687234, guid: 1767c8901d2d45a49817babbcbf3cdf1, - type: 3} - marginLeft: 100 - marginRight: 100 - marginTop: 100 - marginBottom: 100 - connectionContainer: {fileID: 1132659036} - currentBlock: {fileID: 0} - storyBlocks: [] - dialogBlocks: [] - songBlocks: [] - tutorialBlocks: [] - connectors: [] --- !u!114 &1091682065 MonoBehaviour: m_ObjectHideFlags: 0 @@ -15737,50 +16560,6 @@ MonoBehaviour: m_ColorSpace: 0 m_NumColorKeys: 2 m_NumAlphaKeys: 3 ---- !u!1 &1132659035 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1132659036} - - component: {fileID: 1132659037} - m_Layer: 5 - m_Name: ConnectorContainer - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &1132659036 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1132659035} - 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: 1091682062} - 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: 2560, y: 1440} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!222 &1132659037 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1132659035} - m_CullTransparentMesh: 1 --- !u!114 &1132892885 MonoBehaviour: m_ObjectHideFlags: 0 @@ -16673,6 +17452,144 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1225558873} m_CullTransparentMesh: 1 +--- !u!1 &1228626494 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1228626495} + - component: {fileID: 1228626497} + - component: {fileID: 1228626496} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1228626495 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1228626494} + 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: 190695850} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -300, y: -10} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1228626496 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1228626494} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Choice 0 + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} + m_sharedMaterial: {fileID: -5812992765432063453, guid: 87c1b020037c81841b3d98dd497cbdeb, + type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 56 + m_fontSizeBase: 56 + m_fontWeight: 400 + m_enableAutoSizing: 1 + m_fontSizeMin: 36 + m_fontSizeMax: 56 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 4096 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_characterHorizontalScale: 1 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_TextWrappingMode: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_EmojiFallbackSupport: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &1228626497 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1228626494} + m_CullTransparentMesh: 1 --- !u!1 &1229206120 GameObject: m_ObjectHideFlags: 0 @@ -17927,6 +18844,97 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 5151cbfb4a7c39c4599fb711d3332cbd, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!1 &1340448061 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1340448062} + - component: {fileID: 1340448065} + - component: {fileID: 1340448064} + m_Layer: 5 + m_Name: Choice1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1340448062 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1340448061} + 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: 82938574} + - {fileID: 2043234283} + m_Father: {fileID: 762655493} + 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: 1188, y: 150} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &1340448064 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1340448061} + m_CullTransparentMesh: 1 +--- !u!114 &1340448065 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1340448061} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button + 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: 82938575} + m_OnClick: + m_PersistentCalls: + m_Calls: [] --- !u!114 &1351337659 MonoBehaviour: m_ObjectHideFlags: 0 @@ -18625,6 +19633,41 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1424785510} m_CullTransparentMesh: 1 +--- !u!1 &1426347160 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1426347161} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1426347161 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1426347160} + 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: 234096307} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1426965734 GameObject: m_ObjectHideFlags: 0 @@ -19920,12 +20963,12 @@ RectTransform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1492554504} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + 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: 6347218673911890593} + m_Father: {fileID: 1055026778} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} @@ -22408,42 +23451,6 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 0.5} ---- !u!1 &1675131295 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1675131296} - m_Layer: 5 - m_Name: GameObject (2) - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &1675131296 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1675131295} - 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: 901926230} - m_Father: {fileID: 762655493} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 400, y: -374.5} - m_SizeDelta: {x: 768, y: 97} - m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1680420590 GameObject: m_ObjectHideFlags: 0 @@ -23042,42 +24049,6 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1718083120} m_CullTransparentMesh: 1 ---- !u!1 &1724035088 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 1724035089} - m_Layer: 5 - m_Name: GameObject (3) - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &1724035089 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1724035088} - 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: 904216263} - m_Father: {fileID: 762655493} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 1} - m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 400, y: -521.5} - m_SizeDelta: {x: 768, y: 97} - m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1733771704 GameObject: m_ObjectHideFlags: 0 @@ -24033,12 +25004,13 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: "\u5495\u5495\u560E\u560E\u5495\u5495\u560E\u560E\u5495\u5495\u560E\u560E\u5495\u5495\u560E\u560E\u3002" + m_text: Dialog Content m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} m_sharedMaterial: {fileID: -5812992765432063453, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} - m_fontSharedMaterials: [] + m_fontSharedMaterials: + - {fileID: -5812992765432063453, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} m_fontMaterial: {fileID: 0} m_fontMaterials: [] m_fontColor32: @@ -24326,12 +25298,13 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: "\u554A\u554A\u554A\u554A\u4F60\u662F\u4E00\u4E2A\u4E00\u4E2A" + m_text: Character Name m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} m_sharedMaterial: {fileID: -5812992765432063453, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} - m_fontSharedMaterials: [] + m_fontSharedMaterials: + - {fileID: -5812992765432063453, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} m_fontMaterial: {fileID: 0} m_fontMaterials: [] m_fontColor32: @@ -24470,6 +25443,7 @@ GameObject: - component: {fileID: 1876757693} - component: {fileID: 1876757695} - component: {fileID: 1876757694} + - component: {fileID: 1876757696} m_Layer: 5 m_Name: Background m_TagString: Untagged @@ -24537,6 +25511,50 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1876757692} m_CullTransparentMesh: 1 +--- !u!114 &1876757696 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1876757692} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button + 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: 0 + 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: 1876757694} + m_OnClick: + m_PersistentCalls: + m_Calls: [] --- !u!114 &1881101689 MonoBehaviour: m_ObjectHideFlags: 0 @@ -30927,6 +31945,144 @@ RectTransform: m_AnchoredPosition: {x: 32.5, y: -27.5} m_SizeDelta: {x: 25, y: 45} m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &2043234282 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2043234283} + - component: {fileID: 2043234285} + - component: {fileID: 2043234284} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2043234283 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2043234282} + 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: 1340448062} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -300, y: -10} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2043234284 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2043234282} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_text: Choice 0 + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 87c1b020037c81841b3d98dd497cbdeb, type: 2} + m_sharedMaterial: {fileID: -5812992765432063453, guid: 87c1b020037c81841b3d98dd497cbdeb, + type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 1, g: 1, b: 1, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_StyleSheet: {fileID: 0} + m_TextStyleHashCode: -1183493901 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_fontSize: 56 + m_fontSizeBase: 56 + m_fontWeight: 400 + m_enableAutoSizing: 1 + m_fontSizeMin: 36 + m_fontSizeMax: 56 + m_fontStyle: 0 + m_HorizontalAlignment: 2 + m_VerticalAlignment: 4096 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_characterHorizontalScale: 1 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_TextWrappingMode: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_EmojiFallbackSupport: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_IsTextObjectScaleStatic: 0 + m_VertexBufferAutoSizeReduction: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_hasFontAssetChanged: 0 + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &2043234285 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2043234282} + m_CullTransparentMesh: 1 --- !u!1 &2050618681 GameObject: m_ObjectHideFlags: 0 @@ -32532,81 +33688,6 @@ MonoBehaviour: m_ColorSpace: 0 m_NumColorKeys: 2 m_NumAlphaKeys: 3 ---- !u!1 &2144823340 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 2144823341} - - component: {fileID: 2144823343} - - component: {fileID: 2144823342} - m_Layer: 5 - m_Name: Image - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &2144823341 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2144823340} - 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: 208016887} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0} - m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &2144823342 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2144823340} - 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: 439f6117a35abbf4e8c4b847c040f025, 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!222 &2144823343 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2144823340} - m_CullTransparentMesh: 1 --- !u!1 &2146048527 GameObject: m_ObjectHideFlags: 0 @@ -33035,56 +34116,6 @@ MonoBehaviour: m_BlockingMask: serializedVersion: 2 m_Bits: 55 ---- !u!114 &2860688340790966187 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4397390401624236569} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 17254192719abee4f9222246fd403de5, type: 3} - m_Name: - m_EditorClassIdentifier: - serializationData: - SerializedFormat: 2 - SerializedBytes: - ReferencedUnityObjects: [] - SerializedBytesString: - Prefab: {fileID: 0} - PrefabModificationsReferencedUnityObjects: [] - PrefabModifications: [] - SerializationNodes: - - Name: fadeInStartAction - Entry: 6 - Data: - - Name: fadeInFinishAction - Entry: 6 - Data: - - Name: fadeOutAction - Entry: 6 - Data: - mainCanvasGroup: {fileID: 6759614698927231795} - closeButton: {fileID: 0} - dialogContentFrame: {fileID: 871906517} ---- !u!1 &3003397923318546413 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 6447520319398441545} - - component: {fileID: 6225692376525528896} - m_Layer: 0 - m_Name: DialogManager - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 --- !u!224 &3166036863501693288 RectTransform: m_ObjectHideFlags: 0 @@ -33347,7 +34378,6 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 5788293620442285278} - - component: {fileID: 2860688340790966187} m_Layer: 5 m_Name: DialogPage m_TagString: Untagged @@ -33381,9 +34411,9 @@ MonoBehaviour: m_GameObject: {fileID: 5686664992784830960} m_Enabled: 1 m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: e58008720832d5045ada32d1161faa69, type: 3} + m_Script: {fileID: 11500000, guid: a26cc5f52c5322d46b18240eb73c0a62, type: 3} m_Name: - m_EditorClassIdentifier: + m_EditorClassIdentifier: Assembly-CSharp::Ichni.Story.StoryManager serializationData: SerializedFormat: 2 SerializedBytes: @@ -33426,8 +34456,9 @@ MonoBehaviour: - Name: Entry: 8 Data: - storyline: {fileID: 1091682064} + treeController: {fileID: 78737090} storyUIPage: {fileID: 78737089} + characterRegistry: {fileID: 11400000, guid: cb28799b724eb714aa0cc820b36a8b5f, type: 2} --- !u!4 &5788293620442285278 Transform: m_ObjectHideFlags: 0 @@ -33463,7 +34494,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!223 &6169289651699952963 Canvas: m_ObjectHideFlags: 0 @@ -33487,109 +34518,6 @@ Canvas: m_SortingLayerID: 0 m_SortingOrder: 100 m_TargetDisplay: 0 ---- !u!114 &6225692376525528896 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3003397923318546413} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 9e89b9d7eea97734baa166072b050239, type: 3} - m_Name: - m_EditorClassIdentifier: - serializationData: - SerializedFormat: 2 - SerializedBytes: - ReferencedUnityObjects: [] - SerializedBytesString: - Prefab: {fileID: 0} - PrefabModificationsReferencedUnityObjects: [] - PrefabModifications: [] - SerializationNodes: - - Name: functionDictionary - Entry: 7 - Data: 0|System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Collections.Generic.List`1[[System.String, - mscorlib]], mscorlib]], mscorlib - - Name: comparer - Entry: 7 - Data: 1|System.Collections.Generic.GenericEqualityComparer`1[[System.String, - mscorlib]], mscorlib - - Name: - Entry: 8 - Data: - - Name: - Entry: 12 - Data: 0 - - Name: - Entry: 13 - Data: - - Name: - Entry: 8 - Data: - - Name: dialogDictionary - Entry: 7 - Data: 2|System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Collections.Generic.List`1[[Ichni.Story.DialogSentence, - Assembly-CSharp]], mscorlib]], mscorlib - - Name: comparer - Entry: 9 - Data: 1 - - Name: - Entry: 12 - Data: 0 - - Name: - Entry: 13 - Data: - - Name: - Entry: 8 - Data: - - Name: choiceDictionary - Entry: 7 - Data: 3|System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[Ichni.Story.ChoiceGroup, - Assembly-CSharp]], mscorlib - - Name: comparer - Entry: 7 - Data: 4|System.Collections.Generic.GenericEqualityComparer`1[[System.String, - mscorlib]], mscorlib - - Name: - Entry: 8 - Data: - - Name: - Entry: 12 - Data: 0 - - Name: - Entry: 13 - Data: - - Name: - Entry: 8 - Data: - - Name: conditionDictionary - Entry: 7 - Data: 5|System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Collections.Generic.List`1[[Ichni.Story.Condition, - Assembly-CSharp]], mscorlib]], mscorlib - - Name: comparer - Entry: 9 - Data: 1 - - Name: - Entry: 12 - Data: 0 - - Name: - Entry: 13 - Data: - - Name: - Entry: 8 - Data: - - Name: dialogEndActions - Entry: 6 - Data: - dialogTextAssets: [] - isPlayingDialog: 0 - isPlayingChoice: 0 - currentDialog: - currentDialogSentenceIndex: 0 - currentFinalType: - dialogUIPage: {fileID: 2860688340790966187} --- !u!224 &6347218673911890593 RectTransform: m_ObjectHideFlags: 0 @@ -33603,10 +34531,9 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1055026778} - - {fileID: 1492554505} - {fileID: 163525692} - {fileID: 871906516} - - {fileID: 762655493} + - {fileID: 234096307} - {fileID: 1908680555} - {fileID: 490649578} - {fileID: 1617002813} @@ -33618,21 +34545,6 @@ RectTransform: m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0, y: 0} ---- !u!4 &6447520319398441545 -Transform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 3003397923318546413} - serializedVersion: 2 - 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: 0} - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!225 &6759614698927231795 CanvasGroup: m_ObjectHideFlags: 0 @@ -33802,7 +34714,7 @@ SceneRoots: - {fileID: 811649975} - {fileID: 1525336453} - {fileID: 951833081} - - {fileID: 6447520319398441545} + - {fileID: 449802387} - {fileID: 4003430166136740793} - {fileID: 389596702} - {fileID: 1998834542} diff --git a/Assets/Scenes/MenuScene/MenuVolume.asset b/Assets/Scenes/MenuScene/MenuVolume.asset index a3a1daa4..9726d411 100644 --- a/Assets/Scenes/MenuScene/MenuVolume.asset +++ b/Assets/Scenes/MenuScene/MenuVolume.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74a172724214accb4bc3737aba42a9e7d614375ef0ad4a83a5abcdd6ac226747 -size 3074 +oid sha256:e13b6971fd88aac96b243a5f611655de6791aace6de7842db0de7abab3d19683 +size 3122 diff --git a/Assets/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodule.cs b/Assets/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodule.cs index 1908d8c2..cfe6fbe3 100644 --- a/Assets/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodule.cs +++ b/Assets/Scripts/Game/GameElements/Track/TrackSubmodules/TrackRendererSubmodule/TrackRendererSubmodule.cs @@ -82,7 +82,7 @@ namespace Ichni.RhythmGame } } - protected void SetEnableEmission() => meshRenderer.material.SetInt("_Emission", enableEmission ? 1 : 0); + protected void SetEnableEmission() => meshRenderer.material.SetInt("_EnableEmission", enableEmission ? 1 : 0); protected void SetEnableZWrite() => meshRenderer.material.SetInt("_ZWrite", zWrite ? 1 : 0); protected void SetEmissionIntensity() => meshRenderer.material.SetColor("_EmissionColor", Color.white * Mathf.Pow(2, emissionIntensity)); protected void SetUV() diff --git a/Assets/Scripts/Menu/MenuManager.cs b/Assets/Scripts/Menu/MenuManager.cs index 0cc78c50..ca2a1717 100644 --- a/Assets/Scripts/Menu/MenuManager.cs +++ b/Assets/Scripts/Menu/MenuManager.cs @@ -21,7 +21,7 @@ namespace Ichni public LoginPage loginPage; public ChapterSelectionUIPage chapterSelectionUIPage; public StoryUIPage storyUIPage; - public DialogUIPage dialogUIPage; + // dialogUIPage å·²éšæ—§å¯¹è¯ç³»ç»Ÿç§»é™¤ï¼›é˜¶æ®µ 3 将接入新的 VN 对è¯é¡µé¢å¼•用 public SongSelectionUIPage songSelectionUIPage; public TransitionUIPage transitionUIPage; public SettingsUIPage settingsUIPage; diff --git a/Assets/Scripts/NewStorySystem.meta b/Assets/Scripts/NewStorySystem.meta new file mode 100644 index 00000000..2c9b0085 --- /dev/null +++ b/Assets/Scripts/NewStorySystem.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e814c481539958443ae18379d442462d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/NewStorySystem/Data.meta b/Assets/Scripts/NewStorySystem/Data.meta new file mode 100644 index 00000000..0b6f737f --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Data.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f44ff0ea98bbdd44794db6a7250ef7f7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/NewStorySystem/Data/CharacterData.cs b/Assets/Scripts/NewStorySystem/Data/CharacterData.cs new file mode 100644 index 00000000..e3908b55 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Data/CharacterData.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni.Story +{ + /// + /// 立绘渲染方å¼ã€‚当å‰ä½¿ç”¨ Sprite,预留 Live2D / Spine 扩展入å£ã€‚ + /// + public enum PortraitStyle + { + Sprite, + Live2D, + Spine + } + + /// + /// å•个角色的立绘数æ®ï¼ˆæŒ‰è§’色å•独创建 ScriptableObject 资产)。 + /// + [CreateAssetMenu(fileName = "CharacterData", menuName = "Ichni/Story/New/CharacterData")] + public class CharacterData : SerializedScriptableObject + { + [LabelText("Character ID")] + [InfoBox("æ­¤ ID 需与 .yarn 文件中对应å°è¯çš„ speaker å完全一致。")] + public string characterId; + + [LabelText("Display Name Key")] + [InfoBox("Unity Localization String Table Keyï¼Œç”¨äºŽå¤šè¯­è¨€è§’è‰²åæ˜¾ç¤ºã€‚")] + public string displayNameKey; + + [LabelText("Portrait Style")] + public PortraitStyle portraitStyle = PortraitStyle.Sprite; + + // ── Sprite ────────────────────────────────────────────────────────────── + + [LabelText("Emotion Sprites")] + [ShowIf("portraitStyle", PortraitStyle.Sprite)] + [DictionaryDrawerSettings(KeyLabel = "Emotion Name", ValueLabel = "Sprite")] + [InfoBox("Key 为情绪å(如 normal / happy / angry),Value 为对应 Sprite 资产。")] + public Dictionary emotionSprites = new Dictionary(); + + // ── Dynamic (Live2D / Spine) ───────────────────────────────────────────── + + [LabelText("Dynamic Portrait Prefab")] + [ShowIf("IsDynamicPortrait")] + [InfoBox("Live2D / Spine 立绘预制体,需挂载实现 IPortraitRenderer 接å£çš„组件(阶段 3 制作)。")] + public GameObject dynamicPortraitPrefab; + + private bool IsDynamicPortrait => + portraitStyle == PortraitStyle.Live2D || portraitStyle == PortraitStyle.Spine; + } +} diff --git a/Assets/Scripts/NewStorySystem/Data/CharacterData.cs.meta b/Assets/Scripts/NewStorySystem/Data/CharacterData.cs.meta new file mode 100644 index 00000000..63e77eee --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Data/CharacterData.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 87a7c342a93341841bf78d2ba004879a \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Data/CharacterRegistry.cs b/Assets/Scripts/NewStorySystem/Data/CharacterRegistry.cs new file mode 100644 index 00000000..68c3731c --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Data/CharacterRegistry.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni.Story +{ + /// + /// 全局角色注册表(ScriptableObject)。 + /// 按 characterId 索引所有 , + /// è¿è¡Œæ—¶æä¾›å¸¦ç¼“存的快速查找。 + /// + [CreateAssetMenu(fileName = "CharacterRegistry", menuName = "Ichni/Story/New/CharacterRegistry")] + public class CharacterRegistry : SerializedScriptableObject + { + [LabelText("Characters")] + [ListDrawerSettings(ShowFoldout = true, DraggableItems = true)] + [InfoBox("将所有角色的 CharacterData 资产拖入此列表,系统è¿è¡Œæ—¶ä¼šè‡ªåŠ¨æŒ‰ characterId 建立索引。")] + public List characters = new List(); + + // è¿è¡Œæ—¶ç¼“存,首次 TryGet 时延迟构建 + private Dictionary _cache; + + /// + /// æ ¹æ® characterId 查找角色数æ®ï¼Œæ‰¾åˆ°æ—¶è¿”回 true 并通过 out 输出结果。 + /// + public bool TryGet(string characterId, out CharacterData data) + { + if (_cache == null) + BuildCache(); + + return _cache.TryGetValue(characterId, out data); + } + + /// + /// æ ¹æ® characterId 获å–角色数æ®ï¼Œæœªæ‰¾åˆ°æ—¶è¿”回 null。 + /// + public CharacterData Get(string characterId) + { + TryGet(characterId, out CharacterData data); + return data; + } + + private void BuildCache() + { + _cache = new Dictionary(characters.Count); + + foreach (CharacterData character in characters) + { + if (character == null) continue; + if (!string.IsNullOrEmpty(character.characterId)) + _cache[character.characterId] = character; + } + } + + private void OnValidate() + { + // Inspector ä¸­ä¿®æ”¹åˆ—è¡¨æ—¶æ¸…é™¤ç¼“å­˜ï¼Œç¡®ä¿ TryGet 结果始终一致 + _cache = null; + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Data/CharacterRegistry.cs.meta b/Assets/Scripts/NewStorySystem/Data/CharacterRegistry.cs.meta new file mode 100644 index 00000000..f531409f --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Data/CharacterRegistry.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e3ea2ce926155ad4285efbc15a0a82b6 \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Data/StoryBlockDefinition.cs b/Assets/Scripts/NewStorySystem/Data/StoryBlockDefinition.cs new file mode 100644 index 00000000..db998d7d --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Data/StoryBlockDefinition.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni.Story +{ + /// + /// Block 在故事树中的状æ€ï¼šå·²é”定ã€å½“å‰å¯äº¤äº’ã€å·²å®Œæˆã€‚ + /// + public enum StoryBlockState + { + Locked, + Current, + Completed + } + + /// + /// Block 的类型,决定点击åŽè§¦å‘的行为。 + /// + public enum StoryBlockType + { + Text, + Song, + Tutorial + } + + /// + /// å˜é‡æ¡ä»¶çš„æ¯”较方å¼ã€‚ + /// + public enum VariableComparison + { + Equal, + NotEqual, + Greater, + GreaterOrEqual, + Less, + LessOrEqual + } + + /// + /// 故事树中å•个 block 的完整数æ®å®šä¹‰ï¼Œç”± æŒæœ‰ã€‚ + /// + [InlineProperty] + [Serializable] + public class StoryBlockDefinition + { + [FoldoutGroup("$blockId", false)] + [LabelText("Block ID")] + public string blockId; + + [FoldoutGroup("$blockId")] + [LabelText("Type")] + public StoryBlockType blockType; + + [FoldoutGroup("$blockId")] + [LabelText("Grid Column")] + [Tooltip("故事树网格中的列:å‘å³ä¸ºæ­£ï¼Œ(0,0) ä½äºŽå®¹å™¨å·¦ä¾§ä¸­éƒ¨ã€‚å¯ä¸ºè´Ÿæ•°ä¸Žå°æ•°ï¼ˆç”¨äºŽå¾®è°ƒï¼‰ã€‚")] + public float gridColumn; + + [FoldoutGroup("$blockId")] + [LabelText("Grid Row")] + [Tooltip("故事树网格中的行:å‘下为正ã€å‘上为负,0 对应垂直中线。å¯ä¸ºå°æ•°ï¼ˆç”¨äºŽå¾®è°ƒï¼‰ã€‚")] + public float gridRow; + + [FoldoutGroup("$blockId")] + [LabelText("Next Block IDs")] + public List nextBlockIds = new List(); + + [FoldoutGroup("$blockId")] + [LabelText("Unlock Condition")] + public UnlockCondition unlockCondition = new UnlockCondition(); + + // ── Text Block ────────────────────────────────────────────────────────── + + [FoldoutGroup("$blockId")] + [ShowIf("blockType", StoryBlockType.Text)] + [LabelText("Yarn Node Name")] + public string yarnNodeName; + + [FoldoutGroup("$blockId")] + [ShowIf("blockType", StoryBlockType.Text)] + [LabelText("Title Key (Localization)")] + public string titleKey; + + // ── Song Block ────────────────────────────────────────────────────────── + + [FoldoutGroup("$blockId")] + [ShowIf("blockType", StoryBlockType.Song)] + [LabelText("Song Name")] + public string songName; + + // ── Tutorial Block ────────────────────────────────────────────────────── + + [FoldoutGroup("$blockId")] + [ShowIf("blockType", StoryBlockType.Tutorial)] + [LabelText("Tutorial Name")] + public string tutorialName; + + /// + /// 预览 / 调试用的显示标题:按类型å–å¯¹åº”å­—æ®µï¼Œç¼ºçœæ—¶å›žé€€åˆ° blockId。 + /// + public string GetDisplayTitle() + { + string title = blockType switch + { + StoryBlockType.Text => titleKey, + StoryBlockType.Song => songName, + StoryBlockType.Tutorial => tutorialName, + _ => null + }; + + return string.IsNullOrEmpty(title) ? blockId : title; + } + } + + /// + /// Block çš„è§£é”æ¡ä»¶ã€‚以一棵å¯ç»„åˆçš„æ¡ä»¶æ ‘ï¼ˆï¼‰è¡¨è¾¾ï¼Œ + /// 支æŒ"与 / 或 / éž + å¶å­æ¡ä»¶"的任æ„嵌套。根节点为空时视为无æ¡ä»¶è§£é”(始终满足)。 + /// + [InlineProperty] + [Serializable] + public class UnlockCondition + { + [HideLabel] + [SerializeReference] + [InfoBox("留空表示无æ¡ä»¶è§£é”(章节起始å³å¯ç”¨ï¼‰ã€‚å¯é€‰æ‹© All Of(AND) / Any Of(OR) / Not å¤åˆèŠ‚ç‚¹è¿›è¡Œä»»æ„嵌套。")] + public StoryConditionNode root; + + /// + /// æ£€æŸ¥è§£é”æ¡ä»¶æ˜¯å¦æ»¡è¶³ã€‚根节点为空视为满足。 + /// + /// 按å˜é‡å返回其整型值的委托。 + /// 按 blockId 判断该 block 是å¦å·²å®Œæˆçš„委托。 + public bool IsSatisfied(Func getVariable, Func isBlockCompleted) + { + return root == null || root.Evaluate(getVariable, isBlockCompleted); + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Data/StoryBlockDefinition.cs.meta b/Assets/Scripts/NewStorySystem/Data/StoryBlockDefinition.cs.meta new file mode 100644 index 00000000..99597f15 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Data/StoryBlockDefinition.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e6951fbb376c1f44aac1da9ebbac7502 \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Data/StoryConditionNode.cs b/Assets/Scripts/NewStorySystem/Data/StoryConditionNode.cs new file mode 100644 index 00000000..52476e8b --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Data/StoryConditionNode.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni.Story +{ + /// + /// è§£é”æ¡ä»¶æ ‘的节点基类。通过多æ€ç»„åˆå‡º"与 / 或 / éž + å¶å­æ¡ä»¶"çš„å¤åˆè¡¨è¾¾å¼ã€‚ + /// ç”± 借助 [SerializeReference] åºåˆ—åŒ–æŒæœ‰ï¼Œ + /// Odin 会以类型下拉框展示å¯é€‰çš„节点类型。 + /// + [Serializable] + public abstract class StoryConditionNode + { + /// + /// 对该节点求值。 + /// + /// 按å˜é‡å返回其整型值的委托。 + /// 按 blockId 判断该 block 是å¦å·²å®Œæˆçš„委托。 + public abstract bool Evaluate(Func getVariable, Func isBlockCompleted); + } + + /// + /// å¤åˆèŠ‚ç‚¹ï¼ˆé€»è¾‘ä¸Ž ANDï¼‰ï¼šæ‰€æœ‰å­æ¡ä»¶å‡æ»¡è¶³æ—¶æ‰æ»¡è¶³ã€‚å­åˆ—表为空视为满足。 + /// + [Serializable] + [LabelText("All Of (AND)")] + public class AllOfCondition : StoryConditionNode + { + [HideLabel] + [SerializeReference] + [ListDrawerSettings(ShowFoldout = true, DefaultExpandedState = true)] + public List conditions = new List(); + + public override bool Evaluate(Func getVariable, Func isBlockCompleted) + { + foreach (StoryConditionNode child in conditions) + { + if (child != null && !child.Evaluate(getVariable, isBlockCompleted)) + return false; + } + + return true; + } + } + + /// + /// å¤åˆèŠ‚ç‚¹ï¼ˆé€»è¾‘æˆ– ORï¼‰ï¼šä»»ä¸€å­æ¡ä»¶æ»¡è¶³å³æ»¡è¶³ã€‚å­åˆ—è¡¨ä¸ºç©ºè§†ä¸ºä¸æ»¡è¶³ã€‚ + /// + [Serializable] + [LabelText("Any Of (OR)")] + public class AnyOfCondition : StoryConditionNode + { + [HideLabel] + [SerializeReference] + [ListDrawerSettings(ShowFoldout = true, DefaultExpandedState = true)] + public List conditions = new List(); + + public override bool Evaluate(Func getVariable, Func isBlockCompleted) + { + if (conditions.Count == 0) + return false; + + foreach (StoryConditionNode child in conditions) + { + if (child != null && child.Evaluate(getVariable, isBlockCompleted)) + return true; + } + + return false; + } + } + + /// + /// å¤åˆèŠ‚ç‚¹ï¼ˆé€»è¾‘éž NOTï¼‰ï¼šå¯¹å­æ¡ä»¶å–åã€‚å­æ¡ä»¶ä¸ºç©ºè§†ä¸ºæ»¡è¶³ï¼ˆç­‰ä»·äºŽ"éž å‡")。 + /// + [Serializable] + [LabelText("Not")] + public class NotCondition : StoryConditionNode + { + [HideLabel] + [SerializeReference] + public StoryConditionNode condition; + + public override bool Evaluate(Func getVariable, Func isBlockCompleted) + { + return condition == null || !condition.Evaluate(getVariable, isBlockCompleted); + } + } + + /// + /// å¶å­èŠ‚ç‚¹ï¼šæŒ‡å®š block å·²å®Œæˆæ—¶æ»¡è¶³ã€‚ + /// + [Serializable] + [LabelText("Block Completed")] + public class BlockCompletedCondition : StoryConditionNode + { + [LabelText("Block ID")] + public string blockId; + + public override bool Evaluate(Func getVariable, Func isBlockCompleted) + { + return !string.IsNullOrEmpty(blockId) && isBlockCompleted(blockId); + } + } + + /// + /// å¶å­èŠ‚ç‚¹ï¼šå°†æŒ‡å®šå˜é‡çš„当å‰å€¼ä¸Žç›®æ ‡å€¼æŒ‰æ¯”è¾ƒæ–¹å¼æ±‚值。 + /// + [Serializable] + [LabelText("Variable Compare")] + public class VariableCondition : StoryConditionNode + { + [LabelText("Variable Name")] + public string variableName; + + [LabelText("Comparison")] + public VariableComparison comparison; + + [LabelText("Value")] + public int value; + + public override bool Evaluate(Func getVariable, Func isBlockCompleted) + { + int actual = getVariable(variableName); + return comparison switch + { + VariableComparison.Equal => actual == value, + VariableComparison.NotEqual => actual != value, + VariableComparison.Greater => actual > value, + VariableComparison.GreaterOrEqual => actual >= value, + VariableComparison.Less => actual < value, + VariableComparison.LessOrEqual => actual <= value, + _ => false + }; + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Data/StoryConditionNode.cs.meta b/Assets/Scripts/NewStorySystem/Data/StoryConditionNode.cs.meta new file mode 100644 index 00000000..4a864602 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Data/StoryConditionNode.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9fcafe0071b87374e88e01816f9a5252 \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Data/StoryData.cs b/Assets/Scripts/NewStorySystem/Data/StoryData.cs new file mode 100644 index 00000000..64fb64ce --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Data/StoryData.cs @@ -0,0 +1,238 @@ +using System.Collections.Generic; +using Sirenix.OdinInspector; +using UnityEngine; +using Yarn.Unity; +#if UNITY_EDITOR +using UnityEditor; +using Sirenix.Utilities.Editor; +#endif + +namespace Ichni.Story +{ + /// + /// å•个章节的故事树数æ®ï¼Œä»¥ ScriptableObject å½¢å¼å­˜å‚¨æ‰€æœ‰ block 定义与关è”资产引用。 + /// 布局完全由æ¯ä¸ª block çš„ (gridColumn, gridRow) 陿€å†³å®šï¼Œè¿è¡Œæ—¶å…¨é‡å±•示。 + /// + [CreateAssetMenu(fileName = "StoryData", menuName = "Ichni/Story/New/StoryData")] + public class StoryData : SerializedScriptableObject + { + [LabelText("Chapter Index")] + public string chapterIndex; + + [LabelText("Yarn Project")] + [InfoBox("该章节对应的 YarnProject 编译资产,阶段 2 接线。")] + public YarnProject yarnProject; + + [LabelText("Blocks")] + [ListDrawerSettings(ShowFoldout = true, DefaultExpandedState = false, DraggableItems = true, ShowItemCount = true)] + public List blocks = new List(); + + // ── Queries ───────────────────────────────────────────────────────────── + + /// + /// æ ¹æ® blockId èŽ·å– block 定义,未找到时返回 null。 + /// + public StoryBlockDefinition GetBlock(string blockId) + { + foreach (StoryBlockDefinition block in blocks) + { + if (block.blockId == blockId) + return block; + } + + return null; + } + + /// + /// èŽ·å–æŒ‡å®š blockId 的所有直接åŽç»§ block 定义。 + /// + public IEnumerable GetNextBlocks(string blockId) + { + StoryBlockDefinition source = GetBlock(blockId); + if (source == null) yield break; + + foreach (string nextId in source.nextBlockIds) + { + StoryBlockDefinition next = GetBlock(nextId); + if (next != null) + yield return next; + } + } + + // ── Editor Preview ──────────────────────────────────────────────────────── +#if UNITY_EDITOR + private enum StoryPreviewMode { Detailed, Simple } + + // 编辑器 UI 临时状æ€ï¼ˆä¸å‚与åºåˆ—化) + [System.NonSerialized] private StoryPreviewMode _previewMode = StoryPreviewMode.Detailed; + [System.NonSerialized] private Vector2 _previewPan; + [System.NonSerialized] private bool _isPanningPreview; + + private const float PreviewViewportHeight = 380f; + private const float PreviewPaddingLeft = 24f; + private const float PreviewTangent = 44f; + + // 详细版:较大方å—,标注 id / 类型 / 标题 + private const float DetailCellWidth = 150f; + private const float DetailCellHeight = 72f; + private const float DetailStepX = 210f; + private const float DetailStepY = 120f; + + // 简略版:很å°çš„æ–¹å—,仅标注 Block ID + private const float SimpleCellWidth = 62f; + private const float SimpleCellHeight = 26f; + private const float SimpleStepX = 92f; + private const float SimpleStepY = 42f; + + /// + /// 在 Inspector 顶部绘制åªè¯»çš„æ•…事树结构预览(æµç¨‹å›¾æ ·å¼ï¼Œè¿žçº¿å·¦å³ç›¸è¿žï¼‰ã€‚ + /// æä¾›"详细 / 简略"两ç§å¯æ‹–拽平移的视图;按 (gridColumn, gridRow) 摆放 block, + /// 支æŒè´Ÿæ•°ä¸Žå°æ•°å标,(0,0) 对应左侧中部。 + /// + [PropertyOrder(-10)] + [OnInspectorGUI] + private void DrawStoryPreview() + { + SirenixEditorGUI.Title("Story Tree Preview", null, TextAlignment.Left, true); + + if (blocks == null || blocks.Count == 0) + { + EditorGUILayout.HelpBox("尚未定义任何 block。", MessageType.Info); + return; + } + + // 顶部工具æ¡ï¼šæ¨¡å¼åˆ‡æ¢ + å¤ä½è§†å›¾ + EditorGUILayout.BeginHorizontal(); + _previewMode = (StoryPreviewMode)GUILayout.Toolbar( + (int)_previewMode, new[] { "Detailed", "Simple" }, GUILayout.Height(20f)); + if (GUILayout.Button("Reset View", GUILayout.Width(90f), GUILayout.Height(20f))) + _previewPan = Vector2.zero; + EditorGUILayout.EndHorizontal(); + + bool simple = _previewMode == StoryPreviewMode.Simple; + float cellW = simple ? SimpleCellWidth : DetailCellWidth; + float cellH = simple ? SimpleCellHeight : DetailCellHeight; + float stepX = simple ? SimpleStepX : DetailStepX; + float stepY = simple ? SimpleStepY : DetailStepY; + + Rect area = GUILayoutUtility.GetRect(0f, PreviewViewportHeight, GUILayout.ExpandWidth(true)); + EditorGUI.DrawRect(area, new Color(0.12f, 0.12f, 0.14f, 1f)); + + HandlePreviewPanning(area); + + // è£å‰ªåˆ°é¢„览区,超出部分(平移åŽï¼‰ä¸å¤–溢到其它 Inspector 元素 + GUI.BeginClip(area); + { + // (0,0) 网格点 = 视å£å·¦ä¾§ä¸­çº¿ï¼ˆå åŠ å¹³ç§»é‡ï¼‰ + float originX = PreviewPaddingLeft + _previewPan.x; + float originY = area.height * 0.5f + _previewPan.y; + + // è®¡ç®—å„ block 矩形(左中对é½ç½‘格点,与è¿è¡Œæ—¶ pivot(0,0.5) 一致),按 blockId 索引 + Dictionary cellRects = new Dictionary(); + foreach (StoryBlockDefinition block in blocks) + { + if (block == null || string.IsNullOrEmpty(block.blockId)) continue; + + float px = originX + block.gridColumn * stepX; // 列å‘å³ä¸ºæ­£ï¼ˆå¯è´Ÿã€å¯å°æ•°ï¼‰ + float py = originY + block.gridRow * stepY; // 行å‘下为正ã€å‘上为负 + cellRects[block.blockId] = new Rect(px, py - cellH * 0.5f, cellW, cellH); + } + + // 先画连线:左å³ç›¸è¿žçš„æµç¨‹å›¾ï¼ˆèµ·ç‚¹å³ä¸­ → 终点左中,水平切线) + Handles.BeginGUI(); + foreach (StoryBlockDefinition block in blocks) + { + if (block == null || !cellRects.TryGetValue(block.blockId, out Rect fromRect)) continue; + + foreach (string nextId in block.nextBlockIds) + { + if (string.IsNullOrEmpty(nextId) || !cellRects.TryGetValue(nextId, out Rect toRect)) continue; + + Vector3 start = new Vector3(fromRect.xMax, fromRect.center.y, 0f); + Vector3 end = new Vector3(toRect.xMin, toRect.center.y, 0f); + Vector3 startTan = start + Vector3.right * PreviewTangent; + Vector3 endTan = end + Vector3.left * PreviewTangent; + + Handles.DrawBezier(start, end, startTan, endTan, + new Color(0.82f, 0.82f, 0.88f, 1f), null, simple ? 2f : 3f); + } + } + Handles.EndGUI(); + + // å†ç”» block æ–¹å— + GUIStyle labelStyle = new GUIStyle(EditorStyles.boldLabel) + { + alignment = TextAnchor.MiddleCenter, + wordWrap = true, + richText = false, + fontSize = simple ? 9 : 11, + normal = { textColor = Color.white } + }; + + foreach (StoryBlockDefinition block in blocks) + { + if (block == null || !cellRects.TryGetValue(block.blockId, out Rect rect)) continue; + + EditorGUI.DrawRect(rect, GetTypeColor(block.blockType)); + string label = simple + ? block.blockId + : $"{block.blockId}\n[{block.blockType}]\n{block.GetDisplayTitle()}"; + GUI.Label(rect, label, labelStyle); + } + } + GUI.EndClip(); + + EditorGUILayout.HelpBox( + "在预览区内拖拽å¯å¹³ç§»è§†å›¾ã€‚列 = 水平(å³ä¸ºæ­£ï¼‰ï¼Œè¡Œ = 垂直(下为正ã€ä¸Šä¸ºè´Ÿï¼‰ï¼Œ(0,0) ä½äºŽå·¦ä¾§ä¸­éƒ¨ï¼Œæ”¯æŒè´Ÿæ•°ä¸Žå°æ•°ã€‚", + MessageType.None); + } + + /// + /// 处ç†é¢„览区内的鼠标拖拽平移。 + /// + private void HandlePreviewPanning(Rect area) + { + Event e = Event.current; + switch (e.type) + { + case EventType.MouseDown: + if (area.Contains(e.mousePosition)) + { + _isPanningPreview = true; + e.Use(); + } + break; + case EventType.MouseDrag: + if (_isPanningPreview) + { + _previewPan += e.delta; + GUI.changed = true; + e.Use(); + } + break; + case EventType.MouseUp: + if (_isPanningPreview) + { + _isPanningPreview = false; + e.Use(); + } + break; + } + } + + /// + /// 预览中ä¸åŒ block 类型的填充色。 + /// + private static Color GetTypeColor(StoryBlockType type) + { + return type switch + { + StoryBlockType.Text => new Color(0.20f, 0.42f, 0.72f, 1f), // è“ + StoryBlockType.Song => new Color(0.62f, 0.30f, 0.66f, 1f), // ç´« + StoryBlockType.Tutorial => new Color(0.28f, 0.56f, 0.36f, 1f), // 绿 + _ => new Color(0.4f, 0.4f, 0.4f, 1f) + }; + } +#endif + } +} diff --git a/Assets/Scripts/NewStorySystem/Data/StoryData.cs.meta b/Assets/Scripts/NewStorySystem/Data/StoryData.cs.meta new file mode 100644 index 00000000..14556103 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Data/StoryData.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1b6af1b536c0bd1418753fffe7a903ac \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Dialogue.meta b/Assets/Scripts/NewStorySystem/Dialogue.meta new file mode 100644 index 00000000..27c55914 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Dialogue.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 79af33f24eff7054aa7cfe2386f6e221 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/NewStorySystem/Dialogue/ConsoleLinePresenter.cs b/Assets/Scripts/NewStorySystem/Dialogue/ConsoleLinePresenter.cs new file mode 100644 index 00000000..5a82624d --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Dialogue/ConsoleLinePresenter.cs @@ -0,0 +1,73 @@ +using UnityEngine; +using Yarn.Unity; + +namespace Ichni.Story.Dialogue +{ + /// + /// ã€ä¸´æ—¶ / 调试用】将 Yarn 对è¯è¡Œä»¥"本地化åŽçš„æ–‡æœ¬"输出到 Console 的呈现器。 + /// 用途:在没有 VN å¯¹è¯ UI çš„æƒ…å†µä¸‹ï¼ŒéªŒè¯æ•´æ¡ Yarn 管线是å¦è·‘é€šï¼Œä»¥åŠ + /// Unity Localization æ˜¯å¦æ­£ç¡®è¿”回当å‰è¯­è¨€çš„æ–‡æœ¬ï¼ˆç‚¹å‡»æ–‡æœ¬å—å³å¯åœ¨ Console 看到结果)。 + /// 阶段 3 接入 VNDialoguePresenter åŽï¼Œå¯ä»Ž DialogueRunner 的呈现器列表移除本组件并删除此脚本。 + /// + public class ConsoleLinePresenter : DialoguePresenterBase + { + public override YarnTask OnDialogueStartedAsync() + { + Debug.Log("[ConsoleLinePresenter] === 对è¯å¼€å§‹ ==="); + return YarnTask.CompletedTask; + } + + /// + /// 输出本地化åŽçš„å°è¯æ–‡æœ¬ã€‚ + /// 注æ„:必须 async + YarnTask.Yield()。Yarn Spinner 3.x çš„ RunLocalisedLine 在所有 + /// presenter å‡åŒæ­¥å®Œæˆæ—¶ä¼šé”™è¯¯åœ°è°ƒç”¨ Dialogue.SignalContentComplete()(该方法仅对 + /// command dispatch åˆæ³•),导致 InvalidOperationException。Yield ç¡®ä¿ä»»åŠ¡è¿›å…¥å¼‚æ­¥è·¯å¾„ã€‚ + /// + public override async YarnTask RunLineAsync(LocalizedLine line, LineCancellationToken token) + { + string speaker = string.IsNullOrEmpty(line.CharacterName) ? "(æ—白)" : line.CharacterName; + string body = line.TextWithoutCharacterName.Text; + + Debug.Log($"[ConsoleLinePresenter] {speaker}: {body}\n" + + $" (lineID={line.TextID}, rawText=\"{line.RawText}\")"); + + await YarnTask.Yield(); + } + + /// + /// è°ƒè¯•ç”¨ï¼šæ‰“å°æ‰€æœ‰é€‰é¡¹å¹¶è‡ªåŠ¨é€‰æ‹©ç¬¬ä¸€ä¸ªå¯ç”¨é€‰é¡¹ï¼Œä»¥ä¾¿ç®¡çº¿å¯ä»¥è·‘å®Œæ•´ä¸ªå¯¹è¯æ ‘。 + /// + public override async YarnTask RunOptionsAsync( + DialogueOption[] options, + LineCancellationToken token) + { + for (int i = 0; i < options.Length; i++) + { + string optText = options[i].Line.TextWithoutCharacterName.Text; + string tag = options[i].IsAvailable ? "" : " [ä¸å¯ç”¨]"; + Debug.Log($"[ConsoleLinePresenter] 选项 {i}{tag}: {optText}"); + } + + // 自动选择第一个å¯ç”¨é€‰é¡¹ + foreach (DialogueOption opt in options) + { + if (opt.IsAvailable) + { + Debug.Log($"[ConsoleLinePresenter] → 自动选择: {opt.Line.TextWithoutCharacterName.Text}"); + await YarnTask.Yield(); + return opt; + } + } + + // æ— å¯ç”¨é€‰é¡¹æ—¶è¿”回 nullï¼ˆè§¦å‘ DialogueRunner çš„ fallthrough 逻辑) + await YarnTask.Yield(); + return null; + } + + public override YarnTask OnDialogueCompleteAsync() + { + Debug.Log("[ConsoleLinePresenter] === 对è¯ç»“æŸ ==="); + return YarnTask.CompletedTask; + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Dialogue/ConsoleLinePresenter.cs.meta b/Assets/Scripts/NewStorySystem/Dialogue/ConsoleLinePresenter.cs.meta new file mode 100644 index 00000000..49ce3a6b --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Dialogue/ConsoleLinePresenter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7f38c46a2f0519249b2b9efb27734c13 \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Dialogue/StoryDialogueController.cs b/Assets/Scripts/NewStorySystem/Dialogue/StoryDialogueController.cs new file mode 100644 index 00000000..1da0d704 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Dialogue/StoryDialogueController.cs @@ -0,0 +1,163 @@ +using System.Collections.Generic; +using System.Reflection; +using Ichni.Story.UI; +using UnityEngine; +using UnityEngine.Events; +using Yarn.Unity; +using Yarn.Unity.UnityLocalization; + +namespace Ichni.Story.Dialogue +{ + /// + /// 故事树 block 与 Yarn 之间的桥接控制器。 + /// ç‚¹å‡»æ–‡æœ¬å— â†’ å¯åŠ¨å¯¹åº” Yarn 节点;对è¯ç»“æŸ â†’ æŒä¹…化剧情å˜é‡ã€æ ‡è®° block 完æˆã€ + /// é‡ç®—è§£é”并存档ã€ä¾æ¬¡æ‰§è¡Œç»“æŸå›žè°ƒï¼ˆå¦‚æ­Œæ›²è§£é”æç¤ºï¼Œé˜¶æ®µ 4 使用)。 + /// + public class StoryDialogueController : MonoBehaviour + { + public static StoryDialogueController instance; + + [Header("References")] + [Tooltip("驱动 Yarn 对è¯çš„ DialogueRunner。")] + public DialogueRunner dialogueRunner; + + [Tooltip("故事树页é¢ï¼šå¯¹è¯è¿›è¡Œæ—¶æ·¡å‡ºã€ç»“æŸåŽæ·¡å…¥ï¼ˆå¯é€‰ï¼‰ã€‚")] + public StoryUIPage storyUIPage; + + /// + /// 对è¯ç»“æŸåŽä¾æ¬¡æ‰§è¡Œå¹¶æ¸…空的回调队列。供 unlock_song ç­‰ Yarn 命令(阶段 4) + /// 把"è§£é”æç¤º"排队到对è¯ç»“æŸåŽå¼¹å‡ºã€‚ + /// + [System.NonSerialized] public List dialogueEndActions = new List(); + + // 当剿­£åœ¨æ’­æ”¾å¯¹è¯çš„ block id;对è¯ç»“æŸæ—¶æ®æ­¤æ ‡è®°å®Œæˆ + private string _activeBlockId; + + private void Awake() + { + instance = this; + EnsureUnityLocalisedLineProvider(); + } + + private void OnEnable() + { + if (dialogueRunner != null && dialogueRunner.onDialogueComplete != null) + dialogueRunner.onDialogueComplete.AddListener(HandleDialogueComplete); + } + + private void OnDisable() + { + if (dialogueRunner != null && dialogueRunner.onDialogueComplete != null) + dialogueRunner.onDialogueComplete.RemoveListener(HandleDialogueComplete); + } + + /// + /// æ’­æ”¾æŸæ–‡æœ¬å—对应的 Yarn 节点。若对è¯å·²åœ¨è¿›è¡Œã€å¼•用缺失或节点å为空则忽略。 + /// + public void PlayBlock(TextBlockView block) + { + if (block == null) return; + + if (dialogueRunner == null) + { + Debug.LogError("[StoryDialogueController] 未é…ç½® DialogueRunner,无法开始对è¯ã€‚"); + return; + } + + if (dialogueRunner.IsDialogueRunning) + { + Debug.LogWarning("[StoryDialogueController] 已有对è¯åœ¨è¿›è¡Œä¸­ï¼Œå¿½ç•¥æœ¬æ¬¡ç‚¹å‡»ã€‚"); + return; + } + + if (string.IsNullOrEmpty(block.YarnNodeName)) + { + Debug.LogWarning($"[StoryDialogueController] block '{block.blockId}' 未é…ç½® yarnNodeName,无法开始对è¯ã€‚"); + return; + } + + // å°† DialogueRunner çš„ YarnProject 对é½åˆ°å½“å‰ç« èŠ‚ï¼ˆæ”¯æŒå¤šç« èŠ‚å…±ç”¨ä¸€ä¸ª Runner) + EnsureProjectForCurrentChapter(); + + _activeBlockId = block.blockId; + + if (storyUIPage != null) + storyUIPage.FadeOut(); + + // StartDialogue 返回 YarnTask;此处å³å‘å³å¿˜ï¼Œå®Œæˆç”± onDialogueComplete å¤„ç† + dialogueRunner.StartDialogue(block.YarnNodeName).Forget(); + } + + // å°† DialogueRunner çš„ YarnProject 切æ¢ä¸ºå½“å‰å·²æž„建章节的 StoryData.yarnProject(若ä¸åŒä¸”未在è¿è¡Œï¼‰ + private void EnsureProjectForCurrentChapter() + { + StoryTreeController tree = StoryManager.instance != null ? StoryManager.instance.treeController : null; + StoryData data = tree != null ? tree.ActiveStoryData : null; + + if (data == null || data.yarnProject == null) return; + + if (dialogueRunner.YarnProject != data.yarnProject && !dialogueRunner.IsDialogueRunning) + dialogueRunner.SetProject(data.yarnProject); + } + + /// + /// DialogueRunner.lineProvider 为 [SerializeReference] internal 字段。 + /// Inspector çš„ "Use Unity Localised Line Provider" 按钮有时无法将 MonoBehaviour + /// 引用正确æŒä¹…化到该字段;此方法在è¿è¡Œæ—¶ç”¨å射补救,确ä¿ä½¿ç”¨æ­£ç¡®çš„ Provider。 + /// + private void EnsureUnityLocalisedLineProvider() + { + if (dialogueRunner == null) return; + + FieldInfo field = typeof(DialogueRunner).GetField( + "lineProvider", + BindingFlags.NonPublic | BindingFlags.Instance); + + if (field == null) return; + + // å·²ç»æ˜¯ UnityLocalisedLineProvider åˆ™æ— éœ€å¤„ç† + if (field.GetValue(dialogueRunner) is UnityLocalisedLineProvider) return; + + UnityLocalisedLineProvider provider = + dialogueRunner.GetComponent(); + + if (provider != null) + { + field.SetValue(dialogueRunner, provider); + Debug.Log("[StoryDialogueController] Assigned UnityLocalisedLineProvider to DialogueRunner.lineProvider."); + } + else + { + Debug.LogWarning("[StoryDialogueController] UnityLocalisedLineProvider not found on DialogueRunner's GameObject. Localized line text will be unavailable."); + } + } + + private void HandleDialogueComplete() + { + // æŒä¹…化对è¯è¿‡ç¨‹ä¸­å†™å…¥çš„全局剧情å˜é‡ + if (GameSaveManager.instance != null && GameSaveManager.instance.StorySaveModule != null) + GameSaveManager.instance.StorySaveModule.SaveVariables(); + + // 标记 block å®Œæˆ â†’ é‡ç®—è§£é” â†’ 存档(OnBlockCompleted 内部已去é‡å¹¶ä¿å­˜ç« èŠ‚è¿›åº¦ï¼‰ + if (!string.IsNullOrEmpty(_activeBlockId) && + StoryManager.instance != null && StoryManager.instance.treeController != null) + { + StoryManager.instance.treeController.OnBlockCompleted(_activeBlockId); + } + + _activeBlockId = null; + + // 便¬¡æ‰§è¡Œå¹¶æ¸…空结æŸå›žè°ƒï¼ˆå¦‚æ­Œæ›²è§£é”æç¤ºï¼‰ + if (dialogueEndActions.Count > 0) + { + List pending = new List(dialogueEndActions); + dialogueEndActions.Clear(); + foreach (UnityAction action in pending) + action?.Invoke(); + } + + if (storyUIPage != null) + storyUIPage.FadeIn(); + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Dialogue/StoryDialogueController.cs.meta b/Assets/Scripts/NewStorySystem/Dialogue/StoryDialogueController.cs.meta new file mode 100644 index 00000000..8b168246 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Dialogue/StoryDialogueController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3c5da6d59dd1b3b4594e01d045b271c8 \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Dialogue/StoryVariableStorage.cs b/Assets/Scripts/NewStorySystem/Dialogue/StoryVariableStorage.cs new file mode 100644 index 00000000..370d1919 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Dialogue/StoryVariableStorage.cs @@ -0,0 +1,207 @@ +using System.Collections.Generic; +using System.Globalization; +using UnityEngine; +using Yarn.Unity; + +namespace Ichni.Story.Dialogue +{ + /// + /// 自定义 Yarn å˜é‡å­˜å‚¨ã€‚直接读写 (全局剧情å˜é‡ï¼‰ï¼Œ + /// 让 Yarn 对è¯ä¸­çš„ <<set $var to ...>> ä¸Žæ•…äº‹æ ‘è§£é”æ¡ä»¶å…±äº«åŒä¸€ä»½æ•°æ®æ¥æºï¼Œ + /// 并借助存档模å—çš„ ES3 åºåˆ—化æŒä¹…化。 + /// å˜é‡å沿用 Yarn 约定(带 $ å‰ç¼€ï¼‰ï¼›è§£é”æ¡ä»¶ä¸­çš„å˜é‡åä¹Ÿåº”ä¿æŒä¸€è‡´ã€‚ + /// + public class StoryVariableStorage : VariableStorageBehaviour + { + // GameSaveManager 尚未就绪(编辑器早期 / 场景未åˆå§‹åŒ–)时的兜底容器 + private readonly StoryVariablesSave _fallback = new StoryVariablesSave(); + + /// 当å‰ç”Ÿæ•ˆçš„å˜é‡å®¹å™¨ï¼šä¼˜å…ˆå–存档模å—,缺失时用本地兜底容器。 + private StoryVariablesSave Variables + { + get + { + if (GameSaveManager.instance != null && GameSaveManager.instance.StorySaveModule != null) + return GameSaveManager.instance.StorySaveModule.variables; + return _fallback; + } + } + + // ── IVariableStorage 实现 ──────────────────────────────────────────────── + + public override bool TryGetValue(string variableName, out T result) + { + StoryVariablesSave vars = Variables; + + if (vars.floatVariables.TryGetValue(variableName, out float f)) + return TryConvert(f, out result); + + if (vars.boolVariables.TryGetValue(variableName, out bool b)) + return TryConvert(b, out result); + + if (vars.stringVariables.TryGetValue(variableName, out string s)) + return TryConvert(s, out result); + + result = default; + return false; + } + + public override void SetValue(string variableName, string stringValue) + { + RemoveStaleEntries(variableName, keepFloat: false, keepString: true, keepBool: false); + Variables.stringVariables[variableName] = stringValue; + NotifyVariableChanged(variableName, stringValue); + } + + public override void SetValue(string variableName, float floatValue) + { + RemoveStaleEntries(variableName, keepFloat: true, keepString: false, keepBool: false); + Variables.floatVariables[variableName] = floatValue; + NotifyVariableChanged(variableName, floatValue); + } + + public override void SetValue(string variableName, bool boolValue) + { + RemoveStaleEntries(variableName, keepFloat: false, keepString: false, keepBool: true); + Variables.boolVariables[variableName] = boolValue; + NotifyVariableChanged(variableName, boolValue); + } + + public override bool Contains(string variableName) + { + StoryVariablesSave vars = Variables; + return vars.floatVariables.ContainsKey(variableName) + || vars.boolVariables.ContainsKey(variableName) + || vars.stringVariables.ContainsKey(variableName); + } + + public override void Clear() + { + StoryVariablesSave vars = Variables; + vars.floatVariables.Clear(); + vars.stringVariables.Clear(); + vars.boolVariables.Clear(); + } + + public override void SetAllVariables( + Dictionary floats, + Dictionary strings, + Dictionary bools, + bool clear = true) + { + StoryVariablesSave vars = Variables; + + if (clear) + { + vars.floatVariables.Clear(); + vars.stringVariables.Clear(); + vars.boolVariables.Clear(); + } + + if (floats != null) + foreach (KeyValuePair kv in floats) vars.floatVariables[kv.Key] = kv.Value; + if (strings != null) + foreach (KeyValuePair kv in strings) vars.stringVariables[kv.Key] = kv.Value; + if (bools != null) + foreach (KeyValuePair kv in bools) vars.boolVariables[kv.Key] = kv.Value; + } + + public override (Dictionary FloatVariables, + Dictionary StringVariables, + Dictionary BoolVariables) GetAllVariables() + { + StoryVariablesSave vars = Variables; + return ( + new Dictionary(vars.floatVariables), + new Dictionary(vars.stringVariables), + new Dictionary(vars.boolVariables) + ); + } + + // ── ä¾›æ•…äº‹æ ‘è§£é”æ¡ä»¶å¤ç”¨çš„便æ·è¯»å– ──────────────────────────────────────── + + /// 读å–å˜é‡çš„æ•´æ•°è¿‘似值(float å››èˆäº”å…¥ã€bool å– 0/1);ä¸å­˜åœ¨è¿”回 0。 + public int GetIntVariable(string variableName) + { + StoryVariablesSave vars = Variables; + if (vars.floatVariables.TryGetValue(variableName, out float f)) + return Mathf.RoundToInt(f); + if (vars.boolVariables.TryGetValue(variableName, out bool b)) + return b ? 1 : 0; + return 0; + } + + /// 该å˜é‡æ˜¯å¦å­˜åœ¨äºŽä»»ä¸€ç±»åž‹çš„存储中。 + public bool HasVariable(string variableName) => Contains(variableName); + + // ── 内部 ──────────────────────────────────────────────────────────────── + + // 当å˜é‡è¢«é‡æ–°èµ‹å€¼ä¸ºå…¶å®ƒç±»åž‹æ—¶ï¼Œä»Žæ—§ç±»åž‹å­—典移除,é¿å…åŒåå˜é‡æ®‹ç•™å¤šä»½ + private void RemoveStaleEntries(string name, bool keepFloat, bool keepString, bool keepBool) + { + StoryVariablesSave vars = Variables; + if (!keepFloat) vars.floatVariables.Remove(name); + if (!keepString) vars.stringVariables.Remove(name); + if (!keepBool) vars.boolVariables.Remove(name); + } + + // 将存储中的原始值转æ¢ä¸º Yarn 请求的类型(float / bool / string / int 之间互转) + private static bool TryConvert(object value, out T result) + { + if (value is T typed) + { + result = typed; + return true; + } + + try + { + if (typeof(T) == typeof(float) || typeof(T) == typeof(double) || typeof(T) == typeof(int)) + { + float f = value switch + { + float fv => fv, + bool bv => bv ? 1f : 0f, + string sv => float.TryParse(sv, NumberStyles.Any, CultureInfo.InvariantCulture, out float p) ? p : 0f, + _ => 0f + }; + result = (T)System.Convert.ChangeType(f, typeof(T), CultureInfo.InvariantCulture); + return true; + } + + if (typeof(T) == typeof(bool)) + { + bool b = value switch + { + bool bv => bv, + float fv => fv != 0f, + string sv => bool.TryParse(sv, out bool p) && p, + _ => false + }; + result = (T)(object)b; + return true; + } + + if (typeof(T) == typeof(string)) + { + string s = value switch + { + string sv => sv, + float fv => fv.ToString(CultureInfo.InvariantCulture), + bool bv => bv.ToString(), + _ => value != null ? value.ToString() : string.Empty + }; + result = (T)(object)s; + return true; + } + } + catch + { + // 转æ¢å¤±è´¥æ—¶è½åˆ°ä¸‹æ–¹å¤±è´¥åˆ†æ”¯ + } + + result = default; + return false; + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Dialogue/StoryVariableStorage.cs.meta b/Assets/Scripts/NewStorySystem/Dialogue/StoryVariableStorage.cs.meta new file mode 100644 index 00000000..dcd1c7fc --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Dialogue/StoryVariableStorage.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7dc7dc14d9f97c043bf6fb67c3a70877 \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Dialogue/VNDialoguePresenter.cs b/Assets/Scripts/NewStorySystem/Dialogue/VNDialoguePresenter.cs new file mode 100644 index 00000000..18c6bddd --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Dialogue/VNDialoguePresenter.cs @@ -0,0 +1,320 @@ +using System.Threading; +using TMPro; +using UnityEngine; +using UnityEngine.UI; +using Yarn.Unity; + +namespace Ichni.Story.Dialogue +{ + /// + /// 视觉å°è¯´ï¼ˆVN)风格的 Yarn 对è¯å‘ˆçŽ°å™¨ã€‚ + /// + /// èŒè´£ï¼š + /// + /// 对è¯å¼€å§‹/ç»“æŸæ—¶æ·¡å…¥/淡出 。 + /// :é€å­—打字效果 + 点击推进。 + /// :显示选项按钮 + 等待玩家选择。 + /// + /// + /// + /// 挂载ä½ç½®ï¼šä¸Ž åŒä¸€ GameObject(StoryDialogueRoot)。 + /// UI 引用字段需在 Inspector 中连接到 DialogPage 下的对应节点。 + /// + /// + public class VNDialoguePresenter : DialoguePresenterBase + { + // ── UI 引用 ───────────────────────────────────────────────────────────── + + [Header("颿¿æŽ§åˆ¶")] + [Tooltip("对è¯é¢æ¿æ•´ä½“çš„ CanvasGroup(DialogPage/DialogCanvas)。用于淡入/淡出åŠäº¤äº’开关。")] + [SerializeField] private CanvasGroup panelGroup; + + [Header("å°è¯åŒº")] + [Tooltip("å°è¯ä¸»ä½“文本 TMP(DialogContentFrame/Background/Text)。")] + [SerializeField] private TextMeshProUGUI dialogueText; + + [Tooltip("说è¯è€…å称的父容器 GameObject(DialogContentFrame/Speaker)。无说è¯è€…时整体éšè—。")] + [SerializeField] private GameObject speakerContainer; + + [Tooltip("说è¯è€…åç§° TMP(DialogContentFrame/Speaker/Text)。")] + [SerializeField] private TextMeshProUGUI speakerText; + + [Header("推进按钮")] + [Tooltip("点击此按钮推进对è¯ã€‚\n" + + " • 打字效果进行中 → 加速(RequestHurryUpLine)显示全文。\n" + + " • æ‰“å­—å·²å®Œæˆ â†’ 进入下一行(RequestNextLine)。\n" + + "推è:将场景中 DialogContentFrame/Background 或 BackButton çš„ Button 拖入此处。")] + [SerializeField] private Button advanceButton; + + [Header("选项区")] + [Tooltip("选项按钮容器根节点(ChoiceFrame)。有选项时显示,无选项时éšè—。")] + [SerializeField] private GameObject choiceFrame; + + [Tooltip("选项按钮数组(最多 4 个,对应 ChoiceFrame 下的四个 GameObject)。")] + [SerializeField] private Button[] choiceButtons = new Button[4]; + + [Tooltip("æ¯ä¸ªé€‰é¡¹æŒ‰é’®ä¸Šçš„æ–‡æœ¬ TMP,与 choiceButtons 按索引一一对应。")] + [SerializeField] private TextMeshProUGUI[] choiceTexts = new TextMeshProUGUI[4]; + + // ── 打字 / 淡入淡出设置 ────────────────────────────────────────────────── + + [Header("打字效果")] + [Tooltip("æ¯ç§’显示的字符数。设为 0 则瞬间显示全文。")] + [SerializeField] private float lettersPerSecond = 40f; + + [Header("淡入淡出")] + [SerializeField] private float fadeInDuration = 0.2f; + [SerializeField] private float fadeOutDuration = 0.15f; + + // ── ç§æœ‰çŠ¶æ€ â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ + + /// + /// 当å‰è¡Œçš„æ‰“字效果是å¦å·²å®Œæˆã€‚ + /// 用于区分"加速打字(第一次点击)"与"推进到下一行(第二次点击)"两个阶段。 + /// + private bool _typewriterComplete; + + /// + /// é€‰é¡¹é€‰æ‹©çš„å¼‚æ­¥ç»“æžœæ¥æºã€‚RunOptionsAsync 等待其 Task 完æˆï¼Œ + /// 点击按钮时 TrySetResult 触å‘完æˆã€‚ + /// + private YarnTaskCompletionSource _optionTcs; + + /// + /// 对应 StoryDialogueRoot 上的 DialogueRunner,用于调用 RequestNextLine / RequestHurryUpLine。 + /// 在 Awake 中通过 GetComponent å–得。 + /// + private DialogueRunner _runner; + + // ── 生命周期 ───────────────────────────────────────────────────────────── + + private void Awake() + { + // VNDialoguePresenter 需è¦ä¸Ž DialogueRunner åŒ GameObject,通过 GetComponent 获å–引用。 + _runner = GetComponent(); + if (_runner == null) + Debug.LogWarning("[VNDialoguePresenter] æœªåœ¨åŒ GameObject 上找到 DialogueRunner。" + + "请确认 VNDialoguePresenter 挂载在 StoryDialogueRoot 上。"); + + // å¯åŠ¨æ—¶éšè—颿¿ï¼ˆalpha=0, ä¸å“应交互) + SetPanelState(visible: false, instant: true); + + // å¯åŠ¨æ—¶éšè—选项容器 + if (choiceFrame != null) choiceFrame.SetActive(false); + + // 注册推进按钮回调 + if (advanceButton != null) + advanceButton.onClick.AddListener(OnAdvanceButtonClicked); + } + + private void OnDestroy() + { + if (advanceButton != null) + advanceButton.onClick.RemoveListener(OnAdvanceButtonClicked); + } + + // ── DialoguePresenterBase 实现 ─────────────────────────────────────────── + + /// + /// 对è¯å¼€å§‹ï¼šå°†é¢æ¿æ·¡å…¥åˆ°å¯è§çжæ€ã€‚ + /// 在 DialogueRunner 开始第一行å‰ç”± Yarn 调用。 + /// + public override async YarnTask OnDialogueStartedAsync() + { + // 先令 CanvasGroup å¯äº¤äº’但 alpha=0(为淡入动画åšå‡†å¤‡ï¼‰ + SetPanelState(visible: true, instant: false); + + if (panelGroup != null) + await Effects.FadeAlphaAsync(panelGroup, 0f, 1f, fadeInDuration, CancellationToken.None); + } + + /// + /// 呈现一行å°è¯ã€‚ + /// + /// éšè—选项区,显示说è¯è€…å称(若有)。 + /// é€å­—打字效果,期间å¯è¢« HurryUpToken 中断(显示全文)。 + /// 等待玩家点击推进(NextContentToken ç”± å–æ¶ˆï¼‰ã€‚ + /// + /// + public override async YarnTask RunLineAsync(LocalizedLine line, LineCancellationToken token) + { + // ç¡®ä¿é€‰é¡¹åŒºä¸å¯è§ï¼›å°è¯åŒºå§‹ç»ˆå¯è§ + if (choiceFrame != null) choiceFrame.SetActive(false); + + // ── 说è¯è€…åç§° ── + bool hasSpeaker = !string.IsNullOrEmpty(line.CharacterName); + if (speakerContainer != null) speakerContainer.SetActive(hasSpeaker); + if (speakerText != null) speakerText.text = hasSpeaker ? line.CharacterName : string.Empty; + + // ── 打字效果 ── + string fullText = line.TextWithoutCharacterName.Text; + _typewriterComplete = false; + + if (dialogueText != null) + { + // 先将全文赋给 TMP,å†é€šè¿‡ maxVisibleCharacters 逿­¥æ˜¾ç¤º + // (é¿å…æ¯å¸§ Substring äº§ç”Ÿå¤§é‡ GC 分é…) + dialogueText.text = fullText; + dialogueText.maxVisibleCharacters = 0; + } + + // é€å­—播放;若 HurryUpToken è¢«å–æ¶ˆï¼ˆçŽ©å®¶ç¬¬ä¸€æ¬¡ç‚¹å‡»ï¼‰åˆ™ç«‹å³è·³å‡ºå¾ªçޝ + await TypewriterAsync(fullText, token.HurryUpToken); + + // ç¡®ä¿å…¨æ–‡å¯è§ï¼ˆæ— è®ºæ­£å¸¸å®Œæˆè¿˜æ˜¯è¢«åŠ é€Ÿä¸­æ–­ï¼‰ + if (dialogueText != null) + dialogueText.maxVisibleCharacters = int.MaxValue; + + _typewriterComplete = true; + + // ── 等待玩家推进 ── + // token.NextContentToken 由外部 DialogueRunner.RequestNextLine() å–æ¶ˆï¼Œ + // SuppressCancellationThrow ç¡®ä¿ä¸æŠ›å‡º OperationCanceledException。 + await YarnTask.WaitUntilCanceled(token.NextContentToken).SuppressCancellationThrow(); + } + + /// + /// 呈现选项列表,等待玩家选择。 + /// + /// 显示选项容器,é…ç½®æ¯ä¸ªæŒ‰é’®çš„æ–‡æœ¬ä¸Žç‚¹å‡»ç›‘å¬ã€‚ + /// 通过 异步等待玩家选择。 + /// 返回玩家选择的 (对è¯è¢«å¤–éƒ¨å–æ¶ˆåˆ™è¿”回 null)。 + /// + /// + public override async YarnTask RunOptionsAsync( + DialogueOption[] options, + LineCancellationToken token) + { + // 清空å°è¯åŒºï¼Œå‡†å¤‡æ˜¾ç¤ºé€‰é¡¹ + if (speakerContainer != null) speakerContainer.SetActive(false); + if (dialogueText != null) dialogueText.text = string.Empty; + + // 显示选项容器 + if (choiceFrame != null) choiceFrame.SetActive(true); + + // åˆ›å»ºé€‰é¡¹é€‰æ‹©çš„å¼‚æ­¥ç»“æžœæ¥æº + _optionTcs = new YarnTaskCompletionSource(); + + // 当 NextContentToken è¢«å¤–éƒ¨å–æ¶ˆï¼ˆå¯¹è¯è¢«å¼ºåˆ¶ç»“æŸï¼‰ï¼Œæå‰å°† TCS 置为 null 结果, + // 使 await _optionTcs.Task èƒ½æ­£å¸¸é€€å‡ºè€Œä¸æ­»é”。 + WaitForExternalCancelAsync(token.NextContentToken).Forget(); + + // ── é…ç½®æ¯ä¸ªé€‰é¡¹æŒ‰é’® ── + for (int i = 0; i < choiceButtons.Length; i++) + { + // åªæ˜¾ç¤ºå½“å‰å®žé™…有效且å¯ç”¨çš„选项 + bool active = i < options.Length && options[i].IsAvailable; + if (choiceButtons[i] != null) + choiceButtons[i].gameObject.SetActive(active); + + if (!active) continue; + + // 设置按钮文本 + if (i < choiceTexts.Length && choiceTexts[i] != null) + choiceTexts[i].text = options[i].Line.TextWithoutCharacterName.Text; + + // æ•获循环å˜é‡ï¼Œé¿å…闭包æ•获错误索引 + DialogueOption captured = options[i]; + choiceButtons[i].onClick.RemoveAllListeners(); + choiceButtons[i].onClick.AddListener(() => _optionTcs.TrySetResult(captured)); + } + + // ── 等待玩家选择 ── + DialogueOption? selected = await _optionTcs.Task; + + // æ¸…ç†æŒ‰é’®ç›‘å¬ï¼Œéšè—选项容器 + foreach (Button btn in choiceButtons) + if (btn != null) btn.onClick.RemoveAllListeners(); + + if (choiceFrame != null) choiceFrame.SetActive(false); + + return selected; + } + + /// + /// 对è¯ç»“æŸï¼šæ·¡å‡ºå¹¶éšè—对è¯é¢æ¿ã€‚ + /// 在 DialogueRunner 处ç†å®Œæœ€åŽä¸€è¡Œ/选项åŽç”± Yarn 调用。 + /// + public override async YarnTask OnDialogueCompleteAsync() + { + if (panelGroup != null) + await Effects.FadeAlphaAsync(panelGroup, 1f, 0f, fadeOutDuration, CancellationToken.None); + + SetPanelState(visible: false, instant: true); + } + + // ── 内部方法 ───────────────────────────────────────────────────────────── + + /// + /// 推进按钮点击回调。 + /// + /// 打字中 → RequestHurryUpLineï¼ˆå–æ¶ˆ HurryUpTokenï¼Œç«‹å³æ˜¾ç¤ºå…¨æ–‡ï¼‰ã€‚ + /// 打字完毕 → RequestNextLineï¼ˆå–æ¶ˆ NextContentToken,进入下一行)。 + /// + /// + private void OnAdvanceButtonClicked() + { + if (_runner == null) return; + + if (!_typewriterComplete) + _runner.RequestHurryUpLine(); // 第一次点击:跳过打字效果 + else + _runner.RequestNextLine(); // 第二次点击:推进到下一行 + } + + /// + /// 控制对è¯é¢æ¿ 的显示状æ€ã€‚ + /// + /// 是å¦å¯è§ä¸”å¯äº¤äº’。 + /// 为 true æ—¶ç«‹å³è®¾ç½® alpha;为 false 时仅设置 interactable/blocksRaycasts, + /// alpha 留给外部的 FadeAlphaAsync 动画处ç†ã€‚ + private void SetPanelState(bool visible, bool instant) + { + if (panelGroup == null) return; + panelGroup.interactable = visible; + panelGroup.blocksRaycasts = visible; + if (instant) panelGroup.alpha = visible ? 1f : 0f; + } + + /// + /// é€å­—打字效果。 + /// 通过 maxVisibleCharacters 逿­¥æ˜¾ç¤ºæ–‡æœ¬ï¼ˆæ—  GC 分é…)。 + /// 被 å–æ¶ˆæ—¶ç«‹å³è¿”回,调用方负责将 maxVisibleCharacters 设为 MaxValue。 + /// + /// è¦é€å­—展示的完整文本(已预先赋值给 dialogueText.text)。 + /// å–æ¶ˆæ­¤ token å¯è·³è¿‡æ‰“字效果。 + private async YarnTask TypewriterAsync(string text, CancellationToken hurryUpToken) + { + if (dialogueText == null) return; + + // lettersPerSecond <= 0 时瞬间显示全文 + if (lettersPerSecond <= 0f) + { + dialogueText.maxVisibleCharacters = int.MaxValue; + return; + } + + int delayMs = Mathf.Max(1, Mathf.RoundToInt(1000f / lettersPerSecond)); + + // TMP çš„ maxVisibleCharacters 控制å¯è§å­—符数(ä¸å« rich-text 标签) + // é€å¸§é€’增直到全文显示或被 hurryUpToken 中断 + for (int i = 1; i <= text.Length; i++) + { + if (hurryUpToken.IsCancellationRequested) return; + dialogueText.maxVisibleCharacters = i; + await YarnTask.Delay(delayMs, hurryUpToken).SuppressCancellationThrow(); + if (hurryUpToken.IsCancellationRequested) return; + } + } + + /// + /// 监å¬å¤–éƒ¨å–æ¶ˆä¿¡å·ï¼ˆå¯¹è¯è¢«å¼ºåˆ¶ç»ˆæ­¢ï¼‰ï¼Œå°† 设为 null 结果, + /// ç¡®ä¿ çš„ await 能正常退出而ä¸ä¼šæ­»é”。 + /// + private async YarnTask WaitForExternalCancelAsync(CancellationToken externalToken) + { + await YarnTask.WaitUntilCanceled(externalToken).SuppressCancellationThrow(); + _optionTcs?.TrySetResult(null); + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Dialogue/VNDialoguePresenter.cs.meta b/Assets/Scripts/NewStorySystem/Dialogue/VNDialoguePresenter.cs.meta new file mode 100644 index 00000000..c8f977eb --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Dialogue/VNDialoguePresenter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b7b08c1a67d3d65478d9d9b4e275393f \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Save.meta b/Assets/Scripts/NewStorySystem/Save.meta new file mode 100644 index 00000000..bfaacc20 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Save.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5b59115c30ec8634887e285597b704c2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/NewStorySystem/Save/StorySave.cs b/Assets/Scripts/NewStorySystem/Save/StorySave.cs new file mode 100644 index 00000000..b40fd8e0 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Save/StorySave.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace Ichni.Story +{ + /// + /// å•章节的存档容器。 + /// 故事树的布局与连接完全由 陿€å®šä¹‰ï¼Œæ•…存档ä¸å†ä¿å­˜ + /// block ä½ç½®ä¸Žè¿žæŽ¥çº¿ï¼›è¿›å…¥ç« èŠ‚æ—¶æŒ‰"已完æˆé›†åˆ + è§£é”æ¡ä»¶"实时推导æ¯ä¸ª block çš„ + /// (Completed / Current / Locked)。 + /// 以å•个 ES3 key 存储,按章节分文件,一次读写å³å¯åŠ è½½/ä¿å­˜æ•´ç« è¿›åº¦ã€‚ + /// + [Serializable] + public class ChapterStorySave + { + public string chapterIndex; + + // å·²å®Œæˆ block çš„ id åˆ—è¡¨ï¼ˆé¡ºåºæ— å…³ï¼ŒåŽ»é‡ç”± StoryTreeController ä¿è¯ï¼‰ + public List completedBlockIds = new(); + + // 章节内的对è¯é€‰é¡¹è®°å½•(choiceKey -> 选中项索引)。选项ä¸è·¨ç« èŠ‚å¼•ç”¨ã€‚ + public Dictionary selectedChoices = new(); + } + + /// + /// 全局剧情å˜é‡å­˜æ¡£ã€‚float / string / bool 三类型与 Yarn Spinner çš„ + /// VariableStorageBehaviour.GetAllVariables/SetAllVariables 契约一致, + /// 供阶段 2 çš„ StoryVariableStorage 直接读写。 + /// + [Serializable] + public class StoryVariablesSave + { + public Dictionary floatVariables = new(); + public Dictionary stringVariables = new(); + public Dictionary boolVariables = new(); + } +} diff --git a/Assets/Scripts/NewStorySystem/Save/StorySave.cs.meta b/Assets/Scripts/NewStorySystem/Save/StorySave.cs.meta new file mode 100644 index 00000000..015ea997 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Save/StorySave.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ca9f4aa0dff5c774baf0eaab0d404115 \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Save/StorySaveModule.cs b/Assets/Scripts/NewStorySystem/Save/StorySaveModule.cs new file mode 100644 index 00000000..7577e43a --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Save/StorySaveModule.cs @@ -0,0 +1,149 @@ +using System.Collections.Generic; +using Ichni.Menu; +using Ichni.Story; +using UnityEngine; + +namespace Ichni +{ + /// + /// 剧情存档模å—。 + /// 故事树状æ€ä¸Žå¯¹è¯é€‰é¡¹ 按章节 ä¿å­˜ï¼ˆå•文件ã€å• ES3 key)。 + /// 剧情å˜é‡ 全局 ä¿å­˜ï¼Œå¯è·¨ç« èŠ‚å¼•ç”¨ï¼Œå¥‘åˆ Yarn å˜é‡æ¨¡åž‹ã€‚ + /// + public class StorySaveModule + { + private const string ChapterStoryKey = "ChapterStory"; + private const string VariablesKey = "StoryVariables"; + + // 已加载章节的内存缓存(chapterIndex -> 章节存档) + private readonly Dictionary _chapterSaves = + new Dictionary(); + + // 全局剧情å˜é‡ï¼ˆfloat / string / bool ä¸‰ç±»åž‹ï¼ŒåŒ¹é… Yarn 契约) + public StoryVariablesSave variables = new StoryVariablesSave(); + + private static string GetChapterSavePath(string chapterIndex) => + Application.persistentDataPath + "/StorySaves/" + chapterIndex + ".json"; + + private static string VariablesPath => + Application.persistentDataPath + "/StorySaves/StoryVariables.json"; + + // ── 故事树 + 选项(按章节)──────────────────────────────────────────────── + + /// + /// 判断æŸç« èŠ‚æ˜¯å¦å°šæ— å­˜æ¡£ï¼ˆé¦–次进入)。 + /// + public bool IsNewChapter(string chapterIndex) => + !ES3.FileExists(GetChapterSavePath(chapterIndex)); + + /// + /// 加载指定章节的存档容器;无存档时返回一个新的空容器。加载结果会写入内存缓存。 + /// + public ChapterStorySave LoadChapter(string chapterIndex) + { + string path = GetChapterSavePath(chapterIndex); + + ChapterStorySave save; + if (ES3.FileExists(path) && ES3.KeyExists(ChapterStoryKey, path)) + { + save = ES3.Load(ChapterStoryKey, path); + } + else + { + save = new ChapterStorySave { chapterIndex = chapterIndex }; + } + + _chapterSaves[chapterIndex] = save; + return save; + } + + /// + /// ä¿å­˜æ•´ç« å­˜æ¡£ï¼ˆblocks + connectors + choicesï¼‰ï¼Œå•æ¬¡ ES3 写入。 + /// + public void SaveChapter(ChapterStorySave save) + { + _chapterSaves[save.chapterIndex] = save; + ES3.Save(ChapterStoryKey, save, GetChapterSavePath(save.chapterIndex)); + } + + /// + /// 获å–内存中的章节存档;若未加载则从ç£ç›˜åŠ è½½ã€‚ + /// + public ChapterStorySave GetChapter(string chapterIndex) + { + return _chapterSaves.TryGetValue(chapterIndex, out ChapterStorySave save) + ? save + : LoadChapter(chapterIndex); + } + + // ── 对è¯é€‰é¡¹ï¼ˆæŒ‰ç« èŠ‚ï¼‰â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ + + /// + /// 记录æŸç« èŠ‚å†…ä¸€æ¬¡å¯¹è¯é€‰é¡¹çš„选择结果。 + /// + public void SetChoice(string chapterIndex, string choiceKey, int optionIndex) + { + GetChapter(chapterIndex).selectedChoices[choiceKey] = optionIndex; + } + + /// + /// è¯»å–æŸç« èŠ‚å†…å·²è®°å½•çš„å¯¹è¯é€‰é¡¹é€‰æ‹©ç»“果。 + /// + public bool TryGetChoice(string chapterIndex, string choiceKey, out int optionIndex) + { + return GetChapter(chapterIndex).selectedChoices.TryGetValue(choiceKey, out optionIndex); + } + + // ── 剧情å˜é‡ï¼ˆå…¨å±€ï¼‰â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ + + /// + /// 从ç£ç›˜åŠ è½½å…¨å±€å‰§æƒ…å˜é‡ï¼ˆå¯åŠ¨æ—¶è°ƒç”¨ï¼‰ã€‚ + /// + public void LoadVariables() + { + if (ES3.FileExists(VariablesPath) && ES3.KeyExists(VariablesKey, VariablesPath)) + { + variables = ES3.Load(VariablesKey, VariablesPath); + } + else + { + variables = new StoryVariablesSave(); + } + } + + /// + /// 将全局剧情å˜é‡å†™å…¥ç£ç›˜ã€‚ + /// + public void SaveVariables() + { + ES3.Save(VariablesKey, variables, VariablesPath); + } + + // ── 清档 ──────────────────────────────────────────────────────────────── + + /// + /// 清除所有章节的故事树存档与全局剧情å˜é‡ã€‚ + /// + public void ClearAllStoryline() + { + if (ChapterSelectionManager.instance != null) + { + foreach (ChapterSelectionUnit chapter in ChapterSelectionManager.instance.chapters) + { + string path = GetChapterSavePath(chapter.chapterIndex); + if (ES3.FileExists(path)) + ES3.DeleteFile(path); + } + } + + _chapterSaves.Clear(); + + if (ES3.FileExists(VariablesPath)) + ES3.DeleteFile(VariablesPath); + + variables = new StoryVariablesSave(); + + Debug.Log("Cleared all story saves (chapters + global variables)."); + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Save/StorySaveModule.cs.meta b/Assets/Scripts/NewStorySystem/Save/StorySaveModule.cs.meta new file mode 100644 index 00000000..326af945 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Save/StorySaveModule.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3e133cbb328fd394f820d7bc4136ffd7 \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Tree.meta b/Assets/Scripts/NewStorySystem/Tree.meta new file mode 100644 index 00000000..c8775496 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f3a0060903a9da947ae9283956518086 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/NewStorySystem/Tree/BlockConnectorView.cs b/Assets/Scripts/NewStorySystem/Tree/BlockConnectorView.cs new file mode 100644 index 00000000..19505737 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/BlockConnectorView.cs @@ -0,0 +1,81 @@ +using UnityEngine; +using UnityEngine.UI.Extensions; + +namespace Ichni.Story.UI +{ + /// + /// 故事树中两个 block 之间的连接线视图,使用 UILineRenderer 绘制 S 形曲线。 + /// + public class BlockConnectorView : MonoBehaviour + { + // 判定两端å£å标是å¦å¡Œç¼©ä¸ºåŒä¸€ç‚¹çš„阈值(åƒç´ ï¼‰ã€‚ + private const float DegenerateDistanceThreshold = 1f; + + public UILineRenderer curve; + public StoryBlockView startBlock; + public StoryBlockView endBlock; + + /// + /// 设置连接线的起止 block å¹¶é‡ç®—曲线点。传入 null æ—¶ä¿ç•™å·²æœ‰å¼•用。 + /// 注æ„:必须在 block çš„ RectTransform 完æˆå¸ƒå±€ï¼ˆCanvas 已刷新)åŽè°ƒç”¨ï¼Œ + /// å¦åˆ™ç«¯å£ä¸–ç•Œåæ ‡å°šæœªç”Ÿæ•ˆï¼Œä¼šå¯¼è‡´æ‰€æœ‰é¡¶ç‚¹å¡Œç¼©åˆ°åŒä¸€ç‚¹ã€‚ + /// + public void SetCurve(StoryBlockView start = null, StoryBlockView end = null) + { + if (start != null) startBlock = start; + if (end != null) endBlock = end; + + if (startBlock == null || endBlock == null) + { + Debug.LogWarning("[BlockConnectorView] 起点或终点 block 未设置,无法绘制曲线。"); + return; + } + + RectTransform selfRect = (RectTransform)transform; + + // ScreenSpaceCamera / WorldSpace 画布下的世界→å±å¹•转æ¢éœ€è¦å¯¹åº”相机;Overlay ä¼  null。 + Camera uiCamera = ResolveUICamera(); + + Vector2 startPosition = GetLocalPoint(startBlock.outPort, selfRect, uiCamera); + Vector2 endPosition = GetLocalPoint(endBlock.inPort, selfRect, uiCamera); + + if (Vector2.Distance(startPosition, endPosition) < DegenerateDistanceThreshold) + { + Debug.LogWarning( + $"[BlockConnectorView] '{startBlock.blockId}' -> '{endBlock.blockId}' 端å£å标塌缩为åŒä¸€ç‚¹ " + + $"(start={startPosition}, end={endPosition}),通常是在 block 布局完æˆå‰è°ƒç”¨äº† SetCurve。"); + } + + // ä¸¤ä¸ªæŽ§åˆ¶ç‚¹æž„æˆ S å½¢ï¼šå…ˆæ°´å¹³ç¦»å¼€èµ·ç‚¹ï¼Œå†æ°´å¹³è¿›å…¥ç»ˆç‚¹ã€‚ + float midX = (startPosition.x + endPosition.x) / 2f; + Vector2 mid1 = new Vector2(midX, startPosition.y); + Vector2 mid2 = new Vector2(midX, endPosition.y); + + curve.Points = new[] { startPosition, mid1, mid2, endPosition }; + curve.SetVerticesDirty(); + } + + /// + /// 使用 å°† 的世界ä½ç½® + /// 转æ¢åˆ° çš„å±€éƒ¨åæ ‡ï¼ˆç›¸å¯¹ pivotï¼‰ï¼Œå³ UILineRenderer é¡¶ç‚¹æ‰€ç”¨çš„åæ ‡ç³»ã€‚ + /// + private static Vector2 GetLocalPoint(RectTransform port, RectTransform space, Camera uiCamera) + { + Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(uiCamera, port.position); + RectTransformUtility.ScreenPointToLocalPointInRectangle(space, screenPoint, uiCamera, out Vector2 localPoint); + return localPoint; + } + + /// + /// è§£æžç”¨äºŽå标转æ¢çš„相机:Overlay 画布返回 null,其余(ScreenSpaceCamera / WorldSpace)返回画布相机。 + /// + private Camera ResolveUICamera() + { + Canvas canvas = curve != null ? curve.canvas : GetComponentInParent(); + if (canvas == null || canvas.renderMode == RenderMode.ScreenSpaceOverlay) + return null; + + return canvas.worldCamera; + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Tree/BlockConnectorView.cs.meta b/Assets/Scripts/NewStorySystem/Tree/BlockConnectorView.cs.meta new file mode 100644 index 00000000..0e066da6 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/BlockConnectorView.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9d179a524aa28d74f9ae667fd78aed20 \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Tree/SongBlockView.cs b/Assets/Scripts/NewStorySystem/Tree/SongBlockView.cs new file mode 100644 index 00000000..4abe5b07 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/SongBlockView.cs @@ -0,0 +1,28 @@ +using TMPro; +using UnityEngine; + +namespace Ichni.Story.UI +{ + /// + /// 音ä¹å—视图(å ä½ï¼‰ã€‚点击åŽå°†åˆ‡æ¢åˆ°æ›²ç›®é€‰æ‹©å¹¶è‡ªåŠ¨é€‰ä¸­å¯¹åº”æ­Œæ›²ï¼ŒåŽç»­é˜¶æ®µæŽ¥å…¥ã€‚ + /// + public class SongBlockView : StoryBlockView + { + [Header("Song Block")] + public TMP_Text songNameText; + + public override void Initialize(StoryBlockDefinition def, Vector2 position, StoryBlockState blockState) + { + base.Initialize(def, position, blockState); + + if (songNameText != null) + songNameText.text = def.songName; + } + + protected override void OnClicked() + { + // å ä½ï¼šåŽç»­æŽ¥å…¥æ›²ç›®é€‰æ‹©æµç¨‹ + Debug.Log($"[SongBlockView] 点击音ä¹å— '{blockId}' (song='{definition.songName}')。å ä½å®žçŽ°ï¼Œæ›²ç›®é€‰æ‹©å°†åœ¨åŽç»­é˜¶æ®µæŽ¥å…¥ã€‚"); + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Tree/SongBlockView.cs.meta b/Assets/Scripts/NewStorySystem/Tree/SongBlockView.cs.meta new file mode 100644 index 00000000..a5c8f61f --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/SongBlockView.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ad764389c6c056c4d84cdf0155d7e7aa \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Tree/StoryBlockView.cs b/Assets/Scripts/NewStorySystem/Tree/StoryBlockView.cs new file mode 100644 index 00000000..c870438c --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/StoryBlockView.cs @@ -0,0 +1,80 @@ +using Sirenix.OdinInspector; +using UnityEngine; +using UnityEngine.UI; + +namespace Ichni.Story.UI +{ + /// + /// 故事树中å•个 block 的视图基类。负责通用的定ä½ã€çжæ€è§†è§‰ä¸Žç‚¹å‡»åˆ†å‘。 + /// 具体类型(文本 / éŸ³ä¹ / 教程)由å­ç±»å®žçް 。 + /// + public abstract class StoryBlockView : SerializedMonoBehaviour + { + [Header("Block State")] + public string blockId; + public StoryBlockState state; + + [Header("Common References")] + public RectTransform blockRect; + public RectTransform inPort; + public RectTransform outPort; + public Button button; + + // å…³è”的定义,供å­ç±»è¯»å–类型专属字段 + protected StoryBlockDefinition definition; + + /// + /// åˆå§‹åŒ– block è§†å›¾ã€‚é€»è¾‘åæ ‡å·²ç”±æŽ§åˆ¶å™¨æŒ‰ç½‘æ ¼æ¢ç®—为 + /// (以 BlockContainer 左侧中线为原点ã€x å‘å³ä¸ºæ­£ã€y å‘下为正的 anchoredPosition)。 + /// + /// block 定义(æä¾›å°ºå¯¸ä¸Žç±»åž‹ä¸“属数æ®ï¼‰ã€‚ + /// å·²æ¢ç®—好的 anchoredPosition。 + /// 推导得到的 block 状æ€ã€‚ + public virtual void Initialize(StoryBlockDefinition def, Vector2 position, StoryBlockState blockState) + { + definition = def; + blockId = def.blockId; + state = blockState; + + // ç»Ÿä¸€åæ ‡çº¦å®šï¼šä»¥ BlockContainer 的左侧中线为原点,å‘å³ä¸º +xã€å‘下为 +y。 + blockRect.anchorMin = new Vector2(0f, 0.5f); + blockRect.anchorMax = new Vector2(0f, 0.5f); + blockRect.pivot = new Vector2(0f, 0.5f); + blockRect.sizeDelta = new Vector2(400, 200); + blockRect.anchoredPosition = position; + + if (button != null) + button.onClick.AddListener(HandleClick); + + ApplyState(state); + } + + /// + /// 应用状æ€å¹¶æ›´æ–°è§†è§‰ã€‚基类默认按状æ€åˆ‡æ¢æŒ‰é’®å¯äº¤äº’性; + /// å­ç±»å¯ override 以更新é”定图标ã€é¢œè‰²ç­‰å…·ä½“表现。 + /// + public virtual void ApplyState(StoryBlockState newState) + { + state = newState; + + if (button != null) + button.interactable = state != StoryBlockState.Locked; + } + + private void HandleClick() + { + if (state == StoryBlockState.Locked) + return; + + if (StoryManager.instance != null && StoryManager.instance.treeController != null) + StoryManager.instance.treeController.currentBlock = this; + + OnClicked(); + } + + /// + /// 点击(éžé”定状æ€ï¼‰æ—¶çš„具体行为,由å­ç±»å®žçŽ°ã€‚ + /// + protected abstract void OnClicked(); + } +} diff --git a/Assets/Scripts/NewStorySystem/Tree/StoryBlockView.cs.meta b/Assets/Scripts/NewStorySystem/Tree/StoryBlockView.cs.meta new file mode 100644 index 00000000..32ec7cae --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/StoryBlockView.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 74cc3bad2b6adeb4b93cd509340a23be \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Tree/StoryManager.cs b/Assets/Scripts/NewStorySystem/Tree/StoryManager.cs new file mode 100644 index 00000000..b86b56e7 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/StoryManager.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using Ichni.Story.UI; +using Sirenix.OdinInspector; +using UnityEngine; + +namespace Ichni.Story +{ + /// + /// 剧情系统å调器(å•ä¾‹ï¼‰ã€‚æŒæœ‰æ•…事树控制器ã€å‰§æƒ…页é¢ä¸Žç« èŠ‚æ•°æ®å¼•用, + /// 是外部系统(章节选择ã€å­˜æ¡£ç­‰ï¼‰ä¸Žå‰§æƒ…系统交互的入å£ã€‚ + /// + public class StoryManager : SerializedMonoBehaviour + { + public static StoryManager instance; + + [Header("References")] + public StoryTreeController treeController; + public StoryUIPage storyUIPage; + public CharacterRegistry characterRegistry; + + [Header("Chapter Data")] + [Tooltip("章节索引 (chapterIndex) -> 该章节的 StoryData 资产")] + public Dictionary storyDatas = new Dictionary(); + + private void Awake() + { + instance = this; + } + + /// + /// 打开指定章节,构建其故事树。 + /// + public void OpenChapter(string chapterIndex) + { + treeController.BuildChapter(chapterIndex); + } + + /// + /// æ ¹æ®ç« èŠ‚ç´¢å¼•èŽ·å–对应的 StoryData,未找到返回 null。 + /// + public StoryData GetStoryData(string chapterIndex) + { + return storyDatas.TryGetValue(chapterIndex, out StoryData data) ? data : null; + } + + /// + /// 清除全部剧情存档(所有章节故事树 + 全局å˜é‡ + å‰§æƒ…è§£é” key)。 + /// + [Button] + public void ClearAllStorySave() + { + GameSaveManager.instance.StorySaveModule.ClearAllStoryline(); + GameSaveManager.instance.SongSaveModule.ClearStoryKeys(); + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Tree/StoryManager.cs.meta b/Assets/Scripts/NewStorySystem/Tree/StoryManager.cs.meta new file mode 100644 index 00000000..d4f288af --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/StoryManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a26cc5f52c5322d46b18240eb73c0a62 \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Tree/StoryTreeController.cs b/Assets/Scripts/NewStorySystem/Tree/StoryTreeController.cs new file mode 100644 index 00000000..70433f29 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/StoryTreeController.cs @@ -0,0 +1,390 @@ +using System.Collections.Generic; +using System.Linq; +using Ichni.Story.UI; +using Sirenix.OdinInspector; +using UniRx; +using UnityEngine; + +namespace Ichni.Story +{ + /// + /// 故事树控制器。从 ä¸€æ¬¡æ€§å…¨é‡æž„建所有 block 视图与连接线, + /// 布局由æ¯ä¸ª block çš„ (gridColumn, gridRow) 陿€å†³å®šï¼›æ¯ä¸ª block çš„ + /// ç”±"已完æˆé›†åˆ + è§£é”æ¡ä»¶"实时推导。 + /// 存档åªä¿å­˜å·²å®Œæˆ block çš„ id 集åˆã€‚ + /// + public class StoryTreeController : MonoBehaviour + { + [Header("UI References")] + [Tooltip("blocks 与 connectors 共用的å•ä¸€å®¹å™¨ï¼ˆå³ ScrollRect çš„ content,localScale 必须为 1)")] + public RectTransform content; + public GameObject textBlockPrefab; + public GameObject songBlockPrefab; + public GameObject tutorialBlockPrefab; + public GameObject connectorPrefab; + + [Header("Layout Settings")] + [Tooltip("网格å•元格尺寸(决定相邻列/行的步è·ï¼Œå…¨å±€ç»Ÿä¸€ï¼‰")] + public Vector2 cellSize = new Vector2(400f, 200f); + + [Tooltip("相邻å•元格之间的间隔(水平 xã€åž‚ç›´ y)")] + public Vector2 cellSpacing = new Vector2(120f, 150f); + + public float marginLeft = 50f; + public float marginRight = 50f; + public float marginTop = 50f; + public float marginBottom = 50f; + public float minContentWidth = 2560f; + public float minContentHeight = 1440f; + + [Header("Runtime")] + public StoryBlockView currentBlock; + public List blocks = new List(); + public List connectors = new List(); + + private string _chapterIndex; + private StoryData _storyData; + + /// 当å‰å·²æž„建章节的 StoryDataï¼ˆä¾›å¯¹è¯æŽ§åˆ¶å™¨åˆ‡æ¢ YarnProject 等使用)。 + public StoryData ActiveStoryData => _storyData; + + /// 当å‰å·²æž„建章节的索引。 + public string ActiveChapterIndex => _chapterIndex; + + // 当å‰ç« èŠ‚å·²å®Œæˆçš„ block id 集åˆï¼ˆçŠ¶æ€æŽ¨å¯¼çš„å”¯ä¸€è¿›åº¦æ¥æºï¼‰ + private readonly HashSet _completed = new HashSet(); + + private void Start() + { + // å­˜æ¡£ç³»ç»Ÿæœªå°±ç»ªæ—¶è·³è¿‡ï¼›æ­£å¼æµç¨‹ç”± StoryManager.OpenChapter åœ¨è¿›å…¥å‰§æƒ…é¡µæ—¶è§¦å‘æž„建。 + if (GameSaveManager.instance == null || GameSaveManager.instance.StorySaveModule == null) + return; + + if (ChapterSelectionManager.instance != null && + ChapterSelectionManager.instance.currentChapter != null) + { + BuildChapter(ChapterSelectionManager.instance.currentChapter.chapterIndex); + } + } + + // ── Build ───────────────────────────────────────────────────────────────── + + /// + /// 构建指定章节的故事树:全é‡ç”Ÿæˆæ‰€æœ‰ block 与连接线,状æ€ç”±å­˜æ¡£ä¸­çš„已完æˆé›†åˆæŽ¨å¯¼ã€‚ + /// + public void BuildChapter(string chapterIndex) + { + _chapterIndex = chapterIndex; + _storyData = StoryManager.instance != null ? StoryManager.instance.GetStoryData(chapterIndex) : null; + + if (_storyData == null) + { + Debug.LogError($"[StoryTreeController] 找ä¸åˆ°ç« èŠ‚ '{chapterIndex}' çš„ StoryData,请在 StoryManager.storyDatas 中登记。"); + return; + } + + if (GameSaveManager.instance == null || GameSaveManager.instance.StorySaveModule == null) + { + Debug.LogWarning("[StoryTreeController] 存档系统尚未就绪(GameSaveManager.StorySaveModule 为空),跳过构建。"); + return; + } + + ClearTree(); + + // 载入已完æˆé›†åˆï¼ˆæ–°ç« èŠ‚ä¸ºç©ºï¼‰ + ChapterStorySave save = GameSaveManager.instance.StorySaveModule.GetChapter(chapterIndex); + _completed.Clear(); + foreach (string id in save.completedBlockIds) + _completed.Add(id); + + // å…¨é‡ç”Ÿæˆæ‰€æœ‰ block(ä½ç½®æŒ‰ç½‘æ ¼æ¢ç®—ï¼ŒçŠ¶æ€æŒ‰å·²å®Œæˆé›†åˆ + è§£é”æ¡ä»¶æŽ¨å¯¼ï¼‰ + foreach (StoryBlockDefinition def in _storyData.blocks) + GenerateBlock(def); + + // å…¨é‡ç”Ÿæˆæ‰€æœ‰è¿žæŽ¥çº¿ + foreach (StoryBlockDefinition def in _storyData.blocks) + foreach (string nextId in def.nextBlockIds) + GenerateConnector(def.blockId, nextId); + + SetUpBackground(); + + // è¿žçº¿åœ¨ä¸‹ä¸€å¸§åˆ·æ–°ï¼ˆè§ RefreshConnectors):等待 Unity å®Œæˆ Canvas 布局, + // 使 block 端å£çš„ä¸–ç•Œåæ ‡ç¨³å®šï¼Œé¿å…端å£å标尚未生效时塌缩到åŒä¸€ç‚¹ã€‚ + RefreshConnectors(); + } + + // ── Generation ────────────────────────────────────────────────────────── + + /// + /// 按定义生æˆä¸€ä¸ª block 视图:ä½ç½®ç”±ç½‘æ ¼æ¢ç®—,状æ€ç”±æŽ¨å¯¼å¾—出。 + /// + public StoryBlockView GenerateBlock(StoryBlockDefinition def) + { + GameObject prefab = def.blockType switch + { + StoryBlockType.Text => textBlockPrefab, + StoryBlockType.Song => songBlockPrefab, + StoryBlockType.Tutorial => tutorialBlockPrefab, + _ => null + }; + + if (prefab == null) + { + Debug.LogError($"[StoryTreeController] block 类型 {def.blockType} 未é…置预制体。"); + return null; + } + + StoryBlockView view = Instantiate(prefab, content).GetComponent(); + if (view == null) + { + Debug.LogError($"[StoryTreeController] 预制体 '{prefab.name}' 缺少 StoryBlockView 组件。"); + return null; + } + + view.Initialize(def, GetGridPosition(def), DeriveState(def)); + blocks.Add(view); + return view; + } + + /// + /// 在两个已存在的 block 之间生æˆè¿žæŽ¥çº¿ã€‚ + /// + public void GenerateConnector(string fromBlockId, string toBlockId) + { + StoryBlockView from = GetBlockView(fromBlockId); + StoryBlockView to = GetBlockView(toBlockId); + + if (from == null || to == null) + { + Debug.LogWarning($"[StoryTreeController] 无法连线:'{fromBlockId}' -> '{toBlockId}'(block ä¸å­˜åœ¨ï¼‰ã€‚"); + return; + } + + // 连接线与 block åŒå¤„一个容器(content);置为第一个å­ç‰©ä½“,渲染在所有 block ä¹‹åŽæ–¹ã€‚ + GameObject connectorObject = Instantiate(connectorPrefab, content); + connectorObject.transform.SetAsFirstSibling(); + + BlockConnectorView connector = connectorObject.GetComponent(); + if (connector == null) + { + Debug.LogError($"[StoryTreeController] 连接线预制体 '{connectorPrefab.name}' 缺少 BlockConnectorView 组件。"); + Destroy(connectorObject); + return; + } + + // 仅记录起止 block 引用;曲线在布局刷新åŽç”± RefreshConnectors 统一计算。 + connector.startBlock = from; + connector.endBlock = to; + connectors.Add(connector); + } + + // ── State / Unlocking ───────────────────────────────────────────────────── + + /// + /// 便®"已完æˆé›†åˆ + è§£é”æ¡ä»¶"推导å•个 block 的状æ€ã€‚ + /// å·²å®Œæˆ â†’ Completedï¼›å¦åˆ™æ»¡è¶³è§£é”æ¡ä»¶ → Currentï¼›å¦åˆ™ → Locked。 + /// è§£é”æ¡ä»¶ä¸ºç©ºè§†ä¸ºæ— æ¡ä»¶æ»¡è¶³ï¼ˆç« èŠ‚èµ·å§‹å³ä¸º Current)。 + /// + private StoryBlockState DeriveState(StoryBlockDefinition def) + { + if (_completed.Contains(def.blockId)) + return StoryBlockState.Completed; + + if (def.unlockCondition.IsSatisfied(GetVariable, IsBlockCompleted)) + return StoryBlockState.Current; + + return StoryBlockState.Locked; + } + + /// + /// 釿–°æŽ¨å¯¼å¹¶åº”用所有 block 的状æ€ï¼ˆåœ¨å®ŒæˆæŸ block 或å˜é‡å˜åŒ–åŽè°ƒç”¨ï¼‰ã€‚ + /// + public void RefreshAllStates() + { + foreach (StoryBlockView view in blocks) + { + StoryBlockDefinition def = _storyData.GetBlock(view.blockId); + if (def == null) continue; + + view.ApplyState(DeriveState(def)); + } + } + + /// + /// æ ‡è®°æŸ block 完æˆï¼ŒæŽ¨å¯¼é‡ç®—å¹¶ä¿å­˜ã€‚对è¯ç³»ç»Ÿï¼ˆé˜¶æ®µ 2)在对è¯ç»“æŸæ—¶è°ƒç”¨ã€‚ + /// + public void OnBlockCompleted(string blockId) + { + if (!_completed.Add(blockId)) + return; // 已完æˆï¼Œæ— éœ€å¤„ç† + + RefreshAllStates(); + SaveChapter(); + } + + private bool IsBlockCompleted(string blockId) => _completed.Contains(blockId); + + // 阶段 2 将改由 StoryVariableStorage æä¾›ï¼›æ­¤å¤„从全局å˜é‡å­˜æ¡£è¯»å–作å ä½ + private int GetVariable(string variableName) + { + StoryVariablesSave vars = GameSaveManager.instance.StorySaveModule.variables; + + if (vars.floatVariables.TryGetValue(variableName, out float f)) + return Mathf.RoundToInt(f); + + if (vars.boolVariables.TryGetValue(variableName, out bool b)) + return b ? 1 : 0; + + return 0; + } + + // ── Persistence ───────────────────────────────────────────────────────── + + /// + /// ä¿å­˜å½“å‰ç« èŠ‚è¿›åº¦ï¼ˆå·²å®Œæˆ block 集åˆï¼‰ã€‚选项由对è¯ç³»ç»Ÿç»´æŠ¤ã€ä¸åœ¨æ­¤è¦†ç›–。 + /// + [Button] + public void SaveChapter() + { + if (string.IsNullOrEmpty(_chapterIndex)) return; + + ChapterStorySave save = GameSaveManager.instance.StorySaveModule.GetChapter(_chapterIndex); + save.chapterIndex = _chapterIndex; + save.completedBlockIds = _completed.ToList(); + + GameSaveManager.instance.StorySaveModule.SaveChapter(save); + } + + // ── Layout ──────────────────────────────────────────────────────────────── + + /// + /// å°† block çš„ç½‘æ ¼åæ ‡ (gridColumn, gridRow) æ¢ç®—为 BlockContainer 中的 anchoredPosition。 + /// 约定左侧中线为原点:列å‘å³ä¸ºæ­£ï¼Œè¡Œå‘下为正ã€å‘上为负(anchoredPosition.y å–负)。 + /// gridColumn / gridRow 支æŒè´Ÿæ•°ä¸Žå°æ•°ï¼Œå¯ç”¨äºŽå¾®è°ƒ block ä½ç½®ã€‚ + /// + private Vector2 GetGridPosition(StoryBlockDefinition def) + { + float x = marginLeft + def.gridColumn * (cellSize.x + cellSpacing.x); + float y = -def.gridRow * (cellSize.y + cellSpacing.y); + return new Vector2(x, y); + } + + /// + /// æ ¹æ® block 分布计算并设置 content(BlockContainer) 尺寸。 + /// block 采用左中 pivot:anchoredPosition.x 为左边缘,y 相对垂直中线(上正下负)。 + /// + public void SetUpBackground() + { + if (blocks.Count == 0) + { + content.sizeDelta = new Vector2(minContentWidth, minContentHeight); + return; + } + + float maxRight = 0f; + float maxVertical = 0f; // è·åž‚直中线的最大延伸(上下å–较大者,用于对称扩展 content 高度) + + foreach (StoryBlockView block in blocks) + { + RectTransform rect = block.blockRect; + Vector2 pos = rect.anchoredPosition; + Vector2 size = rect.sizeDelta; + + float rightEdge = pos.x + size.x; + float topExtent = pos.y + size.y * 0.5f; + float bottomExtent = -(pos.y - size.y * 0.5f); + + if (rightEdge > maxRight) maxRight = rightEdge; + if (topExtent > maxVertical) maxVertical = topExtent; + if (bottomExtent > maxVertical) maxVertical = bottomExtent; + } + + float width = Mathf.Max(maxRight + marginRight, minContentWidth); + float height = Mathf.Max(maxVertical * 2f + marginTop + marginBottom, minContentHeight); + + content.sizeDelta = new Vector2(width, height); + } + + /// + /// é‡ç®—所有连接线曲线(在 block ä½ç½®ç¡®å®šåŽè°ƒç”¨ï¼‰ã€‚ + /// + public void RefreshConnectors() + { + Observable.NextFrame().Subscribe(_ => + { + foreach (BlockConnectorView connector in connectors) + { + connector.SetCurve(); + } + }).AddTo(this); + } + + // ── Helpers / Debug ───────────────────────────────────────────────────── + + public StoryBlockView GetBlockView(string blockId) => + blocks.FirstOrDefault(b => b.blockId == blockId); + + private void ClearTree() + { + foreach (StoryBlockView block in blocks) + if (block != null) Destroy(block.gameObject); + blocks.Clear(); + + foreach (BlockConnectorView connector in connectors) + if (connector != null) Destroy(connector.gameObject); + connectors.Clear(); + + if (content != null) + content.sizeDelta = Vector2.zero; + } + + /// + /// 编辑器测试用:以当å‰ç« èЂ釿–°æž„建故事树。 + /// + [Button] + public void DebugBuildCurrentChapter() + { + if (ChapterSelectionManager.instance != null && + ChapterSelectionManager.instance.currentChapter != null) + { + BuildChapter(ChapterSelectionManager.instance.currentChapter.chapterIndex); + } + else + { + Debug.LogWarning("[StoryTreeController] 当剿²¡æœ‰é€‰ä¸­çš„章节。"); + } + } + + /// + /// 编辑器测试用:模拟完æˆå½“å‰é€‰ä¸­çš„ block(验è¯è§£é”é‡ç®—与存档),阶段 2 å‰ç”¨äºŽæ›¿ä»£å¯¹è¯ã€‚ + /// + [Button] + public void DebugCompleteCurrentBlock() + { + if (currentBlock == null) + { + Debug.LogWarning("[StoryTreeController] currentBlock 为空,请先点击一个 block。"); + return; + } + + OnBlockCompleted(currentBlock.blockId); + } + + /// + /// 编辑器测试用:清空当å‰ç« èŠ‚è¿›åº¦å¹¶é‡å»ºï¼ˆå…¨éƒ¨å›žåˆ°æœªå®Œæˆçš„åˆå§‹æŽ¨å¯¼çжæ€ï¼‰ã€‚ + /// + [Button] + public void DebugResetChapterProgress() + { + if (string.IsNullOrEmpty(_chapterIndex)) + { + Debug.LogWarning("[StoryTreeController] 尚未构建任何章节。"); + return; + } + + _completed.Clear(); + SaveChapter(); + BuildChapter(_chapterIndex); + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Tree/StoryTreeController.cs.meta b/Assets/Scripts/NewStorySystem/Tree/StoryTreeController.cs.meta new file mode 100644 index 00000000..19c58fdf --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/StoryTreeController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7b00483ab980b3248a62618eeb0bcb4b \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Tree/TextBlockView.cs b/Assets/Scripts/NewStorySystem/Tree/TextBlockView.cs new file mode 100644 index 00000000..4c966663 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/TextBlockView.cs @@ -0,0 +1,95 @@ +using System.Collections.Generic; +using Ichni.Story.Dialogue; +using TMPro; +using UnityEngine; + +namespace Ichni.Story.UI +{ + /// + /// 文本å—视图。点击åŽï¼ˆé˜¶æ®µ 2)将通过 Yarn Spinner 进入对应节点的对è¯ã€‚ + /// + public class TextBlockView : StoryBlockView + { + [Header("Text Block")] + public TMP_Text storyIdText; + public TMP_Text titleText; + + [Tooltip("三ç§çжæ€å¯¹åº”çš„èƒŒæ™¯é¢„åˆ¶ä½“ï¼Œç”Ÿæˆ block 时按当å‰çжæ€å®žä¾‹åŒ–为第一个å­ç‰©ä½“")] + public Dictionary blockVisuals = new Dictionary(); + + /// 该文本å—对应的 Yarn 节点å。 + public string YarnNodeName { get; private set; } + + // 当å‰å·²å®žä¾‹åŒ–的状æ€èƒŒæ™¯å¯¹è±¡ï¼›çжæ€åˆ‡æ¢æ—¶é”€æ¯å¹¶æ›¿æ¢ + private GameObject _currentVisual; + + public override void Initialize(StoryBlockDefinition def, Vector2 position, StoryBlockState blockState) + { + // 注æ„:base.Initialize 末尾会调用 ApplyState(state),届时已生æˆå¯¹åº”背景 + base.Initialize(def, position, blockState); + + YarnNodeName = def.yarnNodeName; + + if (storyIdText != null) + storyIdText.text = def.blockId; + + // 阶段 5 将接入 Unity Localization;此处å ä½ç›´æŽ¥æ˜¾ç¤º titleKey + if (titleText != null) + titleText.text = def.titleKey; + } + + /// + /// 应用状æ€ï¼šåœ¨åŸºç±»é€»è¾‘(按钮å¯äº¤äº’性)之外,切æ¢åˆ°å¯¹åº”状æ€çš„背景视觉。 + /// + public override void ApplyState(StoryBlockState newState) + { + base.ApplyState(newState); + UpdateVisual(newState); + } + + /// + /// 按当å‰çжæ€å®žä¾‹åŒ–对应的背景预制体,作为第一个å­ç‰©ä½“(ä½äºŽæ–‡æœ¬/端å£/按钮之下), + /// 并拉伸铺满整个 block。状æ€åˆ‡æ¢æ—¶å…ˆé”€æ¯æ—§èƒŒæ™¯ã€‚ + /// + private void UpdateVisual(StoryBlockState newState) + { + if (_currentVisual != null) + { + Destroy(_currentVisual); + _currentVisual = null; + } + + if (blockVisuals == null || + !blockVisuals.TryGetValue(newState, out GameObject prefab) || + prefab == null) + { + Debug.LogWarning($"[TextBlockView] block '{blockId}' ç¼ºå°‘çŠ¶æ€ {newState} 的背景预制体(blockVisuals 未é…置)。"); + return; + } + + _currentVisual = Instantiate(prefab, blockRect); + _currentVisual.transform.SetAsFirstSibling(); + + // 背景铺满整个 block + if (_currentVisual.transform is RectTransform visualRect) + { + visualRect.anchorMin = Vector2.zero; + visualRect.anchorMax = Vector2.one; + visualRect.offsetMin = Vector2.zero; + visualRect.offsetMax = Vector2.zero; + visualRect.localScale = Vector3.one; + } + } + + protected override void OnClicked() + { + if (StoryDialogueController.instance == null) + { + Debug.LogWarning($"[TextBlockView] 场景中缺少 StoryDialogueController,无法播放 block '{blockId}' 的对è¯ã€‚"); + return; + } + + StoryDialogueController.instance.PlayBlock(this); + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Tree/TextBlockView.cs.meta b/Assets/Scripts/NewStorySystem/Tree/TextBlockView.cs.meta new file mode 100644 index 00000000..ad9299fa --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/TextBlockView.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 28d84946aa995484dbcbe24b7aab1652 \ No newline at end of file diff --git a/Assets/Scripts/NewStorySystem/Tree/TutorialBlockView.cs b/Assets/Scripts/NewStorySystem/Tree/TutorialBlockView.cs new file mode 100644 index 00000000..fab91b09 --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/TutorialBlockView.cs @@ -0,0 +1,28 @@ +using TMPro; +using UnityEngine; + +namespace Ichni.Story.UI +{ + /// + /// 教程å—视图(å ä½ï¼‰ã€‚点击åŽå°†è¿›å…¥è¯¥ç« èŠ‚çš„æ•™ç¨‹æ¸¸çŽ©ï¼ŒåŽç»­é˜¶æ®µæŽ¥å…¥ã€‚ + /// + public class TutorialBlockView : StoryBlockView + { + [Header("Tutorial Block")] + public TMP_Text tutorialNameText; + + public override void Initialize(StoryBlockDefinition def, Vector2 position, StoryBlockState blockState) + { + base.Initialize(def, position, blockState); + + if (tutorialNameText != null) + tutorialNameText.text = def.tutorialName; + } + + protected override void OnClicked() + { + // å ä½ï¼šåŽç»­æŽ¥å…¥æ•™ç¨‹è¿›å…¥æµç¨‹ + Debug.Log($"[TutorialBlockView] ç‚¹å‡»æ•™ç¨‹å— '{blockId}' (tutorial='{definition.tutorialName}')。å ä½å®žçŽ°ï¼Œæ•™ç¨‹è¿›å…¥å°†åœ¨åŽç»­é˜¶æ®µæŽ¥å…¥ã€‚"); + } + } +} diff --git a/Assets/Scripts/NewStorySystem/Tree/TutorialBlockView.cs.meta b/Assets/Scripts/NewStorySystem/Tree/TutorialBlockView.cs.meta new file mode 100644 index 00000000..9e5a130a --- /dev/null +++ b/Assets/Scripts/NewStorySystem/Tree/TutorialBlockView.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 66b3d929576d6c54ca51069ae0e313e5 \ No newline at end of file diff --git a/Assets/Scripts/SLSUtilities/General/SpaceConverter.cs b/Assets/Scripts/SLSUtilities/General/SpaceConverter.cs index 9925bc24..aa2e7673 100644 --- a/Assets/Scripts/SLSUtilities/General/SpaceConverter.cs +++ b/Assets/Scripts/SLSUtilities/General/SpaceConverter.cs @@ -104,15 +104,5 @@ namespace SLSUtilities.General // 最åŽèµ‹å€¼ target.anchoredPosition = localPos; return localPos; } - - /// - /// 获å–目标 RectTransform 在目标空间 RectTransform 中的本地ä½ç½® - /// - public static Vector2 GetLocalUIPosition(RectTransform targetRect, RectTransform targetSpace) - { - Vector2 screenPos = RectTransformUtility.WorldToScreenPoint(null, targetRect.position); - RectTransformUtility.ScreenPointToLocalPointInRectangle(targetSpace, screenPos, null, out Vector2 localPos); - return localPos; - } } } \ No newline at end of file diff --git a/Assets/Scripts/Saving/GameSaveManager.cs b/Assets/Scripts/Saving/GameSaveManager.cs index c086abfc..467869a8 100644 --- a/Assets/Scripts/Saving/GameSaveManager.cs +++ b/Assets/Scripts/Saving/GameSaveManager.cs @@ -36,8 +36,7 @@ namespace Ichni SongSaveModule.LoadStoryUnlockKeys(); StorySaveModule = new StorySaveModule(); - StorySaveModule.LoadStoryVariables(); - StorySaveModule.LoadChoices(); + StorySaveModule.LoadVariables(); } } @@ -226,113 +225,5 @@ namespace Ichni } } - public partial class StorySaveModule - { - public Dictionary> tutorialBlockSaves; - public Dictionary> songBlockSaves; - public Dictionary> dialogBlockSaves; - public Dictionary> connectorSaves; - - public Dictionary storyVariables; - public Dictionary selectedChoices; - - private string GetStorySavePath(string chapterName) => Application.persistentDataPath + "/StorySaves/" + chapterName + ".json"; - private string StoryVariablesPath => Application.persistentDataPath + "/StorySaves/StoryVariables.json"; - private string ChoicesPath => Application.persistentDataPath + "/StorySaves/Choices.json"; - - public StorySaveModule() - { - tutorialBlockSaves = new Dictionary>(); - songBlockSaves = new Dictionary>(); - dialogBlockSaves = new Dictionary>(); - connectorSaves = new Dictionary>(); - storyVariables = new Dictionary(); - selectedChoices = new Dictionary(); - //Debug.Log("Story Variables path: " + StoryVariablesPath); - } - } - - public partial class StorySaveModule - { - public bool IsNewStoryline(string chapterName) - { - return !ES3.FileExists(GetStorySavePath(chapterName)); - } - - public void LoadStoryline(string chapterName) - { - tutorialBlockSaves[chapterName] = ES3.Load>("TutorialBlockSaves", GetStorySavePath(chapterName)); - songBlockSaves[chapterName] = ES3.Load>("SongBlockSaves", GetStorySavePath(chapterName)); - dialogBlockSaves[chapterName] = ES3.Load>("TextBlockSaves", GetStorySavePath(chapterName)); - connectorSaves[chapterName] = ES3.Load>("BlockConnectorSaves", GetStorySavePath(chapterName)); - } - - public void SaveStoryline(string chapterName, List tutorialBlocks, - List songBlocks, List dialogBlocks, - List connectors) - { - ES3.Save("TutorialBlockSaves", tutorialBlocks, GetStorySavePath(chapterName)); - ES3.Save("SongBlockSaves", songBlocks, GetStorySavePath(chapterName)); - ES3.Save("TextBlockSaves", dialogBlocks, GetStorySavePath(chapterName)); - ES3.Save("BlockConnectorSaves", connectors, GetStorySavePath(chapterName)); - - SaveStoryVariables(); - SaveChoices(); - } - - public void ClearAllStoryline() - { - string path = GetStorySavePath("Chapter0"); - if (ES3.FileExists(path)) - { - ES3.DeleteFile(path); - Debug.Log("Cleared all story saves at: " + path); - } - else - { - Debug.LogWarning("No story saves found at: " + path); - } - } - } - - public partial class StorySaveModule - { - public void SaveStoryVariables() - { - string path = Application.persistentDataPath + "/StorySaves/" + "StoryVariables.json"; - ES3.Save("StoryVariables", storyVariables, path); - } - - public void LoadStoryVariables() - { - string path = Application.persistentDataPath + "/StorySaves/" + "StoryVariables.json"; - if (ES3.FileExists(path)) - { - storyVariables = ES3.Load>("StoryVariables", path); - } - else - { - storyVariables = new Dictionary(); - } - } - - public void SaveChoices() - { - string path = Application.persistentDataPath + "/StorySaves/" + "Choices.json"; - ES3.Save("Choices", selectedChoices, path); - } - - public void LoadChoices() - { - string path = Application.persistentDataPath + "/StorySaves/" + "Choices.json"; - if (ES3.FileExists(path)) - { - selectedChoices = ES3.Load>("Choices", path); - } - else - { - selectedChoices = new Dictionary(); - } - } - } + // StorySaveModule å·²è¿ç§»è‡³ NewStorySystem/Save/StorySaveModule.cs(按章节存树/选项,全局存å˜é‡ï¼‰ } \ No newline at end of file diff --git a/Assets/Scripts/Saving/StorySave.cs b/Assets/Scripts/Saving/StorySave.cs deleted file mode 100644 index a2150fea..00000000 --- a/Assets/Scripts/Saving/StorySave.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -namespace Ichni.Story -{ - public class StoryBlockSave - { - public string blockName; - public Vector2 position; - public StoryBlockState state; - - public StoryBlockSave(string blockName, Vector2 position, StoryBlockState state) - { - this.blockName = blockName; - this.state = state; - this.position = position; - } - } - - public class TutorialBlockSave : StoryBlockSave - { - public TutorialBlockSave(string blockName, Vector2 position, StoryBlockState state) : base(blockName, position, state) - { - } - } - - public class DialogBlockSave : StoryBlockSave - { - public DialogBlockSave(string blockName, Vector2 position, StoryBlockState state) : base(blockName, position, state) - { - } - } - - public class SongBlockSave : StoryBlockSave - { - public SongBlockSave(string blockName, Vector2 position, StoryBlockState state) : base(blockName, position, state) - { - } - } - - public class BlockConnectorSave - { - public string startBlockName; - public string endBlockName; - - public BlockConnectorSave(string startBlockName, string endBlockName) - { - this.startBlockName = startBlockName; - this.endBlockName = endBlockName; - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Settings/GameSettings.cs b/Assets/Scripts/Settings/GameSettings.cs index 9f954c5b..19cdd137 100644 --- a/Assets/Scripts/Settings/GameSettings.cs +++ b/Assets/Scripts/Settings/GameSettings.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; using UnityEngine; +using UnityEngine.Localization.Settings; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; @@ -65,6 +66,7 @@ namespace Ichni public void ApplyLanguage() { I2.Loc.LocalizationManager.CurrentLanguage = MenuManager.instance.languageList[languageIndex]; + LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[languageIndex]; I2.Loc.LocalizationManager.UpdateSources(); } } diff --git a/Assets/Scripts/Story/Dialog/DialogManager.cs b/Assets/Scripts/Story/Dialog/DialogManager.cs deleted file mode 100644 index 172a0f41..00000000 --- a/Assets/Scripts/Story/Dialog/DialogManager.cs +++ /dev/null @@ -1,447 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using Ichni.Story.UI; -using Sirenix.OdinInspector; -using SLSUtilities.General; -using UnityEngine; -using UnityEngine.Events; -using UnityEngine.Serialization; - -namespace Ichni.Story -{ - public partial class DialogManager : SerializedMonoBehaviour - { - public static DialogManager instance; - - public List dialogTextAssets; - - public bool isPlayingDialog; - public bool isPlayingChoice; - - public string currentDialog; - public int currentDialogSentenceIndex; - public string currentFinalType; - - public Dictionary> functionDictionary; - public Dictionary> dialogDictionary; - public Dictionary choiceDictionary; - public Dictionary> conditionDictionary; - public List dialogEndActions; - - private string currentLoadingDialog; - - public DialogUIPage dialogUIPage; - - private void Awake() - { - instance = this; - } - - public void SetDialog(string dialogName) - { - string chapter = ChapterSelectionManager.instance.currentChapter.chapterIndex; - TextAsset dialog = Resources.Load("Story/" + chapter + "/Dialogs/" + dialogName); - dialogUIPage.dialogContentFrame.ClearAllSentences(); - SetDialog(new List { dialog }); - } - - public void SetDialog(List dialogFiles, string dialogParagraphName = "") - { - dialogUIPage.FadeIn(); - - isPlayingDialog = true; - - currentDialog = "NULL"; - - dialogEndActions = new List(); - - LoadDialog(dialogFiles, out string firstHeader); - - currentDialog = dialogParagraphName == "" ? firstHeader : dialogParagraphName; - } - - public void PlayNextDialogParagraph(string nextDialog, bool invokeFunctions = true) - { - currentDialog = nextDialog; - currentDialogSentenceIndex = 0; - - if (invokeFunctions && functionDictionary.TryGetValue(currentDialog, out List functionList)) - { - functionList.ForEach(x => StoryInterpreters.FunctionInterpreter.Eval(x)); - } - - if (choiceDictionary.ContainsKey(currentDialog)) - { - currentFinalType = "Choice"; - } - else if (conditionDictionary.ContainsKey(currentDialog)) - { - currentFinalType = "Condition"; - } - else - { - currentFinalType = "None"; - } - } - - public void PlayDialog() - { - if(currentDialog == "NULL") - { - throw new Exception("Current dialog is NULL"); - } - - if (isPlayingChoice) - { - return; - } - - if (dialogDictionary[currentDialog].Count > 0 && currentDialogSentenceIndex < dialogDictionary[currentDialog].Count) - { - DialogSentence currentSentence = dialogDictionary[currentDialog][currentDialogSentenceIndex]; - - string interpretedContent = currentSentence.GetInterpretedContent(); - - dialogUIPage.dialogContentFrame.PlaySentence(currentSentence.characterName, interpretedContent); - currentDialogSentenceIndex++; - - if (currentDialogSentenceIndex <= dialogDictionary[currentDialog].Count) - { - return; - } - } - - if(currentDialogSentenceIndex >= dialogDictionary[currentDialog].Count) - { - if (currentFinalType == "Choice") - { - isPlayingChoice = true; - dialogUIPage.dialogContentFrame.PlayChoice(choiceDictionary[currentDialog]); - return; - } - - if (currentFinalType == "Condition") - { - foreach (var condition in conditionDictionary[currentDialog]) - { - if (condition.GetConditionResult()) - { - PlayNextDialogParagraph(condition.nextDialogName); - return; - } - } - } - } - - if (currentFinalType == "None" && currentDialogSentenceIndex >= dialogDictionary[currentDialog].Count) - { - isPlayingDialog = false; - dialogUIPage.FadeOut(); - - if (StoryManager.instance.storyline.currentBlock.state == StoryBlockState.Current) - { - StoryManager.instance.storyline.currentBlock.state = StoryBlockState.Completed; - StoryManager.instance.storyUIPage.messageBox.Clear(); - dialogEndActions.ForEach(action => action.Invoke()); - StoryManager.instance.storyUIPage.messageBox.SetUp(); - StoryManager.instance.storyline.SaveStoryline(ChapterSelectionManager.instance.currentChapter.chapterIndex); - Debug.Log("Dialog completed, setting block state to Completed."); - } - } - } - - public void RevealDialog() - { - string finalType; - int max = 0; - Debug.Log($"Revealing dialog: {currentDialog}, currentFinalType: {currentFinalType}"); - do - { - finalType = currentFinalType; - currentDialogSentenceIndex = 0; - - foreach (DialogSentence sentence in dialogDictionary[currentDialog]) - { - string interpretedContent = sentence.GetInterpretedContent(); - dialogUIPage.dialogContentFrame.PlaySentence(sentence.characterName, interpretedContent); - currentDialogSentenceIndex++; - } - - if (finalType == "Choice") - { - ChoiceGroup choiceGroup = choiceDictionary[currentDialog]; - int choiceIndex = GameSaveManager.instance.StorySaveModule.selectedChoices[choiceGroup.choiceName]; - dialogUIPage.dialogContentFrame.SelectChoice(choiceGroup, choiceIndex); - } - - if (finalType == "Condition") - { - foreach (var condition in conditionDictionary[currentDialog]) - { - if (condition.GetConditionResult()) - { - PlayNextDialogParagraph(condition.nextDialogName, false); - } - } - } - - max++; - - if (max > 1024) - { - throw new Exception("An infinite loop may detected in dialog parsing. Please check the dialog structure."); - } - - } while (finalType != "None"); - } - } - - public partial class DialogManager - { - public void LoadDialog(List dialogFiles, out string firstHeader) - { - ClearDictionaries(); - - firstHeader = string.Empty; - - dialogTextAssets = dialogFiles; - List dialogLines = new List(); - - foreach (TextAsset textAsset in dialogTextAssets) - { - dialogLines.AddRange(ExtractValidFragments(textAsset.text)); - } - - dialogLines.RemoveAll(line => line.Trim() == ""); - - //dialogLines.ForEach(Debug.Log); - - foreach (string line in dialogLines) - { - if (!ParseHeader(line)) - { - if (!ParseChoiceModule(line)) - { - if (!ParseConditionModule(line)) - { - if (!ParseDialogSentence(line)) - { - throw new Exception($"Invalid dialog line: {line}"); // 抛出异常,æç¤ºä¸åˆæ³•的对è¯è¡Œ - } - } - } - } - else - { - if (firstHeader == string.Empty) - { - firstHeader = currentDialog; - } - } - } - - //dialogDictionary.RemoveWhere((header, sentences) => sentences == null || sentences.Count == 0); - choiceDictionary.RemoveWhere((header, choices) => choices == null || choices.choices.Count == 0); - conditionDictionary.RemoveWhere((header, conditions) => conditions == null || conditions.Count == 0); - } - - /// - /// 从原始大文本中æå–所有以 '$' 开头的有效片段,忽略以 '#' 开头的注释片段。 - /// æ‹†åˆ†ä¾æ®ï¼šæ¯å½“é‡åˆ° '$' 或 '#' 字符,å³è§†ä¸ºä¸€ä¸ªæ–°ç‰‡æ®µçš„起始。 - /// - /// 未分割的完整文本(å¯èƒ½åŒ…å«ä»»æ„æ¢è¡Œæˆ–连续内容)。 - /// 剥离首 '$' åŽçš„æœ‰æ•ˆæ–‡æœ¬åˆ—表。 - public static List ExtractValidFragments(string inputText) - { - if (inputText == null) - { - throw new ArgumentNullException(nameof(inputText)); - } - - // 正则:(?[$#]) // 片段起始å‰ç¼€ - // (?.*? ) // éžè´ªå©ªæ•获所有内容 - // (?=(?:[$#])|\z) // 直到下一个 '$'ã€'#' 或文末 - - const string pattern = @"(?[$#])(?.*?)(?=(?:[$#])|\z)"; - MatchCollection matches = Regex.Matches(inputText, pattern, RegexOptions.Singleline); - - var result = new List(matches.Count); - foreach (Match m in matches) - { - char prefix = m.Groups["prefix"].Value[0]; - string content = m.Groups["content"].Value; - - if (prefix == '$') - { - result.Add(content.Trim()); - } - // prefix == '#' 时自动忽略 - } - - return result; - } - - } - - public partial class DialogManager - { - public void ClearDictionaries() - { - dialogDictionary.Clear(); - choiceDictionary.Clear(); - conditionDictionary.Clear(); - functionDictionary.Clear(); - } - - public bool ParseHeader(string line) - { - //æ ¼å¼ï¼š[currentLoadingDialog]{Function0();Function1();Function2();} - line = line.Trim(); - - string dialogTitle = line.Split("{")[0]; - if (dialogTitle[0] != '[' || dialogTitle[^1] != ']') - { - return false; - } - - currentLoadingDialog = dialogTitle.Replace("[", "").Replace("]", ""); - - dialogDictionary.Add(currentLoadingDialog, new List()); - //choiceDictionary.Add(currentLoadingDialog, new ChoiceGroup("Error")); - conditionDictionary.Add(currentLoadingDialog, new List()); - - if (currentDialog == "NULL") - { - currentDialog = currentLoadingDialog; - } - - if (!line.Contains("{")) // 这个Headeræ²¡æœ‰å‡½æ•°éœ€è¦æ‰§è¡Œ - { - return true; - } - - string functions = line.Split("{")[1]; - if (functions.Contains("}")) - { - functions = functions.Split("}")[0]; - } - else - { - throw new System.Exception("Dialog header's function list must be enclosed in {}."); - } - - functions = functions.Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim(); //忽略空格,æ¢è¡Œ - - List functionList = functions.Split(';').ToList(); //分割函数 - functionList = functionList.Where(x => !string.IsNullOrEmpty(x)).ToList(); //去除空函数 - functionDictionary.Add(currentLoadingDialog, functionList); - - return true; - } - - public bool ParseDialogSentence(string line) - { - //speakerName:sentence - - string[] sentenceData; - if (line.Contains(":")) - { - sentenceData = line.Split(":", 2); - } - else - { - return false; - } - - string character = sentenceData[0]; - string speakerName = character; - - DialogSentence dialogSentence = new DialogSentence - { - characterName = speakerName.Trim(), - content = sentenceData[1].Trim() - }; - - dialogDictionary[currentLoadingDialog].Add(dialogSentence); - - return true; - } - - public bool ParseChoiceModule(string line) - { - //$Choice(ChoiceName){ - //choiceText0->[nextDialogName0]; - //choiceText1->[nextDialogName1]; - //} - - line = line.Trim(); - - if (line.Contains("Choice")) - { - string[] choiceModuleData = line.Split('{'); - - string choiceName = choiceModuleData[0].Split('(')[1].Replace(")", "").Trim(); - ChoiceGroup choiceGroup = new ChoiceGroup(choiceName); - - string[] choiceData = choiceModuleData[1].Split(';'); - for (var index = 0; index < choiceData.Length - 1; index++) - { - choiceData[index] = choiceData[index].Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim(); - - string choiceText = choiceData[index].Split("->[")[0].Trim(); - string nextDialogName = choiceData[index].Split("->[")[1].Replace("]", "").Trim(); - - choiceGroup.choices.Add(new Choice(choiceText, nextDialogName)); - } - - choiceDictionary[currentLoadingDialog] = choiceGroup; - - return true; - } - - return false; - } - - /// - /// è§£æžæ¡ä»¶æ¨¡å— - /// - /// - /// - private bool ParseConditionModule(string line) - { - //$Condition{ - //conditionSentence0->[nextDialogName0]; - //conditionSentence1->[nextDialogName1]; - //} - - if (line.Contains("Condition")) - { - string[] conditionModuleData = line.Split('{'); - - List conditions = new List(); - - string[] conditionData = conditionModuleData[1].Split(';'); - - for (var index = 0; index < conditionData.Length - 1; index++) - { - conditionData[index] = conditionData[index].Replace(" ", "").Replace("\n", "").Replace("\r", "").Trim(); - Condition condition = new Condition - { - conditionSentence = conditionData[index].Split("->[")[0].Trim(), - nextDialogName = conditionData[index].Split("->[")[1].Replace("]", "").Trim(), - }; - - conditions.Add(condition); - } - - conditionDictionary[currentLoadingDialog] = conditions; - - return true; - } - - return false; - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Story/Dialog/DialogModule.cs b/Assets/Scripts/Story/Dialog/DialogModule.cs deleted file mode 100644 index 11eebc85..00000000 --- a/Assets/Scripts/Story/Dialog/DialogModule.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; -using UnityEngine; - -namespace Ichni.Story -{ - public class DialogSentence - { - public string content; - public string audioEventName; - - public string characterName; - - public DialogSentence() - { - - } - - public DialogSentence(string content, string audioEventName, string characterName) - { - this.content = content; - this.audioEventName = audioEventName; - this.characterName = characterName; - } - - /// - /// 匹é…{@FUNCTION},解æžå‡½æ•°å¹¶è¿”回解æžåŽçš„语å¥å†…容。 - /// - public string GetInterpretedContent() - { - List parts = new List(); - Regex regex = new Regex(@"\{\@.*?\}"); - int lastIndex = 0; - - foreach (Match match in regex.Matches(content)) - { - if (match.Index > lastIndex) - { - parts.Add(content.Substring(lastIndex, match.Index - lastIndex)); - } - parts.Add(match.Value); - lastIndex = match.Index + match.Length; - } - - if (lastIndex < content.Length) - { - parts.Add(content.Substring(lastIndex)); - } - - for (int i = 0; i < parts.Count; i++) - { - if (parts[i].StartsWith("{@") && parts[i].EndsWith("}")) - { - string expression = parts[i].Substring(2, parts[i].Length - 3); - object result = StoryInterpreters.FunctionInterpreter.Eval(expression); - parts[i] = result.ToString(); - } - } - - return string.Join("", parts); - } - } - - public class ChoiceGroup - { - public string choiceName; - public List choices; - - public ChoiceGroup(string choiceName) - { - this.choiceName = choiceName; - this.choices = new List(); - } - } - - public class Choice - { - public string choiceText; - public string nextDialogName; - - public Choice(string choiceText, string nextDialogName) - { - this.choiceText = choiceText; - this.nextDialogName = nextDialogName; - } - } - - public class Condition - { - public string conditionSentence; - public string nextDialogName; - - public bool GetConditionResult() - { - bool result = StoryInterpreters.ConditionInterpreter.Eval(conditionSentence); - return result; - } - } - - public class DialogCharacter - { - public string name; - public string title; - public Dictionary emotions; - } -} \ No newline at end of file diff --git a/Assets/Scripts/Story/Dialog/StoryInterpreters.cs b/Assets/Scripts/Story/Dialog/StoryInterpreters.cs deleted file mode 100644 index 06fcc832..00000000 --- a/Assets/Scripts/Story/Dialog/StoryInterpreters.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Generic; -using DynamicExpresso; -using Ichni.Menu; -using Ichni.Story; -using Ichni.Story.UI; -using UnityEngine; - -namespace Ichni.Story -{ - public static partial class StoryInterpreters - { - public static readonly Interpreter FunctionInterpreter; - public static readonly Interpreter ConditionInterpreter; - - static StoryInterpreters() - { - FunctionInterpreter = new Interpreter(); - ConditionInterpreter = new Interpreter(); - - SetFunctionInterpreter(); - SetConditionInterpreter(); - } - - static void SetFunctionInterpreter() - { - FunctionInterpreter.SetFunction("SetVariable", new Action(SetStoryVariable)); - FunctionInterpreter.SetFunction("GetVariable", new Func(GetStoryVariable)); - FunctionInterpreter.SetFunction("GenerateDialogBlock", new Action(GenerateDialogBlock)); - FunctionInterpreter.SetFunction("GenerateSongBlock", new Action(GenerateSongBlock)); - FunctionInterpreter.SetFunction("SetUnlockKey", new Action(SetUnlockKey)); - } - - static void SetConditionInterpreter() - { - ConditionInterpreter.SetFunction("GetVariable", new Func(GetStoryVariable)); - } - } - - public static partial class StoryInterpreters - { - /// - /// 设置全局å˜é‡çš„值 - /// - static void SetStoryVariable(string variableName, int value) - { - GameSaveManager.instance.StorySaveModule.storyVariables[variableName] = value; - } - - /// - /// 获å–全局å˜é‡çš„值 - /// - static int GetStoryVariable(string variableName) - { - if (GameSaveManager.instance.StorySaveModule.storyVariables.TryGetValue(variableName, out int value)) - { - return value; - } - - throw new ArgumentException($"Global variable '{variableName}' not found."); - } - } - - public static partial class StoryInterpreters - { - static void GenerateDialogBlock(string blockName) - { - StoryBlockUIBase currentBlock = StoryManager.instance.storyline.currentBlock; - Vector2 positionOffset = new Vector2(500, 0); - DialogBlockUI newBlock = StoryManager.instance.storyline.GenerateDialogBlock(blockName, currentBlock.blockPosition + positionOffset, StoryBlockState.Current); - StoryManager.instance.storyline.GenerateConnector(currentBlock, newBlock); - StoryManager.instance.storyline.SetUpBackground(); - StoryManager.instance.storyline.connectors.ForEach(c => c.SetCurve()); - } - - static void GenerateSongBlock(string blockName) - { - StoryBlockUIBase currentBlock = StoryManager.instance.storyline.currentBlock; - Vector2 positionOffset = new Vector2(500, 0); - SongBlockUI newBlock = StoryManager.instance.storyline.GenerateSongBlock(blockName, currentBlock.blockPosition + positionOffset, StoryBlockState.Current); - StoryManager.instance.storyline.GenerateConnector(currentBlock, newBlock); - } - - static void SetUnlockKey(string key) - { - if (GameSaveManager.instance.SongSaveModule.storyUnlockKeys.Add(key)) - { - GameSaveManager.instance.SongSaveModule.SaveStoryUnlockKeys(); - - DialogManager.instance.dialogEndActions.Add(() => - { - ChapterSelectionUnit currentChapter = ChapterSelectionManager.instance.currentChapter; - List unlockedSongs = currentChapter.GetRelatedSongNamesOfUnlockKey(key); - foreach (string songName in unlockedSongs) - { - StoryManager.instance.storyUIPage.messageBox.AddInfo( - "Message/Unlock_Song_Title", "Message/Unlock_Song", - () => StoryManager.instance.storyUIPage.messageBox.SetParameter("SongName", songName)); - } - }); - } - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Story/Dialog/StoryManager.cs b/Assets/Scripts/Story/Dialog/StoryManager.cs deleted file mode 100644 index ace9244a..00000000 --- a/Assets/Scripts/Story/Dialog/StoryManager.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using Ichni.Story.UI; -using Ichni.UI; -using Sirenix.OdinInspector; -using UnityEngine; -using UnityEngine.Serialization; - -namespace Ichni.Story -{ - public partial class StoryManager : SerializedMonoBehaviour - { - public static StoryManager instance; - - [FormerlySerializedAs("storylineDisplay")] public Storyline storyline; - public StoryUIPage storyUIPage; - - public Dictionary storyDatas; - - - void Awake() - { - instance = this; - } - } - - public partial class StoryManager - { - [Button] - public void ClearAllStorySave() - { - GameSaveManager.instance.StorySaveModule.ClearAllStoryline(); - GameSaveManager.instance.SongSaveModule.ClearStoryKeys(); - } - } - - public enum StoryBlockState - { - Locked, - Current, - Completed - } -} \ No newline at end of file diff --git a/Assets/Scripts/Story/Dialog/StoryManager.cs.meta b/Assets/Scripts/Story/Dialog/StoryManager.cs.meta deleted file mode 100644 index 2f93f10e..00000000 --- a/Assets/Scripts/Story/Dialog/StoryManager.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e58008720832d5045ada32d1161faa69 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Story/StoryData/StoryData.cs b/Assets/Scripts/Story/StoryData/StoryData.cs deleted file mode 100644 index f946d651..00000000 --- a/Assets/Scripts/Story/StoryData/StoryData.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Sirenix.OdinInspector; -using UnityEngine; -using UnityEngine.Serialization; - -namespace Ichni.Story -{ - [CreateAssetMenu(fileName = "StoryData", menuName = "Ichni/Story/StoryData")] - public class StoryData : SerializedScriptableObject - { - public List dialogBlockDatas; // 剧情å•元格å称列表 - public List songBlockDatas; // 音ä¹å•元格å称列表 - public List tutorialBlockDatas; // 教程å•元格å称列表 - public List initialBlocks; // åˆå§‹å‰§æƒ…å•å…ƒæ ¼åˆ—è¡¨ï¼ŒåŒ…å«æ‰€æœ‰åˆå§‹å‰§æƒ…å•元格的åç§° - - public StoryBlockData GetDataByName(string blockName, out Type dataType) - { - foreach (var block in tutorialBlockDatas.Where(block => block.blockName == blockName)) - { - dataType = typeof(TutorialBlockData); - return block; - } - - foreach (var block in songBlockDatas.Where(block => block.blockName == blockName)) - { - dataType = typeof(SongBlockData); - return block; - } - - foreach (var block in dialogBlockDatas.Where(block => block.blockName == blockName)) - { - dataType = typeof(DialogBlockData); - return block; - } - - throw new ArgumentException($"No block found with name: {blockName}"); - } - } - - [InlineProperty] - [Serializable] - public class InitialBlockData - { - public string blockName; - public StoryBlockState initialState; // åˆå§‹çŠ¶æ€ - public Vector2 blockPosition; // åˆå§‹ä½ç½® - public List nextBlocks; // 下一步å¯é€‰çš„剧情å•元格å称列表 - } - - [InlineProperty] - [Serializable] - public class StoryBlockData - { - [FoldoutGroup("$blockName", true)] - public string blockName; - [FoldoutGroup("$blockName")] - public string blockID; - [FoldoutGroup("$blockName")] - public Vector2 blockSize; - } - - [InlineProperty] - [Serializable] - public class TutorialBlockData : StoryBlockData - { - [FoldoutGroup("$blockName")] - public string tutorialName; - - - public TutorialBlockData() - { - this.blockSize = new Vector2(400, 200); - } - } - - [InlineProperty] - [Serializable] - public class DialogBlockData : StoryBlockData - { - [FoldoutGroup("$blockName")] - public string dialogTitle; - - - public DialogBlockData() - { - this.blockSize = new Vector2(400, 200); - } - } - - [InlineProperty] - [Serializable] - public class SongBlockData : StoryBlockData - { - [FoldoutGroup("$blockName")] - public string songName; - - - public SongBlockData() - { - this.blockSize = new Vector2(400, 200); - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Story/StoryData/StoryData.cs.meta b/Assets/Scripts/Story/StoryData/StoryData.cs.meta deleted file mode 100644 index b63830da..00000000 --- a/Assets/Scripts/Story/StoryData/StoryData.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 7ab917c50249812429ebd44d6574497c -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs b/Assets/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs deleted file mode 100644 index 4c6a80ec..00000000 --- a/Assets/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using Ichni.Story.UI; -using SLSUtilities.General; -using UnityEngine; -using UnityEngine.UI.Extensions; - -namespace Ichni.Story -{ - public class BlockConnectorUI : MonoBehaviour - { - public UILineRenderer curve; - public StoryBlockUIBase startBlock; - public StoryBlockUIBase endBlock; - - public void SetCurve(StoryBlockUIBase startBlock = null, StoryBlockUIBase endBlock = null) - { - this.startBlock ??= startBlock; - this.endBlock ??= endBlock; - - if(this.startBlock == null || this.endBlock == null) - { - Debug.LogWarning("Start or end block is not set for the curve."); - return; - } - - Vector2 startPosition = SpaceConverter.GetLocalUIPosition(this.startBlock.outPort, GetComponent()); - Vector2 endPosition = SpaceConverter.GetLocalUIPosition(this.endBlock.inPort, GetComponent()); - - Vector2 mid1 = (startPosition + endPosition) / 2; - Vector2 mid2 = (startPosition + endPosition) / 2; - - mid1.y = startPosition.y; - mid2.y = endPosition.y; - - curve.Points = new Vector2[] { startPosition, /*mid1*/ mid2, endPosition}; - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs.meta b/Assets/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs.meta deleted file mode 100644 index b5c5d15d..00000000 --- a/Assets/Scripts/Story/StoryUI/Blocks/BlockConnectorUI.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 98bbf463dca50cc43961c0bb2b8d4f22 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs b/Assets/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs deleted file mode 100644 index fb7f8089..00000000 --- a/Assets/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using TMPro; -using UnityEngine; -using UnityEngine.UI; - -namespace Ichni.Story.UI -{ - public class DialogBlockUI : StoryBlockUIBase - { - public string blockTitle; - public TMP_Text titleText; - - public Button button; - public List choiceGroups; - - public void Initialize(string blockName, Vector2 position, Vector2 positionOffset, - Vector2 size, StoryBlockState state, string blockTitle) - { - base.Initialize(blockName, position, positionOffset, size, state); - - this.blockTitle = blockTitle; - titleText.text = blockTitle; - - button.onClick.AddListener(() => - { - state = this.state; - - if(state == StoryBlockState.Locked) return; - - StoryManager.instance.storyline.currentBlock = this; - - if (state == StoryBlockState.Current) - { - DialogManager.instance.SetDialog(blockName); - DialogManager.instance.PlayNextDialogParagraph(DialogManager.instance.currentDialog); - } - else if (state == StoryBlockState.Completed) - { - DialogManager.instance.SetDialog(blockName); - DialogManager.instance.PlayNextDialogParagraph(DialogManager.instance.currentDialog, false); - DialogManager.instance.RevealDialog(); - } - }); - } - - public override StoryBlockSave GetBlockSave() - { - return new DialogBlockSave(blockName, blockPosition, state); - } - } -} diff --git a/Assets/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs.meta b/Assets/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs.meta deleted file mode 100644 index 9168727b..00000000 --- a/Assets/Scripts/Story/StoryUI/Blocks/DialogBlockUI.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b976128eb3ec59e4e866dbb610441706 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs b/Assets/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs deleted file mode 100644 index d56a4002..00000000 --- a/Assets/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using Ichni.Menu; -using Ichni.RhythmGame; -using TMPro; -using UnityEngine; -using UnityEngine.Serialization; -using UnityEngine.UI; - -namespace Ichni.Story.UI -{ - public class SongBlockUI : StoryBlockUIBase - { - public string songName; - public Button button; - public TMP_Text songNameText; - public RectTransform beatmapStatusMarkContainer; - - public GameObject beatmapStatusMarkPrefab; - - public void Initialize(string blockName, Vector2 position, Vector2 positionOffset, - Vector2 size, StoryBlockState state, string songName) - { - base.Initialize(blockName, position, positionOffset, size, state); - - this.songName = songName; - songNameText.text = songName; - - button.onClick.AddListener(() => - { - //MenuManager.instance.prepareUIPage.SetUpPrepareUIPage(songName); - //MenuManager.instance.prepareUIPage.FadeIn(); - }); - - SetUpBeatmapStatusMarks(); - } - - public override StoryBlockSave GetBlockSave() - { - return new SongBlockSave(blockName, blockPosition, state); - } - - public void SetUpBeatmapStatusMarks() - { - SongStatusSave songStatusSave = GameSaveManager.instance.SongSaveModule.songStatusSaves[songName]; - - string chapter = ChapterSelectionManager.instance.currentChapter.chapterIndex; - ChapterSelectionUnit cpt = ChapterSelectionManager.instance.chapters.First(c => c.chapterIndex == chapter); - SongItemData song = cpt.songs.First(s => s.songName == this.songName); - for (var index = 0; index < song.difficultyDataList.Count; index++) - { - var difficulty = song.difficultyDataList[index]; - var beatmapSave = songStatusSave.beatmapSaves[index]; - - if (beatmapSave.isAllPerfect) - { - GameObject mark = Instantiate(beatmapStatusMarkPrefab, beatmapStatusMarkContainer); - mark.GetComponent().color = difficulty.color; - mark.transform.GetChild(0).GetComponent().color = difficulty.color; - mark.transform.GetChild(0).GetComponent().text = "AP"; - break; - } - - if (beatmapSave.isFullCombo) - { - GameObject mark = Instantiate(beatmapStatusMarkPrefab, beatmapStatusMarkContainer); - mark.GetComponent().color = difficulty.color; - mark.transform.GetChild(0).GetComponent().color = difficulty.color; - mark.transform.GetChild(0).GetComponent().text = "FC"; - break; - } - } - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs.meta b/Assets/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs.meta deleted file mode 100644 index 273499ec..00000000 --- a/Assets/Scripts/Story/StoryUI/Blocks/SongBlockUI.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5efe0a39fe908354e9ab6d1edfdb8843 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs b/Assets/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs deleted file mode 100644 index 36d36506..00000000 --- a/Assets/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.Serialization; - -namespace Ichni.Story.UI -{ - public abstract class StoryBlockUIBase : MonoBehaviour - { - public string blockName; - public Vector2 blockPosition; - public StoryBlockState state; - - public RectTransform blockRect; - public RectTransform inPort; - public RectTransform outPort; - - protected void Initialize(string blockName, Vector2 position, Vector2 positionOffset, Vector2 size, StoryBlockState state) - { - this.blockName = blockName; - this.blockPosition = position; - this.state = state; - - blockRect.anchoredPosition = position + positionOffset; - blockRect.sizeDelta = size; - } - - public abstract StoryBlockSave GetBlockSave(); - } -} diff --git a/Assets/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs.meta b/Assets/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs.meta deleted file mode 100644 index 2c2f1f2c..00000000 --- a/Assets/Scripts/Story/StoryUI/Blocks/StoryBlockUIBase.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d489f986a1eea1e4c938658f0fd468ca -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs b/Assets/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs deleted file mode 100644 index 71a7e445..00000000 --- a/Assets/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using DG.Tweening; -using Ichni.Menu; -using SLSUtilities.WwiseAssistance; -using TMPro; -using UniRx; -using UnityEngine; -using UnityEngine.UI; - -namespace Ichni.Story.UI -{ - public class TutorialBlockUI : StoryBlockUIBase - { - public Button button; - public string tutorialName; - public TMP_Text tutorialNameText; - - public void Initialize(string blockName, Vector2 position, Vector2 positionOffset, Vector2 size, StoryBlockState state, string tutorialName) - { - base.Initialize(blockName, position, positionOffset, size, state); - - this.tutorialName = tutorialName; - tutorialNameText.text = tutorialName; - - button.onClick.AddListener(EnterTutorial); - } - - public override StoryBlockSave GetBlockSave() - { - return new TutorialBlockSave(blockName, blockPosition, state); - } - - private void EnterTutorial() - { - ChapterSelectionUnit chapter = ChapterSelectionManager.instance.currentChapter; - - SongItemData song = ChapterSelectionManager.instance.tutorialCollection.songs[chapter.chapterIndex]; - DifficultyData difficulty = song.difficultyDataList[0]; - InformationTransistor.instance.SetInformation(chapter, song, difficulty); - InformationTransistor.instance.isReturnedFromTutorial = true; - InformationTransistor.instance.isReturnedFromGame = false; - - AudioManager.Post(AK.EVENTS.ENTERTOGAME); - SongSelectionManager.instance.StopPreviewSong(); - - DOTween.KillAll(); - Observable.Timer(TimeSpan.FromSeconds(0.6f)).Subscribe(_ => { MenuManager.instance.EnterGameScene(); }); - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs.meta b/Assets/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs.meta deleted file mode 100644 index 0d4a41d2..00000000 --- a/Assets/Scripts/Story/StoryUI/Blocks/TutorialBlockUI.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 821e50e1519236a46b03eb1f005acebe -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs b/Assets/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs deleted file mode 100644 index aa54080e..00000000 --- a/Assets/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -namespace Ichni.Story.UI -{ - public class ChoiceButtonUI : MonoBehaviour - { - - } -} \ No newline at end of file diff --git a/Assets/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs.meta b/Assets/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs.meta deleted file mode 100644 index 1fcc6e12..00000000 --- a/Assets/Scripts/Story/StoryUI/DialogUI/ChoiceButtonUI.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 90f34f59ff260c44796d71c51b7c0ee6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Scripts/Story/StoryUI/DialogUI/ChoiceGroupUI.cs b/Assets/Scripts/Story/StoryUI/DialogUI/ChoiceGroupUI.cs deleted file mode 100644 index 8ee0d46b..00000000 --- a/Assets/Scripts/Story/StoryUI/DialogUI/ChoiceGroupUI.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using I2.Loc; -using TMPro; -using UnityEngine; -using UnityEngine.UI; - -namespace Ichni.Story.UI -{ - public class ChoiceGroupUI : MonoBehaviour - { - public GameObject choiceButtonPrefab; - public RectTransform container; - - public List