feat(skill): 支持技能参数自定义覆盖

本次修改实现同技能不同角色的差异化技能效果:
1. 新增SkillOverrides接口与mergeSkillParams工具函数,用于合并基础技能配置和角色覆盖参数
2. 更新英雄配置、属性组件、触发辅助系统与施法系统以适配该机制
3. 为盾骑士、医师添加示例差异化配置,验证功能可行性
4. 整理技能配置,删除冗余重复的旧技能条目
5. 新增技能重构设计计划文档,替换旧的迁移计划文档
This commit is contained in:
walkpan
2026-05-23 12:11:00 +08:00
parent eae62f245a
commit 88d7bdae47
9 changed files with 221 additions and 811 deletions

View File

@@ -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<number, HSkillInfo>;
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<SkillDefaults> {
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` | 高 | 参数解析逻辑重构,引入 resolveSkillParamsSkillUpList 适配 |
| `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 — 这些文件不改动。