2026-05-10 11:47:55 -04:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
using Sirenix.OdinInspector;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
2026-05-23 08:27:50 -04:00
|
|
|
|
namespace Cielonos.MainGame.Inventory
|
2026-05-10 11:47:55 -04:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 扩展器基类:作为 PassiveEquipment 附着在指定类型的宿主道具上,为其提供额外能力。
|
|
|
|
|
|
/// <para>
|
|
|
|
|
|
/// 获取时自动在背包中查找第一个类型匹配的宿主并绑定;丢弃时自动解绑。
|
|
|
|
|
|
/// </para>
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public abstract class ExtenderBase : PassiveEquipmentBase
|
|
|
|
|
|
{
|
|
|
|
|
|
[ValueDropdown("GetHostTypeOptions")]
|
|
|
|
|
|
public Type hostType;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 当前绑定的宿主道具。未绑定时为 null。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public ItemBase Host { get; private set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 当前扩展器是否已成功绑定到宿主。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool IsAttached => Host != null;
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnObtained()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (hostType == null) throw new Exception("尚未对Host类型进行初始化,检查子类OnObtained函数,或者在Prefab中否设置了hostType");
|
|
|
|
|
|
|
|
|
|
|
|
base.OnObtained();
|
|
|
|
|
|
|
|
|
|
|
|
ItemBase host = FindHost();
|
|
|
|
|
|
|
|
|
|
|
|
if (host != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Attach(host);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning($"[{GetType().Name}] 背包中未找到类型为 {hostType} 的宿主道具,扩展器暂不生效。");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnDiscarded()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (IsAttached)
|
|
|
|
|
|
{
|
|
|
|
|
|
Detach();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
base.OnDiscarded();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 将扩展器绑定到指定宿主。若已绑定到其它宿主,会先解绑。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Attach(ItemBase host)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (host == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning($"[{GetType().Name}] 尝试绑定到 null 宿主。");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (Host == host) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果已绑定到别的宿主,先解绑
|
|
|
|
|
|
if (IsAttached)
|
|
|
|
|
|
{
|
|
|
|
|
|
Detach();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Host = host;
|
|
|
|
|
|
host.RegisterExtender(this);
|
|
|
|
|
|
OnAttached(host);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 从当前宿主解绑。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Detach()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!IsAttached) return;
|
|
|
|
|
|
|
|
|
|
|
|
ItemBase previousHost = Host;
|
|
|
|
|
|
previousHost.UnregisterExtender(this);
|
|
|
|
|
|
Host = null;
|
|
|
|
|
|
OnDetached(previousHost);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 扩展器成功绑定到宿主后调用。子类在此订阅事件、修改数据或注入行为。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="host">已绑定的宿主道具实例。</param>
|
|
|
|
|
|
protected virtual void OnAttached(ItemBase host) { }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 扩展器从宿主解绑后调用。子类在此清理事件订阅和还原修改。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="previousHost">之前绑定的宿主道具实例。</param>
|
|
|
|
|
|
protected virtual void OnDetached(ItemBase previousHost) { }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 在背包中查找第一个类型匹配的宿主道具。
|
|
|
|
|
|
/// 默认搜索所有道具列表(主武器、辅助装备、被动装备)。
|
|
|
|
|
|
/// 子类可重写以自定义查找逻辑(如指定特定实例)。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>找到的宿主实例,不存在则返回 null。</returns>
|
|
|
|
|
|
protected virtual ItemBase FindHost()
|
|
|
|
|
|
{
|
|
|
|
|
|
var backpack = player.inventorySc.backpackSm;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var mainWeapon in backpack.mainWeapons.Where(mainWeapon => mainWeapon.GetType() == hostType))
|
|
|
|
|
|
{
|
|
|
|
|
|
return mainWeapon;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var support in backpack.supportEquipments.Where(support => support.GetType() == hostType))
|
|
|
|
|
|
{
|
|
|
|
|
|
return support;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var passive in backpack.passiveEquipments.Where(passive => passive.GetType() == hostType))
|
|
|
|
|
|
{
|
|
|
|
|
|
return passive;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 收集所有可作为宿主的具体道具类型:MainWeaponBase、SupportEquipmentBase、PassiveEquipmentBase
|
|
|
|
|
|
/// 的非抽象子类,但排除 ExtenderBase 及其子类。
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private static IEnumerable<ValueDropdownItem<Type>> GetHostTypeOptions()
|
|
|
|
|
|
{
|
|
|
|
|
|
Type[] baseTypes =
|
|
|
|
|
|
{
|
|
|
|
|
|
typeof(MainWeaponBase),
|
|
|
|
|
|
typeof(SupportEquipmentBase),
|
|
|
|
|
|
typeof(PassiveEquipmentBase)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Type extenderType = typeof(ExtenderBase);
|
|
|
|
|
|
|
|
|
|
|
|
IEnumerable<Type> candidateTypes = Assembly.GetAssembly(typeof(ItemBase))
|
|
|
|
|
|
.GetTypes()
|
|
|
|
|
|
.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
|
|
|
|
|
|
.Where(t => baseTypes.Any(bt => bt.IsAssignableFrom(t)))
|
|
|
|
|
|
.Where(t => !extenderType.IsAssignableFrom(t))
|
|
|
|
|
|
.OrderBy(t => t.Name);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (Type type in candidateTypes)
|
|
|
|
|
|
{
|
|
|
|
|
|
string category = GetCategoryLabel(type);
|
|
|
|
|
|
yield return new ValueDropdownItem<Type>($"{category}/{type.Name}", type);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static string GetCategoryLabel(Type type)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (typeof(MainWeaponBase).IsAssignableFrom(type)) return "MainWeapon";
|
|
|
|
|
|
if (typeof(SupportEquipmentBase).IsAssignableFrom(type)) return "SupportEquipment";
|
|
|
|
|
|
return "PassiveEquipment";
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|