216 lines
7.1 KiB
C#
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|