diff --git a/assets/script/game/common/config/HeroSkillDesc.ts b/assets/script/game/common/config/HeroSkillDesc.ts index 37fdd745..b0b17ddb 100644 --- a/assets/script/game/common/config/HeroSkillDesc.ts +++ b/assets/script/game/common/config/HeroSkillDesc.ts @@ -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; 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; diff --git a/assets/script/game/common/config/heroSet.ts b/assets/script/game/common/config/heroSet.ts index 5035ee7b..9d41d6c8 100644 --- a/assets/script/game/common/config/heroSet.ts +++ b/assets/script/game/common/config/heroSet.ts @@ -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 []; diff --git a/assets/script/game/hero/Hero.ts b/assets/script/game/hero/Hero.ts index 61ef9c6e..6f10c6c1 100644 --- a/assets/script/game/hero/Hero.ts +++ b/assets/script/game/hero/Hero.ts @@ -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 消费 diff --git a/assets/script/game/hero/Mon.ts b/assets/script/game/hero/Mon.ts index 2e8213b1..f360917f 100644 --- a/assets/script/game/hero/Mon.ts +++ b/assets/script/game/hero/Mon.ts @@ -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; diff --git a/assets/script/game/map/CHeroComp.ts b/assets/script/game/map/CHeroComp.ts index 24d91e6d..a370e320 100644 --- a/assets/script/game/map/CHeroComp.ts +++ b/assets/script/game/map/CHeroComp.ts @@ -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, diff --git a/assets/script/game/map/HeroBoxComp.ts b/assets/script/game/map/HeroBoxComp.ts index 345ce0de..9cb06480 100644 --- a/assets/script/game/map/HeroBoxComp.ts +++ b/assets/script/game/map/HeroBoxComp.ts @@ -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, diff --git a/assets/script/game/map/MissionCardComp.ts b/assets/script/game/map/MissionCardComp.ts index f6e84048..2a46f237 100644 --- a/assets/script/game/map/MissionCardComp.ts +++ b/assets/script/game/map/MissionCardComp.ts @@ -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;