175 lines
5.8 KiB
C#
175 lines
5.8 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using Sirenix.OdinInspector;
|
||
using UnityEngine;
|
||
|
||
namespace Cielonos.MainGame.Inventory
|
||
{
|
||
/// <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
|
||
}
|
||
}
|