using System; using Inventory; using UnityEngine; namespace Interaction.Conditions { public class RequireItemsInteractCondition : InteractConditionBehaviour { public enum RequireMode { RequireAll = 0, RequireAny = 1 } [Serializable] private struct Requirement { public string requiredItemId; [Min(1)] public int requiredAmount; } [Header("需求")] [SerializeField] private RequireMode mode = RequireMode.RequireAll; [SerializeField] private Requirement[] requirements; [Header("成功后处理")] [SerializeField] private bool consumeOnSuccess; [SerializeField] private bool unlockAfterSuccess; [SerializeField] private bool unlocked; [SerializeField] private InteractUnlockState unlockState; [Header("失败提示")] [SerializeField] private string failReason; [Header("容错")] [SerializeField] private bool allowIfInventoryMissing; [Header("Debug")] [SerializeField] private bool debugLog; private int lastMatchedIndex = -1; public override bool CanInteract(GameObject interactor, out string reason) { reason = null; lastMatchedIndex = -1; if (debugLog) { Debug.Log( $"[RequireItemsInteractCondition] CanInteract on '{name}' " + $"mode={mode} consumeOnSuccess={consumeOnSuccess} unlockAfterSuccess={unlockAfterSuccess} unlocked={GetUnlocked()} " + $"requirementsCount={(requirements == null ? 0 : requirements.Length)}", this); } if (unlockAfterSuccess && GetUnlocked()) { if (debugLog) Debug.Log("[RequireItemsInteractCondition] Bypass: unlocked=true", this); return true; } if (requirements == null || requirements.Length <= 0) { if (debugLog) Debug.Log("[RequireItemsInteractCondition] Bypass: requirements empty", this); return true; } var inventory = InventorySystem.Instance; if (inventory == null) { if (allowIfInventoryMissing) return true; reason = string.IsNullOrWhiteSpace(failReason) ? "缺少背包系统" : failReason; if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Blocked: inventory missing, reason='{reason}'", this); return false; } if (mode == RequireMode.RequireAny) { for (int i = 0; i < requirements.Length; i++) { var req = requirements[i]; if (string.IsNullOrWhiteSpace(req.requiredItemId)) continue; int amount = req.requiredAmount <= 0 ? 1 : req.requiredAmount; int count = CountItemsById(inventory, req.requiredItemId); if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Check(Any) id='{req.requiredItemId}' need={amount} have={count}", this); if (count >= amount) { lastMatchedIndex = i; if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Passed(Any) matchedIndex={lastMatchedIndex}", this); return true; } } reason = BuildFailReason(); if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Blocked(Any) reason='{reason}'", this); return false; } for (int i = 0; i < requirements.Length; i++) { var req = requirements[i]; if (string.IsNullOrWhiteSpace(req.requiredItemId)) continue; int amount = req.requiredAmount <= 0 ? 1 : req.requiredAmount; int count = CountItemsById(inventory, req.requiredItemId); if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Check(All) id='{req.requiredItemId}' need={amount} have={count}", this); if (count < amount) { reason = BuildFailReason(); if (debugLog) Debug.Log($"[RequireItemsInteractCondition] Blocked(All) reason='{reason}'", this); return false; } } if (debugLog) Debug.Log("[RequireItemsInteractCondition] Passed(All)", this); return true; } public override void OnInteractSucceeded(GameObject interactor) { if (unlockAfterSuccess && GetUnlocked()) return; if (consumeOnSuccess) { var inventory = InventorySystem.Instance; if (inventory != null) { if (debugLog) Debug.Log($"[RequireItemsInteractCondition] OnInteractSucceeded: consuming, mode={mode}, matchedIndex={lastMatchedIndex}", this); Consume(inventory); } } if (unlockAfterSuccess) SetUnlocked(true); if (debugLog) Debug.Log($"[RequireItemsInteractCondition] OnInteractSucceeded: done, unlocked={GetUnlocked()}", this); } private bool GetUnlocked() { if (unlockState != null) return unlockState.IsUnlocked; return unlocked; } private void SetUnlocked(bool value) { if (unlockState != null) { unlockState.SetUnlocked(value); return; } unlocked = value; } private void Consume(InventorySystem inventory) { if (requirements == null || requirements.Length <= 0) return; if (mode == RequireMode.RequireAny) { if (lastMatchedIndex < 0 || lastMatchedIndex >= requirements.Length) return; var req = requirements[lastMatchedIndex]; if (string.IsNullOrWhiteSpace(req.requiredItemId)) return; int amount = req.requiredAmount <= 0 ? 1 : req.requiredAmount; ConsumeItemsById(inventory, req.requiredItemId, amount); return; } for (int i = 0; i < requirements.Length; i++) { var req = requirements[i]; if (string.IsNullOrWhiteSpace(req.requiredItemId)) continue; int amount = req.requiredAmount <= 0 ? 1 : req.requiredAmount; ConsumeItemsById(inventory, req.requiredItemId, amount); } } private string BuildFailReason() { if (!string.IsNullOrWhiteSpace(failReason)) return failReason; if (requirements == null || requirements.Length <= 0) return "需要特定道具"; if (mode == RequireMode.RequireAny) { return $"需要任意一种物品: {BuildRequirementsList()}"; } return $"需要全部物品: {BuildRequirementsList()}"; } private string BuildRequirementsList() { string result = null; for (int i = 0; i < requirements.Length; i++) { var req = requirements[i]; if (string.IsNullOrWhiteSpace(req.requiredItemId)) continue; int amount = req.requiredAmount <= 0 ? 1 : req.requiredAmount; string token = amount == 1 ? req.requiredItemId : $"{req.requiredItemId}x{amount}"; result = string.IsNullOrEmpty(result) ? token : $"{result}, {token}"; } return string.IsNullOrEmpty(result) ? "(未配置)" : result; } private static int CountItemsById(InventorySystem inventory, string itemId) { int total = 0; var list = inventory.inventory; if (list == null) return 0; for (int i = 0; i < list.Count; i++) { var entry = list[i]; if (entry?.data == null) continue; if (entry.data.id != itemId) continue; total += entry.stackSize; } return total; } private static void ConsumeItemsById(InventorySystem inventory, string itemId, int amount) { int remaining = amount; var list = inventory.inventory; if (list == null) return; for (int i = 0; i < list.Count; i++) { if (remaining <= 0) break; var entry = list[i]; if (entry?.data == null) continue; if (entry.data.id != itemId) continue; int take = Mathf.Min(entry.stackSize, remaining); if (take <= 0) continue; inventory.RemoveFromSlot(i, take); remaining -= take; } } } }