using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
///
/// 인벤토리에서 관리할 아이템 종류입니다.
/// 아이템을 추가하려면 여기에 enum 값을 추가하고, InventoryManager의 itemDefinitions에도 데이터를 추가하세요.
///
public enum InventoryItemType
{
Fish,
OldCompass,
Trash,
Bottle,
MemoryPiece
}
///
/// 인벤토리 UI 필터/정렬에 사용할 간단한 카테고리입니다.
///
public enum InventoryItemCategory
{
All,
Consumable,
Quest,
Collectible,
KeyItem,
Material,
Other
}
[Serializable]
public class InventoryItemStack
{
public InventoryItemType itemType;
[Min(0)] public int count;
}
[Serializable]
public class InventoryItemDefinition
{
[Header("Identity")]
public InventoryItemType itemType;
public string displayName;
[TextArea(2, 5)] public string description;
[TextArea(1, 3)] public string goalHint;
[Header("Visuals")]
public Sprite icon;
public Sprite slotBackground;
public bool importantItem;
[Header("Rules")]
public InventoryItemCategory category = InventoryItemCategory.Other;
[Tooltip("0 이하이면 InventoryManager의 기본 최대 소지 수량을 사용합니다.")]
public int maxCount = 99;
public bool usable = false;
public bool consumeOnUse = true;
public bool requireUseConfirmation = false;
public string useLabel = "사용";
public string useSuccessMessage;
public string useFailMessage;
[Header("Audio")]
public AudioClip acquisitionClip;
public AudioClip useClip;
[Header("Slot Display Override")]
public bool overrideSlotDisplaySettings = false;
public bool hideWhenZero = false;
public bool dimWhenZero = true;
[Range(0f, 1f)] public float zeroAlpha = 0.35f;
[Range(0f, 1f)] public float ownedAlpha = 1f;
public string SafeDisplayName => string.IsNullOrWhiteSpace(displayName) ? itemType.ToString() : displayName;
}
[Serializable]
public class InventoryLogEntry
{
public InventoryItemType itemType;
public int amount;
public string action;
public string displayName;
public string timeText;
public InventoryLogEntry() { }
public InventoryLogEntry(InventoryItemType itemType, int amount, string action, string displayName)
{
this.itemType = itemType;
this.amount = amount;
this.action = action;
this.displayName = displayName;
timeText = DateTime.Now.ToString("HH:mm:ss");
}
public override string ToString()
{
string amountText = amount > 0 ? $" x{amount}" : string.Empty;
return $"[{timeText}] {displayName}{amountText} {action}";
}
}
[Serializable] public class InventoryItemChangedEvent : UnityEvent { }
[Serializable] public class InventoryItemAmountEvent : UnityEvent { }
[Serializable] public class InventoryStringEvent : UnityEvent { }
[Serializable] public class InventoryProgressEvent : UnityEvent { }
///
/// 실제 아이템 개수를 관리하는 중심 스크립트입니다.
/// 기능: 싱글톤, 저장/불러오기, 최대 소지 수량, 사용, 부족 안내, 획득 로그, 기억의 조각 진행도 이벤트.
///
[DisallowMultipleComponent]
public class InventoryManager : MonoBehaviour
{
public static InventoryManager Instance { get; private set; }
[Header("Singleton")]
[SerializeField] private bool useSingleton = true;
[SerializeField] private bool dontDestroyOnLoad = true;
[SerializeField] private bool destroyDuplicateManagers = true;
[Header("Initial Items")]
[SerializeField] private List initialItems = new List();
[Header("Item Definitions")]
[SerializeField] private List itemDefinitions = new List()
{
new InventoryItemDefinition { itemType = InventoryItemType.Fish, displayName = "생선", description = "고양이 합창단이 좋아합니다.", goalHint = "고양이에게 줄 수 있습니다.", category = InventoryItemCategory.Consumable, maxCount = 99, usable = true, consumeOnUse = true, useSuccessMessage = "생선을 사용했습니다." },
new InventoryItemDefinition { itemType = InventoryItemType.OldCompass, displayName = "낡은 나침반", description = "미로에서 길을 찾는 데 도움이 됩니다.", goalHint = "미로에서 힌트를 볼 때 필요합니다.", category = InventoryItemCategory.KeyItem, maxCount = 1, usable = true, consumeOnUse = false, importantItem = true, useSuccessMessage = "나침반이 방향을 가리킵니다." },
new InventoryItemDefinition { itemType = InventoryItemType.Trash, displayName = "쓰레기", description = "낚시터를 정화하는 데 필요합니다.", goalHint = "정화 장치에 넣을 수 있습니다.", category = InventoryItemCategory.Material, maxCount = 99, usable = true, consumeOnUse = true, useSuccessMessage = "쓰레기를 사용했습니다." },
new InventoryItemDefinition { itemType = InventoryItemType.Bottle, displayName = "마법병", description = "바다 속에서 발견한 수상한 병입니다.", goalHint = "특정 이벤트에서 사용할 수 있습니다.", category = InventoryItemCategory.KeyItem, maxCount = 1, usable = true, consumeOnUse = false, importantItem = true, requireUseConfirmation = true, useSuccessMessage = "마법병을 사용했습니다." },
new InventoryItemDefinition { itemType = InventoryItemType.MemoryPiece, displayName = "기억의 조각", description = "제페토를 구출하기 위한 중요한 조각입니다.", goalHint = "모든 조각을 모으면 중요한 이벤트가 열립니다.", category = InventoryItemCategory.Quest, maxCount = 5, usable = false, consumeOnUse = false, importantItem = true }
};
[Header("Limits")]
[Tooltip("0 이하이면 기본 최대 수량 제한을 사용하지 않습니다. 아이템 정의의 maxCount가 있으면 그것이 우선됩니다.")]
[SerializeField] private int defaultMaxCount = 999;
[Header("Memory Piece Progress")]
[SerializeField] private InventoryItemType memoryPieceItemType = InventoryItemType.MemoryPiece;
[Min(1)] [SerializeField] private int memoryPieceTargetCount = 5;
[Header("Save / Load")]
[SerializeField] private bool loadOnAwake = true;
[SerializeField] private bool saveOnChange = true;
[Tooltip("Persistent Pickup/Reward 완료 상태는 아이템 수량 자동 저장을 꺼도 저장하는 것을 권장합니다.")]
[SerializeField] private bool forceSavePersistentState = true;
[SerializeField] private string saveKey = "Inventory_SaveData";
[Header("Recent Log")]
[SerializeField] private bool keepRecentLogs = true;
[SerializeField] private int maxRecentLogCount = 5;
[Header("Events")]
public InventoryItemChangedEvent onItemCountChanged;
public InventoryItemAmountEvent onItemAdded;
public InventoryItemAmountEvent onItemRemoved;
public InventoryItemChangedEvent onItemUsed;
public InventoryStringEvent onMessageRequested;
public InventoryProgressEvent onMemoryPieceProgressChanged;
public UnityEvent onMemoryPieceCompleted;
public UnityEvent onInventoryChanged;
[Header("Debug")]
[SerializeField] private bool showDebugLog = true;
private readonly Dictionary itemCounts = new Dictionary();
private readonly HashSet consumedPersistentKeys = new HashSet();
private readonly List recentLogs = new List();
public event Action ItemCountChanged;
public event Action ItemAdded;
public event Action ItemRemoved;
public event Action ItemUsed;
public event Action InventoryChanged;
public event Action MessageRequested;
public event Action LogAdded;
public event Action MemoryPieceProgressChanged;
public event Action MemoryPieceCompleted;
private bool memoryPieceCompletedNotified;
public int MemoryPieceTargetCount => memoryPieceTargetCount;
public InventoryItemType MemoryPieceItemType => memoryPieceItemType;
private void Awake()
{
if (useSingleton)
{
if (Instance != null && Instance != this)
{
if (showDebugLog)
Debug.LogWarning($"[InventoryManager] 중복 InventoryManager 발견: {name}");
if (destroyDuplicateManagers)
{
Destroy(gameObject);
return;
}
}
else
{
Instance = this;
if (dontDestroyOnLoad)
DontDestroyOnLoad(gameObject);
}
}
EnsureItemDefinitions();
InitializeFromInspector();
if (loadOnAwake)
LoadInventory(false);
}
private void Start()
{
NotifyAllItemsChanged();
NotifyMemoryPieceProgress();
}
private void EnsureItemDefinitions()
{
if (itemDefinitions == null)
itemDefinitions = new List();
foreach (InventoryItemType itemType in Enum.GetValues(typeof(InventoryItemType)))
{
if (GetDefinitionInternal(itemType) == null)
{
itemDefinitions.Add(new InventoryItemDefinition
{
itemType = itemType,
displayName = itemType.ToString(),
maxCount = defaultMaxCount,
category = InventoryItemCategory.Other
});
}
}
}
private void InitializeFromInspector()
{
itemCounts.Clear();
foreach (InventoryItemType itemType in Enum.GetValues(typeof(InventoryItemType)))
itemCounts[itemType] = 0;
for (int i = 0; i < initialItems.Count; i++)
{
InventoryItemStack stack = initialItems[i];
if (stack == null)
continue;
itemCounts[stack.itemType] = ClampCount(stack.itemType, stack.count);
}
}
public void AddItem(InventoryItemType itemType)
{
AddItem(itemType, 1);
}
public void AddItem(InventoryItemType itemType, int amount)
{
AddItemAndGetAddedAmount(itemType, amount);
}
///
/// 실제로 추가된 개수를 반환합니다. 최대 소지 수량에 막히면 요청량보다 적을 수 있습니다.
///
public int AddItemAndGetAddedAmount(InventoryItemType itemType, int amount)
{
if (amount <= 0)
return 0;
int previousCount = GetItemCount(itemType);
int newCount = ClampCount(itemType, previousCount + amount);
int addedAmount = Mathf.Max(0, newCount - previousCount);
if (addedAmount <= 0)
{
RequestMessage($"{GetDisplayName(itemType)}은(는) 더 이상 가질 수 없습니다.");
if (showDebugLog)
Debug.Log($"[InventoryManager] {itemType} 최대 소지 수량 도달: {previousCount}");
return 0;
}
itemCounts[itemType] = newCount;
NotifyItemChanged(itemType, newCount);
NotifyItemAdded(itemType, addedAmount, newCount);
AddLog(itemType, addedAmount, "획득");
if (saveOnChange)
SaveInventory();
if (showDebugLog)
Debug.Log($"[InventoryManager] {itemType} +{addedAmount} => {newCount}");
return addedAmount;
}
public bool RemoveItem(InventoryItemType itemType)
{
return RemoveItem(itemType, 1);
}
public bool RemoveItem(InventoryItemType itemType, int amount)
{
if (amount <= 0)
return false;
int current = GetItemCount(itemType);
if (current < amount)
{
RequestMessage(GetInsufficientMessage(itemType, amount));
if (showDebugLog)
Debug.LogWarning($"[InventoryManager] {itemType} 부족: 현재 {current}, 필요 {amount}");
return false;
}
int newCount = current - amount;
itemCounts[itemType] = newCount;
NotifyItemChanged(itemType, newCount);
NotifyItemRemoved(itemType, amount, newCount);
AddLog(itemType, amount, "소모");
if (saveOnChange)
SaveInventory();
return true;
}
public bool UseItem(InventoryItemType itemType)
{
return UseItem(itemType, 1);
}
public bool UseItem(InventoryItemType itemType, int amount)
{
InventoryItemDefinition definition = GetDefinition(itemType);
bool consume = definition == null || definition.consumeOnUse;
return TryUseItem(itemType, amount, consume, true, true);
}
///
/// 아이템을 사용합니다. 실제 게임 효과는 성공 이벤트나 외부 스크립트에서 처리하세요.
/// consume=false이면 보유 여부만 확인하고 개수는 줄이지 않습니다.
/// showMessages=false이면 부족/성공 안내를 표시하지 않습니다.
///
public bool TryUseItem(InventoryItemType itemType, int amount, bool consume, bool showMessages = true, bool addUseLog = true)
{
amount = Mathf.Max(1, amount);
if (!HasItem(itemType, amount))
{
if (showMessages)
RequestMessage(GetInsufficientMessage(itemType, amount));
return false;
}
if (consume && !RemoveItem(itemType, amount))
return false;
ItemUsed?.Invoke(itemType, GetItemCount(itemType));
onItemUsed?.Invoke(itemType, GetItemCount(itemType));
// consume=true인 경우 RemoveItem에서 이미 "소모" 로그가 남습니다.
// consume=false인 빠른 사용(예: 나침반 확인)은 별도 "사용" 로그를 남깁니다.
if (addUseLog && !consume)
AddLog(itemType, amount, "사용");
InventoryItemDefinition definition = GetDefinition(itemType);
if (showMessages)
{
string message = definition != null && !string.IsNullOrWhiteSpace(definition.useSuccessMessage)
? definition.useSuccessMessage
: $"{GetDisplayName(itemType)}을(를) 사용했습니다.";
RequestMessage(message);
}
if (saveOnChange)
SaveInventory();
return true;
}
public void SetItemCount(InventoryItemType itemType, int count)
{
int clampedCount = ClampCount(itemType, count);
int previousCount = GetItemCount(itemType);
if (previousCount == clampedCount)
return;
itemCounts[itemType] = clampedCount;
NotifyItemChanged(itemType, clampedCount);
if (saveOnChange)
SaveInventory();
}
public int GetItemCount(InventoryItemType itemType)
{
if (itemCounts.TryGetValue(itemType, out int count))
return count;
return 0;
}
public bool HasItem(InventoryItemType itemType)
{
return GetItemCount(itemType) > 0;
}
public bool HasItem(InventoryItemType itemType, int requiredAmount)
{
return GetItemCount(itemType) >= Mathf.Max(1, requiredAmount);
}
public int GetMaxCount(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
if (definition != null && definition.maxCount > 0)
return definition.maxCount;
return Mathf.Max(0, defaultMaxCount);
}
///
/// 현재 소지 수량과 최대 소지 수량을 기준으로 실제로 더 넣을 수 있는 개수를 반환합니다.
/// maxCount가 0 이하이면 제한 없음으로 취급합니다.
///
public int GetAddableAmount(InventoryItemType itemType, int requestedAmount)
{
requestedAmount = Mathf.Max(0, requestedAmount);
if (requestedAmount <= 0)
return 0;
int maxCount = GetMaxCount(itemType);
if (maxCount <= 0)
return requestedAmount;
int current = GetItemCount(itemType);
return Mathf.Clamp(maxCount - current, 0, requestedAmount);
}
public bool CanAddItem(InventoryItemType itemType, int amount = 1, bool requireFullAmount = false)
{
amount = Mathf.Max(1, amount);
int addableAmount = GetAddableAmount(itemType, amount);
return requireFullAmount ? addableAmount >= amount : addableAmount > 0;
}
public InventoryItemDefinition GetDefinition(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinitionInternal(itemType);
return definition;
}
private InventoryItemDefinition GetDefinitionInternal(InventoryItemType itemType)
{
if (itemDefinitions == null)
return null;
for (int i = 0; i < itemDefinitions.Count; i++)
{
if (itemDefinitions[i] != null && itemDefinitions[i].itemType == itemType)
return itemDefinitions[i];
}
return null;
}
public string GetDisplayName(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
return definition != null && !string.IsNullOrWhiteSpace(definition.displayName)
? definition.displayName
: itemType.ToString();
}
public string GetDescription(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
return definition != null ? definition.description : string.Empty;
}
public string GetGoalHint(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
return definition != null ? definition.goalHint : string.Empty;
}
public Sprite GetIcon(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
return definition != null ? definition.icon : null;
}
public Sprite GetSlotBackground(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
return definition != null ? definition.slotBackground : null;
}
public AudioClip GetAcquisitionClip(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
return definition != null ? definition.acquisitionClip : null;
}
public AudioClip GetUseClip(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
return definition != null ? definition.useClip : null;
}
public bool IsImportantItem(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
return definition != null && definition.importantItem;
}
public InventoryItemCategory GetCategory(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
return definition != null ? definition.category : InventoryItemCategory.Other;
}
public bool IsUsable(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
return definition != null && definition.usable;
}
public bool RequiresUseConfirmation(InventoryItemType itemType)
{
InventoryItemDefinition definition = GetDefinition(itemType);
return definition != null && definition.requireUseConfirmation;
}
public string GetInsufficientMessage(InventoryItemType itemType, int requiredAmount)
{
int current = GetItemCount(itemType);
return $"{GetDisplayName(itemType)}이(가) 부족합니다. 필요: {Mathf.Max(1, requiredAmount)}개 / 현재: {current}개";
}
public IReadOnlyList GetRecentLogs()
{
return new List(recentLogs);
}
public IReadOnlyDictionary GetAllItemCounts()
{
return new Dictionary(itemCounts);
}
public void ClearInventory()
{
foreach (InventoryItemType itemType in Enum.GetValues(typeof(InventoryItemType)))
itemCounts[itemType] = 0;
NotifyAllItemsChanged();
NotifyMemoryPieceProgress();
if (saveOnChange)
SaveInventory();
}
public void ResetToInitialItems()
{
InitializeFromInspector();
NotifyAllItemsChanged();
NotifyMemoryPieceProgress();
if (saveOnChange)
SaveInventory();
}
public bool IsPersistentKeyConsumed(string key)
{
if (string.IsNullOrWhiteSpace(key))
return false;
return consumedPersistentKeys.Contains(key);
}
public void MarkPersistentKeyConsumed(string key)
{
if (string.IsNullOrWhiteSpace(key))
return;
if (consumedPersistentKeys.Add(key) && (saveOnChange || forceSavePersistentState))
SaveInventory();
}
public void ResetPersistentKey(string key)
{
if (string.IsNullOrWhiteSpace(key))
return;
if (consumedPersistentKeys.Remove(key) && (saveOnChange || forceSavePersistentState))
SaveInventory();
}
public void RequestMessage(string message)
{
if (string.IsNullOrWhiteSpace(message))
return;
MessageRequested?.Invoke(message);
onMessageRequested?.Invoke(message);
if (showDebugLog)
Debug.Log($"[InventoryManager] Message: {message}");
}
private void AddLog(InventoryItemType itemType, int amount, string action)
{
if (!keepRecentLogs)
return;
InventoryLogEntry entry = new InventoryLogEntry(itemType, amount, action, GetDisplayName(itemType));
recentLogs.Insert(0, entry);
while (recentLogs.Count > Mathf.Max(1, maxRecentLogCount))
recentLogs.RemoveAt(recentLogs.Count - 1);
LogAdded?.Invoke(entry);
}
public void SaveInventory()
{
InventorySaveData saveData = new InventorySaveData();
foreach (KeyValuePair pair in itemCounts)
{
saveData.items.Add(new InventorySavedItemCount
{
itemTypeName = pair.Key.ToString(),
count = Mathf.Max(0, pair.Value)
});
}
foreach (string key in consumedPersistentKeys)
saveData.consumedPersistentKeys.Add(key);
string json = JsonUtility.ToJson(saveData);
PlayerPrefs.SetString(saveKey, json);
PlayerPrefs.Save();
if (showDebugLog)
Debug.Log("[InventoryManager] 인벤토리 저장 완료");
}
public bool LoadInventory()
{
return LoadInventory(true);
}
private bool LoadInventory(bool notify)
{
if (!PlayerPrefs.HasKey(saveKey))
return false;
string json = PlayerPrefs.GetString(saveKey, string.Empty);
if (string.IsNullOrWhiteSpace(json))
return false;
InventorySaveData saveData;
try
{
saveData = JsonUtility.FromJson(json);
}
catch (Exception exception)
{
Debug.LogWarning($"[InventoryManager] 저장 데이터 읽기 실패: {exception.Message}");
return false;
}
InitializeFromInspector();
consumedPersistentKeys.Clear();
if (saveData != null)
{
for (int i = 0; i < saveData.items.Count; i++)
{
InventorySavedItemCount savedItem = saveData.items[i];
if (savedItem == null || string.IsNullOrWhiteSpace(savedItem.itemTypeName))
continue;
if (Enum.TryParse(savedItem.itemTypeName, out InventoryItemType itemType))
itemCounts[itemType] = ClampCount(itemType, savedItem.count);
}
for (int i = 0; i < saveData.consumedPersistentKeys.Count; i++)
{
string key = saveData.consumedPersistentKeys[i];
if (!string.IsNullOrWhiteSpace(key))
consumedPersistentKeys.Add(key);
}
}
if (notify)
{
NotifyAllItemsChanged();
NotifyMemoryPieceProgress();
}
if (showDebugLog)
Debug.Log("[InventoryManager] 인벤토리 불러오기 완료");
return true;
}
public void DeleteSavedInventory()
{
PlayerPrefs.DeleteKey(saveKey);
PlayerPrefs.Save();
consumedPersistentKeys.Clear();
recentLogs.Clear();
memoryPieceCompletedNotified = false;
// ResetToInitialItems()를 호출하면 saveOnChange가 true일 때 삭제 직후 다시 저장 파일을 만들 수 있으므로
// 여기서는 직접 초기화와 알림만 수행합니다.
InitializeFromInspector();
NotifyAllItemsChanged();
NotifyMemoryPieceProgress();
RequestMessage("인벤토리 저장 데이터를 삭제했습니다.");
}
private int ClampCount(InventoryItemType itemType, int count)
{
int safeCount = Mathf.Max(0, count);
int maxCount = GetMaxCount(itemType);
if (maxCount > 0)
safeCount = Mathf.Min(safeCount, maxCount);
return safeCount;
}
private void NotifyItemChanged(InventoryItemType itemType, int count)
{
ItemCountChanged?.Invoke(itemType, count);
InventoryChanged?.Invoke();
onItemCountChanged?.Invoke(itemType, count);
onInventoryChanged?.Invoke();
if (itemType == memoryPieceItemType)
NotifyMemoryPieceProgress();
}
private void NotifyItemAdded(InventoryItemType itemType, int addedAmount, int totalCount)
{
ItemAdded?.Invoke(itemType, addedAmount, totalCount);
onItemAdded?.Invoke(itemType, addedAmount, totalCount);
}
private void NotifyItemRemoved(InventoryItemType itemType, int removedAmount, int totalCount)
{
ItemRemoved?.Invoke(itemType, removedAmount, totalCount);
onItemRemoved?.Invoke(itemType, removedAmount, totalCount);
}
private void NotifyAllItemsChanged()
{
foreach (KeyValuePair pair in itemCounts)
{
ItemCountChanged?.Invoke(pair.Key, pair.Value);
onItemCountChanged?.Invoke(pair.Key, pair.Value);
}
InventoryChanged?.Invoke();
onInventoryChanged?.Invoke();
}
private void NotifyMemoryPieceProgress()
{
int current = GetItemCount(memoryPieceItemType);
int target = Mathf.Max(1, memoryPieceTargetCount);
MemoryPieceProgressChanged?.Invoke(current, target);
onMemoryPieceProgressChanged?.Invoke(current, target);
if (current < target)
{
memoryPieceCompletedNotified = false;
return;
}
if (!memoryPieceCompletedNotified)
{
memoryPieceCompletedNotified = true;
MemoryPieceCompleted?.Invoke();
onMemoryPieceCompleted?.Invoke();
}
}
// 개발용 버튼/UnityEvent 연결용 메서드입니다.
public void DebugAddFish() => AddItem(InventoryItemType.Fish, 1);
public void DebugAddCompass() => AddItem(InventoryItemType.OldCompass, 1);
public void DebugAddTrash() => AddItem(InventoryItemType.Trash, 1);
public void DebugAddBottle() => AddItem(InventoryItemType.Bottle, 1);
public void DebugAddMemoryPiece() => AddItem(InventoryItemType.MemoryPiece, 1);
public void DebugClearInventory() => ClearInventory();
public void DebugDeleteSave() => DeleteSavedInventory();
#if UNITY_EDITOR
private void OnValidate()
{
if (defaultMaxCount < 0)
defaultMaxCount = 0;
if (memoryPieceTargetCount < 1)
memoryPieceTargetCount = 1;
if (maxRecentLogCount < 1)
maxRecentLogCount = 1;
if (string.IsNullOrWhiteSpace(saveKey))
saveKey = "Inventory_SaveData";
if (initialItems != null)
{
for (int i = 0; i < initialItems.Count; i++)
{
if (initialItems[i] != null)
initialItems[i].count = Mathf.Max(0, initialItems[i].count);
}
}
if (itemDefinitions != null)
{
for (int i = 0; i < itemDefinitions.Count; i++)
{
if (itemDefinitions[i] != null)
itemDefinitions[i].maxCount = Mathf.Max(0, itemDefinitions[i].maxCount);
}
}
}
#endif
[Serializable]
private class InventorySaveData
{
public List items = new List();
public List consumedPersistentKeys = new List();
}
[Serializable]
private class InventorySavedItemCount
{
public string itemTypeName;
public int count;
}
}