251 lines
8.9 KiB
C#
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|