feat(hero): 新增回血坦克5009与火箭哥5010英雄,优化UI展示与技能逻辑

1.  新增5009回血坦克、5010火箭哥英雄配置
2.  重构英雄列表UI布局,新增职业标签配色区分
3.  优化技能详情展示,支持完整覆写参数显示
4.  调整技能默认配置,移除固定summon_count注入
5.  优化触发技能展示逻辑,新增参数详情显示
This commit is contained in:
panFD
2026-09-06 17:43:16 +08:00
parent ff4a4124f0
commit 30e69234b7
4 changed files with 296 additions and 22 deletions

View File

@@ -613,8 +613,7 @@
"cdExpr": null,
"overrides": {
"ap": 250,
"bck": 100,
"summon_count": 1
"bck": 100
},
"desc": "喷出烈焰风暴,对{target}造成攻击力{ap}%伤害,击退{bck}",
"lv_entries": [
@@ -623,8 +622,7 @@
"s_lv": 2,
"overrides": {
"ap": 350,
"bck": 100,
"summon_count": 1
"bck": 100
},
"desc": ""
},
@@ -738,8 +736,7 @@
"name": "圣愈之盾",
"t_num": 3,
"overrides": {
"ap": 30,
"summon_count": 1
"ap": 30
},
"desc": "{target}获得生命最大值{ap}%的护盾"
}
@@ -754,6 +751,197 @@
"hp_bonus": [],
"def_bonus": [],
"info": ""
},
{
"uuid": 5009,
"name": "甲虫领主",
"comment": "",
"path": "hk2",
"rarity": 2,
"roleKey": "Tank",
"growKey": "Tank",
"typeKey": "Melee",
"statBase": {
"hp": 0,
"ap": 0,
"def": 0
},
"dis": null,
"speed": null,
"skills": [
{
"uuid": 6004,
"name": "普攻强化",
"cd": 1.2,
"cdExpr": "AtkSpeedSet[AtkSpeedLv.Slow].cd",
"overrides": {},
"desc": "",
"lv_entries": [
{
"lv": 2,
"s_lv": 2,
"overrides": {
"proc_skill": {
"s_uuid": 6502,
"chance": 10,
"overrides": {
"ap": 10
}
}
},
"desc": ""
}
]
},
{
"uuid": 6102,
"name": "",
"cd": 8,
"cdExpr": null,
"overrides": {
"ap": 250,
"bck": 30
},
"desc": "",
"lv_entries": [
{
"lv": 8,
"s_lv": 2,
"overrides": {
"ap": 300,
"bck": 50
},
"desc": ""
},
{
"lv": 16,
"s_lv": 3,
"overrides": {
"ap": 350,
"bck": 70
},
"desc": ""
}
]
}
],
"atking": {},
"atked": {
"6302": [
{
"lv": 4,
"s_lv": 1,
"name": "",
"t_num": 3,
"overrides": {
"ap": 10
},
"desc": ""
}
]
},
"fstart": {},
"dead": {},
"field": [],
"ap_bonus": [],
"hp_bonus": [],
"def_bonus": [],
"info": ""
},
{
"uuid": 5010,
"name": "火箭哥",
"comment": "",
"path": "ha2",
"rarity": 3,
"roleKey": "Archer",
"growKey": "Archer",
"typeKey": "Mid",
"statBase": {
"hp": 0,
"ap": 0,
"def": 0
},
"dis": null,
"speed": null,
"skills": [
{
"uuid": 6006,
"name": "强化普攻",
"cd": 0.9,
"cdExpr": "AtkSpeedSet[AtkSpeedLv.Fast].cd",
"overrides": {},
"desc": "",
"lv_entries": [
{
"lv": 2,
"s_lv": 2,
"overrides": {
"ap": 100,
"bck_chance": 10
},
"desc": ""
}
]
},
{
"uuid": 6151,
"name": "",
"cd": 8,
"cdExpr": null,
"overrides": {
"ap": 70,
"summon_count": 5,
"bck_chance": 100
},
"desc": "",
"lv_entries": [
{
"lv": 8,
"s_lv": 2,
"overrides": {
"ap": 80,
"summon_count": 5,
"bck_chance": 100
},
"desc": ""
},
{
"lv": 16,
"s_lv": 3,
"overrides": {
"bck_chance": 100,
"ap": 100,
"summon_count": 6
},
"desc": ""
}
]
}
],
"atking": {
"6008": [
{
"lv": 4,
"s_lv": 1,
"name": "",
"t_num": 3,
"overrides": {
"ap": 120,
"bck_chance": 50
},
"desc": ""
}
]
},
"atked": {},
"fstart": {},
"dead": {},
"field": [],
"ap_bonus": [],
"hp_bonus": [],
"def_bonus": [],
"info": ""
}
]
}

