refactor(hero): 重构额外属性加成系统,统一为bonus字段

1.  将原ap_bonus/hp_bonus/def_bonus三个字段合并为通用的bonus数组结构
2.  新增多属性同时配置能力,支持hp/ap/def百分比加成与crt/frz/stun/para固定百分点加成
3.  更新所有前端编辑器、服务端渲染与游戏运行时逻辑适配新配置格式
4.  添加数据兼容迁移逻辑,自动处理旧版英雄配置数据
This commit is contained in:
panFD
2026-09-07 23:05:30 +08:00
parent a1c8f46c50
commit ed738d2e30
10 changed files with 180 additions and 94 deletions

View File

@@ -215,8 +215,8 @@ export class HeroBoxComp extends CCComp {
// ---- 属性成长(固定基础值 + 成长类型指数递增 + 一次性加成) ----
const attrLine = (lv: number): string => {
const grow = calcGrowBonus(hero, lv);
const ap = Math.floor(hero.ap + grow.ap + HeroBoxComp.sumBonus(hero.ap_bonus, lv));
const hp = Math.floor(hero.hp + grow.hp + HeroBoxComp.sumBonus(hero.hp_bonus, lv));
const ap = Math.floor(hero.ap + grow.ap + HeroBoxComp.sumBonusPct(hero.ap + grow.ap, hero.bonus, 'ap', lv));
const hp = Math.floor(hero.hp + grow.hp + HeroBoxComp.sumBonusPct(hero.hp + grow.hp, hero.bonus, 'hp', lv));
return `攻击 <color=#27AE60>${ap}</color> 生命 <color=#27AE60>${hp}</color>`;
};
@@ -237,11 +237,12 @@ export class HeroBoxComp extends CCComp {
}
/** 累加等级额外加成(≤目标等级的所有档位) */
private static sumBonus(entries: { lv: number; value: number }[] | undefined, lv: number): number {
/** 累加 bonus 中某百分比键的档位总量,并按"基础+成长"快照换算为固定增量(≤目标等级的所有档位) */
private static sumBonusPct(snap: number, entries: HeroInfo[number]['bonus'], key: 'hp' | 'ap' | 'def', lv: number): number {
if (!entries) return 0;
let total = 0;
for (const e of entries) { if (e.lv <= lv) total += e.value; }
return total;
let pct = 0;
for (const e of entries) { if (e.lv <= lv) pct += e.attrs[key] ?? 0; }
return Math.round(snap * pct / 100);
}
/**
@@ -543,18 +544,18 @@ export class HeroBoxComp extends CCComp {
}
// ---- AP / HP ----
// plus(附加值)= 最终值 - 当前等级基础值;基础值 = 1 级原始 base + 等级成长 + ap/hp_bonus 一次性奖励。
// plus(附加值)= 最终值 - 当前等级基础值;基础值 = 1 级原始 base + 等级成长 + bonus 百分比加成。
// Why: base_ap/base_hp 是不含任何等级增长的原始值,直接用 final-base 会把升级带来的
// 属性增长也计入增量。这里补齐等级增长作为基准,使 plus 仅反映 buff/光环等临时增益。
const lv = Math.max(1, Math.floor(this.model.lv ?? 1));
const grow = hero ? calcGrowBonus(hero, lv) : { ap: 0, hp: 0 };
const finalAp = Math.max(0, Math.floor(this.model.getFinalAp()));
const lvBaseAp = Math.max(0, Math.floor((this.model.base_ap ?? 0) + grow.ap + HeroBoxComp.sumBonus(hero?.ap_bonus, lv)));
const lvBaseAp = Math.max(0, Math.floor((this.model.base_ap ?? 0) + grow.ap + HeroBoxComp.sumBonusPct((this.model.base_ap ?? 0) + grow.ap, hero?.bonus, 'ap', lv)));
this.setStatRow(this.ap_all_label, this.ap_plus_label, finalAp, finalAp - lvBaseAp);
const finalHp = Math.max(0, Math.floor(this.model.getFinalHpMax()));
const lvBaseHp = Math.max(0, Math.floor((this.model.base_hp ?? 0) + grow.hp + HeroBoxComp.sumBonus(hero?.hp_bonus, lv)));
const lvBaseHp = Math.max(0, Math.floor((this.model.base_hp ?? 0) + grow.hp + HeroBoxComp.sumBonusPct((this.model.base_hp ?? 0) + grow.hp, hero?.bonus, 'hp', lv)));
this.setStatRow(this.hp_all_label, this.hp_plus_label, finalHp, finalHp - lvBaseHp);
// ---- 图标(UUID 变化时重新加载首帧动画) ----