/** * @file HeroSkillDesc.ts * @description 英雄技能描述生成器 * * 职责: * 将英雄配置(heroInfo)中的特殊触发技能转换为可读的描述文本, * 供 HInfoComp 的 info_node 显示。每行一个技能,用换行符连接。 * * 生成格式: * {触发条件}:{技能名} {效果描述} * 例: "受击2次:护盾 护盾2次" * 例: "攻击1次:攻击强化 全体友方攻击力提升5点,持续1次" * 例: "场上存活:攻击加成 英雄攻击力+20%" * * 依赖: * - heroSet :SkillTriggerDesc(触发条件模板)、SkillTriggerType(触发类型枚举)、heroInfo * - SkillSet :SkillSet(技能基础配置)、mergeSkillParams(合并覆盖参数)、SkillKind(技能类型枚举) * - FieldSkillSet(驻场技能配置) */ import { heroInfo, SkillTriggerDesc, SkillTriggerType } from "./heroSet"; import { FieldSkillSet, mergeSkillParams, SkillKind, SkillOverrides, SkillSet } from "./SkillSet"; /** * 需要遍历的触发技能类型列表(不含 Field 和 Revive,这两者结构不同需单独处理) * Field:number[](驻场技能 uuid 列表) * Revive:{ s_uuid, r_num, upr }(单个对象) */ const TRIGGER_KEYS: SkillTriggerType[] = [ SkillTriggerType.Call, // 召唤时触发 SkillTriggerType.Dead, // 死亡时触发 SkillTriggerType.FStart, // 战斗开始时触发 SkillTriggerType.FEnd, // 战斗结束时触发 SkillTriggerType.Atking, // 攻击后触发 SkillTriggerType.Atked, // 受击后触发 ]; /** * 根据合并后的技能配置生成效果描述文本 * * 按 SkillKind 分类输出: * - Heal :"治疗伙伴{ap}" * - Shield :"护盾{ap}次"(ap 表示可抵挡次数) * - Gold :"金币+{gold}" * - Support :直接使用技能自身的 info 字段(如"全体友方攻击力提升5点,持续1次") * - Damage :"伤害{ap}%" + 可选的暴击/冰冻/击退/多段附加描述 * * @param skill 经过 mergeSkillParams 合并 overrides 后的完整技能配置 * @returns 效果描述字符串,各部分用空格连接 */ function buildEffectDesc(skill: ReturnType): string { const parts: string[] = []; const kind = skill.kind; if (kind === SkillKind.Heal) { // 治疗类:ap 为治疗量 parts.push(`治疗伙伴${skill.ap}`); } else if (kind === SkillKind.Shield) { // 护盾类:ap 为可抵挡的攻击次数 parts.push(`护盾${skill.ap}次`); } else if (kind === SkillKind.Gold) { // 金币类:gold 为获取金币数 parts.push(`金币+${skill.gold ?? 0}`); } else if (kind === SkillKind.Support) { // 辅助类(buff):直接使用技能的 info 描述文案 parts.push(String(skill.info)); } else if (kind === SkillKind.Damage || kind === undefined) { // 伤害类(含无 kind 的默认技能):ap 为攻击力百分比 parts.push(`伤害${skill.ap}%`); // 多段伤害 if (skill.hit_count > 1) parts.push(`${skill.hit_count}段`); // 附加暴击率 if (skill.crt) parts.push(`暴击+${skill.crt}%`); // 附加冰冻概率 if (skill.frz) parts.push(`冰冻+${skill.frz}%`); // 附加击退概率 if (skill.bck) parts.push(`击退+${skill.bck}%`); } return parts.join(" "); } /** * 将英雄的特殊触发技能配置转换为可读描述文本 * * 处理流程: * 1. 遍历 6 种触发类型(call/dead/fstart/fend/atking/atked),从 hero[key] 取出 * {s_uuid, t_num, overrides?}[],通过 s_uuid 查 SkillSet 获取技能基础配置, * 用 mergeSkillParams 合并 overrides 得到实际参数,再拼成一行描述。 * 2. 单独处理 field(驻场技能):从 FieldSkillSet 查找配置,输出光环效果。 * 3. 单独处理 revive(复活技能):结构与上述不同,是单个对象而非数组。 * * 输出示例(5006 疾风刺客): * "攻击1次:攻击强化 全体友方攻击力提升5点,持续1次 * 死亡时:攻击强化 全体友方攻击力提升8点,持续1次" * * @param hero 英雄静态配置(HeroInfo 中的条目) * @returns 多行技能描述文本,每行一个技能,用 \n 分隔 */ export function buildSkillDesc(hero: heroInfo): string { const lines: string[] = []; // ---- 第一步:处理 6 种标准触发技能(结构均为 {s_uuid, t_num, overrides?}[]) ---- for (const key of TRIGGER_KEYS) { const arr = hero[key] as { s_uuid: number; t_num: number; overrides?: SkillOverrides }[] | undefined; if (!arr?.length) continue; // 从 SkillTriggerDesc 取模板,如 "攻击n次"、"受击n次"、"召唤时" 等 const tpl = SkillTriggerDesc[key] ?? key; for (const item of arr) { // 通过 s_uuid 查找技能基础配置 const base = SkillSet[item.s_uuid]; if (!base) continue; // 合并 overrides 得到实际技能参数(ap/hit_count/crt/frz/bck 等可能被覆盖) const skill = mergeSkillParams(base, item.overrides); // 将模板中的 "n" 替换为实际触发次数 const trigger = tpl.replace("n", String(item.t_num)); lines.push(`${trigger}:${base.name} ${buildEffectDesc(skill)}`); } } // ---- 第二步:处理驻场技能(field)—— 结构为 number[],uuid 指向 FieldSkillSet ---- const fieldUuids = hero[SkillTriggerType.Field] as number[] | undefined; if (fieldUuids?.length) { const tpl = SkillTriggerDesc[SkillTriggerType.Field] ?? "场上存活"; for (const uuid of fieldUuids) { const fs = FieldSkillSet[uuid]; if (fs) lines.push(`${tpl}:${fs.name} ${fs.info}`); } } // ---- 第三步:处理复活技能(revive)—— 结构为 {s_uuid, r_num, upr} 单个对象 ---- const revive = hero[SkillTriggerType.Revive] as { s_uuid: number; r_num: number; upr: number } | undefined; if (revive) { const base = SkillSet[revive.s_uuid]; if (base) { const skill = mergeSkillParams(base); const tpl = SkillTriggerDesc[SkillTriggerType.Revive] ?? "复活时"; lines.push(`${tpl} : ${base.name} ${buildEffectDesc(skill)}`); } } return lines.join("\n"); }