using System; using System.Collections.Generic; using System.IO; using Bansonic; using UnityEngine; using UnityEngine.UI; public class storeSystem : MonoBehaviour { [Header("Player Data")] [SerializeField] private Player_SO playerData; [Header("list toggles")] public ToggleGroup toggleGroup; public Toggle allItems_toggle; public Toggle recentlyHot_toggle; public Toggle idols_toggle; public Toggle songs_toggle; public Toggle storyPassage_toggle; public Toggle consumable_toggle; public Toggle otherType_toggle; [Header("objects")] public GameObject itemPrefab; public Transform contentParent; [Header("paths")] [SerializeField] private string editorStoreItemPath; [SerializeField] private string runtimeStoreItemPath; [Header("sprites")] public Sprite coinSprite; public Sprite cannotBuySprite; [Header("selecting item purchase")] public Text p_itemName; public Image p_itemImage; public Image p_sumCostImage; public Text p_sumCostText; public Button p_iAmount_plus; public Button p_iAmount_minus; public InputField p_iAmount_input; public Button purchaseButton; public Text p_detailedDescriptionText; private const int MinPurchaseAmount = 1; private const int MaxPurchaseAmount = 999999; private readonly List cachedItems = new List(); private readonly List toggles = new List(); private StoreRuntimeSaveData persistedState = new StoreRuntimeSaveData(); private storeItemSO currentSelectedItem; private int currentSelectedItemId = -1; private int currentPurchaseAmount = MinPurchaseAmount; private bool suppressAmountInputCallback; private string SaveFilePath { get { return Path.Combine(Application.persistentDataPath, "storeSystem_state.json"); } } private void Awake() { CacheToggles(); RegisterToggleCallbacks(); RegisterPurchasePanelCallbacks(); } private void Start() { ResetPriceImageObjects(); SetDefaultToggleState(); LoadPersistedState(); LoadAllStoreItems(); ApplyPersistedStateToItems(); RefreshCurrentView(); } private void OnDestroy() { UnregisterToggleCallbacks(); UnregisterPurchasePanelCallbacks(); } private void CacheToggles() { toggles.Clear(); AddToggle(allItems_toggle); AddToggle(recentlyHot_toggle); AddToggle(idols_toggle); AddToggle(songs_toggle); AddToggle(storyPassage_toggle); AddToggle(consumable_toggle); AddToggle(otherType_toggle); } private void AddToggle(Toggle toggle) { if (toggle != null && !toggles.Contains(toggle)) { toggles.Add(toggle); } } private void RegisterToggleCallbacks() { for (int i = 0; i < toggles.Count; i++) { toggles[i].onValueChanged.AddListener(OnFilterToggleValueChanged); } } private void UnregisterToggleCallbacks() { for (int i = 0; i < toggles.Count; i++) { if (toggles[i] != null) { toggles[i].onValueChanged.RemoveListener(OnFilterToggleValueChanged); } } } private void RegisterPurchasePanelCallbacks() { if (p_iAmount_plus != null) { p_iAmount_plus.onClick.AddListener(OnPurchaseAmountPlusClicked); } if (p_iAmount_minus != null) { p_iAmount_minus.onClick.AddListener(OnPurchaseAmountMinusClicked); } if (p_iAmount_input != null) { p_iAmount_input.onEndEdit.AddListener(OnPurchaseAmountInputEndEdit); } } private void UnregisterPurchasePanelCallbacks() { if (p_iAmount_plus != null) { p_iAmount_plus.onClick.RemoveListener(OnPurchaseAmountPlusClicked); } if (p_iAmount_minus != null) { p_iAmount_minus.onClick.RemoveListener(OnPurchaseAmountMinusClicked); } if (p_iAmount_input != null) { p_iAmount_input.onEndEdit.RemoveListener(OnPurchaseAmountInputEndEdit); } } private void OnFilterToggleValueChanged(bool _) { currentSelectedItem = null; currentSelectedItemId = -1; currentPurchaseAmount = MinPurchaseAmount; RefreshCurrentView(); } private void SetDefaultToggleState() { if (allItems_toggle == null) { return; } for (int i = 0; i < toggles.Count; i++) { if (toggles[i] != null && toggles[i] != allItems_toggle) { toggles[i].SetIsOnWithoutNotify(false); } } allItems_toggle.SetIsOnWithoutNotify(true); } private void LoadAllStoreItems() { cachedItems.Clear(); var uniqueItemIds = new HashSet(); #if UNITY_EDITOR if (!string.IsNullOrEmpty(editorStoreItemPath)) { var guids = UnityEditor.AssetDatabase.FindAssets("t:storeItemSO", new[] { editorStoreItemPath }); for (int i = 0; i < guids.Length; i++) { var assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(guids[i]); var itemSO = UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); TryCacheStoreItem(itemSO, assetPath, uniqueItemIds); } } #endif if (!string.IsNullOrEmpty(runtimeStoreItemPath)) { var runtimeItems = Resources.LoadAll(runtimeStoreItemPath); for (int i = 0; i < runtimeItems.Length; i++) { var itemSO = runtimeItems[i]; var sourceLabel = string.IsNullOrEmpty(itemSO != null ? itemSO.name : string.Empty) ? runtimeStoreItemPath : runtimeStoreItemPath + "/" + itemSO.name; TryCacheStoreItem(itemSO, sourceLabel, uniqueItemIds); } } } private void TryCacheStoreItem(storeItemSO itemSO, string sourceLabel, HashSet uniqueItemIds) { if (itemSO == null) { return; } if (!uniqueItemIds.Add(itemSO.itemID)) { Debug.LogWarning($"storeSystem: duplicate itemID {itemSO.itemID} found at '{sourceLabel}'. Duplicate item was skipped.", itemSO); return; } cachedItems.Add(itemSO); } private void ApplyPersistedStateToItems() { bool stateChanged = false; for (int i = 0; i < cachedItems.Count; i++) { var itemSO = cachedItems[i]; StoreRuntimeEntry savedEntry; if (!TryGetEntry(itemSO.itemID, out savedEntry)) { #if UNITY_EDITOR if (itemSO.user_has_read || itemSO.purchasedCount > 0) { savedEntry = GetOrCreateEntry(itemSO.itemID); savedEntry.userHasRead = itemSO.user_has_read; savedEntry.purchasedCount = itemSO.purchasedCount; stateChanged = true; } #endif continue; } #if UNITY_EDITOR bool editorHasRead = itemSO.user_has_read; int editorPurchasedCount = itemSO.purchasedCount; if (savedEntry.userHasRead != editorHasRead || savedEntry.purchasedCount != editorPurchasedCount) { savedEntry.userHasRead = editorHasRead; savedEntry.purchasedCount = editorPurchasedCount; stateChanged = true; } #else bool mergedHasRead = itemSO.user_has_read || savedEntry.userHasRead; int mergedPurchasedCount = Mathf.Max(itemSO.purchasedCount, savedEntry.purchasedCount); itemSO.user_has_read = mergedHasRead; itemSO.purchasedCount = mergedPurchasedCount; if (savedEntry.userHasRead != mergedHasRead || savedEntry.purchasedCount != mergedPurchasedCount) { savedEntry.userHasRead = mergedHasRead; savedEntry.purchasedCount = mergedPurchasedCount; stateChanged = true; } #endif } if (stateChanged) { SavePersistedState(); } } public void RefreshCurrentView() { if (itemPrefab == null || contentParent == null) { ClearPurchasePanel(); return; } ClearSpawnedItems(); var visibleItems = new List(); for (int i = 0; i < cachedItems.Count; i++) { var itemSO = cachedItems[i]; if (!ShouldDisplay(itemSO)) { continue; } visibleItems.Add(itemSO); SpawnItem(itemSO); } ResolveVisibleSelection(visibleItems); } private bool ShouldDisplay(storeItemSO itemSO) { if (itemSO == null) { return false; } if (!itemSO.isOnShelf) { return false; } if (recentlyHot_toggle != null && recentlyHot_toggle.isOn) { return itemSO.isHot; } if (idols_toggle != null && idols_toggle.isOn) { return itemSO.itemType == storeItemSO.ItemType.character; } if (songs_toggle != null && songs_toggle.isOn) { return itemSO.itemType == storeItemSO.ItemType.song; } if (storyPassage_toggle != null && storyPassage_toggle.isOn) { return itemSO.itemType == storeItemSO.ItemType.storyPassage; } if (consumable_toggle != null && consumable_toggle.isOn) { return itemSO.itemType == storeItemSO.ItemType.consumable; } if (otherType_toggle != null && otherType_toggle.isOn) { return itemSO.itemType == storeItemSO.ItemType.otherType; } return allItems_toggle == null || allItems_toggle.isOn; } private void SpawnItem(storeItemSO itemSO) { var instance = Instantiate(itemPrefab, contentParent); var itemView = instance.GetComponent(); if (itemView == null) { Debug.LogWarning("storeSystem: itemPrefab missing storeItemPrefab component.", instance); Destroy(instance); return; } itemView.thisItemSO = itemSO; itemView.onItemClicked = HandleItemClicked; var button = instance.GetComponent