using UnityEngine;
using UnityEngine.Events;
using UnityEngine.XR;
///
/// VR 플레이어의 몸, 손, 컨트롤러 Collider에 붙여서
/// "이 Collider는 인벤토리 아이템을 획득할 수 있다"고 표시하는 컴포넌트입니다.
/// 기능: 획득 허용/차단, InventoryManager Override, 컨트롤러 햅틱, 빠른 아이템 사용 메서드.
///
[DisallowMultipleComponent]
public class PlayerInventoryCollector : MonoBehaviour
{
[Header("Collection")]
[SerializeField] private bool canCollectItems = true;
[SerializeField] private InventoryManager inventoryManagerOverride;
[Tooltip("확인창이 필요한 아이템을 빠른 사용으로 눌렀을 때 InventoryUI의 확인창을 사용합니다.")]
[SerializeField] private bool respectInventoryUseConfirmation = true;
[SerializeField] private InventoryUI inventoryUIForConfirmedUses;
[Header("Haptic")]
[SerializeField] private bool enableHaptic = true;
[SerializeField] private XRNode hapticNode = XRNode.RightHand;
[Range(0f, 1f)] [SerializeField] private float defaultHapticAmplitude = 0.35f;
[SerializeField] private float defaultHapticDuration = 0.08f;
[Header("Quick Use - Compass")]
[SerializeField] private bool consumeCompassOnQuickUse = false;
[SerializeField] private string compassMissingMessage = "낡은 나침반이 필요합니다.";
[SerializeField] private string compassUsedMessage = "나침반이 방향을 가리킵니다.";
public UnityEvent onCompassUsed;
public UnityEvent onCompassMissing;
[Header("Quick Use - Fish")]
[SerializeField] private int fishUseAmount = 1;
[SerializeField] private string fishMissingMessage = "생선이 부족합니다.";
[SerializeField] private string fishUsedMessage = "생선을 사용했습니다.";
public UnityEvent onFishUsed;
public UnityEvent onFishMissing;
[Header("Quick Use - Trash")]
[SerializeField] private int trashUseAmount = 1;
[SerializeField] private string trashMissingMessage = "쓰레기가 부족합니다.";
[SerializeField] private string trashUsedMessage = "쓰레기를 사용했습니다.";
public UnityEvent onTrashUsed;
public UnityEvent onTrashMissing;
[Header("Quick Use - Bottle")]
[SerializeField] private bool consumeBottleOnQuickUse = false;
[SerializeField] private string bottleMissingMessage = "마법병이 필요합니다.";
[SerializeField] private string bottleUsedMessage = "마법병을 사용했습니다.";
public UnityEvent onBottleUsed;
public UnityEvent onBottleMissing;
[Header("Quick Use - Memory Piece")]
[SerializeField] private string memoryPieceMissingMessage = "기억의 조각이 없습니다.";
[SerializeField] private string memoryPieceUsedMessage = "기억의 조각을 확인했습니다.";
public UnityEvent onMemoryPieceUsed;
public UnityEvent onMemoryPieceMissing;
public bool CanCollectItems => canCollectItems;
public InventoryManager InventoryManagerOverride => inventoryManagerOverride;
public void SetCanCollectItems(bool canCollect)
{
canCollectItems = canCollect;
}
public void SetInventoryManagerOverride(InventoryManager manager)
{
inventoryManagerOverride = manager;
}
public void PlayDefaultHaptic()
{
PlayHaptic(defaultHapticAmplitude, defaultHapticDuration);
}
public void PlayHaptic(float amplitude, float duration)
{
if (!enableHaptic)
return;
InputDevice device = InputDevices.GetDeviceAtXRNode(hapticNode);
if (!device.isValid)
return;
HapticCapabilities capabilities;
if (device.TryGetHapticCapabilities(out capabilities) && capabilities.supportsImpulse)
device.SendHapticImpulse(0, Mathf.Clamp01(amplitude), Mathf.Max(0f, duration));
}
public void UseCompass()
{
InventoryManager manager = ResolveManager();
if (manager == null)
return;
bool result = manager.TryUseItem(InventoryItemType.OldCompass, 1, consumeCompassOnQuickUse, false, true);
if (result)
{
manager.RequestMessage(compassUsedMessage);
PlayDefaultHaptic();
onCompassUsed?.Invoke();
}
else
{
manager.RequestMessage(compassMissingMessage);
onCompassMissing?.Invoke();
}
}
public void UseFish()
{
TryUseQuickItem(InventoryItemType.Fish, fishUseAmount, true, fishUsedMessage, fishMissingMessage, onFishUsed, onFishMissing);
}
public void UseTrash()
{
TryUseQuickItem(InventoryItemType.Trash, trashUseAmount, true, trashUsedMessage, trashMissingMessage, onTrashUsed, onTrashMissing);
}
public void UseBottle()
{
TryUseQuickItem(InventoryItemType.Bottle, 1, consumeBottleOnQuickUse, bottleUsedMessage, bottleMissingMessage, onBottleUsed, onBottleMissing);
}
public void UseMemoryPiece()
{
TryUseQuickItem(InventoryItemType.MemoryPiece, 1, false, memoryPieceUsedMessage, memoryPieceMissingMessage, onMemoryPieceUsed, onMemoryPieceMissing);
}
public bool TryUseQuickItem(InventoryItemType itemType, int amount, bool consume, string successMessage, string missingMessage, UnityEvent onSuccess, UnityEvent onFail)
{
InventoryManager manager = ResolveManager();
if (manager == null)
return false;
amount = Mathf.Max(1, amount);
if (respectInventoryUseConfirmation && manager.RequiresUseConfirmation(itemType))
{
InventoryUI ui = ResolveInventoryUI();
if (ui != null)
{
ui.RequestUseItem(itemType, amount);
return true;
}
}
bool result = manager.TryUseItem(itemType, amount, consume, false, true);
if (result)
{
if (!string.IsNullOrWhiteSpace(successMessage))
manager.RequestMessage(successMessage);
PlayDefaultHaptic();
onSuccess?.Invoke();
return true;
}
if (!string.IsNullOrWhiteSpace(missingMessage))
manager.RequestMessage(missingMessage);
else
manager.RequestMessage(manager.GetInsufficientMessage(itemType, amount));
onFail?.Invoke();
return false;
}
private InventoryUI ResolveInventoryUI()
{
if (inventoryUIForConfirmedUses != null)
return inventoryUIForConfirmedUses;
inventoryUIForConfirmedUses = FindFirstObjectByType();
return inventoryUIForConfirmedUses;
}
private InventoryManager ResolveManager()
{
if (inventoryManagerOverride != null)
return inventoryManagerOverride;
if (InventoryManager.Instance != null)
return InventoryManager.Instance;
return FindFirstObjectByType();
}
#if UNITY_EDITOR
private void OnValidate()
{
fishUseAmount = Mathf.Max(1, fishUseAmount);
trashUseAmount = Mathf.Max(1, trashUseAmount);
defaultHapticDuration = Mathf.Max(0f, defaultHapticDuration);
}
#endif
}