Files
Echo/Assets/_Project/Scripts/Inventory/ItemGiveTakeStation.cs
T
2026-07-08 20:42:51 +08:00

216 lines
7.1 KiB
C#

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