feat(hero/skill): add skill level override resolution logic

1. 新增resolveSkillOverrides工具函数实现技能等级分档覆盖逻辑
2. 为所有英雄/怪物技能模块添加运行时overrides分档处理
3. 扩展技能描述系统以展示分档后的技能等级
4. 新增技能分档配置类型定义与注释
This commit is contained in:
panFD
2026-08-16 20:51:58 +08:00
parent 4388a1ed3a
commit aec84d169f
7 changed files with 77 additions and 10 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

@@ -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; // 此档注入的效果
}
/**
@@ -974,6 +985,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

@@ -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
// 构建技能表并注入运行时冷却字段 ccdoverrides 按英雄等级 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 };
// 最终技能等级 = 初始技能等级 + 怪物等级增量,且下限为 0overrides 按怪物等级 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;