1274 lines
39 KiB
C#
1274 lines
39 KiB
C#
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 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;
|
|
public Button closeBagButton;
|
|
|
|
[Header("right panel")]
|
|
public Image btmImg;
|
|
public Image itemDprofile;
|
|
public TextMeshProUGUI itemAmount;
|
|
public Text itemName;
|
|
public Text itemUse;
|
|
public Text itemDescription;
|
|
|
|
[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 Color fallbackColor = Color.white;
|
|
public Sprite fallbackItemIcon;
|
|
public ItemRarityColorConfigSO rarityColorConfig;
|
|
|
|
[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<GameObject> spawnedItems = new List<GameObject>();
|
|
private readonly List<BagEntry> allEntries = new List<BagEntry>();
|
|
private readonly Dictionary<uBagItemPrefab, BagEntry> viewEntries = new Dictionary<uBagItemPrefab, BagEntry>();
|
|
private Coroutine rebuildRoutine;
|
|
private BagCategory currentCategory = BagCategory.All;
|
|
private GameObject spawnedIntroduction;
|
|
private uBagItemPrefab activePreviewSource;
|
|
|
|
private void Awake()
|
|
{
|
|
InitializeToggleGroup();
|
|
BindToggles();
|
|
BindCloseButton();
|
|
BindLedgerEvents();
|
|
InitializeViewMode();
|
|
SelectDefaultCategory();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
InitializeToggleGroup();
|
|
BindToggles();
|
|
BindCloseButton();
|
|
BindLedgerEvents();
|
|
InitializeViewMode();
|
|
Rebuild();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
UnbindToggle(allToggle, HandleAllChanged);
|
|
UnbindToggle(cultivateToggle, HandleCultivateChanged);
|
|
UnbindToggle(preciousToggle, HandlePreciousChanged);
|
|
UnbindToggle(otherToggle, HandleOtherChanged);
|
|
UnbindToggle(equipSystemToggle, HandleEquipSystemChanged);
|
|
UnbindCloseButton();
|
|
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 BindCloseButton()
|
|
{
|
|
if (closeBagButton == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
closeBagButton.onClick.RemoveListener(HandleCloseBagClicked);
|
|
closeBagButton.onClick.AddListener(HandleCloseBagClicked);
|
|
}
|
|
|
|
private void UnbindCloseButton()
|
|
{
|
|
if (closeBagButton == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
closeBagButton.onClick.RemoveListener(HandleCloseBagClicked);
|
|
}
|
|
|
|
private void RebindToggle(Toggle toggle, UnityAction<bool> action)
|
|
{
|
|
if (toggle == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
toggle.onValueChanged.RemoveListener(action);
|
|
toggle.onValueChanged.AddListener(action);
|
|
}
|
|
|
|
private void UnbindToggle(Toggle toggle, UnityAction<bool> 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 HandleCloseBagClicked()
|
|
{
|
|
Destroy(gameObject);
|
|
}
|
|
|
|
private void ApplyCategory(BagCategory category)
|
|
{
|
|
currentCategory = category;
|
|
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<bagItemPreviewPrefab>();
|
|
if (previewView != null)
|
|
{
|
|
Sprite icon = entry.icon != null ? entry.icon : fallbackItemIcon;
|
|
Color color = rarityColorConfig != null
|
|
? rarityColorConfig.GetColor(entry.rarity, fallbackColor)
|
|
: fallbackColor;
|
|
previewView.Bind(color, 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<storeItemSO> storeItems = new List<storeItemSO>(LoadStoreItems());
|
|
AddExpBottleEntries(storeItems);
|
|
AddGrowthMaterialEntries(storeItems);
|
|
AddEquipmentConsumableEntries(storeItems);
|
|
AddNotebookPreciousEntries();
|
|
AddOwnedStorePreciousEntries(storeItems);
|
|
allEntries.Sort(CompareEntries);
|
|
}
|
|
|
|
private void AddExpBottleEntries(List<storeItemSO> 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 = ResolveExpBottleUsage(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<storeItemSO> 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 = ResolveGrowthMaterialUsage(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<storeItemSO> 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 = ResolveEquipmentConsumableUsage(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<string>();
|
|
foreach (var story in LoadNotebookDefinitions())
|
|
{
|
|
if (story == null || !story.isUnlocked)
|
|
{
|
|
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 = ResolveNotebookUsage(story),
|
|
description = story.class_description,
|
|
icon = ResolvePreferredSprite(story.class_image, fallbackItemIcon),
|
|
category = BagCategory.Precious,
|
|
sortPriority = 100,
|
|
rarity = story.itemRarity
|
|
});
|
|
}
|
|
}
|
|
|
|
private void AddOwnedStorePreciousEntries(List<storeItemSO> 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 = kind == PreciousKind.Character ? "角色" : "关卡",
|
|
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<BagEntry> 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<BagEntry> FilterEntries()
|
|
{
|
|
var result = new List<BagEntry>();
|
|
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);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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<uBagItemPrefab>();
|
|
if (prefabView != null)
|
|
{
|
|
Sprite icon = entry.icon != null ? entry.icon : fallbackItemIcon;
|
|
Color color = rarityColorConfig != null
|
|
? rarityColorConfig.GetColor(entry.rarity, fallbackColor)
|
|
: fallbackColor;
|
|
prefabView.Bind(this, icon, entry.countText, color, 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;
|
|
|
|
if (btmImg != null)
|
|
{
|
|
btmImg.color = color;
|
|
}
|
|
|
|
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.text = entry.displayName ?? string.Empty;
|
|
}
|
|
|
|
if (itemUse != null)
|
|
{
|
|
itemUse.text = entry.usage ?? string.Empty;
|
|
}
|
|
|
|
if (itemDescription != null)
|
|
{
|
|
itemDescription.text = entry.description ?? string.Empty;
|
|
}
|
|
}
|
|
|
|
private void ClearRightPanel()
|
|
{
|
|
if (btmImg != null)
|
|
{
|
|
btmImg.color = fallbackColor;
|
|
}
|
|
|
|
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.text = string.Empty;
|
|
}
|
|
|
|
if (itemUse != null)
|
|
{
|
|
itemUse.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 expBottlesSO[] LoadExpBottleDefinitions()
|
|
{
|
|
var result = new List<expBottlesSO>();
|
|
#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<expBottlesSO>(path);
|
|
if (asset != null)
|
|
{
|
|
result.Add(asset);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
result.AddRange(Resources.LoadAll<expBottlesSO>(string.Empty));
|
|
return result.ToArray();
|
|
}
|
|
|
|
private growthMaterialSO[] LoadGrowthMaterialDefinitions()
|
|
{
|
|
var result = new List<growthMaterialSO>();
|
|
#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<growthMaterialSO>(path);
|
|
if (asset != null)
|
|
{
|
|
result.Add(asset);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
result.AddRange(Resources.LoadAll<growthMaterialSO>(string.Empty));
|
|
return result.ToArray();
|
|
}
|
|
|
|
private equipmentConsumableSO[] LoadEquipmentConsumableDefinitions()
|
|
{
|
|
var result = new List<equipmentConsumableSO>();
|
|
#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<equipmentConsumableSO>(path);
|
|
if (asset != null)
|
|
{
|
|
result.Add(asset);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
result.AddRange(Resources.LoadAll<equipmentConsumableSO>(string.Empty));
|
|
return result.ToArray();
|
|
}
|
|
|
|
private IEnumerable<notebook_faterType> LoadNotebookDefinitions()
|
|
{
|
|
var result = new List<notebook_faterType>();
|
|
var seen = new HashSet<int>();
|
|
|
|
#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<notebook_faterType>(path);
|
|
if (asset != null && seen.Add(asset.class_id))
|
|
{
|
|
result.Add(asset);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!string.IsNullOrEmpty(notebookRuntimePath))
|
|
{
|
|
notebook_faterType[] resources = Resources.LoadAll<notebook_faterType>(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<storeItemSO> LoadStoreItems()
|
|
{
|
|
var result = new List<storeItemSO>();
|
|
var seen = new HashSet<int>();
|
|
|
|
#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<storeItemSO>(path);
|
|
if (asset != null && seen.Add(asset.itemID))
|
|
{
|
|
result.Add(asset);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!string.IsNullOrEmpty(storeItemRuntimePath))
|
|
{
|
|
storeItemSO[] resources = Resources.LoadAll<storeItemSO>(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<storeItemSO> 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<storeItemSO> 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<storeItemSO> 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 string ResolveNotebookUsage(notebook_faterType story)
|
|
{
|
|
if (story == null)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
switch (story.fatherType)
|
|
{
|
|
case notebook_faterType.notebook_fatherType.idols:
|
|
return "角色线索";
|
|
case notebook_faterType.notebook_fatherType.content:
|
|
return "故事线索";
|
|
default:
|
|
return "珍贵物品";
|
|
}
|
|
}
|
|
|
|
private static string ResolveExpBottleUsage(expBottlesSO definition)
|
|
{
|
|
if (definition != null && !string.IsNullOrWhiteSpace(definition.expBottleUsage))
|
|
{
|
|
return definition.expBottleUsage;
|
|
}
|
|
|
|
return "角色培养";
|
|
}
|
|
|
|
private static string ResolveGrowthMaterialUsage(growthMaterialSO definition)
|
|
{
|
|
if (definition != null && !string.IsNullOrWhiteSpace(definition.growthMaterialUsage))
|
|
{
|
|
return definition.growthMaterialUsage;
|
|
}
|
|
|
|
return "角色突破";
|
|
}
|
|
|
|
private static string ResolveEquipmentConsumableUsage(equipmentConsumableSO definition)
|
|
{
|
|
if (definition != null && !string.IsNullOrWhiteSpace(definition.consumableUsage))
|
|
{
|
|
return definition.consumableUsage;
|
|
}
|
|
|
|
if (definition == null)
|
|
{
|
|
return "装备材料";
|
|
}
|
|
|
|
switch (definition.consumableType)
|
|
{
|
|
case equipmentConsumableSO.EquipmentConsumableType.UpgradeMaterial:
|
|
return "装备升级";
|
|
case equipmentConsumableSO.EquipmentConsumableType.BreakthroughMaterial:
|
|
return "装备幻化";
|
|
case equipmentConsumableSO.EquipmentConsumableType.TransferMaterial:
|
|
return "装备洗炼";
|
|
case equipmentConsumableSO.EquipmentConsumableType.FinalDreamMaterial:
|
|
return "梦醒登顶";
|
|
default:
|
|
return "装备材料";
|
|
}
|
|
}
|
|
|
|
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<Canvas>();
|
|
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<Canvas>();
|
|
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;
|
|
}
|
|
}
|
|
}
|