311 lines
12 KiB
C#
311 lines
12 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using Cielonos.MainGame.Characters.Inventory;
|
|||
|
|
using Cielonos.MainGame.Characters.Inventory.Collections;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using Object = UnityEngine.Object;
|
|||
|
|
|
|||
|
|
namespace Cielonos.MainGame.Characters
|
|||
|
|
{
|
|||
|
|
public partial class PlayerInventorySubcontroller
|
|||
|
|
{
|
|||
|
|
public partial class BackpackSubmodule : SubmoduleBase<PlayerInventorySubcontroller>
|
|||
|
|
{
|
|||
|
|
public List<MainWeaponBase> mainWeapons = new List<MainWeaponBase>();
|
|||
|
|
public List<SupportEquipmentBase> supportEquipments = new List<SupportEquipmentBase>();
|
|||
|
|
public List<PassiveEquipmentBase> passiveEquipments = new List<PassiveEquipmentBase>();
|
|||
|
|
public List<ConsumableBase> consumables = new List<ConsumableBase>();
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Resources 路径中道具类型对应的子文件夹名。
|
|||
|
|
/// 约定路径格式:Resources/Items/{子文件夹}/{类名}.prefab
|
|||
|
|
/// </summary>
|
|||
|
|
private static readonly Dictionary<Type, string> ItemTypeToSubfolder = new()
|
|||
|
|
{
|
|||
|
|
{ typeof(MainWeaponBase), "MainWeapon" },
|
|||
|
|
{ typeof(SupportEquipmentBase), "SupportEquipment" },
|
|||
|
|
{ typeof(PassiveEquipmentBase), "PassiveEquipment" },
|
|||
|
|
{ typeof(ConsumableBase), "Consumable" },
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
public BackpackSubmodule(PlayerInventorySubcontroller owner) : base(owner)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ================================================================
|
|||
|
|
// 获取道具(已有实例)
|
|||
|
|
// ================================================================
|
|||
|
|
|
|||
|
|
public partial class BackpackSubmodule
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 通过 Resources 加载 Prefab,实例化到对应的 Container 下,然后加入背包。
|
|||
|
|
/// 若为消耗品且背包中已存在同类型实例,则直接堆叠,不再重复生成。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <typeparam name="T">道具的具体类型(必须继承 ItemBase)。</typeparam>
|
|||
|
|
/// <param name="amount">消耗品的初始 / 追加堆叠数量,非消耗品忽略此参数。</param>
|
|||
|
|
/// <returns>背包中该道具的实例引用;加载失败时返回 null。</returns>
|
|||
|
|
public T ObtainItem<T>(int amount = 1) where T : ItemBase
|
|||
|
|
{
|
|||
|
|
// 消耗品:如果背包中已有同类型实例,直接堆叠
|
|||
|
|
if (typeof(ConsumableBase).IsAssignableFrom(typeof(T)))
|
|||
|
|
{
|
|||
|
|
ConsumableBase existing = consumables.Find(c => c is T);
|
|||
|
|
if (existing != null)
|
|||
|
|
{
|
|||
|
|
existing.Add(amount);
|
|||
|
|
return existing as T;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确定 Resources 子路径
|
|||
|
|
string subfolder = ResolveSubfolder(typeof(T));
|
|||
|
|
if (subfolder == null)
|
|||
|
|
{
|
|||
|
|
Debug.LogError($"[Backpack] 无法确定 {typeof(T).Name} 的 Resources 子文件夹。");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
string resourcePath = $"Items/{subfolder}/{typeof(T).Name}";
|
|||
|
|
GameObject prefab = Resources.Load<GameObject>(resourcePath);
|
|||
|
|
if (prefab == null)
|
|||
|
|
{
|
|||
|
|
Debug.LogError($"[Backpack] 未找到 Prefab:Resources/{resourcePath}");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 实例化到对应的 Container 下
|
|||
|
|
Transform container = GetContainerForType(typeof(T));
|
|||
|
|
GameObject instance = Object.Instantiate(prefab, container);
|
|||
|
|
instance.name = typeof(T).Name;
|
|||
|
|
|
|||
|
|
T item = instance.GetComponent<T>();
|
|||
|
|
if (item == null)
|
|||
|
|
{
|
|||
|
|
Debug.LogError($"[Backpack] Prefab '{resourcePath}' 上未找到组件 {typeof(T).Name}。");
|
|||
|
|
Object.Destroy(instance);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ObtainItem(item, amount);
|
|||
|
|
return item;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 将已有的道具实例加入背包。
|
|||
|
|
/// 对于消耗品,amount 指定堆叠数量;-1 表示使用道具自身的 stackAmount。
|
|||
|
|
/// </summary>
|
|||
|
|
public void ObtainItem(ItemBase item, int amount = -1)
|
|||
|
|
{
|
|||
|
|
item.Initialize();
|
|||
|
|
|
|||
|
|
if (item is MainWeaponBase mainWeapon)
|
|||
|
|
{
|
|||
|
|
ObtainMainWeapon(mainWeapon);
|
|||
|
|
}
|
|||
|
|
else if (item is SupportEquipmentBase supportEquipment)
|
|||
|
|
{
|
|||
|
|
ObtainSupportEquipment(supportEquipment);
|
|||
|
|
}
|
|||
|
|
else if (item is PassiveEquipmentBase passiveEquipment)
|
|||
|
|
{
|
|||
|
|
ObtainPassiveEquipment(passiveEquipment);
|
|||
|
|
}
|
|||
|
|
else if (item is ConsumableBase consumable)
|
|||
|
|
{
|
|||
|
|
ObtainConsumable(consumable, amount);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
item.OnObtained();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 通过预制体引用获取道具:实例化到对应 Container 下并加入背包。
|
|||
|
|
/// 消耗品会自动堆叠。用于 MechanicalTable、LogisticsCenter 等持有直接预制体引用的场景。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="prefab">道具预制体(必须包含 ItemBase 组件)。</param>
|
|||
|
|
/// <param name="amount">消耗品的堆叠数量,非消耗品忽略。</param>
|
|||
|
|
/// <returns>背包中该道具的实例引用;失败时返回 null。</returns>
|
|||
|
|
public ItemBase ObtainItem(GameObject prefab, int amount = 1)
|
|||
|
|
{
|
|||
|
|
ItemBase prefabItem = prefab.GetComponent<ItemBase>();
|
|||
|
|
if (prefabItem == null)
|
|||
|
|
{
|
|||
|
|
Debug.LogError($"[Backpack] Prefab '{prefab.name}' 上未找到 ItemBase 组件。");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 消耗品:如果背包中已有同类型实例,直接堆叠
|
|||
|
|
if (prefabItem is ConsumableBase)
|
|||
|
|
{
|
|||
|
|
ConsumableBase existing = consumables.Find(c => c.GetType() == prefabItem.GetType());
|
|||
|
|
if (existing != null)
|
|||
|
|
{
|
|||
|
|
existing.Add(amount);
|
|||
|
|
return existing;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Transform container = GetContainerForType(prefabItem.GetType());
|
|||
|
|
GameObject instance = Object.Instantiate(prefab, container);
|
|||
|
|
instance.name = prefabItem.GetType().Name;
|
|||
|
|
|
|||
|
|
ItemBase item = instance.GetComponent<ItemBase>();
|
|||
|
|
if (item == null)
|
|||
|
|
{
|
|||
|
|
Debug.LogError($"[Backpack] 实例化后的 '{prefab.name}' 上未找到 ItemBase 组件。");
|
|||
|
|
Object.Destroy(instance);
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ObtainItem(item, amount);
|
|||
|
|
return item;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void DiscardItem(ItemBase item, int amount = -1)
|
|||
|
|
{
|
|||
|
|
if (item is MainWeaponBase mainWeapon)
|
|||
|
|
{
|
|||
|
|
DiscardMainWeapon(mainWeapon);
|
|||
|
|
}
|
|||
|
|
else if (item is SupportEquipmentBase supportEquipment)
|
|||
|
|
{
|
|||
|
|
DiscardSupportEquipment(supportEquipment);
|
|||
|
|
}
|
|||
|
|
else if (item is PassiveEquipmentBase passiveEquipment)
|
|||
|
|
{
|
|||
|
|
DiscardPassiveEquipment(passiveEquipment);
|
|||
|
|
}
|
|||
|
|
else if (item is ConsumableBase consumable)
|
|||
|
|
{
|
|||
|
|
DiscardConsumable(consumable, amount);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
item.OnDiscarded();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ================================================================
|
|||
|
|
// 内部实现:获取 / 丢弃各类型道具
|
|||
|
|
// ================================================================
|
|||
|
|
|
|||
|
|
public partial class BackpackSubmodule
|
|||
|
|
{
|
|||
|
|
private void ObtainMainWeapon(MainWeaponBase item)
|
|||
|
|
{
|
|||
|
|
if (!mainWeapons.Contains(item))
|
|||
|
|
{
|
|||
|
|
mainWeapons.Add(item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void DiscardMainWeapon(MainWeaponBase item)
|
|||
|
|
{
|
|||
|
|
if (mainWeapons.Contains(item))
|
|||
|
|
{
|
|||
|
|
mainWeapons.Remove(item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ObtainSupportEquipment(SupportEquipmentBase item)
|
|||
|
|
{
|
|||
|
|
if (!supportEquipments.Contains(item))
|
|||
|
|
{
|
|||
|
|
supportEquipments.Add(item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void DiscardSupportEquipment(SupportEquipmentBase item)
|
|||
|
|
{
|
|||
|
|
if (supportEquipments.Contains(item))
|
|||
|
|
{
|
|||
|
|
supportEquipments.Remove(item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ObtainPassiveEquipment(PassiveEquipmentBase item)
|
|||
|
|
{
|
|||
|
|
if (!passiveEquipments.Contains(item))
|
|||
|
|
{
|
|||
|
|
passiveEquipments.Add(item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void DiscardPassiveEquipment(PassiveEquipmentBase item)
|
|||
|
|
{
|
|||
|
|
if (passiveEquipments.Contains(item))
|
|||
|
|
{
|
|||
|
|
passiveEquipments.Remove(item);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ObtainConsumable(ConsumableBase item, int amount = -1)
|
|||
|
|
{
|
|||
|
|
ConsumableBase existingItem = consumables.Find(c => c.GetType() == item.GetType());
|
|||
|
|
if (existingItem != null)
|
|||
|
|
{
|
|||
|
|
// 背包中已有同类型消耗品,堆叠
|
|||
|
|
int stack = amount == -1 ? item.stackAmount : amount;
|
|||
|
|
existingItem.Add(stack);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 首次获得,加入列表并设置初始堆叠
|
|||
|
|
consumables.Add(item);
|
|||
|
|
if (amount >= 0)
|
|||
|
|
{
|
|||
|
|
item.stackAmount = amount;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void DiscardConsumable(ConsumableBase item, int amount = -1)
|
|||
|
|
{
|
|||
|
|
if (consumables.Contains(item))
|
|||
|
|
{
|
|||
|
|
ConsumableBase existingItem = consumables.Find(consumable => consumable.GetType() == item.GetType());
|
|||
|
|
existingItem.stackAmount -= amount == -1 ? item.stackAmount : amount; // 如果amount为-1则丢弃全部
|
|||
|
|
if (existingItem.stackAmount <= 0)
|
|||
|
|
{
|
|||
|
|
consumables.Remove(existingItem);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ================================================================
|
|||
|
|
// 内部实现:Resources 路径 / Container 解析
|
|||
|
|
// ================================================================
|
|||
|
|
|
|||
|
|
public partial class BackpackSubmodule
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 根据道具具体类型查找其基类对应的 Resources 子文件夹名。
|
|||
|
|
/// </summary>
|
|||
|
|
private static string ResolveSubfolder(Type itemType)
|
|||
|
|
{
|
|||
|
|
foreach (var kvp in ItemTypeToSubfolder)
|
|||
|
|
{
|
|||
|
|
if (kvp.Key.IsAssignableFrom(itemType))
|
|||
|
|
return kvp.Value;
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 根据道具类型返回对应的父级 Container Transform。
|
|||
|
|
/// </summary>
|
|||
|
|
private Transform GetContainerForType(Type itemType)
|
|||
|
|
{
|
|||
|
|
if (typeof(MainWeaponBase).IsAssignableFrom(itemType)) return owner.mainWeaponContainer;
|
|||
|
|
if (typeof(SupportEquipmentBase).IsAssignableFrom(itemType)) return owner.supportEquipmentContainer;
|
|||
|
|
if (typeof(PassiveEquipmentBase).IsAssignableFrom(itemType)) return owner.passiveEquipmentContainer;
|
|||
|
|
if (typeof(ConsumableBase).IsAssignableFrom(itemType)) return owner.consumableContainer;
|
|||
|
|
return owner.player.transform;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|