274 lines
9.0 KiB
C#
274 lines
9.0 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// 미니게임 성공, 퀘스트 완료, 버튼 이벤트, 아이템 사용 상호작용에 사용합니다.
|
|
/// 기능: 보상 지급, 한 번만 지급, 저장, 필요 아이템 검사, 필요 아이템 소모, 부족 메시지.
|
|
/// 예: 고양이 영입 = requireItemBeforeAction true + Fish 3개 소모 + giveRewardItem false + onActionCompleted에 고양이 합류 이벤트 연결.
|
|
/// 예: 쓰레기 정화 = requireItemBeforeAction true + Trash 5개 소모 + onActionCompleted에 정화 이벤트 연결.
|
|
/// </summary>
|
|
[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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 이번 호출에서만 amount를 바꿔 지급합니다. Inspector의 rewardAmount 값은 바꾸지 않습니다.
|
|
/// </summary>
|
|
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<InventoryManager>();
|
|
|
|
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
|
|
}
|