From 88d7bdae4766ea9b2c68c5f5aa9870aabe0797e9 Mon Sep 17 00:00:00 2001 From: walkpan Date: Sat, 23 May 2026 12:11:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(skill):=20=E6=94=AF=E6=8C=81=E6=8A=80?= =?UTF-8?q?=E8=83=BD=E5=8F=82=E6=95=B0=E8=87=AA=E5=AE=9A=E4=B9=89=E8=A6=86?= =?UTF-8?q?=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 本次修改实现同技能不同角色的差异化技能效果: 1. 新增SkillOverrides接口与mergeSkillParams工具函数,用于合并基础技能配置和角色覆盖参数 2. 更新英雄配置、属性组件、触发辅助系统与施法系统以适配该机制 3. 为盾骑士、医师添加示例差异化配置,验证功能可行性 4. 整理技能配置,删除冗余重复的旧技能条目 5. 新增技能重构设计计划文档,替换旧的迁移计划文档 --- ...-22-skill-template-refactor-design-plan.md | 49 ++ assets/script/game/common/config/SkillSet.ts | 63 ++- assets/script/game/common/config/heroSet.ts | 26 +- assets/script/game/hero/HeroAttrsComp.ts | 14 +- assets/script/game/hero/SCastSystem.ts | 48 +- assets/script/game/hero/SkillTriggerHelper.ts | 21 +- assets/script/game/skill/Skill.ts | 7 +- .../specs/2026-05-22-skill-migration-plan.md | 385 ---------------- ...26-05-22-skill-template-refactor-design.md | 419 ++++-------------- 9 files changed, 221 insertions(+), 811 deletions(-) create mode 100644 .trae/documents/2026-05-22-skill-template-refactor-design-plan.md delete mode 100644 docs/superpowers/specs/2026-05-22-skill-migration-plan.md diff --git a/.trae/documents/2026-05-22-skill-template-refactor-design-plan.md b/.trae/documents/2026-05-22-skill-template-refactor-design-plan.md new file mode 100644 index 00000000..5f9cb032 --- /dev/null +++ b/.trae/documents/2026-05-22-skill-template-refactor-design-plan.md @@ -0,0 +1,49 @@ +# 技能配置重构实施计划 + +## 1. 探索阶段总结 (Current State Analysis) +经过对代码库的探索,当前技能触发系统及相关配置状态如下: +- `SkillSet.ts` 包含了所有技能的基座配置 `SkillConfig` 和技能字典 `SkillSet`。 +- `heroSet.ts` 中的 `HeroInfo` 存放英雄和怪物的配置,目前 `call`, `dead`, `fstart`, `fend` 被定义为 `number[]`,而 `atking` 和 `atked` 被定义为 `{s_uuid: number, t_num: number}[]`。 +- `HeroAttrsComp.ts` 内部存储了与配置一致的触发技能结构。 +- `SkillTriggerHelper.ts` 负责判定并向外派发 `GameEvent.TriggerSkill` 事件,由 `SCastSystem.ts` 监听并执行 `forceCastTriggerSkill`。 +- 目前 `SCastSystem.ts` 在收集技能目标和施放技能时,都是直接从 `SkillSet[s_uuid]` 读取 `config`,没有针对具体角色的差异化机制。 + +## 2. 拟议变更 (Proposed Changes) + +### 2.1 修改 `SkillSet.ts` +- **新增接口**:定义 `SkillOverrides` 接口,包含所有可被角色覆盖的技能参数(如 `TGroup`, `ap`, `hit_count`, `buffs` 等,全部为可选字段)。 +- **新增函数**:编写 `mergeSkillParams(config, overrides?)` 函数,将基座 `config` 和角色覆盖 `overrides` 进行合并,返回一个新的 `SkillConfig` 对象。 + +### 2.2 修改 `heroSet.ts` +- **扩展接口**: + - 在 `HSkillInfo` 接口中增加 `overrides?: SkillOverrides;`。 + - 将 `heroInfo` 接口中的触发字段 `call`, `dead`, `fstart`, `fend`, `atking`, `atked` 统一更新为 `{ s_uuid: number; t_num: number; overrides?: SkillOverrides }[]` 结构。 +- **更新配置示例**:按照设计文档更新英雄 `5001`, `5002`, `5301`, `5302` 的配置,为特定的触发技能添加 `overrides` 字段(如盾骑士 5002 全队护盾覆盖)。 + +### 2.3 修改 `HeroAttrsComp.ts` +- **同步类型**:将 `call`, `dead`, `fstart`, `fend`, `atking`, `atked` 的类型同步改为与 `heroInfo` 一致的 `{ s_uuid: number; t_num: number; overrides?: SkillOverrides }[]`。 + +### 2.4 修改 `SkillTriggerHelper.ts` +- **派发支持**: + - 更新 `dispatchSingle` 方法签名,增加 `overrides?: SkillOverrides` 参数,并在 `oops.message.dispatchEvent(GameEvent.TriggerSkill, {...})` 中将其传入。 + - 更新 `handleCall`, `handleDead`, `handleArrayTrigger` 处理逻辑,将原先对 `number[]` 的处理改为对 `{s_uuid, t_num, overrides}` 对象数组的处理,并提取 `overrides` 传递给 `dispatchArray`。 + - 更新 `handleAtking`, `handleAtked` 中的 `dispatchSingle` 调用,传入 `atkConfig.overrides`。 + +### 2.5 修改 `SCastSystem.ts` (关键运行时逻辑) +- **事件监听更新**:在 `onTriggerSkill` 方法的 `args` 参数定义中补充 `overrides?: SkillOverrides`,并传递给 `forceCastTriggerSkill`。 +- **合并逻辑上移(架构优化)**: + - 在 `forceCastTriggerSkill` 和 `castSkill` 的**方法入口处**(而非 `applyFriendlySkillEffects` 内部),第一时间调用 `mergeSkillParams(config, overrides)` 获取 `effective` 技能配置。 + - 将后续所有关于阵营判定(如 `effective.TGroup`)、目标收集、以及传递给 `applyFriendlySkillEffects` / `applyEnemySkillEffects` 的参数全部替换为 `effective`。 + - **为何如此设计**:如果在原设计中仅在 `applyFriendlySkillEffects` 入口处合并,那么前置的**目标选择逻辑**(依赖 `TGroup` 判定是 `Self` 还是 `Team`)将会使用未合并的基础配置,导致类似“自己加盾变为全队加盾”的 `TGroup` 覆盖无法生效。将合并操作前置可以彻底解决这一问题。 +- **主动技能支持**:在 `pickCastSkill` 中,读取 `heroAttrs.skills[s_uuid]?.overrides` 并进行合并判定,同时将 `overrides` 放入返回的 `castPlan` 中,以便 `castSkill` 使用。 + +## 3. 假设与决策 (Assumptions & Decisions) +- **统一触发结构**:虽然 `call`, `dead`, `fstart`, `fend` 不严格需要 `t_num`,但为了类型统一并完全遵守设计规范,统一采用了包含 `t_num` 的对象结构。 +- **合并前置决策**:如上所述,坚决在施放方法入口处进行 `mergeSkillParams` 以保证目标收集逻辑能够感知到 `TGroup` 的变化。这比设计规范中要求的修改范围略有扩大,但对于系统功能的正确实现是必须的。 +- **卡牌技能影响**:卡牌技能(`forceCastCardSkill`)当前没有绑定角色的 `overrides`,因此维持读取基础 `SkillSet` 逻辑不变。 + +## 4. 验证步骤 (Verification steps) +1. 编译 TypeScript 代码,确保 `HeroAttrsComp`, `heroSet`, `SCastSystem` 等修改后的接口和类型无报错。 +2. 启动游戏或运行测试,确认 `5001` (见习战士) 触发的基础护盾只对自己生效。 +3. 确认 `5002` (盾骑士) 受击触发的护盾技能,正确地为全队附加护盾,并且护盾值(ap)与次数(hit_count)符合 `overrides` 配置。 +4. 确认所有旧版英雄技能在无 `overrides` 时能够正确回退到 `SkillSet` 的默认配置,游戏运转正常无异常日志。 \ No newline at end of file diff --git a/assets/script/game/common/config/SkillSet.ts b/assets/script/game/common/config/SkillSet.ts index 941ed81d..dc29872c 100644 --- a/assets/script/game/common/config/SkillSet.ts +++ b/assets/script/game/common/config/SkillSet.ts @@ -158,6 +158,31 @@ export interface SkillConfig { info:string, // 技能描述 } +export interface SkillOverrides { + TGroup?: TGroup; + ap?: number; + hit_count?: number; + hitcd?: number; + crt?: number; + frz?: number; + bck?: number; + buffs?: BuffConf[]; +} + +/** + * 将覆盖参数合并到基础技能配置中 + * @param config 基础技能配置 + * @param overrides 技能覆盖参数 + * @returns 合并后的新技能配置 + */ +export function mergeSkillParams(config: SkillConfig, overrides?: SkillOverrides): SkillConfig { + if (!overrides) return config; + return { + ...config, + ...overrides + }; +} + export const SkillUpList = { 1001:{ap:0,hit_count:0,buff_ap:0,buff_hp:0,bck:0,frz:0,crt:0,num:0} } @@ -264,27 +289,12 @@ export const SkillSet: Record = { 6301:{ uuid:6301,name:"护盾",sp_name:"buff_wind",icon:"1255",TGroup:TGroup.Self,readyAnm:"up_blue",endAnm:"",act:"atk", DTType:DTType.single,kind:SkillKind.Shield,ap:3,hit_count:1,hitcd:0.2,speed:720,with:0,ready:0.2,EAnm:0,DAnm:"",IType:IType.support, - RType:RType.fixed,EType:EType.animationEnd,buffs:[],info:"为自己添加护盾,可抵挡3次伤害", + RType:RType.fixed,EType:EType.animationEnd,buffs:[],info:"为伙伴/自己添加护盾,可抵挡3次伤害", }, 6302: { - uuid:6302,name:"群体治疗",sp_name:"buff_wind",icon:"1292",TGroup:TGroup.Team,readyAnm:"up_green",endAnm:"",act:"atk", + uuid:6302,name:"治疗",sp_name:"buff_wind",icon:"1292",TGroup:TGroup.Team,readyAnm:"up_green",endAnm:"",act:"atk", DTType:DTType.single,kind:SkillKind.Heal,ap:300,hit_count:1,hitcd:0.2,speed:720,with:0,ready:0.2,EAnm:0,DAnm:"",IType:IType.support, - RType:RType.fixed,EType:EType.animationEnd,buffs:[],info:"为全体友方恢复攻击力300%的生命值", - }, - 6303:{ - uuid:6303,name:"强化护盾",sp_name:"buff_wind",icon:"1255",TGroup:TGroup.Self,readyAnm:"up_blue",endAnm:"",act:"atk", - DTType:DTType.single,kind:SkillKind.Shield,ap:3,hit_count:1,hitcd:0.2,speed:720,with:0,ready:0.2,EAnm:0,DAnm:"",IType:IType.support, - RType:RType.fixed,EType:EType.animationEnd,buffs:[],info:"为自己添加强化护盾,可抵挡3次伤害", - }, - 6304: { - uuid:6304,name:"持续恢复",sp_name:"buff_wind",icon:"1292",TGroup:TGroup.Team,readyAnm:"up_green",endAnm:"",act:"atk", - DTType:DTType.single,kind:SkillKind.Heal,ap:200,hit_count:3,hitcd:0.2,speed:720,with:0,ready:0.2,EAnm:0,DAnm:"",IType:IType.support, - RType:RType.fixed,EType:EType.animationEnd,buffs:[],info:"为全体友方持续恢复,共3次,每次恢复攻击力200%的生命值", - }, - 6305:{ - uuid:6305,name:"团队护盾",sp_name:"buff_wind",icon:"1255",TGroup:TGroup.Team,readyAnm:"up_blue",endAnm:"",act:"atk", - DTType:DTType.single,kind:SkillKind.Shield,ap:2,hit_count:3,hitcd:0.2,speed:720,with:0,ready:0.2,EAnm:0,DAnm:"",IType:IType.support, - RType:RType.fixed,EType:EType.animationEnd,buffs:[],info:"为全体友方添加护盾,每人可抵挡2次伤害,持续3次", + RType:RType.fixed,EType:EType.animationEnd,buffs:[],info:"治疗伙伴/自己", }, //==========================buff 技能===================== 6401:{ @@ -298,25 +308,10 @@ export const SkillSet: Record = { RType:RType.fixed,EType:EType.animationEnd,buffs:[{buff:Attrs.hp_max,value:20}],info:"全体友方最大生命值提升20点,持续1次", }, 6403:{ - uuid:6403,name:"全面强化",sp_name:"buff_wind",icon:"1255",TGroup:TGroup.Team,readyAnm:"up_hp",endAnm:"",act:"atk", + uuid:6403,name:"超能强化",sp_name:"buff_wind",icon:"1255",TGroup:TGroup.Team,readyAnm:"up_hp",endAnm:"",act:"atk", DTType:DTType.single,kind:SkillKind.Support,ap:0,hit_count:1,hitcd:0.2,speed:720,with:0,ready:0.2,EAnm:0,DAnm:"",IType:IType.support, RType:RType.fixed,EType:EType.animationEnd,buffs:[{buff:Attrs.ap,value:5},{buff:Attrs.hp_max,value:20}],info:"全体友方攻击力提升5点,最大生命值提升20点,持续1次", }, - 6404:{ - uuid:6404,name:"持续攻击强化",sp_name:"buff_wind",icon:"1255",TGroup:TGroup.Team,readyAnm:"up_ap",endAnm:"",act:"atk", - DTType:DTType.single,kind:SkillKind.Support,ap:0,hit_count:3,hitcd:0.2,speed:720,with:0,ready:0.2,EAnm:0,DAnm:"",IType:IType.support, - RType:RType.fixed,EType:EType.animationEnd,buffs:[{buff:Attrs.ap,value:2}],info:"全体友方攻击力提升2点,持续3次", - }, - 6405:{ - uuid:6405,name:"持续生命强化",sp_name:"buff_wind",icon:"1255",TGroup:TGroup.Team,readyAnm:"up_hp",endAnm:"",act:"atk", - DTType:DTType.single,kind:SkillKind.Support,ap:0,hit_count:3,hitcd:0.2,speed:720,with:0,ready:0.2,EAnm:0,DAnm:"",IType:IType.support, - RType:RType.fixed,EType:EType.animationEnd,buffs:[{buff:Attrs.hp_max,value:10}],info:"全体友方最大生命值提升10点,持续3次", - }, - 6406:{ - uuid:6406,name:"持续全面强化",sp_name:"buff_wind",icon:"1255",TGroup:TGroup.Team,readyAnm:"up_ap",endAnm:"",act:"atk", - DTType:DTType.single,kind:SkillKind.Support,ap:0,hit_count:3,hitcd:0.2,speed:720,with:0,ready:0.2,EAnm:0,DAnm:"",IType:IType.support, - RType:RType.fixed,EType:EType.animationEnd,buffs:[{buff:Attrs.ap,value:2},{buff:Attrs.hp_max,value:10}],info:"全体友方攻击力提升2点,最大生命值提升10点,持续3次", - }, 6501:{ uuid:6501,name:"复活",sp_name:"buff_wind",icon:"1255",TGroup:TGroup.Self,readyAnm:"up_ap",endAnm:"",act:"atk", DTType:DTType.single,kind:SkillKind.Support,ap:50,hit_count:3,hitcd:0.2,speed:720,with:0,ready:0.2,EAnm:0,DAnm:"",IType:IType.support, diff --git a/assets/script/game/common/config/heroSet.ts b/assets/script/game/common/config/heroSet.ts index 9db57c54..a6d9cf1b 100644 --- a/assets/script/game/common/config/heroSet.ts +++ b/assets/script/game/common/config/heroSet.ts @@ -1,5 +1,6 @@ import { v3 } from "cc" import { BoxSet, FacSet } from "./GameSet" +import { SkillOverrides, TGroup } from "./SkillSet" export enum HType { Melee = 0, @@ -87,27 +88,28 @@ export interface heroInfo { type: HType; // 攻击定位(近战/中程/远程) hp: number; // 生命值上限 ap: number; // 攻击力 - call?:number[]; // 召唤后触发的技能uuid列表 - dead?:number[]; // 死亡后触发的技能uuid列表 - fstart?:number[]; // 战斗开始时释放的技能uuid列表 - fend?:number[]; // 战斗结束时释放的技能uuid列表 + call?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 召唤后触发的技能配置 + dead?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 死亡后触发的技能配置 + fstart?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 战斗开始时释放的技能配置 + fend?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 战斗结束时释放的技能配置 field?:number[]; // 驻场技能uuid列表,英雄在场时对全局生效 - atking?:{s_uuid:number, t_num:number}[]; // 普通攻击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的普攻次数 - atked?:{s_uuid:number, t_num:number}[]; // 受击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的受击次数 + atking?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 普通攻击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的普攻次数 + atked?:{s_uuid:number, t_num:number, overrides?: SkillOverrides}[]; // 受击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的受击次数 revive?:{s_uuid:number,r_num:number,upr:number}; // 复活技能配置,s_uuid: 技能id, r_num: 触发所需的复活次数, upr 等级对复活次数的影响 -dis?: number; // 攻击距离(像素) + dis?: number; // 攻击距离(像素) speed?: number; // 移动速度(像素/秒) skills: Record ; // 携带技能ID列表 info: string; // 描述文案 } /** - * 英雄/怪物基础信息接口 + * 技能基础信息接口 */ export interface HSkillInfo { - uuid: number; // 唯一标识(英雄5000段,怪物5200段) + uuid: number; // 唯一标识(技能6000段等) lv:number; // 技能等级 cd:number; // 技能cd ccd:number; // 占位当前cd,用于cd计时 + overrides?: SkillOverrides; // 角色专属参数覆盖 } /* *=============英雄配置列表================ @@ -132,9 +134,9 @@ export interface HSkillInfo { export const HeroInfo: Record = { // ========== 近战英雄 ========== 5001:{uuid:5001,name:"见习战士",path:"hk2", fac:FacSet.HERO,cards_lv:1,lv:1,type:HType.Melee,hp:150,ap:25, - skills:{6002:{uuid:6002,lv:1,cd:1.5,ccd:0}},atking:[{s_uuid:6301,t_num:2}],info:" "}, - 5002:{uuid:5002,name:"盾骑士",path:"hk1", fac:FacSet.HERO,cards_lv:3,lv:1,type:HType.Melee,hp:150,ap:75, skills:{6002:{uuid:6002,lv:1,cd:1.5,ccd:0}},atked:[{s_uuid:6301,t_num:2}],info:" "}, + 5002:{uuid:5002,name:"盾骑士",path:"hk1", fac:FacSet.HERO,cards_lv:3,lv:1,type:HType.Melee,hp:150,ap:75, + skills:{6002:{uuid:6002,lv:1,cd:1.5,ccd:0}},atked:[{s_uuid:6301,t_num:2,overrides:{TGroup:TGroup.Team,ap:2,hit_count:3}}],info:" "}, 5003:{uuid:5003,name:"战士3",path:"hk3", fac:FacSet.HERO,cards_lv:2,lv:1,type:HType.Melee,hp:100,ap:100, skills:{6002:{uuid:6002,lv:1,cd:1.5,ccd:0}},info:" "}, 5004:{uuid:5004,name:"战士4",path:"hk4", fac:FacSet.HERO,cards_lv:4,lv:1,type:HType.Melee,hp:100,ap:200, @@ -169,7 +171,7 @@ export const HeroInfo: Record = { 5301:{uuid:5301,name:"牧师",path:"hh1", fac:FacSet.HERO,cards_lv:1,lv:1,type:HType.Long,hp:115,ap:50, skills:{6004:{uuid:6004,lv:1,cd:1.2,ccd:0}},atking:[{s_uuid:6302,t_num:2}],info:"" }, 5302:{uuid:5302,name:"医师",path:"hh2", fac:FacSet.HERO,cards_lv:2,lv:1,type:HType.Long,hp:130,ap:50, - skills:{6004:{uuid:6004,lv:1,cd:1.2,ccd:0}}, atking:[{s_uuid:6304,t_num:2}],info:""}, + skills:{6004:{uuid:6004,lv:1,cd:1.2,ccd:0}}, atking:[{s_uuid:6302,t_num:2,overrides:{hit_count:3,ap:200}}],info:""}, // ========== 辅助英雄 ========== 5401:{uuid:5401,name:"刺客1",path:"hc1", fac:FacSet.HERO,cards_lv:1,lv:1,type:HType.Long,hp:115,ap:50, diff --git a/assets/script/game/hero/HeroAttrsComp.ts b/assets/script/game/hero/HeroAttrsComp.ts index 009d1a9b..3e2204c1 100644 --- a/assets/script/game/hero/HeroAttrsComp.ts +++ b/assets/script/game/hero/HeroAttrsComp.ts @@ -3,7 +3,7 @@ import { HeroDisVal, HeroInfo, HSkillInfo, HType } from "../common/config/heroSe import { mLogger } from "../common/Logger"; import { Timer } from "db://oops-framework/core/common/timer/Timer"; import { FacSet, FightSet } from "../common/config/GameSet"; -import { FieldSkillSet, FieldSkillType } from "../common/config/SkillSet"; +import { FieldSkillSet, FieldSkillType, SkillOverrides } from "../common/config/SkillSet"; import { smc } from "../common/SingletonModuleComp"; import { TalentConfig, TalentType } from "../common/config/TalentSet"; import { FieldSkillHelper } from "./FieldSkillHelper"; @@ -33,12 +33,12 @@ export class HeroAttrsComp extends ecs.Comp { skills: Record = {}; // ==================== 触发类技能 ==================== - call?: number[]; - dead?: number[]; - fstart?: number[]; - fend?: number[]; - atking?: {s_uuid: number, t_num: number}[]; - atked?: {s_uuid: number, t_num: number}[]; + call?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; + dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; + fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; + fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; + atking?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; + atked?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; revive?: {s_uuid: number, r_num: number, upr: number}; // ==================== 特殊属性 ==================== diff --git a/assets/script/game/hero/SCastSystem.ts b/assets/script/game/hero/SCastSystem.ts index ac3b0e33..4229f4b1 100644 --- a/assets/script/game/hero/SCastSystem.ts +++ b/assets/script/game/hero/SCastSystem.ts @@ -2,7 +2,7 @@ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ec import { Vec3, Prefab, instantiate, tween, Node } from "cc"; import { HeroAttrsComp } from "./HeroAttrsComp"; import { HeroViewComp } from "./HeroViewComp"; -import { DTType, RType, SkillConfig, SkillKind, SkillSet, SkillUpList, TGroup } from "../common/config/SkillSet"; +import { DTType, RType, SkillConfig, SkillKind, SkillSet, SkillUpList, TGroup, SkillOverrides, mergeSkillParams } from "../common/config/SkillSet"; import { Skill } from "../skill/Skill"; import { smc } from "../common/SingletonModuleComp"; import { HeroDisVal, HeroInfo, HType } from "../common/config/heroSet"; @@ -48,7 +48,8 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate triggerType?: string, isCardSkill?: boolean, card_lv?: number, - targetPos?: Vec3 + targetPos?: Vec3, + overrides?: any }) { if (!args || !args.s_uuid) return; @@ -60,7 +61,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate // 常规英雄技能触发 if (!args.heroAttrs || !args.heroView) return; - this.forceCastTriggerSkill(args.s_uuid, args.heroAttrs, args.heroView, args.triggerType); + this.forceCastTriggerSkill(args.s_uuid, args.heroAttrs, args.heroView, args.triggerType, args.overrides); } /** @@ -140,7 +141,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate skill.load(actualStartPos, parent, s_uuid, targetPos.clone(), mockView, mockAttrs, skillLv, 0); } /** 空施法计划:用于“当前无可施法技能”时的统一返回 */ - private readonly emptyCastPlan = { skillId: 0, skillLv: 1, isFriendly: false, targetPos: null as Vec3 | null, targetEids: [] as number[] }; + private readonly emptyCastPlan = { skillId: 0, skillLv: 1, isFriendly: false, targetPos: null as Vec3 | null, targetEids: [] as number[], overrides: undefined as SkillOverrides | undefined }; /** 查询缓存:避免每帧重复创建 matcher */ private heroMatcher: ecs.IMatcher | null = null; @@ -188,7 +189,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate * 强制执行触发技能(召唤/死亡触发) * 忽略CD、状态、动画前摇,直接生效 */ - public forceCastTriggerSkill(s_uuid: number, heroAttrs: HeroAttrsComp, heroView: HeroViewComp, triggerType?: string) { + public forceCastTriggerSkill(s_uuid: number, heroAttrs: HeroAttrsComp, heroView: HeroViewComp, triggerType?: string, overrides?: SkillOverrides) { // 播放相应的触发动画 if (triggerType === 'call') { heroView.playReady("yellow"); @@ -199,8 +200,9 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate } // 如果是敌方攻击技能,必须在战斗中才能释放;友方增益/护盾则允许在非战斗中释放 - const config = SkillSet[s_uuid]; + let config = SkillSet[s_uuid]; if (!config) return; + config = mergeSkillParams(config, overrides); const isEnemyTarget = !this.isSelfSkill(config.TGroup) && !this.isFriendlySkill(config.TGroup); if (isEnemyTarget && !smc.mission.in_fight) return; @@ -245,7 +247,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate this.applyFriendlySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, friendlyTargets, null); } else { const enemyTargetPos = this.resolveRepeatCastTargetPos(targetPos, i); - this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i); + this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i, overrides); } } } @@ -255,7 +257,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate * 选择顺序:技能候选列表顺序 + 条件过滤(CD、目标可达、目标类型匹配)。 * 返回内容同时包含“对敌施法”和“友方施法”两种执行所需数据。 */ - private pickCastSkill(heroAttrs: HeroAttrsComp, heroView: HeroViewComp): { skillId: number; skillLv: number; isFriendly: boolean; targetPos: Vec3 | null; targetEids: number[] } { + private pickCastSkill(heroAttrs: HeroAttrsComp, heroView: HeroViewComp): { skillId: number; skillLv: number; isFriendly: boolean; targetPos: Vec3 | null; targetEids: number[]; overrides?: SkillOverrides } { const type = heroAttrs.type as HType; const maxRange = this.resolveMaxCastRange(heroAttrs, type); const target = this.findNearestEnemyInRange(heroAttrs, heroView, maxRange); @@ -263,24 +265,26 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate const selfEid = heroView.ent?.eid; for (const s_uuid of skillCandidates) { if (!s_uuid) continue; - const config = SkillSet[s_uuid]; + let config = SkillSet[s_uuid]; if (!config) continue; if (!heroAttrs.isSkillReady(s_uuid)) continue; const skillLv = heroAttrs.getSkillLevel(s_uuid); + const overrides = heroAttrs.skills[s_uuid]?.overrides; + config = mergeSkillParams(config, overrides); if (this.isSelfSkill(config.TGroup)) { if (typeof selfEid !== "number") continue; - return { skillId: s_uuid, skillLv, isFriendly: true, targetPos: null, targetEids: [selfEid] }; + return { skillId: s_uuid, skillLv, isFriendly: true, targetPos: null, targetEids: [selfEid], overrides }; } if (this.isFriendlySkill(config.TGroup)) { const includeSelf = config.TGroup === TGroup.Ally; const friendlyEids = this.collectFriendlyTargetEids(heroAttrs.fac, selfEid, includeSelf); if (friendlyEids.length === 0) continue; - return { skillId: s_uuid, skillLv, isFriendly: true, targetPos: null, targetEids: friendlyEids }; + return { skillId: s_uuid, skillLv, isFriendly: true, targetPos: null, targetEids: friendlyEids, overrides }; } if (!target || !heroView.node || !target.node) continue; const targetPos = this.resolveEnemyCastTargetPos(config, heroAttrs, heroView, target, maxRange); if (!targetPos) continue; - return { skillId: s_uuid, skillLv, isFriendly: false, targetPos, targetEids: [] }; + return { skillId: s_uuid, skillLv, isFriendly: false, targetPos, targetEids: [], overrides }; } return this.emptyCastPlan; } @@ -291,14 +295,18 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate * - 延迟到出手时机后,按技能目标类型分发到对敌/友方效果处理 * - 触发技能CD */ - private castSkill(castPlan: { skillId: number; skillLv: number; isFriendly: boolean; targetPos: Vec3 | null; targetEids: number[] }, heroAttrs: HeroAttrsComp, heroView: HeroViewComp) { + private castSkill(castPlan: { skillId: number; skillLv: number; isFriendly: boolean; targetPos: Vec3 | null; targetEids: number[]; overrides?: SkillOverrides }, heroAttrs: HeroAttrsComp, heroView: HeroViewComp) { if (!smc.mission.in_fight) return; const s_uuid = castPlan.skillId; const skillLv = castPlan.skillLv; - const config = SkillSet[s_uuid]; + const overrides = castPlan.overrides; + let config = SkillSet[s_uuid]; const sUp = SkillUpList[s_uuid] ? SkillUpList[s_uuid]:SkillUpList[1001]; const cNum = Math.min(2, Math.max(0, Math.floor(sUp.num ?? 0))); if (!config) return; + + config = mergeSkillParams(config, overrides); + //播放前摇技能动画 heroView.playReady(config.readyAnm); //播放角色攻击动画 @@ -331,7 +339,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate continue; } const enemyTargetPos = this.resolveRepeatCastTargetPos(castPlan.targetPos, i); - this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i); + this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i, overrides); } }, delay); heroAttrs.triggerSkillCD(s_uuid); @@ -360,24 +368,24 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate * 创建技能实体(投射物/范围体等)。 * 仅用于对敌伤害技能的实体化表现与碰撞伤害分发。 */ - private createSkillEntity(s_uuid: number, skillLv: number, caster: HeroViewComp,cAttrsComp: HeroAttrsComp, targetPos: Vec3, castIndex: number = 0) { + private createSkillEntity(s_uuid: number, skillLv: number, caster: HeroViewComp,cAttrsComp: HeroAttrsComp, targetPos: Vec3, castIndex: number = 0, overrides?: SkillOverrides) { if (!caster.node || !caster.node.isValid) return; const parent = caster.node.parent; if (!parent) return; const skill = ecs.getEntity(Skill); const startPos = this.resolveRepeatCastStartPos(caster.node.position, castIndex); - skill.load(startPos, parent, s_uuid, targetPos.clone(), caster, cAttrsComp, skillLv, 0); + skill.load(startPos, parent, s_uuid, targetPos.clone(), caster, cAttrsComp, skillLv, 0, overrides); } /** * 对敌技能效果处理。 * 当前职责:仅处理伤害技能并创建技能实体。 */ - private applyEnemySkillEffects(s_uuid: number, skillLv: number, config: SkillConfig, heroView: HeroViewComp, cAttrsComp: HeroAttrsComp, targetPos: Vec3 | null, castIndex: number = 0) { + private applyEnemySkillEffects(s_uuid: number, skillLv: number, config: SkillConfig, heroView: HeroViewComp, cAttrsComp: HeroAttrsComp, targetPos: Vec3 | null, castIndex: number = 0, overrides?: SkillOverrides) { const kind = config.kind ?? SkillKind.Damage; if (kind !== SkillKind.Damage) return; if (config.ap <= 0 || !targetPos) return; - this.createSkillEntity(s_uuid, skillLv, heroView, cAttrsComp, targetPos, castIndex); + this.createSkillEntity(s_uuid, skillLv, heroView, cAttrsComp, targetPos, castIndex, overrides); } private resolveRepeatCastStartPos(startPos: Readonly, castIndex: number): Vec3 { @@ -512,7 +520,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate } /** 判定施法计划是否具备有效目标 */ - private hasCastTarget(castPlan: { skillId: number; skillLv: number; isFriendly: boolean; targetPos: Vec3 | null; targetEids: number[] }): boolean { + private hasCastTarget(castPlan: { skillId: number; skillLv: number; isFriendly: boolean; targetPos: Vec3 | null; targetEids: number[]; overrides?: SkillOverrides }): boolean { if (castPlan.skillId === 0) return false; if (castPlan.isFriendly) return castPlan.targetEids.length > 0; return !!castPlan.targetPos; diff --git a/assets/script/game/hero/SkillTriggerHelper.ts b/assets/script/game/hero/SkillTriggerHelper.ts index 17dc4fe8..5fdb7e67 100644 --- a/assets/script/game/hero/SkillTriggerHelper.ts +++ b/assets/script/game/hero/SkillTriggerHelper.ts @@ -3,7 +3,7 @@ import { GameEvent, SkillTriggerType } from "../common/config/GameEvent"; import { HeroAttrsComp } from "./HeroAttrsComp"; import { HeroViewComp } from "./HeroViewComp"; import { FacSet } from "../common/config/GameSet"; -import { FieldSkillType } from "../common/config/SkillSet"; +import { FieldSkillType, SkillOverrides } from "../common/config/SkillSet"; import { TalentType } from "../common/config/TalentSet"; import { smc } from "../common/SingletonModuleComp"; import { FieldSkillHelper } from "./FieldSkillHelper"; @@ -105,7 +105,7 @@ export class SkillTriggerHelper { model.atking.forEach(atkConfig => { // atk_count 代表已进行的普攻次数。当其余数刚好整除配置阈值时触发。 if (model.atk_count > 0 && model.atk_count % atkConfig.t_num === 0) { - this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atking); + this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atking, atkConfig.overrides); } }); } @@ -119,7 +119,7 @@ export class SkillTriggerHelper { model.atked.forEach(atkConfig => { // atked_count 代表已承受的受击次数。当其余数刚好整除配置阈值时触发。 if (model.atked_count > 0 && model.atked_count % atkConfig.t_num === 0) { - this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atked); + this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atked, atkConfig.overrides); } }); } @@ -127,28 +127,29 @@ export class SkillTriggerHelper { /** * 通用的数组型触发器处理(适用于 FStart / FEnd 等无需额外判定的简单列表触发) */ - private static handleArrayTrigger(uuids: number[] | undefined, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) { - if (!uuids || uuids.length === 0) return; - this.dispatchArray(uuids, model, view, type); + private static handleArrayTrigger(configs: {s_uuid: number, t_num: number, overrides?: SkillOverrides}[] | undefined, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) { + if (!configs || configs.length === 0) return; + this.dispatchArray(configs, model, view, type); } /** * 批量派发技能事件 */ - private static dispatchArray(uuids: number[], model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) { - uuids.forEach(uuid => this.dispatchSingle(uuid, model, view, type)); + private static dispatchArray(configs: {s_uuid: number, t_num?: number, overrides?: SkillOverrides}[], model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) { + configs.forEach(config => this.dispatchSingle(config.s_uuid, model, view, type, config.overrides)); } /** * 单一技能事件派发底层接口 * 事件发出后,将由 SCastSystem 的 forceCastTriggerSkill 监听拦截并执行无视CD的强制施法 */ - private static dispatchSingle(s_uuid: number, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) { + private static dispatchSingle(s_uuid: number, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType, overrides?: SkillOverrides) { oops.message.dispatchEvent(GameEvent.TriggerSkill, { s_uuid: s_uuid, heroAttrs: model, heroView: view, - triggerType: type + triggerType: type, + overrides: overrides }); } } diff --git a/assets/script/game/skill/Skill.ts b/assets/script/game/skill/Skill.ts index 1a599f86..e692ae75 100644 --- a/assets/script/game/skill/Skill.ts +++ b/assets/script/game/skill/Skill.ts @@ -1,6 +1,6 @@ import { BoxCollider2D, instantiate, Node, Prefab, v3, Vec3, NodePool } from "cc"; import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS"; -import { EType, SkillSet, SkillUpList } from "../common/config/SkillSet"; +import { EType, SkillSet, SkillUpList, SkillOverrides, mergeSkillParams } from "../common/config/SkillSet"; import { oops } from "db://oops-framework/core/Oops"; import { HeroAttrsComp } from "../hero/HeroAttrsComp"; import { Attrs } from "../common/config/HeroAttrs"; @@ -101,12 +101,13 @@ export class Skill extends ecs.Entity { this.addComponents(SMoveDataComp); } load(startPos: Vec3, parent: Node, s_uuid: number, targetPos: Vec3, - caster:HeroViewComp,cAttrsComp:HeroAttrsComp, skill_lv:number=0, ext_dmg:number=0) { - const config = SkillSet[s_uuid]; + caster:HeroViewComp,cAttrsComp:HeroAttrsComp, skill_lv:number=0, ext_dmg:number=0, overrides?: SkillOverrides) { + let config = SkillSet[s_uuid]; if (!config) { mLogger.error(this.debugMode, 'Skill', "[Skill] 技能配置不存在:", s_uuid); return; } + config = mergeSkillParams(config, overrides); // 加载预制体 const path = `game/skill/atk/${config.sp_name}`; diff --git a/docs/superpowers/specs/2026-05-22-skill-migration-plan.md b/docs/superpowers/specs/2026-05-22-skill-migration-plan.md deleted file mode 100644 index 0d51211e..00000000 --- a/docs/superpowers/specs/2026-05-22-skill-migration-plan.md +++ /dev/null @@ -1,385 +0,0 @@ -# 技能基座重构 — 迁移计划 - -基于 spec: `docs/superpowers/specs/2026-05-22-skill-template-refactor-design.md` - -## 迁移原则 - -- 每个步骤是**最小功能单元**,完成后可独立验证 -- 使用**兼容层**过渡:新旧接口并存期间,代码同时读取两个位置 -- 验证方式:每步完成后运行游戏,确认技能系统行为不变 - ---- - -## Phase 1:类型定义(零风险,不改行为) - -### Step 1.1:新增 SkillTemplate/SkillDefaults/HeroOverrides 接口 - -**文件**:`SkillSet.ts` - -**改动**: -- 在现有 `SkillConfig` 下方新增 `SkillTemplate`、`SkillDefaults`、`HeroOverrides` 接口定义 -- 新增 `TriggerSkillConf` 接口 -- 更新 `BuffConf`:新增 `attr` 字段,保留旧 `buff` 字段(标注 `@deprecated`) -- 不删除任何旧代码 - -**验证**:编译通过,游戏行为不变(新接口尚未被使用) - -### Step 1.2:新增 resolveSkillParams 工具函数 - -**文件**:`SkillSet.ts` 或新建 `SkillResolve.ts` - -**改动**: -```typescript -export function resolveSkillParams( - template: SkillTemplate, - heroSkill?: HSkillInfo, - triggerConf?: TriggerSkillConf -): { kind: SkillKind } & SkillDefaults { - return { - kind: template.kind, - ...template.defaults, - ...heroSkill?.overrides, - ...triggerConf?.overrides, - }; -} -``` - -**验证**:编译通过,写一个简单的测试用例验证三级覆盖逻辑 - ---- - -## Phase 2:兼容层(数据迁移的安全网) - -### Step 2.1:新增兼容读取函数 - -**文件**:`SkillSet.ts` - -**改动**:新增辅助函数,从旧 `SkillConfig` 构造 `SkillTemplate` + `SkillDefaults`: - -```typescript -/** 从旧 SkillConfig 提取 defaults 字段(过渡期使用) */ -export function legacyDefaults(config: SkillConfig): SkillDefaults { - return { - TGroup: config.TGroup, - ap: config.ap, - t_num: 1, // 旧配置无此字段,使用默认值 - hit_count: config.hit_count, - hitcd: config.hitcd, - crt: config.crt, - frz: config.frz, - bck: config.bck, - buffs: config.buffs, - }; -} - -/** 从旧 SkillConfig 提取 template 字段 */ -export function legacyTemplate(config: SkillConfig): SkillTemplate { - return { - uuid: config.uuid, - name: config.name, - sp_name: config.sp_name, - icon: config.icon, - kind: config.kind ?? SkillKind.Damage, - act: config.act, - readyAnm: config.readyAnm, - endAnm: config.endAnm, - EAnm: config.EAnm, - DAnm: config.DAnm, - ready: config.ready, - IType: config.IType, - RType: config.RType, - EType: config.EType, - speed: config.speed, - DTType: config.DTType, - with: config.with, - bezier_start_y: config.bezier_start_y, - bezier_mid_y: config.bezier_mid_y, - bezier_arc: config.bezier_arc, - time: config.time, - defaults: legacyDefaults(config), - info: config.info, - }; -} -``` - -**验证**:编译通过,可写测试验证提取结果与原配置一致 - ---- - -## Phase 3:数据迁移(逐个技能类型) - -### Step 3.1:迁移 buff 技能数据(6401-6406, 6501) - -**文件**:`SkillSet.ts` - -**改动**:将 6401-6406、6501 从 `SkillConfig` 格式改为 `SkillTemplate` 格式: -- 顶层数值字段 → `defaults` -- 显式添加 `kind: SkillKind.Support` -- `buffs` 中的 `buff:` → `attr:` - -**存储方式**:新增 `BuffSkillTemplates: Record`,旧数据保留在 `SkillSet` 中 - -**验证**:游戏运行,确认卡牌释放 buff 技能效果不变 - -### Step 3.2:迁移辅助技能数据(6301-6305) - -**文件**:`SkillSet.ts` - -**改动**:将护盾/治疗技能从旧格式改为 `SkillTemplate` 格式 -- 护盾类:`kind: SkillKind.Shield` -- 治疗类:`kind: SkillKind.Heal` - -**存储方式**:新增 `SupportSkillTemplates: Record`,旧数据保留 - -**验证**:游戏运行,确认护盾和治疗技能效果不变 - -### Step 3.3:迁移必杀技数据(6104-6107) - -**文件**:`SkillSet.ts` - -**改动**:将必杀技从旧格式改为 `SkillTemplate` 格式 -- 全部 `kind: SkillKind.Damage` -- `crt`/`frz` 移入 defaults - -**验证**:游戏运行,确认必杀技伤害和特效不变 - -### Step 3.4:迁移基础攻击技能数据(6001-6103) - -**文件**:`SkillSet.ts` - -**改动**:将所有攻击技能从旧格式改为 `SkillTemplate` 格式 -- 全部 `kind: SkillKind.Damage` - -**验证**:游戏运行,确认所有英雄和怪物的普攻、远程攻击正常 - -### Step 3.5:合并为新表 - -**文件**:`SkillSet.ts` - -**改动**:将分散的 `BuffSkillTemplates`、`SupportSkillTemplates` 等合并为一个: -```typescript -export const SkillTemplates: Record = { ... 所有已迁移的技能 ... } -``` - -**验证**:通过所有新模板创建技能,确认与旧 `SkillSet` 行为一致 - ---- - -## Phase 4:消费者适配(逐文件) - -### Step 4.1:适配 Skill.ts(技能实体创建) - -**文件**:`assets/script/game/skill/Skill.ts` - -**改动**: -- `skill.load()` 参数从 `config: SkillConfig` 改为 `template: SkillTemplate` -- 内部读取 `template.sp_name`(表现字段,未变) -- 传递给 SMoveSystem/STimeComp 的 config 改为 template -- `ap`/`crt`/`frz`/`hit_count` 从 `template.defaults` 读取(通过兼容函数) - -**关键行**: -- ~112: `config.sp_name` → `template.sp_name` -- ~153: `config.EType` → `template.EType` -- ~181: `config.RType` → `template.RType` -- ~183-185: `config.bezier_*` → `template.bezier_*` -- ~195: `config.time` → `template.time` -- ~196: `config.hitcd` → `template.hitcd` -- ~211-214: `config.crt`/`config.frz`/`config.ap`/`config.hit_count` → `template.defaults.*` - -**验证**:释放各种技能,确认弹道动画和伤害数值正确 - -### Step 4.2:适配 SMoveSystem.ts(技能移动) - -**文件**:`assets/script/game/skill/SMoveSystem.ts` - -**改动**: -- 类型引用 `SkillConfig` → `SkillTemplate` -- 读取的字段(speed、RType、EType、bezier_*)均在模板顶层,无需改逻辑 - -**验证**:确认弹道轨迹(直线/贝塞尔/固定)正常 - -### Step 4.3:适配 STimeComp.ts(技能时间控制) - -**文件**:`assets/script/game/skill/STimeComp.ts` - -**改动**: -- 类型引用 `SkillConfig` → `SkillTemplate` -- 只读 `EType`(模板顶层字段),逻辑不变 - -**验证**:确认技能持续时间、碰撞消失逻辑正常 - -### Step 4.4:适配 SkillView.ts(技能视图) - -**文件**:`assets/script/game/skill/SkillView.ts` - -**改动**: -- 类型引用 `SkillConfig` → `SkillTemplate` -- 只读 `EType`(模板顶层字段),逻辑不变 - -**验证**:确认技能特效显示正常 - -### Step 4.5:适配 HeroViewComp.ts(命中动画) - -**文件**:`assets/script/game/hero/HeroViewComp.ts` - -**改动**: -- ~507: `SConf?.DAnm` → `template?.DAnm`(字段仍在顶层,只改引用名) - -**验证**:确认受击动画(普通/冰/火/风)显示正确 - -### Step 4.6:适配 SCastSystem.ts(施法系统 — 核心) - -**文件**:`assets/script/game/hero/SCastSystem.ts` - -**改动**: -- 引入 `resolveSkillParams()` 和 `legacyTemplate()` -- `forceCastSkill()` 中 `SkillSet[s_uuid]` → `SkillTemplates[s_uuid]`,通过 `resolveSkillParams(template, undefined, undefined)` 获取参数 -- `applyFriendlySkillEffects()` 改为接收 resolved params -- `applyActualFriendlyEffect()` 中: - - `config.buffs` → `resolved.buffs` - - `buffConf.buff` → `buffConf.attr`(同时兼容旧 `buff` 字段) -- 伤害技能路径中 `config.ap`/`config.crt`/`config.frz`/`config.hit_count` → 从 resolved params 读取 -- `config.TGroup` → `resolved.TGroup` -- `config.kind` → `template.kind`(模板固定) - -**验证**: -1. 英雄普攻伤害正确 -2. 必杀技伤害和特效正确 -3. 护盾/治疗/buff 效果正确 -4. 卡牌技能效果正确 - -### Step 4.7:适配 HeroAtkSystem.ts(伤害系统) - -**文件**:`assets/script/game/hero/HeroAtkSystem.ts` - -**改动**: -- ~292: `sConf.ap` → 从 resolved defaults 读取 -- ~221: `reviveSkillConf.ap` → 从 resolved defaults 读取 -- ~236: `reviveSkillConf.readyAnm` → 从 template 读取(模板顶层字段) - -**验证**: -1. 英雄伤害数值正确 -2. 复活技能恢复血量正确 - -### Step 4.8:适配 SkillTriggerHelper.ts(触发技能) - -**文件**:`assets/script/game/hero/SkillTriggerHelper.ts` - -**改动**: -- 触发技能时,将 `triggerConf.overrides` 传递给 `resolveSkillParams()` - -**验证**:确认受击触发护盾、普攻触发治疗等触发技能正常 - -### Step 4.9:适配 Hero.ts + Mon.ts(初始化) - -**文件**:`Hero.ts`、`Mon.ts` - -**改动**: -- 英雄/怪物初始化时,将 `heroInfo.atking`/`atked` 等配置传入 HeroAttrsComp -- HeroAttrsComp 中的类型同步更新为 `TriggerSkillConf[]` - -**验证**:英雄和怪物的触发技能正常工作 - -### Step 4.10:适配显示层文件 - -**文件**:`CardComp.ts`、`IBoxComp.ts`、`TooltipCom.ts`、`SIconComp.ts`、`SkillBoxComp.ts`、`HlistComp.ts` - -**改动**: -- 仅引用名变更(`SkillConfig` → `SkillTemplate`) -- 读取的字段(name、icon、IType、info)均在模板顶层,无需改逻辑 - -**验证**:卡牌显示、信息面板、技能图标显示正常 - ---- - -## Phase 5:角色定制(新功能启用) - -### Step 5.1:更新 heroSet.ts 触发配置类型 - -**文件**:`heroSet.ts` - -**改动**: -- `atking`/`atked` 等类型从 `{s_uuid, t_num}` 内联 → `TriggerSkillConf[]` -- 向后兼容:`overrides` 可选,旧配置无需修改 - -**验证**:编译通过,游戏行为不变(尚未添加 overrides) - -### Step 5.2:为首个角色添加 overrides - -**文件**:`heroSet.ts` - -**改动**:选一个典型角色(如盾骑士 5002)作为试点,为其触发技能添加 overrides: - -```typescript -5002: { - ..., - atked: [{ s_uuid: 6301, t_num: 2, - overrides: { TGroup: TGroup.Team, ap: 2, t_num: 5, hit_count: 3 } - }], -}, -``` - -**验证**:盾骑士受击后给全队加盾,而非只给自己加。对比修改前后行为确认覆盖生效 - -### Step 5.3:逐步为其他角色添加 overrides - -**文件**:`heroSet.ts` - -**改动**:逐个角色添加 overrides,每添加一个验证一次 - -**验证**:每个角色的技能行为符合设计意图 - ---- - -## Phase 6:清理 - -### Step 6.1:删除旧 SkillConfig 接口和 SkillSet 数据 - -**文件**:`SkillSet.ts` - -**改动**: -- 删除 `SkillConfig` 接口 -- 删除旧 `SkillSet` 数据(已被 `SkillTemplates` 替代) -- 删除 `legacyTemplate()`/`legacyDefaults()` 兼容函数 -- `SkillTemplates` 重命名回 `SkillSet`(或保持新名,全局替换引用) - -**验证**:全面回归测试 - -### Step 6.2:BuffConf 清理 - -**文件**:`SkillSet.ts`、`SCastSystem.ts` - -**改动**: -- 删除 `BuffConf.buff` 字段,只保留 `attr` -- 所有配置数据中 `buff:` → `attr:` - -**验证**:buff 技能效果正确 - -### Step 6.3:删除 kind 回退逻辑 - -**文件**:`SCastSystem.ts` - -**改动**: -- 移除 `config.kind ?? SkillKind.Damage` 和 `config.kind ?? SkillKind.Support` 回退 -- 全部使用 `template.kind` - -**验证**:所有技能类型判定正确 - ---- - -## 验证检查清单 - -每个 Step 完成后,运行以下检查: - -| 检查项 | 方法 | -|--------|------| -| 编译通过 | 无 TypeScript 错误 | -| 普攻正常 | 放置近战/远程英雄,观察普攻弹道和伤害 | -| 必杀技正常 | 触发必杀技,确认特效和伤害 | -| 护盾正常 | 触发护盾技能,确认护盾层数和显示 | -| 治疗正常 | 触发治疗技能,确认血量恢复 | -| Buff 正常 | 触发 buff 技能,确认属性变化 | -| 卡牌技能正常 | 使用卡牌释放各种技能 | -| 触发技能正常 | 受击/攻击/死亡/开始触发链正常 | -| 怪物行为正常 | 怪物攻击和技能释放正常 | -| 无控制台报错 | 检查浏览器控制台无 JS 错误 | diff --git a/docs/superpowers/specs/2026-05-22-skill-template-refactor-design.md b/docs/superpowers/specs/2026-05-22-skill-template-refactor-design.md index 2c1b9bd5..1a3cc1d8 100644 --- a/docs/superpowers/specs/2026-05-22-skill-template-refactor-design.md +++ b/docs/superpowers/specs/2026-05-22-skill-template-refactor-design.md @@ -1,409 +1,148 @@ -# 技能基座 + 角色定制 重构设计 +# 技能配置重构设计(简化版) ## 背景 -当前技能系统存在以下问题: - -1. **所有技能类型混在一张表** — 攻击、治疗、护盾、buff 共用 `SkillConfig` 接口,字段复用导致每个技能配置含义不同(如 `ap` 在攻击技能是百分比,在护盾是次数,在 buff 是 0) -2. **字段语义混乱** — `hit_count` 在攻击技能是"命中次数",在 buff 技能是"持续次数" -3. **buff 技能包含大量无用字段** — speed、with、EType、RType 等 buff 完全不需要 -4. **同技能不同角色无法差异化** — 所有使用同一技能的角色效果完全相同 -5. **扩展困难** — 新增 buff 属性类型需要改 SCastSystem 的 switch 代码 +当前技能系统核心问题:**同一个技能 UUID,不同角色无法差异化效果**。例如护盾技能 6301,所有角色都是给自己加盾,无法让某个角色给全队加盾。 ## 设计目标 -- SkillSet 作为**基座模板**,定义技能类型分类(伤害/治疗/护盾/buff)+ 表现层(动画、弹道、特效)+ 默认数值 -- `kind`(技能类型)由模板固定,角色不可覆盖 -- heroSet 负责定义**实际效果值**(伤害、治疗量、目标数量、buff 效果) -- 同一个技能 UUID,不同角色可以有完全不同的数值行为 -- 触发技能(atking/atked/fstart/fend)也支持角色定制 -- 卡牌技能通过基座 defaults 正常工作 +- SkillConfig **保持不变**,作为技能基座(动画、弹道、默认数值) +- 角色通过 **overrides** 覆盖技能的可定制参数(ap、TGroup、hit_count、buffs 等) +- 同一个技能 UUID,不同角色可以有不同效果 +- 触发技能(atking/atked)也支持 overrides +- **不迁移 SkillSet 数据**,不拆分接口,不改消费者文件类型引用 -## 配置结构 +## 改动内容(共 3 个接口 + 1 个函数) -### SkillTemplate — 基座模板 +### 1. SkillOverrides — 角色可覆盖的技能参数 ```typescript -/** 技能基座模板 — 定义技能的类型、表现方式和默认数值 */ -interface SkillTemplate { - uuid: number; - name: string; - sp_name: string; // 特效名 - icon: string; - - // 技能类型分类 — 由模板固定,角色不可覆盖 - kind: SkillKind; // Damage | Heal | Shield | Support - - // 动画表现 - act: string; // 角色动画 - readyAnm: string; // 前摇动画 - endAnm: string; - EAnm: number; // 结束动画 ID - DAnm: string; // 命中后动画名(如 "atked_ice"、"atked_fire") - ready: number; // 前摇时间 - - // 弹道/表现行为(技能"怎么飞") - IType: IType; // 近战/远程/辅助 - RType: RType; // 直线/贝塞尔/固定 - EType: EType; // 结束条件 - speed: number; // 移动速度 - DTType: DTType; // 单体/范围(影响碰撞体) - with: number; // 宽度 - bezier_start_y?: number; - bezier_mid_y?: number; - bezier_arc?: number; - time?: number; // timeEnd 持续时间 - - // 基座默认值(卡牌技能等无角色主体时使用) - defaults: SkillDefaults; - - info: string; +/** 角色可覆盖的技能参数 — 所有字段可选 */ +export interface SkillOverrides { + TGroup?: TGroup; + ap?: number; + hit_count?: number; + hitcd?: number; + crt?: number; + frz?: number; + bck?: number; + buffs?: BuffConf[]; } ``` -### SkillDefaults — 可覆盖的逻辑参数 +### 2. HSkillInfo 增加 overrides ```typescript -/** 技能逻辑参数 — 可被角色 overrides 覆盖 */ -interface SkillDefaults { - TGroup: TGroup; // 目标群体(敌方/友方/自身等) - ap: number; // 伤害百分比 / 治疗百分比 / 护盾次数 / buff 固定为 0 - t_num: number; // 目标数量上限 - hit_count: number; // 命中/持续次数 - hitcd: number; // 间隔 - crt?: number; // 额外暴击率 - frz?: number; // 额外冰冻概率 - bck?: number; // 额外击退概率 - buffs?: BuffConf[]; // buff 效果列表(仅 kind=Support 时使用) -} -``` - -### HeroOverrides — 角色定义的技能参数 - -```typescript -/** 角色技能参数覆盖 — 与 SkillDefaults 结构一致,不含 kind */ -interface HeroOverrides { - TGroup?: TGroup; // 覆盖目标群体 - ap?: number; // 覆盖效果值 - t_num?: number; // 覆盖目标数量上限 - hit_count?: number; // 覆盖命中/持续次数 - hitcd?: number; // 覆盖间隔 - crt?: number; // 覆盖暴击率 - frz?: number; // 覆盖冰冻概率 - bck?: number; // 覆盖击退概率 - buffs?: BuffConf[]; // 覆盖 buff 效果列表 -} -``` - -所有字段可选 — 只覆盖需要定制的参数,其余继承基座 defaults。**kind 不可覆盖**。 - -### HSkillInfo — 角色技能信息 - -```typescript -interface HSkillInfo { +export interface HSkillInfo { uuid: number; lv: number; cd: number; ccd: number; - /** 角色专属技能参数,覆盖基座 defaults */ - overrides?: HeroOverrides; + overrides?: SkillOverrides; // 新增:角色专属参数覆盖 } ``` -### TriggerSkillConf — 触发技能配置 +### 3. 触发技能配置增加 overrides ```typescript -/** 触发技能配置 */ -interface TriggerSkillConf { - s_uuid: number; // 触发的技能 UUID - t_num: number; // 激活阈值(攻击/受击多少次后触发),与 SkillDefaults.t_num 无关 - /** 角色定制参数,覆盖基座 defaults */ - overrides?: HeroOverrides; -} +// heroInfo 中触发字段扩展: +atking?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; +atked?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; +call?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; +dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; +fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; +fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; ``` -> **语义区分**:`TriggerSkillConf.t_num` = 激活阈值("打几下触发"),`SkillDefaults.t_num` = 目标数量上限("打几个人")。两者同名但语义完全不同,代码中不会交叉使用。 - -### heroInfo 触发字段类型更新 +### 4. mergeSkillParams — 合并基座 + 覆盖 ```typescript -interface heroInfo { - // ... 现有字段不变 - skills: Record; - call?: TriggerSkillConf[]; - dead?: TriggerSkillConf[]; - fstart?: TriggerSkillConf[]; - fend?: TriggerSkillConf[]; - atking?: TriggerSkillConf[]; - atked?: TriggerSkillConf[]; - revive?: { s_uuid: number; r_num: number; upr: number }; +/** 合并技能基座参数和角色覆盖参数 */ +export function mergeSkillParams( + config: SkillConfig, + overrides?: SkillOverrides +): SkillConfig { + if (!overrides) return config; + return { ...config, ...overrides }; } ``` -向后兼容:旧格式 `{s_uuid, t_num}` 仍然有效,`overrides` 为可选字段。现有代码遍历这些数组时,`overrides` 不存在则跳过,`s_uuid` 和 `t_num` 仍在顶层。 - -## BuffConf 接口更新 - -```typescript -// 旧 -interface BuffConf { - buff: Attrs; - value: number; -} - -// 新 — 字段名更清晰 -interface BuffConf { - attr: Attrs; // 修改哪个属性 - value: number; // 效果值 -} -``` - -重命名范围:SCastSystem.ts:433 的 `switch (buffConf.buff)` → `switch (buffConf.attr)`,以及 SkillSet.ts 中 6401-6406 配置数据中的 `buff:` → `attr:`。 - -## 运行时参数解析 - -### resolveSkillParams — 三级覆盖 - -优先级:**基座 defaults → 角色技能 overrides → 触发配置 overrides** - -```typescript -function resolveSkillParams( - template: SkillTemplate, - heroSkill?: HSkillInfo, - triggerConf?: TriggerSkillConf -): { kind: SkillKind } & Required { - return { - // kind 由模板固定,不参与覆盖 - kind: template.kind, - // 数值参数三级覆盖 - ...template.defaults, - ...heroSkill?.overrides, - ...triggerConf?.overrides, - }; -} -``` - -`kind` 始终取自 `template.kind`,即使 overrides 中误写了 kind 也不会生效。 - -### SkillUpList 升级加成 - -SkillUpList 升级加成作用于 **resolved 之后的参数**,而非直接从 config 读取。调用方式: - -```typescript -const resolved = resolveSkillParams(template, heroSkill, triggerConf); -const sUp = SkillUpList[s_uuid] ?? SkillUpList[1001]; -const finalAp = resolved.ap + sUp.ap * skillLv; -const finalHitCount = resolved.hit_count + sUp.hit_count * skillLv; -const finalCrt = (resolved.crt ?? 0) + sUp.crt * skillLv; -``` - -在 SCastSystem.ts 和 Skill.ts 中,所有当前直接读取 `config.ap` + `sUp.ap` 的地方,统一改为先 resolve 再应用 sUp。 - -### 卡牌技能路径 - -卡牌技能无角色上下文,调用 `resolveSkillParams(template, undefined, undefined)` 返回 `template.defaults`。在 `forceCastCardSkill()` 中使用此方式获取参数。 - -## 迁移注意事项 - -### 攻击技能必须显式设置 kind - -当前攻击技能(6001-6107)的 `SkillConfig` 没有设置 `kind`,运行时通过 `config.kind ?? SkillKind.Damage` 回退。迁移时,所有攻击技能必须显式添加 `kind: SkillKind.Damage`,`?? SkillKind.Damage` 回退逻辑可在迁移完成后移除。 - -### call_hero 字段处置 - -`SkillConfig` 中存在 `call_hero?: number` 字段(召唤技能召唤英雄 ID)。当前无技能配置使用此字段。在 `SkillTemplate` 中暂不保留,如后续需要可通过 `defaults` 扩展或单独处理。 +就这么简单:把 config 展开,把 overrides 展开覆盖上去。没有 overrides 时原样返回。 ## 配置示例 -### 基座配置(SkillSet.ts)— 伤害类 +### SkillSet.ts — 不变 ```typescript -// 普通攻击 — 伤害类基座 -6001: { - uuid: 6001, name: "普通攻击", sp_name: "atk", icon: "1026", - kind: SkillKind.Damage, - act: "atk", readyAnm: "", endAnm: "", EAnm: 0, DAnm: "", ready: 0.2, - IType: IType.Melee, RType: RType.linear, EType: EType.collision, - speed: 720, DTType: DTType.single, with: 0, - defaults: { - TGroup: TGroup.Enemy, ap: 100, t_num: 1, hit_count: 1, hitcd: 0.2, - }, - info: "造成攻击力100%的伤害", -}, - -// 火球 — 伤害类基座,带暴击 -6101: { - uuid: 6101, name: "火球", sp_name: "ball_fire", icon: "1126", - kind: SkillKind.Damage, - act: "atk", readyAnm: "", endAnm: "", EAnm: 0, DAnm: "", ready: 0.2, - IType: IType.remote, RType: RType.linear, EType: EType.collision, - speed: 720, DTType: DTType.single, with: 90, - defaults: { - TGroup: TGroup.Enemy, ap: 100, t_num: 1, hit_count: 1, hitcd: 0.3, crt: 20, - }, - info: "火球攻击", -}, -``` - -### 基座配置 — 治疗类 - -```typescript -// 群体治疗 — 治疗类基座 -6302: { - uuid: 6302, name: "群体治疗", sp_name: "buff_wind", icon: "1292", - kind: SkillKind.Heal, - act: "atk", readyAnm: "up_green", endAnm: "", EAnm: 0, DAnm: "", ready: 0.2, - IType: IType.support, RType: RType.fixed, EType: EType.animationEnd, - speed: 720, DTType: DTType.single, with: 0, - defaults: { - TGroup: TGroup.Team, ap: 300, t_num: 5, hit_count: 1, hitcd: 0.2, - }, - info: "治疗基座", -}, - -// 持续恢复 — 治疗类基座 -6304: { - uuid: 6304, name: "持续恢复", sp_name: "buff_wind", icon: "1292", - kind: SkillKind.Heal, - act: "atk", readyAnm: "up_green", endAnm: "", EAnm: 0, DAnm: "", ready: 0.2, - IType: IType.support, RType: RType.fixed, EType: EType.animationEnd, - speed: 720, DTType: DTType.single, with: 0, - defaults: { - TGroup: TGroup.Team, ap: 200, t_num: 5, hit_count: 3, hitcd: 0.2, - }, - info: "持续恢复基座", -}, -``` - -### 基座配置 — 护盾类 - -```typescript -// 护盾 — 护盾类基座 +// 6301 护盾 — 保持原样 6301: { - uuid: 6301, name: "护盾", sp_name: "buff_wind", icon: "1255", - kind: SkillKind.Shield, - act: "atk", readyAnm: "up_blue", endAnm: "", EAnm: 0, DAnm: "", ready: 0.2, - IType: IType.support, RType: RType.fixed, EType: EType.animationEnd, - speed: 720, DTType: DTType.single, with: 0, - defaults: { - TGroup: TGroup.Self, ap: 3, t_num: 1, hit_count: 1, hitcd: 0.2, - }, - info: "护盾基座", + uuid: 6301, name: "护盾", ..., TGroup: TGroup.Self, kind: SkillKind.Shield, ap: 3, ... }, ``` -### 基座配置 — Buff 类 +### heroSet.ts — 角色差异化 ```typescript -// 攻击强化 — buff 类基座 -6401: { - uuid: 6401, name: "攻击强化", sp_name: "buff_wind", icon: "1255", - kind: SkillKind.Support, - act: "atk", readyAnm: "up_ap", endAnm: "", EAnm: 0, DAnm: "", ready: 0.2, - IType: IType.support, RType: RType.fixed, EType: EType.animationEnd, - speed: 720, DTType: DTType.single, with: 0, - defaults: { - TGroup: TGroup.Team, ap: 0, t_num: 5, hit_count: 1, hitcd: 0.2, - buffs: [{ attr: Attrs.ap, value: 5 }], - }, - info: "攻击力强化", -}, -``` - -### 角色配置(heroSet.ts)— 同技能不同角色差异化 - -```typescript -// 见习战士:受击2次触发护盾 → 自己加3层盾 +// 见习战士:受击2次触发护盾,使用基座默认值(给自己加3次盾) 5001: { - uuid: 5001, name: "见习战士", ..., - skills: { 6002: { uuid: 6002, lv: 1, cd: 1.5, ccd: 0 } }, - atked: [{ s_uuid: 6301, t_num: 2 }], - // 6301 基座 defaults: TGroup.Self, ap:3, t_num:1 - // → 使用基座默认值,给自己加3次盾 + ..., atked: [{ s_uuid: 6301, t_num: 2 }], }, -// 盾骑士:受击2次触发护盾 → 全队加2层盾(覆盖 TGroup 和 ap) +// 盾骑士:受击2次触发护盾,覆盖为全队加2次盾 5002: { - uuid: 5002, name: "盾骑士", ..., - skills: { 6002: { uuid: 6002, lv: 1, cd: 1.5, ccd: 0 } }, - atked: [{ s_uuid: 6301, t_num: 2, - overrides: { TGroup: TGroup.Team, ap: 2, t_num: 5, hit_count: 3 } + ..., atked: [{ s_uuid: 6301, t_num: 2, + overrides: { TGroup: TGroup.Team, ap: 2, hit_count: 3 } }], - // → kind=Shield 由基座固定,TGroup/ap/t_num 由角色覆盖 }, -// 牧师:普攻2次触发群体治疗 → 恢复 300%(使用基座默认值) +// 牧师:普攻2次触发治疗,使用基座默认值 5301: { - uuid: 5301, name: "牧师", ..., - skills: { 6004: { uuid: 6004, lv: 1, cd: 1.2, ccd: 0 } }, - atking: [{ s_uuid: 6302, t_num: 2 }], - // → 6302 基座 defaults: TGroup.Team, ap:300, t_num:5, hit_count:1 + ..., atking: [{ s_uuid: 6302, t_num: 2 }], }, -// 医师:普攻2次触发持续治疗 → 共3次每次200%(覆盖 hit_count) +// 医师:普攻2次触发治疗,覆盖为持续3次 5302: { - uuid: 5302, name: "医师", ..., - skills: { 6004: { uuid: 6004, lv: 1, cd: 1.2, ccd: 0 } }, - atking: [{ s_uuid: 6304, t_num: 2, - overrides: { hit_count: 3 } + ..., atking: [{ s_uuid: 6302, t_num: 2, + overrides: { hit_count: 3, ap: 200 } }], - // → 6304 基座 defaults: ap:200, t_num:5, hit_count:3 → 只覆盖了 hit_count }, ``` -## SCastSystem 改动要点 +## 运行时改动(仅 SCastSystem + SkillTriggerHelper) -1. 所有读取 `config.ap`、`config.TGroup`、`config.hit_count` 等逻辑参数的地方,改为从 `resolveSkillParams()` 获取 -2. `kind` 始终从 `template.kind` 获取,不参与覆盖 -3. `applyFriendlySkillEffects` 和 `applyActualFriendlyEffect` 的参数从 `config: SkillConfig` 调整为接收 `SkillTemplate` + resolved 后的 `SkillDefaults` -4. buff 应用逻辑的 `switch` 分支扩展时只需改一处 -5. 卡牌技能路径:调用 `resolveSkillParams(template, undefined, undefined)` 获取参数 -6. SkillUpList 升级加成统一应用于 resolved 之后的参数 +### SCastSystem 改动 -## HeroAtkSystem 改动要点 +`applyFriendlySkillEffects` 和 `applyActualFriendlyEffect` 中: -`HeroAtkSystem.ts` 直接读取 `config.ap` 用于伤害计算和复活技能。必须同步适配: -- 伤害计算路径(~第 287-292 行):从 resolved defaults 获取 `ap` -- 复活技能路径(~第 220-236 行):从 resolved defaults 获取 `ap` 和 `readyAnm` +```typescript +// 旧:直接读 config +const kind = config.kind ?? SkillKind.Support; +const sAp = config.ap + sUp.ap * _skillLv; -## 兼容性 +// 新:先合并 overrides,再读 +const effective = mergeSkillParams(config, triggerConf?.overrides); +const kind = effective.kind ?? SkillKind.Support; +const sAp = effective.ap + sUp.ap * _skillLv; +``` -- 现有攻击技能(6001-6107):数值从顶层字段移入 `defaults`,必须显式添加 `kind: SkillKind.Damage` -- 触发技能配置(atking/atked 等):旧格式 `{s_uuid, t_num}` 通过类型兼容继续工作,`overrides` 为可选,现有代码无需改动即可兼容 -- 卡牌技能:使用 `template.defaults`,无需角色上下文 -- SkillUpList:升级加成作用于最终 resolved 后的参数,调用位置不变 +改动范围:只在 `applyFriendlySkillEffects` 入口处加一行 `mergeSkillParams`,其余逻辑不变。 -## 迁移策略 +### SkillTriggerHelper 改动 -分步进行,每步可独立验证: +触发技能时传递 `triggerConf.overrides` 给 SCastSystem。 -1. **Step 1:新增接口** — 添加 `SkillTemplate`、`SkillDefaults`、`HeroOverrides`、`TriggerSkillConf` 类型定义,与旧 `SkillConfig` 并存 -2. **Step 2:迁移 SkillSet 数据** — 逐个将现有技能配置从旧格式转为新格式。注意:所有攻击技能(6001-6107)必须显式添加 `kind: SkillKind.Damage`;`DAnm`/`EAnm` 保留在模板顶层 -3. **Step 3:迁移 heroSet 数据** — 为需要差异化的角色添加 overrides;同步更新 `HeroAttrsComp.ts` 中 atking/atked 的内联类型定义为 `TriggerSkillConf[]` -4. **Step 4:重构 SCastSystem + HeroAtkSystem** — 引入 `resolveSkillParams()`,替换所有直接读取 config 字段的逻辑,SkillUpList 统一应用于 resolved 参数 -5. **Step 5:适配引用文件** — 逐个更新 Skill.ts、SkillView.ts、SMoveSystem.ts、HeroViewComp.ts、CardComp.ts 等消费者 -6. **Step 6:清理** — 删除旧 `SkillConfig` 接口,全局 `BuffConf.buff` → `BuffConf.attr` 重命名(配置数据 + 运行时代码),移除 `?? SkillKind.Damage` 回退逻辑 +### HeroAttrsComp 类型同步 + +atking/atked 的内联类型加上 `overrides?: SkillOverrides`。 ## 涉及文件 -| 文件 | 改动级别 | 改动内容 | -|------|----------|----------| -| `assets/script/game/common/config/SkillSet.ts` | 高 | 重构接口,新增 SkillTemplate/SkillDefaults,数据迁移 | -| `assets/script/game/common/config/heroSet.ts` | 中 | HSkillInfo 增加 overrides,触发配置类型更新 | -| `assets/script/game/common/config/HeroAttrs.ts` | 低 | BuffConf 字段名 buff → attr | -| `assets/script/game/hero/SCastSystem.ts` | 高 | 参数解析逻辑重构,引入 resolveSkillParams,SkillUpList 适配 | -| `assets/script/game/hero/HeroAtkSystem.ts` | 高 | 伤害计算和复活技能读取 ap/readyAnm 适配新接口 | -| `assets/script/game/hero/SkillTriggerHelper.ts` | 中 | 触发技能传参适配,传递 overrides | -| `assets/script/game/hero/HeroAttrsComp.ts` | 中 | atking/atked 内联类型更新为 TriggerSkillConf[] | -| `assets/script/game/hero/HeroViewComp.ts` | 低 | 读取 DAnm 字段,接口名变更 | -| `assets/script/game/skill/Skill.ts` | 高 | skill.load() 适配新接口,区分模板字段和逻辑参数 | -| `assets/script/game/skill/SkillView.ts` | 低 | SkillConfig 引用改为 SkillTemplate | -| `assets/script/game/skill/SMoveSystem.ts` | 低 | 读取 speed/RType 等表现字段,接口名变更 | -| `assets/script/game/skill/STimeComp.ts` | 低 | 读取 EType/time,接口名变更 | -| `assets/script/game/hero/Hero.ts` | 中 | 英雄初始化,传递 overrides | -| `assets/script/game/hero/Mon.ts` | 中 | 怪物初始化,传递 overrides | -| `assets/script/game/map/CardComp.ts` | 低 | SkillSet 引用适配 | -| `assets/script/game/map/IBoxComp.ts` | 低 | SkillSet 引用适配 | +| 文件 | 改动 | +|------|------| +| `SkillSet.ts` | 新增 `SkillOverrides` 接口 + `mergeSkillParams` 函数 | +| `heroSet.ts` | HSkillInfo 增加 `overrides?`,触发配置增加 `overrides?` | +| `HeroAttrsComp.ts` | atking/atked 内联类型增加 `overrides?` | +| `SCastSystem.ts` | applyFriendlySkillEffects 中调用 mergeSkillParams | +| `SkillTriggerHelper.ts` | 传递 overrides | -以下文件仅读取 SkillTemplate 中保留顶层的字段(name、icon、IType 等),改动级别极低或无需改动: -`TooltipCom.ts`、`SIconComp.ts`、`SkillBoxComp.ts`、`HlistComp.ts`、`HInfoComp.ts`、`MissionHeroComp.ts`、`MissionEconomy.ts`、`MissionComp.ts` +**不涉及的文件**:Skill.ts、SkillView.ts、SMoveSystem.ts、STimeComp.ts、HeroAtkSystem.ts、HeroViewComp.ts、Hero.ts、Mon.ts、CardComp.ts、IBoxComp.ts — 这些文件不改动。