View File

@@ -21,7 +21,10 @@
<div class="row-main">
<span class="row-uuid">{{ h.uuid }}</span>
<span class="row-name">{{ h.name || '(未命名)' }}</span>
<span class="card-rarity">{{ '★'.repeat(h.rarity) }}</span>
<div class="row-sub">
<span class="row-role" :class="`role-${h.roleKey?.toLowerCase()}`">{{ roleLabel(h.roleKey) }}</span>
<span class="card-rarity">{{ '★'.repeat(h.rarity) }}</span>
</div>
</div>
<div class="row-meta">
<template v-if="heroStats(h)">
@@ -33,11 +36,11 @@
<div class="row-skills">
<div class="row-skill">
<span class="tag">普攻</span>{{ skillLabel(h.skills?.[0]) }}
<span class="skill-detail">{{ skillDetail(h.skills?.[0]) }}</span>
<span class="skill-detail">{{ skillDetailFull(h.skills?.[0]) }}</span>
</div>
<div class="row-skill">
<span class="tag">大招</span>{{ skillLabel(h.skills?.[1]) }}
<span class="skill-detail">{{ skillDetail(h.skills?.[1]) }}</span>
<span class="skill-detail">{{ skillDetailFull(h.skills?.[1]) }}</span>
</div>
</div>
<div class="row-triggers" v-if="hasTriggers(h) || fieldSummary(h).length">
@@ -45,7 +48,6 @@
<span v-for="line in triggerSummary(h)" :key="line" class="trigger-chip">{{ line }}</span>
<span v-if="bonusCount(h)" class="trigger-chip">加成: ×{{ bonusCount(h) }}</span>
</div>
<div class="row-comment" v-if="h.comment || h.info">{{ h.comment || h.info }}</div>
</div>
</section>
@@ -247,6 +249,40 @@ function skillDetail(sk) {
if (sk.lv_entries?.length) parts.push(`→lv${Math.max(...sk.lv_entries.map((e) => e.lv))}`)
return parts.join(' ')
}
// 覆写对象转简写(如 ap100、crt10、stun20、summon2)
function overrideParts(ov) {
if (!ov) return []
const parts = []
if (ov.ap != null) parts.push(`ap${ov.ap}`)
if (ov.hit_count != null) parts.push(`hit${ov.hit_count}`)
if (ov.crt != null) parts.push(`crt${ov.crt}`)
if (ov.frz != null) parts.push(`frz${ov.frz}`)
if (ov.stun != null) parts.push(`stun${ov.stun}`)
if (ov.para != null) parts.push(`para${ov.para}`)
if (ov.bck != null) parts.push(`bck${ov.bck}`)
if (ov.thorns_dmg != null) parts.push(`thorns${ov.thorns_dmg}`)
if (ov.thorns_rate != null) parts.push(`thorns${ov.thorns_rate}%`)
if (ov.summon_count != null) parts.push(`数${ov.summon_count}`)
if (ov.num != null) parts.push(`num${ov.num}`)
if (ov.timed_buff_id != null) parts.push(`buff${ov.timed_buff_id}`)
if (ov.buff_value != null) parts.push(`val${ov.buff_value}`)
if (ov.buff_chance != null) parts.push(`chance${ov.buff_chance}`)
return parts
}
// 技能完整明细:lv1 + 各 lv_entry 档位参数
function skillDetailFull(sk) {
if (!sk) return ''
const parts = []
const lv1 = overrideParts(sk.overrides).join(' ')
if (lv1) parts.push(lv1)
for (const e of sk.lv_entries || []) {
const ep = overrideParts(e.overrides).join(' ')
parts.push(`lv${e.lv}:${ep || '-'}`)
}
return parts.join(' | ')
}
// lv1 基础三维:HERO_ROLE_BASE + 编辑器偏移(meta 未加载时返回 null 不展示)
function heroStats(h) {
const roleEnum = HERO_ROLES.find((r) => r.key === h.roleKey)?.value
@@ -258,7 +294,7 @@ function heroStats(h) {
def: base.def + (h.statBase?.def || 0),
}
}
// 触发明细行:atking: 6004 奋力一击 lv4 格式,仅输出已启用的组
// 触发明细行:atking: 6004 奋力一击 lv4 ap300 格式,仅输出已启用的组
function triggerSummary(h) {
const lines = []
for (const tk of ['atking', 'atked', 'fstart', 'dead']) {
@@ -267,7 +303,20 @@ function triggerSummary(h) {
for (const [sid, entries] of Object.entries(grp)) {
const minLv = Math.min(...entries.map((e) => e.lv))
const name = entries[0]?.name || skillMap.value[sid]?.name || ''
lines.push(`${tk}: ${sid} ${name} lv${minLv}`)
const e = entries[0]
const ov = e?.overrides || {}
const parts = [`t_num${e?.t_num}`]
if (ov.ap != null) parts.push(`ap${ov.ap}`)
if (ov.crt != null) parts.push(`crt${ov.crt}`)
if (ov.stun != null) parts.push(`stun${ov.stun}`)
if (ov.frz != null) parts.push(`frz${ov.frz}`)
if (ov.para != null) parts.push(`para${ov.para}`)
if (ov.bck != null) parts.push(`bck${ov.bck}`)
if (ov.thorns_dmg != null) parts.push(`thorns${ov.thorns_dmg}`)
if (ov.thorns_rate != null) parts.push(`thorns${ov.thorns_rate}%`)
if (ov.timed_buff_id != null) parts.push(`buff${ov.timed_buff_id}`)
if (ov.summon_count != null) parts.push(`数${ov.summon_count}`)
lines.push(`${tk}: ${sid} ${name} lv${minLv} ${parts.join(' ')}`)
}
}
return lines
@@ -345,13 +394,13 @@ function addUltSkill() {
const first = ultSkillOptions.value[0]
if (!first) return ElMessage.warning('当前职业无可用大招底座')
const sk = defaultSkill(first.uuid)
// 大招默认 summon_count=1,并提供 lv9/lv16 档位
// 大招提供 lv8(SkillLv.Lv2)/lv16(SkillLv.Lv3) 档位,不默认注入 summon_count
sk.cd = 8
sk.cdExpr = null
sk.overrides = { summon_count: 1 }
sk.overrides = {}
sk.lv_entries = [
{ lv: 9, s_lv: 2, overrides: { summon_count: 1 }, desc: '' },
{ lv: 16, s_lv: 3, overrides: { summon_count: 2 }, desc: '' },
{ lv: 8, s_lv: 2, overrides: {}, desc: '' },
{ lv: 16, s_lv: 3, overrides: {}, desc: '' },
]
current.value.skills[1] = sk
}

View File

@@ -96,14 +96,28 @@ body {
}
.hero-row:hover { box-shadow: 0 3px 12px rgba(46, 91, 160, 0.15); }
/* 行首:uuid + 名称 + 星级 */
.row-main { display: flex; align-items: center; gap: 8px; min-width: 130px; }
.row-uuid { font-family: Menlo, Consolas, monospace; color: var(--text-light); }
/* 行首:uuid / 名称 / 星级+职业,三行竖排 */
.row-main { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-width: 100px; }
.row-sub { display: flex; align-items: center; gap: 6px; }
.row-uuid { font-family: Menlo, Consolas, monospace; color: var(--text-light); font-size: 11px; }
.row-name { font-size: 13px; font-weight: 600; }
.card-rarity { color: #f5a623; font-size: 11px; }
.row-role {
border-radius: 4px;
padding: 0 6px;
font-size: 11px;
line-height: 18px;
}
/* 职业标签颜色区分 */
.role-tank { background: #e6f7ff; color: #1890ff; }
.role-warrior { background: #fff2e8; color: #fa541c; }
.role-assassin { background: #f9f0ff; color: #722ed1; }
.role-archer { background: #f6ffed; color: #52c41a; }
.role-mage { background: #fff0f6; color: #eb2f96; }
.role-support { background: #fcffe6; color: #a0d911; }
/* 三维徽章 */
.row-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; }
/* 三维徽章:竖排三行显示 */
.row-meta { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; }
.stat {
background: #f0f7ef;
color: #4a7c3f;
@@ -115,7 +129,7 @@ body {
/* 技能段:普攻/大招两行 */
.row-skills { display: flex; flex-direction: column; gap: 2px; min-width: 200px; }
.skill-detail { color: var(--text-light); font-size: 11px; margin-left: 4px; }
.skill-detail { color: var(--text-light); font-size: 11px; margin-left: 4px; white-space: pre-wrap; word-break: break-all; }
/* 触发/光环/加成:chip 流式排布 */
.row-triggers { display: flex; flex-wrap: wrap; gap: 4px 6px; flex: 1; min-width: 160px; }