UI换了很多,spine主视觉停用
This commit is contained in:
@@ -70,6 +70,7 @@ public sealed class DushMaterialLedger : MonoBehaviour
|
||||
loadedFromSave = DushMaterialLedgerStorage.TryLoad(out payload);
|
||||
RebuildFromPayload(payload);
|
||||
initialized = true;
|
||||
TryRecoverFromDefaultPlayerData();
|
||||
SyncMirrorCounts();
|
||||
SaveNow();
|
||||
}
|
||||
@@ -278,6 +279,46 @@ public sealed class DushMaterialLedger : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryRecoverFromDefaultPlayerData()
|
||||
{
|
||||
Player_SO player = RuntimeResourcesCache.LoadDefaultPlayerSo();
|
||||
if (player == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
for (int i = 0; i < DushMaterialCatalog.All.Count; i++)
|
||||
{
|
||||
var descriptor = DushMaterialCatalog.All[i];
|
||||
if (string.IsNullOrEmpty(descriptor.LegacyPlayerFieldName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int legacyValue = Mathf.Max(0, player.GetLegacyDushMaterialCount(descriptor.LegacyPlayerFieldName));
|
||||
if (legacyValue <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GetCountByKey(descriptor.Key) > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
countsByKey[descriptor.Key] = legacyValue;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
Debug.LogWarning("[DushMaterialLedger] Recovered growth materials from Player_SO mirror fields.");
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private DushMaterialLedgerPayload BuildPayload()
|
||||
{
|
||||
var payload = DushMaterialLedgerStorage.CreateDefaultPayload();
|
||||
|
||||
@@ -76,6 +76,7 @@ public sealed class ExpBottleLedger : MonoBehaviour
|
||||
loadedFromSave = ExpBottleLedgerStorage.TryLoad(out payload);
|
||||
RebuildFromPayload(payload);
|
||||
initialized = true;
|
||||
TryRecoverFromDefaultPlayerData();
|
||||
SyncMirrorCounts();
|
||||
SaveNow();
|
||||
if (OnLedgerReloaded != null)
|
||||
@@ -421,6 +422,46 @@ public sealed class ExpBottleLedger : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryRecoverFromDefaultPlayerData()
|
||||
{
|
||||
Player_SO player = RuntimeResourcesCache.LoadDefaultPlayerSo();
|
||||
if (player == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
for (int i = 0; i < ExpBottleCatalog.All.Count; i++)
|
||||
{
|
||||
var descriptor = ExpBottleCatalog.All[i];
|
||||
if (string.IsNullOrEmpty(descriptor.LegacyPlayerFieldName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int legacyValue = Mathf.Max(0, player.GetLegacyExpBottleCount(descriptor.LegacyPlayerFieldName));
|
||||
if (legacyValue <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GetCountByKey(descriptor.Key) > 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
countsByKey[descriptor.Key] = legacyValue;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
Debug.LogWarning("[ExpBottleLedger] Recovered bottle counts from Player_SO mirror fields.");
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private void SyncToPlayerData()
|
||||
{
|
||||
if (boundPlayerData == null)
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
public static class SceneObjectLookupCache
|
||||
{
|
||||
private static readonly Dictionary<string, GameObject> Cache = new Dictionary<string, GameObject>(StringComparer.Ordinal);
|
||||
private static readonly Dictionary<Type, UnityEngine.Object> AnyTypeCache = new Dictionary<Type, UnityEngine.Object>();
|
||||
private static readonly Dictionary<Type, UnityEngine.Object> FirstTypeCache = new Dictionary<Type, UnityEngine.Object>();
|
||||
private static bool hooked;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void ResetStaticState()
|
||||
{
|
||||
Cache.Clear();
|
||||
AnyTypeCache.Clear();
|
||||
FirstTypeCache.Clear();
|
||||
hooked = false;
|
||||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
private static void Bootstrap()
|
||||
{
|
||||
if (hooked)
|
||||
return;
|
||||
|
||||
hooked = true;
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
SceneManager.activeSceneChanged -= OnActiveSceneChanged;
|
||||
SceneManager.activeSceneChanged += OnActiveSceneChanged;
|
||||
}
|
||||
|
||||
public static GameObject Find(string nameOrPath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(nameOrPath))
|
||||
return null;
|
||||
|
||||
string key = nameOrPath.Trim();
|
||||
if (Cache.TryGetValue(key, out GameObject cached))
|
||||
{
|
||||
if (IsUsableCachedGameObject(cached, key))
|
||||
return cached;
|
||||
|
||||
Cache.Remove(key);
|
||||
}
|
||||
|
||||
GameObject found = GameObject.Find(key);
|
||||
if (found != null)
|
||||
Cache[key] = found;
|
||||
return found;
|
||||
}
|
||||
|
||||
public static T FindComponent<T>(string nameOrPath) where T : Component
|
||||
{
|
||||
GameObject go = Find(nameOrPath);
|
||||
return go != null ? go.GetComponent<T>() : null;
|
||||
}
|
||||
|
||||
public static T FindAny<T>() where T : UnityEngine.Object
|
||||
{
|
||||
var type = typeof(T);
|
||||
if (AnyTypeCache.TryGetValue(type, out var cached))
|
||||
{
|
||||
if (IsUsableCachedObject(cached))
|
||||
return cached as T;
|
||||
|
||||
AnyTypeCache.Remove(type);
|
||||
}
|
||||
|
||||
T found = UnityEngine.Object.FindAnyObjectByType<T>();
|
||||
if (found != null)
|
||||
AnyTypeCache[type] = found;
|
||||
return found;
|
||||
}
|
||||
|
||||
public static T FindFirst<T>() where T : UnityEngine.Object
|
||||
{
|
||||
var type = typeof(T);
|
||||
if (FirstTypeCache.TryGetValue(type, out var cached))
|
||||
{
|
||||
if (IsUsableCachedObject(cached))
|
||||
return cached as T;
|
||||
|
||||
FirstTypeCache.Remove(type);
|
||||
}
|
||||
|
||||
T found = UnityEngine.Object.FindFirstObjectByType<T>();
|
||||
if (found != null)
|
||||
FirstTypeCache[type] = found;
|
||||
return found;
|
||||
}
|
||||
|
||||
public static Transform FindTransform(string nameOrPath)
|
||||
{
|
||||
GameObject go = Find(nameOrPath);
|
||||
return go != null ? go.transform : null;
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
Cache.Clear();
|
||||
AnyTypeCache.Clear();
|
||||
FirstTypeCache.Clear();
|
||||
}
|
||||
|
||||
private static void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
Cache.Clear();
|
||||
AnyTypeCache.Clear();
|
||||
FirstTypeCache.Clear();
|
||||
}
|
||||
|
||||
private static void OnActiveSceneChanged(Scene oldScene, Scene newScene)
|
||||
{
|
||||
Cache.Clear();
|
||||
AnyTypeCache.Clear();
|
||||
FirstTypeCache.Clear();
|
||||
}
|
||||
|
||||
private static bool IsUsableCachedObject(UnityEngine.Object cached)
|
||||
{
|
||||
if (cached == null)
|
||||
return false;
|
||||
|
||||
if (cached is Component component)
|
||||
return component != null && component.gameObject != null && component.gameObject.activeInHierarchy;
|
||||
|
||||
if (cached is GameObject go)
|
||||
return go.activeInHierarchy;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsUsableCachedGameObject(GameObject cached, string key)
|
||||
{
|
||||
if (cached == null || !cached.activeInHierarchy)
|
||||
return false;
|
||||
|
||||
int slashIndex = key.IndexOf('/');
|
||||
if (slashIndex < 0)
|
||||
return cached.name == key;
|
||||
|
||||
return BuildHierarchyPath(cached.transform) == key;
|
||||
}
|
||||
|
||||
private static string BuildHierarchyPath(Transform transform)
|
||||
{
|
||||
if (transform == null)
|
||||
return string.Empty;
|
||||
|
||||
var path = transform.name;
|
||||
while (transform.parent != null)
|
||||
{
|
||||
transform = transform.parent;
|
||||
path = transform.name + "/" + path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e16a58b0ef1c6649b98b3f1c2f604eb
|
||||
@@ -18,71 +18,71 @@ public static class StoreExpBottlePurchaseService
|
||||
|
||||
if (playerData == null)
|
||||
{
|
||||
failureMessage = "玩家数据丢失";
|
||||
failureMessage = LocalizationService.Get("store.error.player_data_missing", "玩家数据丢失");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (itemSO == null)
|
||||
{
|
||||
failureMessage = "商品数据丢失";
|
||||
failureMessage = LocalizationService.Get("store.error.item_data_missing", "商品数据丢失");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!itemSO.isOnShelf || !itemSO.canbepurchased)
|
||||
{
|
||||
failureMessage = "物品未上架";
|
||||
failureMessage = LocalizationService.Get("store.error.item_not_on_shelf", "物品未上架");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packageCount <= 0)
|
||||
{
|
||||
failureMessage = "本店禁止无实物交易";
|
||||
failureMessage = LocalizationService.Get("store.error.invalid_purchase_count", "本店禁止无实物交易");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (RequiresSinglePurchase(itemSO) && packageCount > 1)
|
||||
{
|
||||
failureMessage = "该物品仅支持单份购买";
|
||||
failureMessage = LocalizationService.Get("store.error.single_purchase_only", "该物品仅支持单份购买");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (itemSO.costRequirements == null || itemSO.costRequirements.Count == 0)
|
||||
{
|
||||
failureMessage = "不可购买";
|
||||
failureMessage = LocalizationService.Get("store.error.cannot_purchase", "不可购买");
|
||||
return false;
|
||||
}
|
||||
|
||||
var primaryCost = itemSO.costRequirements[0];
|
||||
if (itemSO.associatedSelectableEquipmentRewardSource != null)
|
||||
{
|
||||
failureMessage = "该物品需要通过选择器购买";
|
||||
failureMessage = LocalizationService.Get("store.error.selector_required", "该物品需要通过选择器购买");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsSupportedCurrency(primaryCost.currencyType))
|
||||
{
|
||||
failureMessage = "不可购买";
|
||||
failureMessage = LocalizationService.Get("store.error.cannot_purchase", "不可购买");
|
||||
return false;
|
||||
}
|
||||
|
||||
long longGrantedCount = (long)Mathf.Max(1, itemSO.itemSinglePurchaseQty) * packageCount;
|
||||
if (longGrantedCount > int.MaxValue)
|
||||
{
|
||||
failureMessage = "达到单次购买限额";
|
||||
failureMessage = LocalizationService.Get("store.error.purchase_limit_per_transaction", "达到单次购买限额");
|
||||
return false;
|
||||
}
|
||||
|
||||
grantedCount = (int)longGrantedCount;
|
||||
if (itemSO.itemPurchaseQuota >= 0 && itemSO.purchasedCount + grantedCount > itemSO.itemPurchaseQuota)
|
||||
{
|
||||
failureMessage = "超过限购额度";
|
||||
failureMessage = LocalizationService.Get("store.error.purchase_quota_exceeded", "超过限购额度");
|
||||
return false;
|
||||
}
|
||||
|
||||
long totalCostLong = (long)primaryCost.amount * packageCount;
|
||||
if (totalCostLong > int.MaxValue)
|
||||
{
|
||||
failureMessage = "达到单次购买限额";
|
||||
failureMessage = LocalizationService.Get("store.error.purchase_limit_per_transaction", "达到单次购买限额");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -97,13 +97,13 @@ public static class StoreExpBottlePurchaseService
|
||||
|
||||
if (!HasEnoughCurrency(primaryCost.currencyType, totalCost))
|
||||
{
|
||||
failureMessage = "货币不足";
|
||||
failureMessage = LocalizationService.Get("store.error.insufficient_currency", "货币不足");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TrySpendCurrency(primaryCost.currencyType, totalCost))
|
||||
{
|
||||
failureMessage = "货币不足";
|
||||
failureMessage = LocalizationService.Get("store.error.insufficient_currency", "货币不足");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ public static class StoreExpBottlePurchaseService
|
||||
failureMessage = string.Empty;
|
||||
if (itemSO == null)
|
||||
{
|
||||
failureMessage = "商品数据丢失";
|
||||
failureMessage = LocalizationService.Get("store.error.item_data_missing", "商品数据丢失");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ public static class StoreExpBottlePurchaseService
|
||||
EquipmentConsumableKind equipmentConsumableKind;
|
||||
if (!TryResolveConsumableGrant(itemSO, out grantType, out bottleKind, out materialKind, out equipmentConsumableKind))
|
||||
{
|
||||
failureMessage = "当前未配置发放逻辑";
|
||||
failureMessage = LocalizationService.Get("store.error.grant_logic_missing", "当前未配置发放逻辑");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -158,7 +158,7 @@ public static class StoreExpBottlePurchaseService
|
||||
return StoreOwnershipLedger.EnsureInstance().TryGrantOwnershipPreview(itemSO, out failureMessage);
|
||||
|
||||
default:
|
||||
failureMessage = "当前未配置发放逻辑";
|
||||
failureMessage = LocalizationService.Get("store.error.grant_logic_missing", "当前未配置发放逻辑");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -168,7 +168,7 @@ public static class StoreExpBottlePurchaseService
|
||||
failureMessage = string.Empty;
|
||||
if (itemSO == null)
|
||||
{
|
||||
failureMessage = "商品数据丢失";
|
||||
failureMessage = LocalizationService.Get("store.error.item_data_missing", "商品数据丢失");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ public static class StoreExpBottlePurchaseService
|
||||
EquipmentConsumableKind equipmentConsumableKind;
|
||||
if (!TryResolveConsumableGrant(itemSO, out grantType, out bottleKind, out materialKind, out equipmentConsumableKind))
|
||||
{
|
||||
failureMessage = "当前未配置发放逻辑";
|
||||
failureMessage = LocalizationService.Get("store.error.grant_logic_missing", "当前未配置发放逻辑");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ public static class StoreExpBottlePurchaseService
|
||||
return true;
|
||||
}
|
||||
|
||||
failureMessage = "当前未配置发放逻辑";
|
||||
failureMessage = LocalizationService.Get("store.error.grant_logic_missing", "当前未配置发放逻辑");
|
||||
return false;
|
||||
|
||||
case storeItemSO.ItemType.character:
|
||||
@@ -214,7 +214,7 @@ public static class StoreExpBottlePurchaseService
|
||||
return StoreOwnershipLedger.EnsureInstance().TryGrantOwnership(itemSO, out failureMessage);
|
||||
|
||||
default:
|
||||
failureMessage = "当前未配置发放逻辑";
|
||||
failureMessage = LocalizationService.Get("store.error.grant_logic_missing", "当前未配置发放逻辑");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -223,7 +223,7 @@ public static class StoreExpBottlePurchaseService
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append("[StorePurchase] 已成功购买:");
|
||||
builder.Append(itemSO != null ? itemSO.itemName : "未知物品");
|
||||
builder.Append(itemSO != null ? itemSO.itemName : LocalizationService.Get("item.unknown.store_item", "Unknown Item"));
|
||||
builder.Append(" | 花费Coins=");
|
||||
builder.Append(totalCost);
|
||||
builder.Append(" | 发放数量=");
|
||||
|
||||
@@ -71,11 +71,21 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
|
||||
|
||||
initialized = true;
|
||||
StoreOwnershipPayload payload;
|
||||
StoreOwnershipStorage.TryLoad(out payload);
|
||||
bool loadedFromSave = StoreOwnershipStorage.TryLoad(out payload);
|
||||
RebuildFromPayload(payload);
|
||||
LoadStoreItems();
|
||||
SyncAllMirrorFlags();
|
||||
SaveNow();
|
||||
|
||||
bool recoveredFromMirrors = false;
|
||||
if (!loadedFromSave)
|
||||
{
|
||||
recoveredFromMirrors = SeedFromCurrentMirrorFlags();
|
||||
}
|
||||
|
||||
if (loadedFromSave || recoveredFromMirrors)
|
||||
{
|
||||
SyncAllMirrorFlags();
|
||||
SaveNow();
|
||||
}
|
||||
}
|
||||
|
||||
public void ForceSyncMirrorFlags()
|
||||
@@ -132,7 +142,7 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
|
||||
break;
|
||||
|
||||
default:
|
||||
failureMessage = "当前未配置发放逻辑";
|
||||
failureMessage = LocalizationService.Get("store.error.grant_logic_missing", "当前未配置发放逻辑");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -149,7 +159,7 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
|
||||
|
||||
if (itemSO == null)
|
||||
{
|
||||
failureMessage = "商品数据丢失";
|
||||
failureMessage = LocalizationService.Get("store.error.item_data_missing", "商品数据丢失");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -255,7 +265,7 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
private void SeedFromCurrentMirrorFlags()
|
||||
private bool SeedFromCurrentMirrorFlags()
|
||||
{
|
||||
bool changed = false;
|
||||
for (int i = 0; i < cachedStoreItems.Count; i++)
|
||||
@@ -276,10 +286,7 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
SaveNow();
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
private StoreOwnershipEntry TrySeedEntry(storeItemSO itemSO)
|
||||
@@ -446,13 +453,13 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
|
||||
failureMessage = string.Empty;
|
||||
if (itemSO == null)
|
||||
{
|
||||
failureMessage = "商品数据丢失";
|
||||
failureMessage = LocalizationService.Get("store.error.item_data_missing", "商品数据丢失");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsOwned(itemSO))
|
||||
{
|
||||
failureMessage = "物品已解锁";
|
||||
failureMessage = LocalizationService.Get("store.error.item_already_unlocked", "物品已解锁");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -461,7 +468,7 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
|
||||
case storeItemSO.ItemType.character:
|
||||
if (itemSO.associatedAllyHero == null)
|
||||
{
|
||||
failureMessage = "角色数据未配置";
|
||||
failureMessage = LocalizationService.Get("store.error.character_data_missing", "角色数据未配置");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -469,7 +476,7 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
|
||||
case storeItemSO.ItemType.song:
|
||||
if (itemSO.associatedSong == null)
|
||||
{
|
||||
failureMessage = "歌曲数据未配置";
|
||||
failureMessage = LocalizationService.Get("store.error.song_data_missing", "歌曲数据未配置");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -477,7 +484,7 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
|
||||
case storeItemSO.ItemType.storyPassage:
|
||||
if (itemSO.associatedStoryPassage == null)
|
||||
{
|
||||
failureMessage = "剧情数据未配置";
|
||||
failureMessage = LocalizationService.Get("store.error.story_data_missing", "剧情数据未配置");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -485,14 +492,14 @@ public sealed class StoreOwnershipLedger : MonoBehaviour
|
||||
{
|
||||
if (itemSO.associatedStorySonId < 0 || FindStorySonIndex(itemSO.associatedStoryPassage, itemSO.associatedStorySonId) < 0)
|
||||
{
|
||||
failureMessage = "剧情子章节未配置";
|
||||
failureMessage = LocalizationService.Get("store.error.story_chapter_missing", "剧情子章节未配置");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
failureMessage = "当前未配置发放逻辑";
|
||||
failureMessage = LocalizationService.Get("store.error.grant_logic_missing", "当前未配置发放逻辑");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user