Files
Echo/Assets/_Project/Scripts/Interaction/Conditions/RequireItemsInteractCondition.cs
T
2026-07-08 20:42:51 +08:00

251 lines
8.9 KiB
C#

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;
}
}
}
}