refactor(ui&logic): 调整UI边距与英雄属性计算逻辑
1. 修改ui3.plist.meta里两个UI元素的border边距配置 2. 重构HeroBoxComp的属性增量计算逻辑,修正属性加成只统计临时增益而非等级成长 3. 优化HeroSkillDesc的富文本样式,新增数值描边效果统一UI视觉风格
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -5958,10 +5958,10 @@
|
|||||||
"height": 61,
|
"height": 61,
|
||||||
"rawWidth": 54,
|
"rawWidth": 54,
|
||||||
"rawHeight": 61,
|
"rawHeight": 61,
|
||||||
"borderTop": 0,
|
"borderTop": 28,
|
||||||
"borderBottom": 0,
|
"borderBottom": 33,
|
||||||
"borderLeft": 0,
|
"borderLeft": 27,
|
||||||
"borderRight": 0,
|
"borderRight": 27,
|
||||||
"packable": true,
|
"packable": true,
|
||||||
"pixelsToUnit": 100,
|
"pixelsToUnit": 100,
|
||||||
"pivotX": 0.5,
|
"pivotX": 0.5,
|
||||||
@@ -6006,8 +6006,8 @@
|
|||||||
"rawHeight": 24,
|
"rawHeight": 24,
|
||||||
"borderTop": 0,
|
"borderTop": 0,
|
||||||
"borderBottom": 0,
|
"borderBottom": 0,
|
||||||
"borderLeft": 0,
|
"borderLeft": 25,
|
||||||
"borderRight": 0,
|
"borderRight": 25,
|
||||||
"packable": true,
|
"packable": true,
|
||||||
"pixelsToUnit": 100,
|
"pixelsToUnit": 100,
|
||||||
"pivotX": 0.5,
|
"pivotX": 0.5,
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
/**
|
/**
|
||||||
* @file HeroSkillDesc.ts
|
* @file HeroSkillDesc.ts
|
||||||
* @description 英雄技能/能力描述的【唯一文字表达标准层】
|
* @description 英雄技能/能力描述的【唯一文字表达标准层】
|
||||||
*
|
*
|
||||||
* 设计目标:
|
* 设计目标:
|
||||||
* heroSet / SkillSet 中的 info / infos 仅作"基础备注",不再承担面板展示。
|
* heroSet / SkillSet 中的 info / infos 仅作"基础备注",不再承担面板展示。
|
||||||
* 所有功能性描述(触发技能、驻场光环、复活、等级额外属性加成)统一由本模块
|
* 所有功能性描述(触发技能、驻场光环、复活、等级额外属性加成)统一由本模块
|
||||||
* 依据结构化配置动态拼出,数值永远来自 mergeSkillParams 合并后的实时配置,
|
* 依据结构化配置动态拼出,数值永远来自 mergeSkillParams 合并后的实时配置,
|
||||||
* 杜绝"改数值忘改文案"的漂移。
|
* 杜绝"改数值忘改文案"的漂移。
|
||||||
*
|
*
|
||||||
* 输出(双格式,供 UI 按需取用):
|
* 输出(双格式,供 UI 按需取用):
|
||||||
* - buildSkillDesc(source) → 纯文本(Label 用),数值无颜色
|
* - buildSkillDesc(source) → 纯文本(Label 用),数值无颜色
|
||||||
* - buildSkillDescRich(source) → 富文本(RichText 用,BBCode),技能名蓝色、数值绿色高亮
|
* - buildSkillDescRich(source) → 富文本(RichText 用,BBCode),技能名蓝色、数值绿色高亮
|
||||||
* 两者共用同一套结构化行构建逻辑,仅渲染阶段区分,保证纯/富文本内容严格一致。
|
* 两者共用同一套结构化行构建逻辑,仅渲染阶段区分,保证纯/富文本内容严格一致。
|
||||||
*
|
*
|
||||||
* 通用性(纯配置驱动):
|
* 通用性(纯配置驱动):
|
||||||
* 1. 触发框架:按触发类型遍历(SkillSet + overrides,不动)
|
* 1. 触发框架:按触发类型遍历(SkillSet + overrides,不动)
|
||||||
* 2. 效果文案:查 SkillDescSet 结构化模板渲染
|
* 2. 效果文案:查 SkillDescSet 结构化模板渲染
|
||||||
* 3. 目标/属性名:由本模块统一语义化(target/hit_count、ATTR_NAME)
|
* 3. 目标/属性名:由本模块统一语义化(target/hit_count、ATTR_NAME)
|
||||||
*
|
*
|
||||||
* 数据源(入参抽象 ISkillDescSource):
|
* 数据源(入参抽象 ISkillDescSource):
|
||||||
* heroInfo(静态 1 级配置)与 HeroAttrsComp(运行时应用 evolve 后的当前等级配置)
|
* heroInfo(静态 1 级配置)与 HeroAttrsComp(运行时应用 evolve 后的当前等级配置)
|
||||||
* 字段结构一致,天然都满足。HeroBoxComp 传 model 显示当前等级;卡池/商店传 heroInfo 显示 1 级。
|
* 字段结构一致,天然都满足。HeroBoxComp 传 model 显示当前等级;卡池/商店传 heroInfo 显示 1 级。
|
||||||
*
|
*
|
||||||
* 依赖:
|
* 依赖:
|
||||||
* - heroSet :SkillTriggerName、SkillTriggerType、TriggerGrouped/LvFieldEntry/LvReviveEntry/LvBonusEntry 及 resolver
|
* - heroSet :SkillTriggerName、SkillTriggerType、TriggerGrouped/LvFieldEntry/LvReviveEntry/LvBonusEntry 及 resolver
|
||||||
* - SkillSet :SkillSet、mergeSkillParams、SkillOverrides、SkillKind、TGroup
|
* - SkillSet :SkillSet、mergeSkillParams、SkillOverrides、SkillKind、TGroup
|
||||||
* - SkillDescSet:结构化描述模板
|
* - SkillDescSet:结构化描述模板
|
||||||
* - FieldSkillSet(驻场技能)、BuffList(计时 buff 默认值兜底)、Attrs(属性枚举)
|
* - FieldSkillSet(驻场技能)、BuffList(计时 buff 默认值兜底)、Attrs(属性枚举)
|
||||||
*/
|
*/
|
||||||
import { SkillTriggerName, SkillTriggerType, TriggerGrouped, LvTriggerEntry, LvFieldEntry, LvReviveEntry, LvBonusEntry, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, getTriggerSkillName, HGrowType, calcGrowBonus } from "./heroSet";
|
import { SkillTriggerName, SkillTriggerType, TriggerGrouped, LvTriggerEntry, LvFieldEntry, LvReviveEntry, LvBonusEntry, resolveTriggerByLv, resolveFieldByLv, resolveReviveByLv, getTriggerSkillName, HGrowType, calcGrowBonus } from "./heroSet";
|
||||||
import { FieldSkillSet, mergeSkillParams, SkillConfig, SkillKind, SkillOverrides, SkillSet, TGroup } from "./SkillSet";
|
import { FieldSkillSet, mergeSkillParams, SkillConfig, SkillKind, SkillOverrides, SkillSet, TGroup } from "./SkillSet";
|
||||||
import { FieldDescSet, FieldValueFmt, SkillDescSet } from "./SkillDescSet";
|
import { FieldDescSet, FieldValueFmt, SkillDescSet } from "./SkillDescSet";
|
||||||
@@ -99,6 +99,18 @@ const ATTR_NAME: Partial<Record<Attrs, string>> = {
|
|||||||
/** 富文本数值高亮色(鲜亮绿色,深色面板上高亮醒目) */
|
/** 富文本数值高亮色(鲜亮绿色,深色面板上高亮醒目) */
|
||||||
const RICH_VALUE_COLOR = "#3CE66E";
|
const RICH_VALUE_COLOR = "#3CE66E";
|
||||||
|
|
||||||
|
/** 数值描边色(深绿,衬托亮绿填充,与技能名橙填充+深褐描边同一设计思路) */
|
||||||
|
const RICH_VALUE_OUTLINE = "#145A32";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数值富文本片段:亮绿填充 + 深绿描边。
|
||||||
|
* 高亮数值明度高,深色描边增强边缘对比、提升深色面板上的锐度。
|
||||||
|
* @param text 数值文本(含单位,如 "15%"、"4次")
|
||||||
|
*/
|
||||||
|
function valueRich(text: string | number): string {
|
||||||
|
return `<outline color=${RICH_VALUE_OUTLINE} width=1><color=${RICH_VALUE_COLOR}>${text}</color></outline>`;
|
||||||
|
}
|
||||||
|
|
||||||
/** 富文本技能名高亮色(蜜桃橙,暖色系,与面板绿/蓝/红/黄/紫均不冲突) */
|
/** 富文本技能名高亮色(蜜桃橙,暖色系,与面板绿/蓝/红/黄/紫均不冲突) */
|
||||||
const RICH_NAME_COLOR = "#F8C471";
|
const RICH_NAME_COLOR = "#F8C471";
|
||||||
|
|
||||||
@@ -498,14 +510,14 @@ export function buildSingleSkillRich(skillUuid: number, overrides?: SkillOverrid
|
|||||||
const skill = mergeSkillParams(base, overrides);
|
const skill = mergeSkillParams(base, overrides);
|
||||||
// SkillConfig 未定义 cd 字段,冷却时间由外部运行时数据传入;计时模式展示持续时间
|
// SkillConfig 未定义 cd 字段,冷却时间由外部运行时数据传入;计时模式展示持续时间
|
||||||
const dur = skill.buff_duration ?? (skill.timed_buff_id !== undefined ? BuffList[skill.timed_buff_id]?.duration : undefined) ?? 0;
|
const dur = skill.buff_duration ?? (skill.timed_buff_id !== undefined ? BuffList[skill.timed_buff_id]?.duration : undefined) ?? 0;
|
||||||
const cdTag = dur > 0 ? `<color=${RICH_VALUE_COLOR}>持续:${dur}s</color>` : "";
|
const cdTag = dur > 0 ? valueRich(`持续:${dur}s`) : "";
|
||||||
const nameTag = `${skillNameRich(skill.name)} Lv.${lv}`;
|
const nameTag = `${skillNameRich(skill.name)} Lv.${lv}`;
|
||||||
|
|
||||||
// 复用结构化效果行,head 为空避免重复触发类型前缀
|
// 复用结构化效果行,head 为空避免重复触发类型前缀
|
||||||
const line = buildEffectLine("", skill);
|
const line = buildEffectLine("", skill);
|
||||||
const prefix = line.prefix.replace(SKILL_NAME_PATTERN, (m, g1) => skillNameRich(g1));
|
const prefix = line.prefix.replace(SKILL_NAME_PATTERN, (m, g1) => skillNameRich(g1));
|
||||||
const effect = line.value
|
const effect = line.value
|
||||||
? `${prefix}<color=${RICH_VALUE_COLOR}>${line.value}</color>${line.suffix}`
|
? `${prefix}${valueRich(line.value)}${line.suffix}`
|
||||||
: `${prefix}${line.suffix}`;
|
: `${prefix}${line.suffix}`;
|
||||||
|
|
||||||
return cdTag ? `${nameTag} ${cdTag}\n${effect}` : `${nameTag}\n${effect}`;
|
return cdTag ? `${nameTag} ${cdTag}\n${effect}` : `${nameTag}\n${effect}`;
|
||||||
@@ -525,7 +537,7 @@ export function buildEquipRich(fieldUuids: number[], showName: boolean = false):
|
|||||||
if (!line) continue;
|
if (!line) continue;
|
||||||
const prefix = line.prefix.replace(SKILL_NAME_PATTERN, (m, g1) => skillNameRich(g1));
|
const prefix = line.prefix.replace(SKILL_NAME_PATTERN, (m, g1) => skillNameRich(g1));
|
||||||
const text = line.value
|
const text = line.value
|
||||||
? `${prefix}<color=${RICH_VALUE_COLOR}>${line.value}</color>${line.suffix}`
|
? `${prefix}${valueRich(line.value)}${line.suffix}`
|
||||||
: `${prefix}${line.suffix}`;
|
: `${prefix}${line.suffix}`;
|
||||||
lines.push(text);
|
lines.push(text);
|
||||||
}
|
}
|
||||||
@@ -553,7 +565,7 @@ export function buildSkillDescMoreRich(source: ISkillDescSource): string {
|
|||||||
const effectRich = (line: ISkillLine): string => {
|
const effectRich = (line: ISkillLine): string => {
|
||||||
const prefix = line.prefix.replace(SKILL_NAME_PATTERN, (m, g1) => skillNameRich(g1));
|
const prefix = line.prefix.replace(SKILL_NAME_PATTERN, (m, g1) => skillNameRich(g1));
|
||||||
const text = line.value
|
const text = line.value
|
||||||
? `${prefix}<color=${RICH_VALUE_COLOR}>${line.value}</color>${line.suffix}`
|
? `${prefix}${valueRich(line.value)}${line.suffix}`
|
||||||
: `${prefix}${line.suffix}`;
|
: `${prefix}${line.suffix}`;
|
||||||
return highlightDuration(text);
|
return highlightDuration(text);
|
||||||
};
|
};
|
||||||
@@ -584,7 +596,7 @@ export function buildSkillDescMoreRich(source: ISkillDescSource): string {
|
|||||||
const curSLv = cur.s_lv ?? (curIdx + 1);
|
const curSLv = cur.s_lv ?? (curIdx + 1);
|
||||||
// 触发次数直接写为富文本(数字绿色高亮),如 "受伤3次"
|
// 触发次数直接写为富文本(数字绿色高亮),如 "受伤3次"
|
||||||
const curTrig = needCount
|
const curTrig = needCount
|
||||||
? `${trigName}<color=${RICH_VALUE_COLOR}>${cur.t_num}</color>次`
|
? `${trigName}${valueRich(cur.t_num)}次`
|
||||||
: trigName;
|
: trigName;
|
||||||
// 技能名 + 档位数字后缀(如 "不屈壁垒1"),与光环命名口径一致
|
// 技能名 + 档位数字后缀(如 "不屈壁垒1"),与光环命名口径一致
|
||||||
const curHead = `「${skillName}${curSLv}」: ${curTrig}, `;
|
const curHead = `「${skillName}${curSLv}」: ${curTrig}, `;
|
||||||
@@ -608,7 +620,7 @@ export function buildSkillDescMoreRich(source: ISkillDescSource): string {
|
|||||||
const revive = resolveReviveByLv(source[SkillTriggerType.Revive] as LvReviveEntry[] | undefined, lv);
|
const revive = resolveReviveByLv(source[SkillTriggerType.Revive] as LvReviveEntry[] | undefined, lv);
|
||||||
if (revive) {
|
if (revive) {
|
||||||
const reviveName = getTriggerSkillName(SkillTriggerType.Revive, revive.s_uuid);
|
const reviveName = getTriggerSkillName(SkillTriggerType.Revive, revive.s_uuid);
|
||||||
lines.push(`${skillNameRich(reviveName)}: 每回合拥有<color=${RICH_VALUE_COLOR}>${revive.r_num}</color>次重生机会, 死亡后以<color=${RICH_VALUE_COLOR}>${Math.round(revive.upr * 100)}%</color>血量原地重生`);
|
lines.push(`${skillNameRich(reviveName)}: 每回合拥有${valueRich(revive.r_num)}次重生机会, 死亡后以${valueRich(`${Math.round(revive.upr * 100)}%`)}血量原地重生`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines.join("\n");
|
return lines.join("\n");
|
||||||
@@ -620,7 +632,7 @@ export function buildSkillDescMoreRich(source: ISkillDescSource): string {
|
|||||||
* 持续时间对药水/计时 buff 同样关键,这里补充高亮,保证视觉一致。
|
* 持续时间对药水/计时 buff 同样关键,这里补充高亮,保证视觉一致。
|
||||||
*/
|
*/
|
||||||
function highlightDuration(text: string): string {
|
function highlightDuration(text: string): string {
|
||||||
return text.replace(DURATION_PATTERN, `持续<color=${RICH_VALUE_COLOR}>$1</color>秒`);
|
return text.replace(DURATION_PATTERN, `持续${valueRich("$1")}秒`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -634,7 +646,7 @@ function renderLineRich(line: ISkillLine): string {
|
|||||||
// 未来档数值灰色追加在当前高亮数值后("15%/20%/30%"),suffix 仍位于整段数值之后
|
// 未来档数值灰色追加在当前高亮数值后("15%/20%/30%"),suffix 仍位于整段数值之后
|
||||||
const next = line.nextValues?.length ? `<color=${RICH_LOCKED_COLOR}>/${line.nextValues.join("/")}</color>` : "";
|
const next = line.nextValues?.length ? `<color=${RICH_LOCKED_COLOR}>/${line.nextValues.join("/")}</color>` : "";
|
||||||
return line.value
|
return line.value
|
||||||
? `${prefix}<color=${RICH_VALUE_COLOR}>${line.value}</color>${next}${line.suffix}`
|
? `${prefix}${valueRich(line.value)}${next}${line.suffix}`
|
||||||
: `${prefix}${next}${line.suffix}`;
|
: `${prefix}${next}${line.suffix}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,7 +658,7 @@ function renderLineRich(line: ISkillLine): string {
|
|||||||
function buildChanceSuffix(skill: SkillConfig): string {
|
function buildChanceSuffix(skill: SkillConfig): string {
|
||||||
const items: string[] = [];
|
const items: string[] = [];
|
||||||
const push = (val: number | undefined, label: string) => {
|
const push = (val: number | undefined, label: string) => {
|
||||||
if (val && val > 0) items.push(`附加<color=${RICH_VALUE_COLOR}>${val}</color>%${label}`);
|
if (val && val > 0) items.push(`附加${valueRich(val)}%${label}`);
|
||||||
};
|
};
|
||||||
push(skill.crt, "暴击");
|
push(skill.crt, "暴击");
|
||||||
push(skill.frz, "冰冻");
|
push(skill.frz, "冰冻");
|
||||||
@@ -699,8 +711,8 @@ export function buildCardDescRich(card: CardConfig): string {
|
|||||||
return rich;
|
return rich;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rhythm = `每<color=${RICH_VALUE_COLOR}>${tInv}</color>秒召唤<color=${RICH_VALUE_COLOR}>${(card.overrides?.num ?? 0) + 1}</color>个${skillNameRich(skill.name)}攻击敌人`;
|
const rhythm = `每${valueRich(tInv)}秒召唤${valueRich((card.overrides?.num ?? 0) + 1)}个${skillNameRich(skill.name)}攻击敌人`;
|
||||||
const times = `, 共<color=${RICH_VALUE_COLOR}>${tTimes}</color>次`;
|
const times = `, 共${valueRich(tTimes)}次`;
|
||||||
return `${rhythm}, ${rich}${times}`;
|
return `${rhythm}, ${rich}${times}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -557,13 +557,19 @@ export class HeroBoxComp extends CCComp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---- AP / HP ----
|
// ---- AP / HP ----
|
||||||
|
// plus(附加值)= 最终值 - 当前等级基础值;基础值 = 1 级原始 base + 等级成长 + ap/hp_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 finalAp = Math.max(0, Math.floor(this.model.getFinalAp()));
|
||||||
const baseAp = Math.max(0, Math.floor(this.model.base_ap ?? 0));
|
const lvBaseAp = Math.max(0, Math.floor((this.model.base_ap ?? 0) + grow.ap + HeroBoxComp.sumBonus(hero?.ap_bonus, lv)));
|
||||||
this.setStatRow(this.ap_all_label, this.ap_plus_label, finalAp, finalAp - baseAp);
|
this.setStatRow(this.ap_all_label, this.ap_plus_label, finalAp, finalAp - lvBaseAp);
|
||||||
|
|
||||||
const finalHp = Math.max(0, Math.floor(this.model.getFinalHpMax()));
|
const finalHp = Math.max(0, Math.floor(this.model.getFinalHpMax()));
|
||||||
const baseHp = Math.max(0, Math.floor(this.model.base_hp ?? 0));
|
const lvBaseHp = Math.max(0, Math.floor((this.model.base_hp ?? 0) + grow.hp + HeroBoxComp.sumBonus(hero?.hp_bonus, lv)));
|
||||||
this.setStatRow(this.hp_all_label, this.hp_plus_label, finalHp, finalHp - baseHp);
|
this.setStatRow(this.hp_all_label, this.hp_plus_label, finalHp, finalHp - lvBaseHp);
|
||||||
|
|
||||||
// ---- 图标(UUID 变化时重新加载首帧动画) ----
|
// ---- 图标(UUID 变化时重新加载首帧动画) ----
|
||||||
if (hero && heroUuid !== this.iconHeroUuid) {
|
if (hero && heroUuid !== this.iconHeroUuid) {
|
||||||
|
|||||||
Reference in New Issue
Block a user