diff --git a/assets/script/game/common/config/HeroSkillDesc.ts b/assets/script/game/common/config/HeroSkillDesc.ts index 0985cfc8..fd5ae87e 100644 --- a/assets/script/game/common/config/HeroSkillDesc.ts +++ b/assets/script/game/common/config/HeroSkillDesc.ts @@ -1,44 +1,141 @@ -import { heroInfo, SkillTriggerName, SkillTriggerType } from "./heroSet"; -import { FieldSkillSet, SkillSet } from "./SkillSet"; +/** + * @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, + 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 }[] | undefined; + const arr = hero[key] as { s_uuid: number; t_num: number; overrides?: SkillOverrides }[] | undefined; if (!arr?.length) continue; - const triggerName = SkillTriggerName[key] ?? key; + // 从 SkillTriggerDesc 取模板,如 "攻击n次"、"受击n次"、"召唤时" 等 + const tpl = SkillTriggerDesc[key] ?? key; for (const item of arr) { - const skill = SkillSet[item.s_uuid]; - if (!skill) continue; - const suffix = item.t_num > 1 ? ` (每${item.t_num}次触发)` : ""; - lines.push(`${triggerName}: ${skill.name}${suffix}`); + // 通过 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 triggerName = SkillTriggerName[SkillTriggerType.Field] ?? "光环"; + const tpl = SkillTriggerDesc[SkillTriggerType.Field] ?? "场上存活"; for (const uuid of fieldUuids) { const fs = FieldSkillSet[uuid]; - if (fs) lines.push(`${triggerName}: ${fs.info}`); + 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 triggerName = SkillTriggerName[SkillTriggerType.Revive] ?? "复活"; - const skill = SkillSet[revive.s_uuid]; - if (skill) lines.push(`${triggerName}: ${skill.name}`); + 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");