3 Commits

Author SHA1 Message Date
panFD
22bd377275 fix(config): 修正技能ID配置错误与范围注释偏移
1.  调整技能商店范围攻击卡的注释范围,移除废弃的6201配置
2.  同步更新技能配置、卡牌配置与文档中的6201/6201关联错误
3.  将原6201技能数据迁移至正确的6107、6108等ID下
2026-08-16 20:55:46 +08:00
panFD
f2dbd30470 feat(heroSet): 为破晓先知添加大招技能及等级成长配置
新增6101号火焰喷射大招技能,配置独立冷却时间与随英雄等级提升的属性成长,5级时解锁双发射击效果
2026-08-16 20:55:37 +08:00
panFD
aec84d169f feat(hero/skill): add skill level override resolution logic
1. 新增resolveSkillOverrides工具函数实现技能等级分档覆盖逻辑
2. 为所有英雄/怪物技能模块添加运行时overrides分档处理
3. 扩展技能描述系统以展示分档后的技能等级
4. 新增技能分档配置类型定义与注释
2026-08-16 20:51:58 +08:00
11 changed files with 95 additions and 28 deletions

View File

@@ -28,7 +28,7 @@
* - SkillDescSet:结构化描述模板
* - FieldSkillSet(驻场技能)、BuffList(计时 buff 默认值兜底)、Attrs(属性枚举)
*/
import { SkillTriggerName, SkillTriggerType, TriggerGrouped, LvTriggerEntry, LvFieldEntry, LvReviveEntry, LvBonusEntry, FieldEntry, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, getTriggerSkillName, HGrowType, calcGrowBonus } from "./heroSet";
import { SkillTriggerName, SkillTriggerType, TriggerGrouped, LvTriggerEntry, LvFieldEntry, LvReviveEntry, LvBonusEntry, FieldEntry, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, resolveSkillOverrides, getTriggerSkillName, HGrowType, calcGrowBonus, HSkillInfo } from "./heroSet";
import { FieldSkillSet, mergeSkillParams, SkillConfig, SkillKind, SkillOverrides, SkillSet, TGroup } from "./SkillSet";
import { FieldDescSet, FieldValueFmt, SkillDescSet } from "./SkillDescSet";
import { BuffList } from "./BuffSet";
@@ -42,6 +42,8 @@ import { CardConfig } from "./CardSet";
export interface ISkillDescSource {
/** 当前等级(用于 resolve 触发技能/光环/复活取档;静态 heroInfo 恒为 1) */
lv?: number;
/** 携带技能(普攻技能,支持 lv_entries 按等级分档 resolve) */
skills?: Record<number, HSkillInfo>;
call?: TriggerGrouped;
dead?: TriggerGrouped;
fstart?: TriggerGrouped;
@@ -368,6 +370,24 @@ function buildSkillLines(source: ISkillDescSource, showName: boolean = true, sho
const lines: ISkillLine[] = [];
const lv = Math.max(1, source.lv ?? 1);
// ---- 普攻技能(skills):按英雄等级 resolve 分档 overrides ----
if (source.skills) {
for (const key in source.skills) {
const hSkill = source.skills[key];
if (!hSkill) continue;
const base = SkillSet[hSkill.uuid];
if (!base) continue;
const overrides = resolveSkillOverrides(hSkill, lv);
const skill = mergeSkillParams(base, overrides);
// 技能等级展示:lv_entries 有档时取档位 s_lv,否则用运行时 lv
const sLv = hSkill.lv_entries?.length
? (hSkill.lv_entries.filter(e => e.lv <= lv).pop()?.s_lv ?? hSkill.lv)
: hSkill.lv;
const nameSeg = showName ? `「${base.name}」 ` : "";
lines.push(buildEffectLine(`Lv${sLv}: ${nameSeg}`, skill));
}
}
// ---- 6 种标准触发技能 ----
for (const key of TRIGGER_KEYS) {
const grouped = source[key] as TriggerGrouped | undefined;
@@ -569,6 +589,24 @@ export function buildSkillDescMoreRich(source: ISkillDescSource, showName: boole
return highlightDuration(text);
};
// ---- 普攻技能(skills):按英雄等级 resolve 分档 overrides,展示当前生效档 ----
if (source.skills) {
for (const key in source.skills) {
const hSkill = source.skills[key];
if (!hSkill) continue;
const base = SkillSet[hSkill.uuid];
if (!base) continue;
const overrides = resolveSkillOverrides(hSkill, lv);
const skill = mergeSkillParams(base, overrides);
// 技能等级展示:lv_entries 有档时取档位 s_lv,否则用运行时 lv
const sLv = hSkill.lv_entries?.length
? (hSkill.lv_entries.filter(e => e.lv <= lv).pop()?.s_lv ?? hSkill.lv)
: hSkill.lv;
const head = showName ? `「${base.name}」 Lv.${sLv}: ` : `Lv.${sLv}: `;
lines.push(effectRich(buildEffectLine(head, skill)));
}
}
for (const key of TRIGGER_KEYS) {
const grouped = source[key] as TriggerGrouped | undefined;
if (!grouped) continue;

View File

@@ -60,7 +60,6 @@ const SkillCardData: SkillCardRaw[] = [
{ uuid: 8202, skill: 6102, card_lv: 2, icon: "Stat_Mana", name: "飓风卷轴", info: "召唤飓风攻击敌人,造成攻击力200%的伤害,30%击晕,可命中2个目标", is_inst: false, t_times: 3, t_inv: 5, trigger_type: CardTriggerType.Interval, overrides: { ap: 200, stun: 30 } },
// ==================== card_lv 3 · 高阶范围(ap:250,群体附加) ====================
{ uuid: 8301, skill: 6201, card_lv: 3, icon: "Stat_SplashDamage", name: "陨石雨卷轴", info: "召唤陨石雨攻击敌人,一次释放3发,每发造成攻击力250%的范围伤害,40%击晕", is_inst: false, t_times: 4, t_inv: 4, trigger_type: CardTriggerType.Interval, overrides: { ap: 250, stun: 40, num: 2 } },
];
/**

View File

@@ -160,12 +160,8 @@ export const SkillDescSet: Record<number, SkillDescConfig> = {
templPerm: "造成攻击力{value}{unit}的伤害",
},
// ==================== 技能商店范围攻击卡(6201-6206):ap = 单段伤害 ====================
// ==================== 技能商店范围攻击卡(6202-6206):ap = 单段伤害 ====================
// 注:暴击/冰冻/击晕/击退等附加概率由 buildCardDescRich 扫描 crt/frz/stun/bck 动态追加,不在模板写死
6201: {
verb: "", unit: "", permField: "ap",
templPerm: "每段造成{value}点伤害",
},
6202: {
verb: "", unit: "", permField: "ap",
templPerm: "每段造成{value}点伤害",

View File

@@ -336,17 +336,17 @@ export const SkillSet: Record<number, SkillConfig> = {
RType: RType.bezier, EType: EType.collision, info: "对周围的敌人造成攻击力150%的伤害",//英雄配置技能的时候,可以疲惫触发6013闪电术
},
6107: {
uuid: 6201, name: "万剑朝宗", sp_name: "range3", icon: "Stat_Tripleshot", TGroup: TGroup.Enemy, readyAnm: "blues", endAnm: "", act: "max1",
uuid: 6107, name: "万剑朝宗", sp_name: "range3", icon: "Stat_Tripleshot", TGroup: TGroup.Enemy, readyAnm: "blues", endAnm: "", act: "max1",
DTType: DTType.aoe_grid, stun: 0, ap: 150, hit_count: 6, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.fixed, EType: EType.animationEnd, info: "召唤6把冰剑攻击敌人",//可以附加冰冻增强
},
6108: {
uuid: 6201, name: "开山斧", sp_name: "range2", icon: "Stat_Tripleshot", TGroup: TGroup.Enemy, readyAnm: "blues", endAnm: "", act: "max0",
uuid: 6108, name: "开山斧", sp_name: "range2", icon: "Stat_Tripleshot", TGroup: TGroup.Enemy, readyAnm: "blues", endAnm: "", act: "max0",
DTType: DTType.aoe_grid, stun: 0, ap: 300, hit_count: 6, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.fixed, EType: EType.animationEnd, info: "召唤巨斧劈向前方敌人",//可以附加击晕增强
},
6109: {
uuid: 6019, name: "万箭齐发.冰", sp_name: "maxa1", icon: "Stat_RangedAttack", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
uuid: 6109, name: "万箭齐发.冰", sp_name: "maxa1", icon: "Stat_RangedAttack", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
DTType: DTType.single, ap: 100, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.bezier, EType: EType.collision, bezier_start_y: 20, bezier_mid_y: 140, bezier_arc: 1.05, info: "造成攻击力100%的伤害",
},
@@ -356,12 +356,6 @@ export const SkillSet: Record<number, SkillConfig> = {
RType: RType.bezier, EType: EType.collision, bezier_start_y: 20, bezier_mid_y: 140, bezier_arc: 1.05, info: "造成攻击力100%的伤害",
},
6201: {
uuid: 6201, name: "陨石术", sp_name: "fb-3", icon: "Stat_Tripleshot", TGroup: TGroup.Enemy, readyAnm: "blues", endAnm: "", act: "max1",
DTType: DTType.aoe_grid, stun: 0, ap: 150, hit_count: 6, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
RType: RType.fixed, EType: EType.animationEnd, info: "召唤陨石范围攻击敌人, 有概率击晕",
},
//============================= ====== 辅助技能 回合技能 ====== ==========================
6301: {
uuid: 6301, name: "护盾", sp_name: "buff_wind", icon: "Stat_Defense", TGroup: TGroup.Self, readyAnm: "up_blue", endAnm: "", act: "atk",

View File

@@ -316,7 +316,18 @@ export interface HSkillInfo {
lv: number; // 技能等级
cd: number; // 技能cd
ccd: number; // 占位当前cd,用于cd计时
overrides?: SkillOverrides; // 角色专属参数覆盖
overrides?: SkillOverrides; // 角色专属参数覆盖(全等级生效,作为底)
lv_entries?: LvSkillEntry[]; // 按英雄等级分档(按 lv 升序,运行时取 ≤英雄等级 的最大档)
}
/**
* 普攻技能分档配置:达到 lv 级英雄时此档 overrides 生效(与基础 overrides 浅合并,档位优先)。
* s_lv 标注此档对应的技能等级(SkillLv 枚举),便于面板显示"技能Lv.x"。
*/
export interface LvSkillEntry {
lv: number; // 达到该英雄等级时此档生效
s_lv?: SkillLv; // 此档对应的技能等级(标注用)
overrides: SkillOverrides; // 此档注入的效果
}
/**
@@ -424,7 +435,18 @@ export const HeroInfo: Record<number, heroInfo> = {
// 5004 破晓先知 — fstart 全队攻击
5004: {
uuid: 5004, name: "破晓先知", path: "hm1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 143, ap: 32,
skills: { 6014: { uuid: 6014, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: { ap: 80, timed_buff_id: 7003 } } },
skills: {
6014: { uuid: 6014, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: { ap: 80, timed_buff_id: 7003 } },
// 大招:火焰喷射,独立 cd,按英雄等级分档成长(5级双发)
6101: {
uuid: 6101, lv: 1, cd: 8, ccd: 0,
overrides: { ap: 100 },
lv_entries: [
{ lv: 3, s_lv: SkillLv.Lv2, overrides: { ap: 150 } },
{ lv: 5, s_lv: SkillLv.Lv3, overrides: { ap: 200, num: 1 } },
],
},
},
fstart: {
6401: [
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } },
@@ -974,6 +996,21 @@ export function resolveTriggerByLv(grouped: TriggerGrouped | undefined, lv: numb
return result;
}
/**
* 解析普攻技能在目标英雄等级下的有效 overrides。
* 取 lv_entries 中 ≤hero_lv 的最大档,与基础 overrides 浅合并(档位字段优先)。
* Why: 普攻技能与触发技能对齐"英雄等级驱动成长"语义,且下游读取 skills[uuid].overrides 无需感知分档。
*/
export function resolveSkillOverrides(skill: HSkillInfo, hero_lv: number): SkillOverrides | undefined {
if (!skill.lv_entries || skill.lv_entries.length === 0) return skill.overrides;
let pick: LvSkillEntry | undefined;
for (const e of skill.lv_entries) {
if (e.lv <= hero_lv && (!pick || e.lv > pick.lv)) pick = e;
}
if (!pick) return skill.overrides;
return { ...skill.overrides, ...pick.overrides };
}
/** 解析驻场光环在目标等级下的有效词条列表(取 ≤lv 的最大档) */
export function resolveFieldByLv(field: LvFieldEntry[] | undefined, lv: number): FieldEntry[] {
if (!field || field.length === 0) return [];

View File

@@ -123,7 +123,7 @@
```typescript
[SkillTriggerType.Atking]: {
// 普攻触发 360% 范围伤害 + 击退
6201: [{ lv: 1, t_num: 1, overrides: { ap: 150, bck: 30 } }],
6107: [{ lv: 1, t_num: 1, overrides: { ap: 150, bck: 30 } }],
// 普攻吸血 35%
6302: [{ lv: 1, t_num: 1, overrides: { TGroup: TGroup.Self, ap: 35 } }],
},
@@ -201,7 +201,7 @@
```typescript
[SkillTriggerType.Atking]: {
// 范围伤害 + 群体击退
6201: [{ lv: 1, t_num: 1, overrides: { ap: 200, bck: 50, hit_count: 6 } }],
6108: [{ lv: 1, t_num: 1, overrides: { ap: 200, bck: 50, hit_count: 6 } }],
},
```

View File

@@ -5,7 +5,7 @@ import { smc } from "../common/SingletonModuleComp";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
import { BoxSet, FacSet, FightSet, IndexSet } from "../common/config/GameSet";
import { HeroInfo, resolveFormationTargetX, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, calcGrowBonus, HeroDisVal } from "../common/config/heroSet";
import { HeroInfo, resolveFormationTargetX, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, resolveSkillOverrides, calcGrowBonus, HeroDisVal } from "../common/config/heroSet";
import { GameEvent } from "../common/config/GameEvent";
import { SkillTriggerType, HType } from "../common/config/heroSet";
import { SkillTriggerHelper } from "./SkillTriggerHelper";
@@ -158,13 +158,13 @@ export class Hero extends ecs.Entity {
model.speed = hero.speed ?? 200;
// 构建技能表并注入运行时冷却字段 ccd
// 构建技能表并注入运行时冷却字段 ccd;overrides 按英雄等级 resolve 分档(下游无感知)
model.skills = {};
for (const key in hero.skills) {
const skill = hero.skills[key];
if (!skill) continue;
// 最终技能等级 = 初始技能等级 + 英雄等级增量,下限 0、上限封顶(无限升级下技能不涨过上限)
model.skills[skill.uuid] = { ...skill, lv: Math.min(FightSet.HERO_SKILL_MAX_LV, Math.max(0, skill.lv + hero_lv - 2)), ccd: 0 };
model.skills[skill.uuid] = { ...skill, lv: Math.min(FightSet.HERO_SKILL_MAX_LV, Math.max(0, skill.lv + hero_lv - 2)), ccd: 0, overrides: resolveSkillOverrides(skill, hero_lv) };
}
// 按当前英雄等级 resolve 触发技能/光环/复活到运行时缓存,供 SkillTriggerHelper 消费

View File

@@ -3,7 +3,7 @@ import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/O
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { smc } from "../common/SingletonModuleComp";
import { BoxSet, FacSet, FightSet, IndexSet } from "../common/config/GameSet";
import { HeroInfo, resolveTriggerByLv, resolveReviveByLv, HeroDisVal } from "../common/config/heroSet";
import { HeroInfo, resolveTriggerByLv, resolveReviveByLv, resolveSkillOverrides, HeroDisVal } from "../common/config/heroSet";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
import { MoveComp } from "./MoveComp";
@@ -201,8 +201,8 @@ export class Monster extends ecs.Entity {
for (const key in hero.skills) {
const skill = hero.skills[key];
if (!skill) continue;
// 最终技能等级 = 初始技能等级 + 怪物等级增量,且下限为 0
model.skills[skill.uuid] = { ...skill, lv: Math.max(0, skill.lv + mon_lv - 2), ccd: 0 };
// 最终技能等级 = 初始技能等级 + 怪物等级增量,且下限为 0;overrides 按怪物等级 resolve 分档
model.skills[skill.uuid] = { ...skill, lv: Math.max(0, skill.lv + mon_lv - 2), ccd: 0, overrides: resolveSkillOverrides(skill, mon_lv) };
}
// 【测试模式专属】如果有覆盖技能配置则应用
const testSkills = (this as any)._testSkills;

View File

@@ -475,6 +475,7 @@ export class CHeroComp extends CCComp {
private buildHeroDescSource(hero: typeof HeroInfo[number]): ISkillDescSource {
return {
lv: 5,
skills: hero.skills,
call: hero.call,
dead: hero.dead,
fstart: hero.fstart,

View File

@@ -214,6 +214,7 @@ export class HeroBoxComp extends CCComp {
const buildSource = (lv: number): ISkillDescSource => ({
lv: lv,
skills: hero.skills,
call: hero.call,
dead: hero.dead,
fstart: hero.fstart,

View File

@@ -52,7 +52,7 @@ import { MissSkillsComp } from "./MissSkillsComp";
import { oops } from "db://oops-framework/core/Oops";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { smc } from "../common/SingletonModuleComp";
import { HeroInfo, HType, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, calcGrowBonus } from "../common/config/heroSet";
import { HeroInfo, HType, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, resolveSkillOverrides, calcGrowBonus } from "../common/config/heroSet";
import { HeroViewComp } from "../hero/HeroViewComp";
import { FacSet, FightSet } from "../common/config/GameSet";
import { MissionEconomy } from "./MissionEconomy";
@@ -2273,7 +2273,8 @@ export class MissionCardComp extends CCComp {
for (const key in hero.skills) {
const skill = hero.skills[key];
if (!skill) continue;
model.skills[skill.uuid] = { ...skill, lv: Math.min(FightSet.HERO_SKILL_MAX_LV, Math.max(0, skill.lv + nextLv - 2)), ccd: 0 };
// 升级后按 nextLv 重新 resolve 分档 overrides(与 Hero.load 对齐)
model.skills[skill.uuid] = { ...skill, lv: Math.min(FightSet.HERO_SKILL_MAX_LV, Math.max(0, skill.lv + nextLv - 2)), ccd: 0, overrides: resolveSkillOverrides(skill, nextLv) };
}
model.updateSkillDistanceCache();
model.dirty_hp = true;