refactor(heroBox,skillDesc): 优化英雄面板技能描述显示
1. 新增showName参数控制技能描述是否显示技能名 2. 英雄盒面板隐藏技能名仅保留效果描述 3. 新增英雄强度评分公式设计文档
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -229,7 +229,7 @@ function buildFieldLine(head: string, uuid: number, showName: boolean = true): I
|
||||
* 复活(revive)结构化行:复刻战斗公式,高亮复活次数与回血百分比
|
||||
* 复活次数 = r_num + floor((lv - 1) * upr);回血百分比 = SkillSet[s_uuid].ap
|
||||
*/
|
||||
function buildReviveLine(source: ISkillDescSource): ISkillLine | null {
|
||||
function buildReviveLine(source: ISkillDescSource, showName: boolean = true): ISkillLine | null {
|
||||
const revive = source.revive;
|
||||
if (!revive) return null;
|
||||
const base = SkillSet[revive.s_uuid];
|
||||
@@ -238,9 +238,10 @@ function buildReviveLine(source: ISkillDescSource): ISkillLine | null {
|
||||
const maxCount = revive.r_num + Math.floor((lv - 1) * revive.upr);
|
||||
const hpPct = base.ap ?? 0;
|
||||
const tpl = SkillTriggerName[SkillTriggerType.Revive] ?? "复活";
|
||||
const nameSeg = showName ? `「${base.name}」 ` : "";
|
||||
// 高亮片段选回血百分比(数值中最关键的收益指标)
|
||||
return {
|
||||
prefix: `${tpl}:「${base.name}」 可复活${maxCount}次,每次恢复`,
|
||||
prefix: `${tpl}:${nameSeg}可复活${maxCount}次,每次恢复`,
|
||||
value: `${hpPct}%`,
|
||||
suffix: "生命",
|
||||
};
|
||||
@@ -268,8 +269,9 @@ function buildEvolveBonusLines(source: ISkillDescSource): ISkillLine[] {
|
||||
/**
|
||||
* 构建全部能力结构化行(触发技能 + 驻场光环 + 复活 + evolve 加成)。
|
||||
* 纯文本与富文本两个公开接口共用此结果,仅渲染阶段不同。
|
||||
* @param showName 是否在行中包含「技能名」片段(英雄盒面板传 false 隐藏技能名)
|
||||
*/
|
||||
function buildSkillLines(source: ISkillDescSource): ISkillLine[] {
|
||||
function buildSkillLines(source: ISkillDescSource, showName: boolean = true): ISkillLine[] {
|
||||
const lines: ISkillLine[] = [];
|
||||
|
||||
// ---- 6 种标准触发技能 ----
|
||||
@@ -285,7 +287,8 @@ function buildSkillLines(source: ISkillDescSource): ISkillLine[] {
|
||||
if (!base) continue;
|
||||
const skill = mergeSkillParams(base, item.overrides);
|
||||
const trigger = needCount ? `${name}×${item.t_num}` : name;
|
||||
lines.push(buildEffectLine(`${trigger}:「${base.name}」 `, skill));
|
||||
const nameSeg = showName ? `「${base.name}」 ` : "";
|
||||
lines.push(buildEffectLine(`${trigger}:${nameSeg}`, skill));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,13 +297,13 @@ function buildSkillLines(source: ISkillDescSource): ISkillLine[] {
|
||||
if (fieldUuids?.length) {
|
||||
const tpl = SkillTriggerName[SkillTriggerType.Field] ?? "光环";
|
||||
for (const uuid of fieldUuids) {
|
||||
const line = buildFieldLine(`${tpl}:`, uuid, true);
|
||||
const line = buildFieldLine(`${tpl}:`, uuid, showName);
|
||||
if (line) lines.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 复活(revive)----
|
||||
const reviveLine = buildReviveLine(source);
|
||||
const reviveLine = buildReviveLine(source, showName);
|
||||
if (reviveLine) lines.push(reviveLine);
|
||||
|
||||
// ---- evolve 额外属性加成(ap/hp)----
|
||||
@@ -323,10 +326,11 @@ export function buildSkillDesc(source: ISkillDescSource): string {
|
||||
/**
|
||||
* 富文本描述(RichText 用,BBCode):技能名用 <color=#2E86C1> 蓝色、数值用 <color=#27AE60> 亮绿色高亮。
|
||||
* @param source 触发技能数据源(同 buildSkillDesc)
|
||||
* @param showName 是否包含「技能名」片段(英雄盒面板传 false 隐藏技能名,默认 true)
|
||||
* @returns 多行富文本串,技能名片段包裹 <color=#2E86C1>、数值片段包裹 <color=#27AE60>,用 \n 分隔
|
||||
*/
|
||||
export function buildSkillDescRich(source: ISkillDescSource): string {
|
||||
return buildSkillLines(source)
|
||||
export function buildSkillDescRich(source: ISkillDescSource, showName: boolean = true): string {
|
||||
return buildSkillLines(source, showName)
|
||||
.map(l => {
|
||||
const prefix = l.prefix.replace(SKILL_NAME_PATTERN, `「<color=${RICH_NAME_COLOR}>$1</color>」`);
|
||||
const text = l.value
|
||||
|
||||
@@ -401,8 +401,8 @@ export class HeroBoxComp extends CCComp {
|
||||
|
||||
// ---- 技能描述(当前等级触发技能,读运行时 model 最终配置;数值富文本高亮) ----
|
||||
if (this.skill_label && this.skill_label.isValid) {
|
||||
// 缓存未变则跳过重设,避免 refresh 降频内 RichText 重复重解析排版
|
||||
const desc = buildSkillDescRich(this.model);
|
||||
// 缓存未变则跳过重设,避免 refresh 降频内 RichText 重复重解析排版;隐藏「技能名」仅保留效果描述
|
||||
const desc = buildSkillDescRich(this.model, false);
|
||||
if (desc !== this.skillDescCache) {
|
||||
this.skillDescCache = desc;
|
||||
this.skill_label.string = desc;
|
||||
|
||||
136
design/quick-specs/hero-power-formula-2026-08-01.md
Normal file
136
design/quick-specs/hero-power-formula-2026-08-01.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# Quick Design Spec: 英雄强度评分公式
|
||||
|
||||
**Type**: Addition
|
||||
**System**: 英雄/技能配置系统(`assets/script/game/common/config/heroSet.ts`)
|
||||
**GDD Reference**: 暂无项目级 GDD;本规范作为英雄强度锚定的临时设计依据。
|
||||
**Date**: 2026-08-01
|
||||
|
||||
## Change Summary
|
||||
|
||||
在 `heroSet.ts` 中新增一个纯函数 `calcHeroPower(hero, targetLv)`,将现有 `heroInfo` 配置(含普攻、触发技能、`evolve` 成长、驻场光环)转换为**无量纲强度分**。该分数用于:
|
||||
|
||||
1. 对比同 `card_lv` 英雄的相对强度;
|
||||
2. 为关卡怪物总强度提供英雄侧锚定基准;
|
||||
3. 为抽卡/经济定价提供量化参考。
|
||||
|
||||
**不改变现有英雄配置、战斗行为或技能效果。**
|
||||
|
||||
## Motivation
|
||||
|
||||
当前英雄池已从 6 名扩展至 11 名,且每名英雄拥有 6 级成长。策划仅靠基础 HP/AP 判断强度已不够用,尤其 `evolve` 带来的触发阈值变化、驻场光环、复活等机制难以横向比较。一个统一的评分函数可以让关卡难度和卡池分级有据可依。
|
||||
|
||||
## Design Delta
|
||||
|
||||
现有 `heroSet.ts` 只有静态配置,无强度量化。本规范新增 `calcHeroPower` 函数,读取 `heroInfo`、`SkillSet`、`FieldSkillSet`、`BuffList` 等配置,输出标量 power。
|
||||
|
||||
## New Rules / Values
|
||||
|
||||
### 1. 核心公式
|
||||
|
||||
```ts
|
||||
calcHeroPower(hero: heroInfo, targetLv: number = 1): number
|
||||
```
|
||||
|
||||
```text
|
||||
P = HP_EFF * W_HP
|
||||
+ DPS_EFF * W_DPS
|
||||
+ Σ triggerPower(trigger, hero, targetLv)
|
||||
```
|
||||
|
||||
| 符号 | 计算方式 |
|
||||
|---|---|
|
||||
| `HP_EFF` | `(hero.hp + sumHpBonus) * HERO_LV_MULTIPLIER^(targetLv - 1)` |
|
||||
| `AP_EFF` | `(hero.ap + sumApBonus) * HERO_LV_MULTIPLIER^(targetLv - 1)` |
|
||||
| `DPS_EFF` | `AP_EFF / hero.skills[0].cd` |
|
||||
| `sumHpBonus` | `targetLv` 之前所有 `evolve[l].hp_bonus` 之和 |
|
||||
| `sumApBonus` | `targetLv` 之前所有 `evolve[l].ap_bonus` 之和 |
|
||||
|
||||
### 2. 触发技能估值 `triggerPower`
|
||||
|
||||
对 `heroInfo` 中的每种触发字段分别求和:
|
||||
|
||||
| 触发类型 | 公式 |
|
||||
|---|---|
|
||||
| `atking` | `effectValue(skill, overrides) * (1 / t_num) * scopeMul * W_atking` |
|
||||
| `atked` | `effectValue(skill, overrides) * (1 / t_num) * scopeMul * W_atked` |
|
||||
| `fstart` | `effectValue(skill, overrides) * scopeMul * W_fstart` |
|
||||
| `fend` | `effectValue(skill, overrides) * scopeMul * W_fend` |
|
||||
| `dead` | `effectValue(skill, overrides) * scopeMul * W_dead` |
|
||||
| `field` | `Σ fieldSkillPower(uuid) * W_field_alive` |
|
||||
| `revive` | `HP_EFF * upr * r_num * W_revive` |
|
||||
|
||||
`scopeMul`:
|
||||
- `TGroup.Self` = 1
|
||||
- `TGroup.Ally` / `TGroup.Team` = `HERO_MAX_NUM`(当前为 3)
|
||||
|
||||
### 3. 技能效果估值 `effectValue`
|
||||
|
||||
基于 `SkillConfig.kind`:
|
||||
|
||||
| Kind | 估值 |
|
||||
|---|---|
|
||||
| Damage | `ap_pct / 100 * AP_EFF` |
|
||||
| Heal | `ap_pct / 100 * AP_EFF * W_heal` |
|
||||
| Shield | `shield_layers * expectedEnemyDmg * W_shield`(`shield_layers` 取 `SkillConfig.ap`) |
|
||||
| Support | 按 `buff_type` 折算:<br>• AP → `value / CD * W_DPS`<br>• HP → `value * W_HP`<br>• crit/crit_dmg → 按期望 DPS 增量折算<br>• stun 等控制 → `value * W_control` |
|
||||
| Gold | 忽略(非战斗强度) |
|
||||
|
||||
对于**计时 buff**(`timed_buff_id` 存在),先计算其永久等效值,再按覆盖率折算:
|
||||
|
||||
```text
|
||||
timedValue = permanentEquivalent * min(buff_duration / FIGHT_TIME, 1)
|
||||
```
|
||||
|
||||
其中 `FIGHT_TIME` 取 `GameSet.FiIGHT_TIME`(30 秒)。
|
||||
|
||||
### 4. 调参旋钮
|
||||
|
||||
所有旋钮以常量对象形式放在 `heroSet.ts` 顶部,方便运行时热调:
|
||||
|
||||
| 旋钮 | 默认值 | 建议范围 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `W_HP` | 1.0 | 0.5–2.0 | 每点有效 HP 的强度价值 |
|
||||
| `W_DPS` | 30.0 | 10–100 | 每秒有效伤害的强度价值 |
|
||||
| `W_atking` | 1.0 | 0.5–2.0 | 攻击触发技能频率折算 |
|
||||
| `W_atked` | 0.8 | 0.3–1.5 | 受击触发技能频率折算(低于 atking,因依赖敌方攻击频率) |
|
||||
| `W_fstart` | 1.0 | 0.5–2.0 | 每波开始一次性技能价值 |
|
||||
| `W_fend` | 1.2 | 0.5–2.0 | 每波结束一次性技能价值 |
|
||||
| `W_dead` | 0.6 | 0.2–1.0 | 死亡一次性技能价值 |
|
||||
| `W_field_alive` | 1.0 | 0.5–2.0 | 驻场光环存活期间价值 |
|
||||
| `W_revive` | 1.0 | 0.5–2.0 | 每次复活的价值系数 |
|
||||
| `W_heal` | 0.8 | 0.5–1.2 | 治疗相对直接伤害的折价 |
|
||||
| `W_shield` | 1.0 | 0.5–2.0 | 护盾相对 HP 的折价/溢价 |
|
||||
| `W_control` | 0.5 | 0.1–1.0 | 控制效果(击晕等)的强度折算 |
|
||||
| `expectedEnemyDmg` | 20 | 10–100 | 估算护盾价值时假设的敌方单次伤害 |
|
||||
|
||||
### 5. 归一化(可选)
|
||||
|
||||
函数返回原始分。如需以某个英雄为 100 分基准,可额外提供:
|
||||
|
||||
```ts
|
||||
calcHeroPowerNormalized(hero: heroInfo, targetLv = 1, baseHeroId = 5011, baseScore = 100): number
|
||||
```
|
||||
|
||||
即 `calcHeroPower(hero) / calcHeroPower(HeroInfo[baseHeroId]) * baseScore`。
|
||||
|
||||
## Affected Systems
|
||||
|
||||
| 系统 | 影响 | 所需操作 |
|
||||
|---|---|---|
|
||||
| `heroSet.ts` | 新增 `calcHeroPower` 及调参常量 | 实现时写入 |
|
||||
| `SkillSet.ts` | 读取技能配置计算触发价值 | 无需改动 |
|
||||
| `BuffSet.ts` | 读取计时 buff 默认数值 | 无需改动 |
|
||||
| 关卡/经济系统 | 获得英雄强度量化输入 | 后续消费端按需调用 |
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `calcHeroPower(HeroInfo[5011], 1)` 返回可复现的正数分数。
|
||||
- [ ] 同 `card_lv` 英雄中,功能更全面的角色分数普遍更高(如 5045 不灭战魂 > 5011 小铁卫)。
|
||||
- [ ] `targetLv` 从 1 提升到 6 时,分数随 `HERO_LV_MULTIPLIER` 与 `evolve` 加成单调上升。
|
||||
- [ ] 对怪物调用时不报错,且分数趋势与基础 HP/AP 一致。
|
||||
- [ ] 不改变现有 `heroInfo`、`HeroList` 任何字段。
|
||||
- [ ] 无回归:现有战斗逻辑、UI 展示不依赖新增函数。
|
||||
|
||||
## GDD Update Required?
|
||||
|
||||
No。项目暂无 `design/gdd/` 文档,本 Quick Design Spec 作为英雄强度锚定的临时设计依据。后续若建立系统 GDD,需将本公式合并进 Formulas 与 Tuning Knobs 章节。
|
||||
Reference in New Issue
Block a user