using Interaction.Conditions; using UnityEngine; using UnityEngine.Events; namespace Inventory { public class ItemGiveTakeStation : InteractConditionBehaviour, IInteractable { public enum Mode { Auto = 0, GiveOnly = 1, TakeOnly = 2 } [Header("物品")] [SerializeField] private ItemData itemData; [SerializeField, Min(1)] private int amountPerInteract = 1; [Header("容量")] [SerializeField, Min(0)] private int capacity = 10; [SerializeField, Min(0)] private int stock = 10; [Header("模式")] [SerializeField] private Mode mode = Mode.Auto; [SerializeField] private bool preferTakeWhenPossible = true; [Header("提示")] [SerializeField] private string outOfStockReason = "库存不足"; [SerializeField] private string stationFullReason = "回收箱已满"; [SerializeField] private string inventoryFullReason = "背包已满"; [SerializeField] private string nothingToTakeReason = "没有可回收的物品"; [SerializeField] private string missingInventoryReason = "缺少背包系统"; [SerializeField] private string missingItemReason = "未配置物品"; [Header("事件")] [SerializeField] private UnityEvent onGaveItem; [SerializeField] private UnityEvent onTookBackItem; private void OnValidate() { if (capacity < 0) capacity = 0; if (stock < 0) stock = 0; if (stock > capacity) stock = capacity; if (amountPerInteract < 1) amountPerInteract = 1; } public override bool CanInteract(GameObject interactor, out string failReason) { failReason = null; if (itemData == null) { failReason = missingItemReason; return false; } var inventory = InventorySystem.Instance; if (inventory == null) { failReason = missingInventoryReason; return false; } int playerCount = CountItemsById(inventory, itemData.id); int freeCapacity = Mathf.Max(0, capacity - stock); bool canGive = stock > 0 && CanAdd(inventory, itemData, Mathf.Min(amountPerInteract, stock)); bool canTake = playerCount > 0 && freeCapacity > 0; switch (mode) { case Mode.GiveOnly: if (stock <= 0) { failReason = outOfStockReason; return false; } if (!canGive) { failReason = inventoryFullReason; return false; } return true; case Mode.TakeOnly: if (playerCount <= 0) { failReason = nothingToTakeReason; return false; } if (freeCapacity <= 0) { failReason = stationFullReason; return false; } return true; default: if (preferTakeWhenPossible && canTake) return true; if (canGive) return true; if (canTake) return true; if (stock <= 0) failReason = outOfStockReason; else if (!canGive) failReason = inventoryFullReason; else if (freeCapacity <= 0) failReason = stationFullReason; else failReason = nothingToTakeReason; return false; } } public override void OnInteractSucceeded(GameObject interactor) { } public void Interact() { if (itemData == null) return; var inventory = InventorySystem.Instance; if (inventory == null) return; int playerCount = CountItemsById(inventory, itemData.id); int freeCapacity = Mathf.Max(0, capacity - stock); bool didTake = false; if (mode != Mode.GiveOnly) { int takeCount = Mathf.Min(amountPerInteract, Mathf.Min(playerCount, freeCapacity)); if (takeCount > 0 && (mode == Mode.TakeOnly || (mode == Mode.Auto && preferTakeWhenPossible))) { inventory.Remove(itemData, takeCount); stock = Mathf.Min(capacity, stock + takeCount); didTake = true; onTookBackItem?.Invoke(); } } if (didTake) return; if (mode == Mode.TakeOnly) return; if (stock <= 0) return; int giveRequest = Mathf.Min(amountPerInteract, stock); if (giveRequest <= 0) return; int before = CountItemsById(inventory, itemData.id); inventory.Add(itemData, giveRequest); int after = CountItemsById(inventory, itemData.id); int given = Mathf.Clamp(after - before, 0, giveRequest); if (given <= 0) return; stock = Mathf.Max(0, stock - given); onGaveItem?.Invoke(); } 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 bool CanAdd(InventorySystem inventory, ItemData data, int amount) { if (inventory == null) return false; if (data == null) return false; if (amount <= 0) return true; var list = inventory.inventory; if (list == null) return false; int remaining = amount; if (data.isStackable) { for (int i = 0; i < list.Count; i++) { var entry = list[i]; if (entry?.data != data) continue; if (entry.stackSize >= data.maxStackSize) continue; int space = data.maxStackSize - entry.stackSize; int take = Mathf.Min(space, remaining); remaining -= take; if (remaining <= 0) return true; } } int emptySlots = 0; for (int i = 0; i < list.Count; i++) { if (list[i] == null) emptySlots++; } if (!data.isStackable) return emptySlots >= remaining; int perEmpty = Mathf.Max(1, data.maxStackSize); int capacityFromEmpty = emptySlots * perEmpty; return capacityFromEmpty >= remaining; } } }