174 lines
6.0 KiB
TypeScript
174 lines
6.0 KiB
TypeScript
/**
|
||
* @file IBoxComp.ts
|
||
* @description 技能/装备详情弹窗组件(IBox,UI 视图层)
|
||
*
|
||
* 职责:
|
||
* 1. 作为详情弹窗,展示单个技能或装备的 **功能描述详情**。
|
||
* 2. 通过 init(args) 接收技能 UUID、装备 UUID 或运行时技能数据。
|
||
* 3. 使用单个 RichText 以 BBCode 展示名称(蓝色)、CD/持续时间(亮绿色)、结构化效果描述。
|
||
* 4. 点击弹窗任意区域关闭自身。
|
||
*
|
||
* 关键设计:
|
||
* - 复用 HeroSkillDesc 的结构化模板渲染,数值实时从配置读取,杜绝漂移。
|
||
* - 技能:展示名称、等级、CD、效果描述(SkillDescSet 模板)。
|
||
* - 装备:展示名称、全部驻场光环词条(FieldDescSet 模板)。
|
||
* - 不走 oops.gui,由调用方手动实例化并添加到场景,通过 init() 初始化。
|
||
*
|
||
* 依赖:
|
||
* - HeroSkillDesc(buildSingleSkillRich / buildEquipRich)—— 描述生成
|
||
* - SkillSet / FieldSkillSet(SkillSet)—— 技能/光环静态配置
|
||
* - EquipSet(findEquipByUuid)—— 装备静态配置
|
||
*/
|
||
import { _decorator, Label, NodeEventType, RichText } from "cc";
|
||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||
import { SkillSet, SkillOverrides } from "../common/config/SkillSet";
|
||
import { buildEquipRich, buildSingleSkillRich } from "../common/config/HeroSkillDesc";
|
||
import { findEquipByUuid } from "../common/config/EquipSet";
|
||
const { ccclass, property } = _decorator;
|
||
|
||
/** IBox 初始化参数 */
|
||
export interface IBoxInitArgs {
|
||
/** SkillSet 技能 UUID(技能详情模式) */
|
||
skillUuid?: number;
|
||
/** 技能等级(默认 1) */
|
||
skillLv?: number;
|
||
/** 技能覆写参数(如药水卡自定义档位) */
|
||
overrides?: SkillOverrides;
|
||
/** EquipSet 装备 UUID(装备详情模式) */
|
||
equipUuid?: number;
|
||
/** 运行时技能数据(运行时技能详情模式,取第一个技能展示) */
|
||
skills?: Record<number, { uuid: number; lv: number; cd: number; ccd: number }>;
|
||
}
|
||
|
||
/**
|
||
* IBoxComp —— 技能/装备详情弹窗视图组件
|
||
*
|
||
* 由调用方通过 instantiate + addChild 手动实例化,然后调用 init(args) 初始化。
|
||
* 示例:
|
||
* const node = instantiate(this.iboxPrefab);
|
||
* this.node.addChild(node);
|
||
* node.getComponent(IBoxComp).init({ skillUuid: 6301, anchorPos: this.node.worldPosition });
|
||
*/
|
||
@ccclass('IBoxComp')
|
||
@ecs.register('IBoxComp', false)
|
||
export class IBoxComp extends CCComp {
|
||
// ======================== 编辑器绑定节点 ========================
|
||
@property(RichText)
|
||
massage_node: RichText = null!
|
||
|
||
/**
|
||
* 初始化并渲染弹窗内容。
|
||
* @param args 见 IBoxInitArgs
|
||
*/
|
||
public init(args: IBoxInitArgs) {
|
||
this.render(args);
|
||
}
|
||
|
||
/** 绑定点击关闭事件 */
|
||
onLoad() {
|
||
this.node.on(NodeEventType.TOUCH_END, this.onTapClose, this);
|
||
}
|
||
|
||
/** 解绑点击事件 */
|
||
onDestroy() {
|
||
super.onDestroy();
|
||
if (this.node && this.node.isValid) {
|
||
this.node.off(NodeEventType.TOUCH_END, this.onTapClose, this);
|
||
}
|
||
}
|
||
|
||
/** ECS 组件移除时销毁节点 */
|
||
reset() {
|
||
this.node.destroy();
|
||
}
|
||
|
||
// ======================== 渲染逻辑 ========================
|
||
|
||
/**
|
||
* 主渲染方法:根据 args 模式分发到技能详情或装备详情。
|
||
*/
|
||
private render(args: IBoxInitArgs) {
|
||
// 装备详情模式
|
||
if (args?.equipUuid !== undefined) {
|
||
this.renderEquip(args.equipUuid);
|
||
return;
|
||
}
|
||
// 运行时技能详情模式(取第一个技能)
|
||
if (args?.skills) {
|
||
const first = Object.values(args.skills)[0];
|
||
if (first) {
|
||
this.renderSkill(first.uuid, first.lv);
|
||
return;
|
||
}
|
||
}
|
||
// 技能详情模式
|
||
if (args?.skillUuid !== undefined) {
|
||
this.renderSkill(args.skillUuid, args.skillLv ?? 1, args.overrides);
|
||
return;
|
||
}
|
||
this.setHeroName("");
|
||
this.setRichText("暂无信息");
|
||
}
|
||
|
||
/**
|
||
* 渲染技能详情:名称、等级、CD、结构化效果描述。
|
||
* @param skillUuid SkillSet 技能 UUID
|
||
* @param lv 技能等级
|
||
* @param overrides 可选覆写参数
|
||
*/
|
||
private renderSkill(skillUuid: number, lv: number, overrides?: SkillOverrides) {
|
||
const skill = SkillSet[skillUuid];
|
||
if (!skill) {
|
||
this.setHeroName("");
|
||
this.setRichText("未知技能");
|
||
return;
|
||
}
|
||
this.setHeroName(skill.name);
|
||
this.setRichText(buildSingleSkillRich(skillUuid, overrides, lv));
|
||
}
|
||
|
||
/**
|
||
* 渲染装备详情:名称、全部驻场光环词条。
|
||
* @param equipUuid EquipSet 装备 UUID
|
||
*/
|
||
private renderEquip(equipUuid: number) {
|
||
const equip = findEquipByUuid(equipUuid);
|
||
if (!equip) {
|
||
this.setHeroName("");
|
||
this.setRichText("未知装备");
|
||
return;
|
||
}
|
||
this.setHeroName(equip.name);
|
||
this.setRichText(buildEquipRich(equip.field ?? []));
|
||
}
|
||
|
||
/** 将 BBCode 文本写入 RichText */
|
||
private setRichText(text: string) {
|
||
if (this.massage_node && this.massage_node.isValid) {
|
||
this.massage_node.string = text;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置标题名称标签
|
||
* @param name 技能/装备名称
|
||
*/
|
||
private setHeroName(name: string) {
|
||
const bgNode = this.node.getChildByName("Bg");
|
||
const nameNode = bgNode?.getChildByName("name");
|
||
const valNode = nameNode?.getChildByName("val");
|
||
const label = valNode?.getComponent(Label) || valNode?.getComponentInChildren(Label);
|
||
if (label) {
|
||
label.string = name;
|
||
}
|
||
}
|
||
|
||
/** 点击弹窗任意区域关闭自身 */
|
||
private onTapClose() {
|
||
if (this.node && this.node.isValid) {
|
||
this.node.destroy();
|
||
}
|
||
}
|
||
}
|