Files
Cielonos/Assets/Scripts/MainGame/Items/Base/ExtenderBase.cs

175 lines
5.8 KiB
C#
Raw Normal View History

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