using UnityEngine; using UnityEngine.Events; #if UNITY_EDITOR using UnityEditor; #endif /// /// 미니게임 성공, 퀘스트 완료, 버튼 이벤트, 아이템 사용 상호작용에 사용합니다. /// 기능: 보상 지급, 한 번만 지급, 저장, 필요 아이템 검사, 필요 아이템 소모, 부족 메시지. /// 예: 고양이 영입 = requireItemBeforeAction true + Fish 3개 소모 + giveRewardItem false + onActionCompleted에 고양이 합류 이벤트 연결. /// 예: 쓰레기 정화 = requireItemBeforeAction true + Trash 5개 소모 + onActionCompleted에 정화 이벤트 연결. /// [DisallowMultipleComponent] public class ItemReward : MonoBehaviour { [Header("Reference")] [SerializeField] private InventoryManager inventoryManager; [SerializeField] private bool autoFindManager = true; [Header("Required Item / Interaction Cost")] [Tooltip("true면 액션 전에 특정 아이템이 필요한지 검사합니다.")] [SerializeField] private bool requireItemBeforeAction = false; [SerializeField] private InventoryItemType requiredItemType = InventoryItemType.Fish; [Min(1)] [SerializeField] private int requiredAmount = 1; [SerializeField] private bool consumeRequiredItem = true; [SerializeField] private string requirementFailMessage; [SerializeField] private string requirementSuccessMessage; [Header("Reward")] [Tooltip("false면 보상 아이템을 지급하지 않고, 필요 아이템 소모와 이벤트만 실행합니다.")] [SerializeField] private bool giveRewardItem = true; [SerializeField] private InventoryItemType rewardItemType = InventoryItemType.Fish; [Min(1)] [SerializeField] private int rewardAmount = 1; [Tooltip("true이면 보상을 전부 받을 수 있을 때만 필요 아이템을 소모합니다. 보상 공간 부족으로 비용만 사라지는 상황을 방지합니다.")] [SerializeField] private bool requireFullRewardCapacity = true; [SerializeField] private bool giveOnlyOnce = false; [Header("Persistent State")] [Tooltip("true면 액션 완료 여부가 저장됩니다. 퀘스트/미니게임 보상 중복 방지에 사용하세요.")] [SerializeField] private bool persistActionState = false; [SerializeField] private string persistentActionKey; [Header("Messages")] [SerializeField] private bool showMessages = true; [SerializeField] private string rewardGivenMessage; [SerializeField] private string alreadyCompletedMessage = "이미 완료된 보상입니다."; [Header("Events")] public UnityEvent onActionCompleted; public UnityEvent onRewardGiven; public UnityEvent onAlreadyCompleted; public UnityEvent onRequirementMissing; public UnityEvent onRequiredItemConsumed; public UnityEvent onActionFailed; [Header("Debug")] [SerializeField] private bool showDebugLog = true; private bool alreadyCompleted; private void Awake() { ResolveManager(); ResolvePersistentKey(); } private void Start() { if (persistActionState && ResolveManager() != null && inventoryManager.IsPersistentKeyConsumed(persistentActionKey)) alreadyCompleted = true; } public void GiveReward() { ExecuteAction(); } public void ExecuteAction() { InventoryManager manager = ResolveManager(); if (manager == null) { Debug.LogWarning("[ItemReward] InventoryManager가 연결되지 않았습니다.", this); onActionFailed?.Invoke(); return; } if (giveOnlyOnce && alreadyCompleted) { if (showMessages) manager.RequestMessage(alreadyCompletedMessage); onAlreadyCompleted?.Invoke(); if (showDebugLog) Debug.Log("[ItemReward] 이미 완료된 액션입니다.", this); return; } int requestedRewardAmount = Mathf.Max(1, rewardAmount); if (giveRewardItem) { int addableAmount = manager.GetAddableAmount(rewardItemType, requestedRewardAmount); bool rewardCapacityOk = requireFullRewardCapacity ? addableAmount >= requestedRewardAmount : addableAmount > 0; if (!rewardCapacityOk) { if (showMessages) manager.RequestMessage($"{manager.GetDisplayName(rewardItemType)}은(는) 더 이상 받을 수 없습니다."); onActionFailed?.Invoke(); return; } } if (requireItemBeforeAction) { if (!manager.HasItem(requiredItemType, requiredAmount)) { string message = string.IsNullOrWhiteSpace(requirementFailMessage) ? manager.GetInsufficientMessage(requiredItemType, requiredAmount) : requirementFailMessage; if (showMessages) manager.RequestMessage(message); onRequirementMissing?.Invoke(); onActionFailed?.Invoke(); return; } if (consumeRequiredItem) { bool consumed = manager.TryUseItem(requiredItemType, requiredAmount, true, false, true); if (!consumed) { onActionFailed?.Invoke(); return; } onRequiredItemConsumed?.Invoke(); } if (showMessages && !string.IsNullOrWhiteSpace(requirementSuccessMessage)) manager.RequestMessage(requirementSuccessMessage); } int actuallyGiven = 0; if (giveRewardItem) { actuallyGiven = manager.AddItemAndGetAddedAmount(rewardItemType, requestedRewardAmount); if (actuallyGiven <= 0) { onActionFailed?.Invoke(); return; } onRewardGiven?.Invoke(); } alreadyCompleted = true; if (persistActionState) manager.MarkPersistentKeyConsumed(persistentActionKey); if (showMessages) { if (!string.IsNullOrWhiteSpace(rewardGivenMessage)) manager.RequestMessage(rewardGivenMessage); else if (!giveRewardItem) manager.RequestMessage("완료되었습니다."); } onActionCompleted?.Invoke(); if (showDebugLog) { if (giveRewardItem) Debug.Log($"[ItemReward] {rewardItemType} +{actuallyGiven}", this); else Debug.Log("[ItemReward] 아이템 지급 없이 액션 완료", this); } } /// /// 이번 호출에서만 amount를 바꿔 지급합니다. Inspector의 rewardAmount 값은 바꾸지 않습니다. /// public void GiveRewardAmount(int amount) { int previousAmount = rewardAmount; rewardAmount = Mathf.Max(1, amount); ExecuteAction(); rewardAmount = previousAmount; } public void ResetActionState() { alreadyCompleted = false; if (persistActionState && ResolveManager() != null) inventoryManager.ResetPersistentKey(persistentActionKey); } public void SetRewardItemType(InventoryItemType itemType) { rewardItemType = itemType; } public void SetRequiredItemType(InventoryItemType itemType) { requiredItemType = itemType; } public void SetInventoryManager(InventoryManager manager) { inventoryManager = manager; } private InventoryManager ResolveManager() { if (inventoryManager != null) return inventoryManager; if (InventoryManager.Instance != null) inventoryManager = InventoryManager.Instance; else if (autoFindManager) inventoryManager = FindFirstObjectByType(); return inventoryManager; } private void ResolvePersistentKey() { if (!persistActionState) return; if (string.IsNullOrWhiteSpace(persistentActionKey)) persistentActionKey = $"Reward_{gameObject.scene.name}_{GetHierarchyPath(transform)}"; } private string GetHierarchyPath(Transform target) { if (target == null) return "Unknown"; string path = target.name; Transform parent = target.parent; while (parent != null) { path = parent.name + "/" + path; parent = parent.parent; } return path; } #if UNITY_EDITOR private void OnValidate() { requiredAmount = Mathf.Max(1, requiredAmount); rewardAmount = Mathf.Max(1, rewardAmount); if (persistActionState && string.IsNullOrWhiteSpace(persistentActionKey)) { string editorId = GlobalObjectId.GetGlobalObjectIdSlow(this).ToString(); persistentActionKey = string.IsNullOrWhiteSpace(editorId) ? $"Reward_{gameObject.scene.name}_{GetHierarchyPath(transform)}" : $"Reward_{editorId}"; } } #endif }