using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using TMPro; #if UNITY_EDITOR using UnityEditor; #endif public class uBag : MonoBehaviour { private enum BagFilterMode { All, Cultivation, Precious, Other } private enum BagSortMode { Default, Name, Rarity, Count } private enum BagCategory { All, Cultivation, Precious, Other } private enum PreciousKind { NotebookClue, Character, Level } private sealed class BagEntry { public string stableId; public string displayName; public string countText; public string amountText; public string usage; public string description; public Sprite icon; public BagCategory category; public int sortPriority; public ItemRarity rarity; } [Header("category toggles")] public ToggleGroup bagToggleGroup; public Toggle allToggle; public Toggle cultivateToggle; public Toggle preciousToggle; public Toggle otherToggle; public Toggle equipSystemToggle; [Header("OBJ")] public GameObject bagObj; public GameObject equipObj; [Header("right panel")] public Image btmImg; public Image itemDprofile; public TextMeshProUGUI itemAmount; public Text itemName; public Text itemUse; public Text itemUsageLegacy; public Text itemDescription; public Image bottom_rarityImg; public Sprite[] bottom_rarityImg_sprites; [Header("list")] public Transform itemParent; public GameObject itemPrefab; public Text emptyText; [Min(1)] public int spawnBatchSize = 16; [Header("introduction")] public GameObject itemIntroductionPrefab; public float introductionHoverDelay = 0.5f; public int introductionHorizontalOffset = 12; [Header("colors")] public bool useRarityColorForItemName = true; public Color fallbackColor = Color.white; public Sprite fallbackItemIcon; public Sprite fallbackBackgroundSprite; public ItemRarityColorConfigSO rarityColorConfig; [Header("filters")] public Dropdown bag_filterDropdown; public Dropdown bag_sortDropdown; [Header("paths")] [SerializeField] private string notebookEditorPath = "Assets/Resources/so/notebook"; [SerializeField] private string notebookRuntimePath = "so/notebook"; [SerializeField] private string storeItemEditorPath = "Assets/Resources/so/storeSO"; [SerializeField] private string storeItemRuntimePath = "so/storeSO"; [SerializeField] private string expBottleEditorPath = "Assets/storeSystem/items/medicines"; [SerializeField] private string growthMaterialEditorPath = "Assets/storeSystem/items/medicines"; [SerializeField] private string equipmentConsumableEditorPath = "Assets/storeSystem/items/medicines"; private readonly List spawnedItems = new List(); private readonly List allEntries = new List(); private readonly Dictionary viewEntries = new Dictionary(); private readonly List filterOptions = new List(); private readonly List sortOptions = new List(); private Coroutine rebuildRoutine; private BagCategory currentCategory = BagCategory.All; private BagFilterMode currentFilterMode = BagFilterMode.All; private BagSortMode currentSortMode = BagSortMode.Default; private GameObject spawnedIntroduction; private uBagItemPrefab activePreviewSource; private void Awake() { InitializeToggleGroup(); InitializeFilterDropdowns(); BindToggles(); BindFilterDropdowns(); BindLedgerEvents(); InitializeViewMode(); SelectDefaultCategory(); } private void OnEnable() { InitializeToggleGroup(); InitializeFilterDropdowns(); BindToggles(); BindFilterDropdowns(); BindLedgerEvents(); InitializeViewMode(); Rebuild(); } private void OnDisable() { UnbindToggle(allToggle, HandleAllChanged); UnbindToggle(cultivateToggle, HandleCultivateChanged); UnbindToggle(preciousToggle, HandlePreciousChanged); UnbindToggle(otherToggle, HandleOtherChanged); UnbindToggle(equipSystemToggle, HandleEquipSystemChanged); UnbindDropdown(bag_filterDropdown, HandleFilterDropdownChanged); UnbindDropdown(bag_sortDropdown, HandleSortDropdownChanged); UnbindLedgerEvents(); HideCurrentPreview(); } private void OnDestroy() { btmandtopController.RefreshOverlayVisibilityState(); } private void InitializeToggleGroup() { if (bagToggleGroup == null) { return; } bagToggleGroup.allowSwitchOff = false; AssignToggleGroup(allToggle); AssignToggleGroup(cultivateToggle); AssignToggleGroup(preciousToggle); AssignToggleGroup(otherToggle); } private void AssignToggleGroup(Toggle toggle) { if (toggle == null || bagToggleGroup == null) { return; } toggle.group = bagToggleGroup; } private void BindToggles() { RebindToggle(allToggle, HandleAllChanged); RebindToggle(cultivateToggle, HandleCultivateChanged); RebindToggle(preciousToggle, HandlePreciousChanged); RebindToggle(otherToggle, HandleOtherChanged); RebindToggle(equipSystemToggle, HandleEquipSystemChanged); } private void InitializeFilterDropdowns() { InitializeFilterDropdown(bag_filterDropdown, filterOptions, new[] { "全部", "培养", "珍贵", "其他" }); InitializeFilterDropdown(bag_sortDropdown, sortOptions, new[] { "默认", "名称", "稀有度", "数量" }); } private void InitializeFilterDropdown(Dropdown dropdown, List cache, string[] labels) { if (dropdown == null) { return; } bool wasActive = dropdown.gameObject.activeInHierarchy; cache.Clear(); for (int i = 0; i < labels.Length; i++) { cache.Add(new Dropdown.OptionData(labels[i])); } dropdown.ClearOptions(); dropdown.AddOptions(cache); if (dropdown.value < 0 || dropdown.value >= labels.Length) { dropdown.value = 0; } dropdown.RefreshShownValue(); dropdown.gameObject.SetActive(wasActive); } private void BindFilterDropdowns() { RebindDropdown(bag_filterDropdown, HandleFilterDropdownChanged); RebindDropdown(bag_sortDropdown, HandleSortDropdownChanged); } private void RebindDropdown(Dropdown dropdown, UnityAction action) { if (dropdown == null) { return; } dropdown.onValueChanged.RemoveListener(action); dropdown.onValueChanged.AddListener(action); } private void UnbindDropdown(Dropdown dropdown, UnityAction action) { if (dropdown == null) { return; } dropdown.onValueChanged.RemoveListener(action); } private void HandleFilterDropdownChanged(int value) { currentFilterMode = (BagFilterMode)Mathf.Clamp(value, 0, 3); currentCategory = (BagCategory)currentFilterMode; Rebuild(); } private void HandleSortDropdownChanged(int value) { currentSortMode = (BagSortMode)Mathf.Clamp(value, 0, 3); Rebuild(); } private void RebindToggle(Toggle toggle, UnityAction action) { if (toggle == null) { return; } toggle.onValueChanged.RemoveListener(action); toggle.onValueChanged.AddListener(action); } private void UnbindToggle(Toggle toggle, UnityAction action) { if (toggle == null) { return; } toggle.onValueChanged.RemoveListener(action); } private void BindLedgerEvents() { ExpBottleLedger.EnsureInstance().OnBottleCountChanged -= HandleInventoryChanged; ExpBottleLedger.EnsureInstance().OnBottleCountChanged += HandleInventoryChanged; ExpBottleLedger.EnsureInstance().OnLedgerReloaded -= HandleInventoryReloaded; ExpBottleLedger.EnsureInstance().OnLedgerReloaded += HandleInventoryReloaded; DushMaterialLedger.EnsureInstance().OnMaterialCountChanged -= HandleGrowthMaterialChanged; DushMaterialLedger.EnsureInstance().OnMaterialCountChanged += HandleGrowthMaterialChanged; EquipmentConsumableLedger.EnsureInstance().OnCountChanged -= HandleEquipmentConsumableChanged; EquipmentConsumableLedger.EnsureInstance().OnCountChanged += HandleEquipmentConsumableChanged; } private void UnbindLedgerEvents() { ExpBottleLedger.EnsureInstance().OnBottleCountChanged -= HandleInventoryChanged; ExpBottleLedger.EnsureInstance().OnLedgerReloaded -= HandleInventoryReloaded; DushMaterialLedger.EnsureInstance().OnMaterialCountChanged -= HandleGrowthMaterialChanged; EquipmentConsumableLedger.EnsureInstance().OnCountChanged -= HandleEquipmentConsumableChanged; } private void SelectDefaultCategory() { if (allToggle != null) { allToggle.isOn = true; ApplyCategory(BagCategory.All); return; } ApplyCategory(BagCategory.All); } private void HandleAllChanged(bool isOn) { if (isOn) { ApplyCategory(BagCategory.All); } } private void HandleCultivateChanged(bool isOn) { if (isOn) { ApplyCategory(BagCategory.Cultivation); } } private void HandlePreciousChanged(bool isOn) { if (isOn) { ApplyCategory(BagCategory.Precious); } } private void HandleOtherChanged(bool isOn) { if (isOn) { ApplyCategory(BagCategory.Other); } } private void HandleEquipSystemChanged(bool isOn) { SetObjectActive(bagObj, !isOn); SetObjectActive(equipObj, isOn); } private void ApplyCategory(BagCategory category) { currentCategory = category; currentFilterMode = (BagFilterMode)category; if (bag_filterDropdown != null) { bag_filterDropdown.SetValueWithoutNotify((int)currentFilterMode); bag_filterDropdown.RefreshShownValue(); } SetToggleInteractable(allToggle, category != BagCategory.All); SetToggleInteractable(cultivateToggle, category != BagCategory.Cultivation); SetToggleInteractable(preciousToggle, category != BagCategory.Precious); SetToggleInteractable(otherToggle, category != BagCategory.Other); Rebuild(); } private void SetToggleInteractable(Toggle toggle, bool interactable) { if (toggle != null) { toggle.interactable = interactable; } } private void SetObjectActive(GameObject target, bool active) { if (target != null) { target.SetActive(active); } } private void InitializeViewMode() { if (equipSystemToggle != null) { equipSystemToggle.onValueChanged.RemoveListener(HandleEquipSystemChanged); equipSystemToggle.onValueChanged.AddListener(HandleEquipSystemChanged); HandleEquipSystemChanged(equipSystemToggle.isOn); return; } SetObjectActive(bagObj, true); SetObjectActive(equipObj, false); } private void HandleInventoryChanged(ExpBottleKind _, int __) { Rebuild(); } private void HandleInventoryReloaded() { Rebuild(); } private void HandleGrowthMaterialChanged(DushMaterialKind _, int __) { Rebuild(); } private void HandleEquipmentConsumableChanged(EquipmentConsumableKind _, int __) { Rebuild(); } public void Rebuild() { BuildEntries(); if (rebuildRoutine != null) { StopCoroutine(rebuildRoutine); rebuildRoutine = null; } ClearSpawnedItems(); if (!isActiveAndEnabled) { return; } ClearRightPanel(); rebuildRoutine = StartCoroutine(RebuildAsync()); } public void SelectItemForDetail(uBagItemPrefab source) { if (source == null) { return; } if (!viewEntries.TryGetValue(source, out BagEntry entry) || entry == null) { ClearRightPanel(); return; } ApplyRightPanel(entry); } public void ShowPreviewFor(uBagItemPrefab source) { if (source == null || itemIntroductionPrefab == null) { return; } if (!viewEntries.TryGetValue(source, out BagEntry entry) || entry == null) { return; } HideCurrentPreview(); Transform previewParent = ResolveIntroductionParent(source); if (previewParent == null) { return; } spawnedIntroduction = Instantiate(itemIntroductionPrefab, previewParent); spawnedIntroduction.name = $"uBagPreview_{entry.stableId}"; spawnedIntroduction.SetActive(true); spawnedIntroduction.transform.SetAsLastSibling(); activePreviewSource = source; var previewView = spawnedIntroduction.GetComponent(); if (previewView != null) { Sprite icon = entry.icon != null ? entry.icon : fallbackItemIcon; Color color = rarityColorConfig != null ? rarityColorConfig.GetColor(entry.rarity, fallbackColor) : fallbackColor; Sprite backgroundSprite = rarityColorConfig != null ? rarityColorConfig.GetBackgroundSprite(entry.rarity, fallbackBackgroundSprite) : fallbackBackgroundSprite; previewView.Bind(color, backgroundSprite, icon, entry.displayName, entry.usage, entry.description, entry.amountText); } PositionIntroduction(source, spawnedIntroduction); } public void HidePreviewFor(uBagItemPrefab source) { if (source != null && source != activePreviewSource) { return; } HideCurrentPreview(); } private void BuildEntries() { allEntries.Clear(); List storeItems = new List(LoadStoreItems()); AddExpBottleEntries(storeItems); AddGrowthMaterialEntries(storeItems); AddEquipmentConsumableEntries(storeItems); AddNotebookPreciousEntries(); AddOwnedStorePreciousEntries(storeItems); allEntries.Sort(CompareEntries); ApplySortMode(allEntries); } private void AddExpBottleEntries(List storeItems) { var ledger = ExpBottleLedger.EnsureInstance(); var definitions = LoadExpBottleDefinitions(); foreach (var descriptor in ExpBottleCatalog.All) { int count = ledger.GetCount(descriptor.Kind); if (count <= 0) { continue; } expBottlesSO definition = FindExpBottleDefinition(definitions, descriptor.Kind); storeItemSO storeItem = FindStoreItemForExpBottle(storeItems, descriptor.Kind); expBottlesSO sourceDefinition = storeItem != null && storeItem.associatedExpBottle != null ? storeItem.associatedExpBottle : definition; allEntries.Add(new BagEntry { stableId = descriptor.Key, displayName = storeItem != null && !string.IsNullOrEmpty(storeItem.itemName) ? storeItem.itemName : definition != null && !string.IsNullOrEmpty(definition.expBottleName) ? definition.expBottleName : descriptor.DisplayName, countText = count.ToString(), amountText = count.ToString(), usage = ItemUsageTagUtility.ResolveDisplayName(sourceDefinition), description = BuildPreferredDescription( storeItem != null ? storeItem.itemDetailedDescription : null, sourceDefinition != null ? sourceDefinition.expBottleDescription : null, storeItem != null ? storeItem.itemDescription : null), icon = ResolvePreferredSprite( storeItem != null ? storeItem.itemIcon : null, sourceDefinition != null ? sourceDefinition.expBottleSprite : null, fallbackItemIcon), category = BagCategory.Cultivation, sortPriority = 10, rarity = storeItem != null ? storeItem.itemRarity : sourceDefinition != null ? sourceDefinition.itemRarity : ItemRarity.Common }); } } private void AddGrowthMaterialEntries(List storeItems) { var ledger = DushMaterialLedger.EnsureInstance(); var definitions = LoadGrowthMaterialDefinitions(); foreach (var descriptor in DushMaterialCatalog.All) { int count = ledger.GetCount(descriptor.Kind); if (count <= 0) { continue; } growthMaterialSO definition = FindGrowthMaterialDefinition(definitions, descriptor.Kind); storeItemSO storeItem = FindStoreItemForGrowthMaterial(storeItems, descriptor.Kind); growthMaterialSO sourceDefinition = storeItem != null && storeItem.associatedGrowthMaterial != null ? storeItem.associatedGrowthMaterial : definition; allEntries.Add(new BagEntry { stableId = descriptor.Key, displayName = storeItem != null && !string.IsNullOrEmpty(storeItem.itemName) ? storeItem.itemName : definition != null && !string.IsNullOrEmpty(definition.growthMaterialName) ? definition.growthMaterialName : descriptor.DisplayName, countText = count.ToString(), amountText = count.ToString(), usage = ItemUsageTagUtility.ResolveDisplayName(sourceDefinition), description = BuildPreferredDescription( storeItem != null ? storeItem.itemDetailedDescription : null, sourceDefinition != null ? sourceDefinition.growthMaterialDescription : null, storeItem != null ? storeItem.itemDescription : null), icon = ResolvePreferredSprite( storeItem != null ? storeItem.itemIcon : null, sourceDefinition != null ? sourceDefinition.growthMaterialSprite : null, fallbackItemIcon), category = BagCategory.Cultivation, sortPriority = 20, rarity = storeItem != null ? storeItem.itemRarity : sourceDefinition != null ? sourceDefinition.itemRarity : ItemRarity.Uncommon }); } } private void AddEquipmentConsumableEntries(List storeItems) { var ledger = EquipmentConsumableLedger.EnsureInstance(); var definitions = LoadEquipmentConsumableDefinitions(); foreach (var descriptor in EquipmentConsumableCatalog.All) { int count = ledger.GetCount(descriptor.Kind); if (count <= 0) { continue; } equipmentConsumableSO definition = FindEquipmentConsumableDefinition(definitions, descriptor.Kind); storeItemSO storeItem = FindStoreItemForEquipmentConsumable(storeItems, descriptor.Kind); equipmentConsumableSO sourceDefinition = storeItem != null && storeItem.associatedEquipmentConsumable != null ? storeItem.associatedEquipmentConsumable : definition; allEntries.Add(new BagEntry { stableId = descriptor.Key, displayName = storeItem != null && !string.IsNullOrEmpty(storeItem.itemName) ? storeItem.itemName : definition != null && !string.IsNullOrEmpty(definition.consumableName) ? definition.consumableName : descriptor.DisplayName, countText = count.ToString(), amountText = count.ToString(), usage = ItemUsageTagUtility.ResolveDisplayName(sourceDefinition), description = BuildPreferredDescription( storeItem != null ? storeItem.itemDetailedDescription : null, sourceDefinition != null ? sourceDefinition.consumableDescription : null, storeItem != null ? storeItem.itemDescription : null), icon = ResolvePreferredSprite( storeItem != null ? storeItem.itemIcon : null, sourceDefinition != null ? sourceDefinition.consumableSprite : null, fallbackItemIcon), category = BagCategory.Cultivation, sortPriority = 30, rarity = storeItem != null ? storeItem.itemRarity : sourceDefinition != null ? sourceDefinition.itemRarity : ItemRarity.Uncommon }); } } private void AddNotebookPreciousEntries() { var seenIds = new HashSet(); foreach (var story in LoadNotebookDefinitions()) { if (story == null || !story.isUnlocked) { continue; } if (story.fatherType == notebook_faterType.notebook_fatherType.content && !story.showInBag) { continue; } string stableId = $"notebook_{story.class_id}"; if (!seenIds.Add(stableId)) { continue; } allEntries.Add(new BagEntry { stableId = stableId, displayName = story.class_name, countText = "1", amountText = "1", usage = ItemUsageTagUtility.ResolveDisplayName(story), description = story.class_description, icon = ResolvePreferredSprite(story.class_image, fallbackItemIcon), category = BagCategory.Precious, sortPriority = 100, rarity = story.itemRarity }); } } private void AddOwnedStorePreciousEntries(List storeItems) { var ownership = StoreOwnershipLedger.EnsureInstance(); foreach (var item in storeItems) { if (item == null || !ownership.IsOwned(item)) { continue; } if (item.itemType != storeItemSO.ItemType.character && item.itemType != storeItemSO.ItemType.song) { continue; } PreciousKind kind = item.itemType == storeItemSO.ItemType.character ? PreciousKind.Character : PreciousKind.Level; allEntries.Add(new BagEntry { stableId = $"store_{item.itemID}", displayName = item.itemName, countText = "1", amountText = "1", usage = ItemUsageTagUtility.ResolveDisplayName(item), description = BuildPreferredDescription(item.itemDetailedDescription, item.itemDescription), icon = ResolvePreferredSprite(item.itemIcon, fallbackItemIcon), category = BagCategory.Precious, sortPriority = kind == PreciousKind.Character ? 110 : 120, rarity = item.itemRarity }); } } private IEnumerator RebuildAsync() { List visibleEntries = FilterEntries(); UpdateEmptyState(visibleEntries.Count == 0); uBagItemPrefab firstView = null; int spawnedInBatch = 0; for (int i = 0; i < visibleEntries.Count; i++) { uBagItemPrefab spawnedView = SpawnEntry(visibleEntries[i]); if (firstView == null && spawnedView != null) { firstView = spawnedView; } spawnedInBatch++; if (spawnedInBatch >= spawnBatchSize) { spawnedInBatch = 0; yield return null; } } if (firstView != null) { SelectItemForDetail(firstView); } else { ClearRightPanel(); } rebuildRoutine = null; } private List FilterEntries() { var result = new List(); for (int i = 0; i < allEntries.Count; i++) { BagEntry entry = allEntries[i]; if (entry == null) { continue; } if (currentCategory != BagCategory.All && entry.category != currentCategory) { continue; } result.Add(entry); } ApplySortMode(result); return result; } private void ApplySortMode(List entries) { if (entries == null || entries.Count <= 1) { return; } switch (currentSortMode) { case BagSortMode.Name: entries.Sort((left, right) => string.CompareOrdinal( left != null ? left.displayName : string.Empty, right != null ? right.displayName : string.Empty)); break; case BagSortMode.Rarity: entries.Sort((left, right) => { int rarityResult = (left != null ? left.rarity : ItemRarity.None).CompareTo(right != null ? right.rarity : ItemRarity.None); if (rarityResult != 0) return rarityResult; return CompareEntries(left, right); }); break; case BagSortMode.Count: entries.Sort((left, right) => { int leftCount = ParseSafeInt(left != null ? left.amountText : null); int rightCount = ParseSafeInt(right != null ? right.amountText : null); int countResult = rightCount.CompareTo(leftCount); if (countResult != 0) return countResult; return CompareEntries(left, right); }); break; } } private static int ParseSafeInt(string value) { return int.TryParse(value, out int parsed) ? parsed : 0; } private uBagItemPrefab SpawnEntry(BagEntry entry) { if (entry == null || itemPrefab == null || itemParent == null) { return null; } GameObject instance = Instantiate(itemPrefab, itemParent); instance.name = $"uBagItem_{entry.stableId}"; spawnedItems.Add(instance); uBagItemPrefab prefabView = instance.GetComponent(); if (prefabView != null) { Sprite icon = entry.icon != null ? entry.icon : fallbackItemIcon; Color color = rarityColorConfig != null ? rarityColorConfig.GetColor(entry.rarity, fallbackColor) : fallbackColor; Sprite backgroundSprite = rarityColorConfig != null ? rarityColorConfig.GetBackgroundSprite(entry.rarity, fallbackBackgroundSprite) : fallbackBackgroundSprite; prefabView.Bind(this, icon, entry.countText, color, backgroundSprite, introductionHoverDelay); viewEntries[prefabView] = entry; } return prefabView; } private void ClearSpawnedItems() { HideCurrentPreview(); viewEntries.Clear(); for (int i = itemParent != null ? itemParent.childCount - 1 : -1; i >= 0; i--) { Transform child = itemParent.GetChild(i); if (child != null) { Destroy(child.gameObject); } } spawnedItems.Clear(); UpdateEmptyState(false); } private void UpdateEmptyState(bool visible) { if (emptyText != null) { emptyText.gameObject.SetActive(visible); if (visible) { emptyText.text = "暂无物品"; } } } private void ApplyRightPanel(BagEntry entry) { if (entry == null) { ClearRightPanel(); return; } Sprite icon = entry.icon != null ? entry.icon : fallbackItemIcon; Color color = rarityColorConfig != null ? rarityColorConfig.GetColor(entry.rarity, fallbackColor) : fallbackColor; Sprite backgroundSprite = rarityColorConfig != null ? rarityColorConfig.GetBackgroundSprite(entry.rarity, fallbackBackgroundSprite) : fallbackBackgroundSprite; if (btmImg != null) { btmImg.sprite = backgroundSprite; btmImg.color = backgroundSprite != null ? Color.white : color; } if (bottom_rarityImg != null) { Sprite raritySprite = ResolveBottomRaritySprite(entry.rarity); bottom_rarityImg.sprite = raritySprite; bottom_rarityImg.enabled = raritySprite != null; bottom_rarityImg.color = Color.white; } if (itemDprofile != null) { itemDprofile.sprite = icon; itemDprofile.enabled = icon != null; itemDprofile.color = Color.white; } if (itemAmount != null) { itemAmount.text = entry.amountText ?? string.Empty; } if (itemName != null) { itemName.supportRichText = false; itemName.text = StripRichTextTags(entry.displayName); itemName.color = useRarityColorForItemName ? color : Color.white; } if (itemUse != null) { itemUse.text = entry.usage ?? string.Empty; } if (itemUsageLegacy != null) { itemUsageLegacy.text = entry.usage ?? string.Empty; } if (itemDescription != null) { itemDescription.text = entry.description ?? string.Empty; } } private void ClearRightPanel() { if (btmImg != null) { btmImg.sprite = fallbackBackgroundSprite; btmImg.color = fallbackBackgroundSprite != null ? Color.white : fallbackColor; } if (bottom_rarityImg != null) { Sprite fallbackRaritySprite = GetBottomRarityFallbackSprite(); bottom_rarityImg.sprite = fallbackRaritySprite; bottom_rarityImg.enabled = fallbackRaritySprite != null; bottom_rarityImg.color = Color.white; } if (itemDprofile != null) { itemDprofile.sprite = fallbackItemIcon; itemDprofile.enabled = fallbackItemIcon != null; itemDprofile.color = Color.white; } if (itemAmount != null) { itemAmount.text = string.Empty; } if (itemName != null) { itemName.supportRichText = false; itemName.text = string.Empty; itemName.color = Color.white; } if (itemUse != null) { itemUse.text = string.Empty; } if (itemUsageLegacy != null) { itemUsageLegacy.text = string.Empty; } if (itemDescription != null) { itemDescription.text = string.Empty; } } private static int CompareEntries(BagEntry left, BagEntry right) { if (ReferenceEquals(left, right)) { return 0; } if (left == null) { return 1; } if (right == null) { return -1; } int categoryResult = left.category.CompareTo(right.category); if (categoryResult != 0) { return categoryResult; } int priorityResult = left.sortPriority.CompareTo(right.sortPriority); if (priorityResult != 0) { return priorityResult; } return string.CompareOrdinal(left.displayName, right.displayName); } private static string StripRichTextTags(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } bool insideTag = false; System.Text.StringBuilder builder = null; for (int i = 0; i < value.Length; i++) { char current = value[i]; if (current == '<') { if (!insideTag) { builder ??= new System.Text.StringBuilder(value.Length); } insideTag = true; continue; } if (current == '>') { insideTag = false; continue; } if (!insideTag) { if (builder != null) { builder.Append(current); } } } return builder != null ? builder.ToString() : value; } private Sprite ResolveBottomRaritySprite(ItemRarity rarity) { if (bottom_rarityImg_sprites != null) { if (bottom_rarityImg_sprites.Length >= System.Enum.GetValues(typeof(ItemRarity)).Length) { int enumIndex = (int)rarity; if (enumIndex >= 0 && enumIndex < bottom_rarityImg_sprites.Length) { Sprite directSprite = bottom_rarityImg_sprites[enumIndex]; if (directSprite != null) { return directSprite; } } } else if (rarity != ItemRarity.None) { int compactIndex = (int)rarity - 1; if (compactIndex >= 0 && compactIndex < bottom_rarityImg_sprites.Length) { Sprite compactSprite = bottom_rarityImg_sprites[compactIndex]; if (compactSprite != null) { return compactSprite; } } } } return GetBottomRarityFallbackSprite(); } private Sprite GetBottomRarityFallbackSprite() { if (rarityColorConfig != null) { Sprite noneSprite = rarityColorConfig.GetBackgroundSprite(ItemRarity.None, fallbackBackgroundSprite); if (noneSprite != null) { return noneSprite; } } return fallbackBackgroundSprite; } private expBottlesSO[] LoadExpBottleDefinitions() { var result = new List(); #if UNITY_EDITOR if (!Application.isPlaying && !string.IsNullOrEmpty(expBottleEditorPath)) { string[] guids = AssetDatabase.FindAssets("t:expBottlesSO", new[] { expBottleEditorPath }); for (int i = 0; i < guids.Length; i++) { string path = AssetDatabase.GUIDToAssetPath(guids[i]); expBottlesSO asset = AssetDatabase.LoadAssetAtPath(path); if (asset != null) { result.Add(asset); } } } #endif result.AddRange(Resources.LoadAll(string.Empty)); return result.ToArray(); } private growthMaterialSO[] LoadGrowthMaterialDefinitions() { var result = new List(); #if UNITY_EDITOR if (!Application.isPlaying && !string.IsNullOrEmpty(growthMaterialEditorPath)) { string[] guids = AssetDatabase.FindAssets("t:growthMaterialSO", new[] { growthMaterialEditorPath }); for (int i = 0; i < guids.Length; i++) { string path = AssetDatabase.GUIDToAssetPath(guids[i]); growthMaterialSO asset = AssetDatabase.LoadAssetAtPath(path); if (asset != null) { result.Add(asset); } } } #endif result.AddRange(Resources.LoadAll(string.Empty)); return result.ToArray(); } private equipmentConsumableSO[] LoadEquipmentConsumableDefinitions() { var result = new List(); #if UNITY_EDITOR if (!Application.isPlaying && !string.IsNullOrEmpty(equipmentConsumableEditorPath)) { string[] guids = AssetDatabase.FindAssets("t:equipmentConsumableSO", new[] { equipmentConsumableEditorPath }); for (int i = 0; i < guids.Length; i++) { string path = AssetDatabase.GUIDToAssetPath(guids[i]); equipmentConsumableSO asset = AssetDatabase.LoadAssetAtPath(path); if (asset != null) { result.Add(asset); } } } #endif result.AddRange(Resources.LoadAll(string.Empty)); return result.ToArray(); } private IEnumerable LoadNotebookDefinitions() { var result = new List(); var seen = new HashSet(); #if UNITY_EDITOR if (!Application.isPlaying && !string.IsNullOrEmpty(notebookEditorPath)) { string[] guids = AssetDatabase.FindAssets("t:notebook_faterType", new[] { notebookEditorPath }); for (int i = 0; i < guids.Length; i++) { string path = AssetDatabase.GUIDToAssetPath(guids[i]); notebook_faterType asset = AssetDatabase.LoadAssetAtPath(path); if (asset != null && seen.Add(asset.class_id)) { result.Add(asset); } } } #endif if (!string.IsNullOrEmpty(notebookRuntimePath)) { notebook_faterType[] resources = Resources.LoadAll(notebookRuntimePath); for (int i = 0; i < resources.Length; i++) { notebook_faterType asset = resources[i]; if (asset != null && seen.Add(asset.class_id)) { result.Add(asset); } } } return result; } private IEnumerable LoadStoreItems() { var result = new List(); var seen = new HashSet(); #if UNITY_EDITOR if (!Application.isPlaying && !string.IsNullOrEmpty(storeItemEditorPath)) { string[] guids = AssetDatabase.FindAssets("t:storeItemSO", new[] { storeItemEditorPath }); for (int i = 0; i < guids.Length; i++) { string path = AssetDatabase.GUIDToAssetPath(guids[i]); storeItemSO asset = AssetDatabase.LoadAssetAtPath(path); if (asset != null && seen.Add(asset.itemID)) { result.Add(asset); } } } #endif if (!string.IsNullOrEmpty(storeItemRuntimePath)) { storeItemSO[] resources = Resources.LoadAll(storeItemRuntimePath); for (int i = 0; i < resources.Length; i++) { storeItemSO asset = resources[i]; if (asset != null && seen.Add(asset.itemID)) { result.Add(asset); } } } return result; } private static expBottlesSO FindExpBottleDefinition(expBottlesSO[] definitions, ExpBottleKind kind) { if (definitions == null) { return null; } for (int i = 0; i < definitions.Length; i++) { if (definitions[i] != null && definitions[i].bottleKind == kind) { return definitions[i]; } } return null; } private static growthMaterialSO FindGrowthMaterialDefinition(growthMaterialSO[] definitions, DushMaterialKind kind) { if (definitions == null) { return null; } for (int i = 0; i < definitions.Length; i++) { if (definitions[i] != null && definitions[i].materialKind == kind) { return definitions[i]; } } return null; } private static equipmentConsumableSO FindEquipmentConsumableDefinition(equipmentConsumableSO[] definitions, EquipmentConsumableKind kind) { if (definitions == null) { return null; } for (int i = 0; i < definitions.Length; i++) { if (definitions[i] != null && definitions[i].consumableKind == kind) { return definitions[i]; } } return null; } private static storeItemSO FindStoreItemForExpBottle(List storeItems, ExpBottleKind kind) { if (storeItems == null) { return null; } for (int i = 0; i < storeItems.Count; i++) { storeItemSO item = storeItems[i]; if (item != null && item.itemType == storeItemSO.ItemType.consumable && item.associatedExpBottle != null && item.associatedExpBottle.bottleKind == kind) { return item; } } return null; } private static storeItemSO FindStoreItemForGrowthMaterial(List storeItems, DushMaterialKind kind) { if (storeItems == null) { return null; } for (int i = 0; i < storeItems.Count; i++) { storeItemSO item = storeItems[i]; if (item != null && item.itemType == storeItemSO.ItemType.consumable && item.associatedGrowthMaterial != null && item.associatedGrowthMaterial.materialKind == kind) { return item; } } return null; } private static storeItemSO FindStoreItemForEquipmentConsumable(List storeItems, EquipmentConsumableKind kind) { if (storeItems == null) { return null; } for (int i = 0; i < storeItems.Count; i++) { storeItemSO item = storeItems[i]; if (item != null && item.itemType == storeItemSO.ItemType.consumable && item.associatedEquipmentConsumable != null && item.associatedEquipmentConsumable.consumableKind == kind) { return item; } } return null; } private static Sprite ResolvePreferredSprite(params Sprite[] sprites) { for (int i = 0; i < sprites.Length; i++) { if (sprites[i] != null) { return sprites[i]; } } return null; } private static string BuildPreferredDescription(params string[] candidates) { for (int i = 0; i < candidates.Length; i++) { if (!string.IsNullOrWhiteSpace(candidates[i])) { return candidates[i]; } } return string.Empty; } private Transform ResolveIntroductionParent(uBagItemPrefab source) { if (source == null) { return itemParent; } Canvas canvas = source.GetComponentInParent(); if (canvas != null) { return canvas.transform; } return itemParent; } private void PositionIntroduction(uBagItemPrefab source, GameObject introduction) { if (source == null || introduction == null) { return; } RectTransform sourceRect = source.transform as RectTransform; RectTransform introductionRect = introduction.transform as RectTransform; Canvas canvas = introduction.GetComponentInParent(); RectTransform canvasRect = canvas != null ? canvas.transform as RectTransform : null; if (sourceRect == null || introductionRect == null || canvasRect == null) { return; } Canvas.ForceUpdateCanvases(); LayoutRebuilder.ForceRebuildLayoutImmediate(introductionRect); Vector3[] corners = new Vector3[4]; sourceRect.GetWorldCorners(corners); Vector3 sourceRightCenter = (corners[2] + corners[3]) * 0.5f; Camera uiCamera = canvas != null ? canvas.worldCamera : null; Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(uiCamera, sourceRightCenter); RectTransformUtility.ScreenPointToLocalPointInRectangle( canvasRect, screenPoint, uiCamera, out Vector2 localPoint); introductionRect.anchorMin = new Vector2(0.5f, 0.5f); introductionRect.anchorMax = new Vector2(0.5f, 0.5f); introductionRect.pivot = new Vector2(0f, 0.5f); introductionRect.anchoredPosition = localPoint + new Vector2(introductionHorizontalOffset, 0f); introductionRect.localScale = Vector3.one; introductionRect.localRotation = Quaternion.identity; } private void HideCurrentPreview() { activePreviewSource = null; if (spawnedIntroduction != null) { Destroy(spawnedIntroduction); spawnedIntroduction = null; } } }