feat(editor): 完善技能覆写编辑器功能,优化配置体验

1. 为OverrideEditor新增baseSkillUuid参数,支持对敌技能debuff配置校验
2. 优化proc_skill和timed_buff_id的编辑体验,自动补全关联配置项
3. 更新常量注释与英雄技能配置,修正overrides字段配置
4. 新增对敌技能buff配置警示提示,引导正确配置方式
This commit is contained in:
pan
2026-09-08 10:38:18 +08:00
parent 4d7e54f833
commit f5908a9adf
7 changed files with 119 additions and 14 deletions

View File

@@ -36,6 +36,7 @@
size="small"
style="width: 220px"
filterable
@change="onProcSkillChange(item)"
>
<el-option v-for="s in skillOptions" :key="s.uuid" :label="`${s.uuid} ${s.name}`" :value="s.uuid" />
</el-select>
@@ -50,7 +51,7 @@
/>
<div class="proc-skill-overrides">
<span class="proc-skill-label">被触发技能覆写:</span>
<OverrideEditor v-model="item.value.overrides" :skill-options="skillOptions" :buff-options="buffOptions" />
<OverrideEditor v-model="item.value.overrides" :skill-options="skillOptions" :buff-options="buffOptions" :base-skill-uuid="item.value.s_uuid" />
</div>
</div>
</template>
@@ -74,12 +75,21 @@
<el-input-number v-else v-model="item.value" size="small" :controls="false" style="width: 120px" />
<el-button type="danger" size="small" text @click="remove(idx)">删除</el-button>
</div>
<el-alert
v-if="showEnemyBuffWarn"
type="info"
:closable="false"
show-icon
style="margin-bottom: 6px"
title="对敌技能 debuff 配置提示"
description="对敌技能的命中附带 debuff 以 SkillSet 底座配置为准,此处 overrides 注入建议用于友方/辅助技能(可用 buff_target 分流挂敌);吸血类需 buff_target=1(施法者自身)。proc_skill 用于命中后概率触发主动技能,不是挂 buff 的方式。"
/>
<el-button size="small" @click="add">+ 添加覆盖</el-button>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
import { ref, watch, computed } from 'vue'
import { OVERRIDE_KEYS, SKILL_ICON_OPTIONS } from '../constants'
const props = defineProps({
@@ -88,9 +98,22 @@ const props = defineProps({
skillOptions: { type: Array, default: () => [] },
// 全部 buff 候选(timed_buff_id 下拉用),由 App 从 meta.BuffList 计算后透传
buffOptions: { type: Array, default: () => [] },
// 宿主技能 uuid:用于判定对敌技能(TGroup=Enemy)注入 timed_buff_id 的断链警示
baseSkillUuid: { type: Number, default: null },
})
const emit = defineEmits(['update:modelValue'])
// 宿主技能是否走对敌路径:有效 TGroup = overrides.TGroup 覆写值 ?? 底座 TGroup(Enemy=3;meta 未加载时不判定)
const isEnemyHost = computed(() => {
if (props.baseSkillUuid == null) return false
const base = props.skillOptions.find((s) => s.uuid === props.baseSkillUuid)
const tgroupRow = rows.value.find((r) => r.key === 'TGroup')
const effective = tgroupRow?.value ?? base?.TGroup
return effective === 3 || effective === 'Enemy'
})
// 对敌技能且注入了 timed_buff_id → 运行时断链不生效,警示引导改用 proc_skill / 底座表
const showEnemyBuffWarn = computed(() => isEnemyHost.value && rows.value.some((r) => r.key === 'timed_buff_id'))
// 本地行数组,允许 key 为空
const rows = ref(Object.entries(props.modelValue || {}).map(([key, value]) => ({ key, value })))
@@ -155,11 +178,42 @@ function isBuffKey(key) {
return OVERRIDE_KEYS.find((k) => k.key === key)?.type === 'buff'
}
// 选中 proc_skill 被触发技能后,在其内嵌 overrides 自动补 TGroup 行(已存在不覆盖):
// 值取该技能底座的 TGroup,让内嵌编辑器能立刻按有效目标群体做对敌断链警示等判定,也便于直改释放对象
function onProcSkillChange(item) {
const overrides = item.value?.overrides
if (!overrides) return
if (overrides.TGroup !== undefined) return
const base = props.skillOptions.find((s) => s.uuid === item.value.s_uuid)
const name = TGROUP_NAMES[base?.TGroup]
if (name) overrides.TGroup = name
}
// 底座 TGroup 数值 → 字符串名(编辑器 TGroup 下拉 value 为字符串,导出时还原 TGroup.X 枚举表达式)
const TGROUP_NAMES = ['Self', 'Ally', 'Team', 'Enemy', 'All']
// allow-create 手输的 buff id 是字符串,归一化为数字,保证导出 TS 配置类型正确
function onBuffValueChange(item) {
if (typeof item.value === 'string' && item.value.trim() !== '' && !isNaN(Number(item.value))) {
item.value = Number(item.value)
}
ensureBuffCompanionRows(item.value)
}
// 挂载 timed_buff_id 后自动补齐配套三键(已存在的不重复添加):
// 数值预填 BuffList 默认值(modifiers[0].value / tick.damage_or_heal / duration),避免导出 0 值覆盖 buff 语义
function ensureBuffCompanionRows(buffId) {
const buff = typeof buffId === 'number' ? props.buffOptions.find((b) => b.id === buffId) : null
const has = (key) => rows.value.some((r) => r.key === key)
if (!has('buff_value')) {
rows.value.push({ key: 'buff_value', value: buff?.modifiers?.[0]?.value ?? buff?.tick?.damage_or_heal ?? 0 })
}
if (!has('buff_chance')) {
rows.value.push({ key: 'buff_chance', value: 100 })
}
if (!has('buff_duration')) {
rows.value.push({ key: 'buff_duration', value: buff?.duration ?? 5 })
}
}
// 对象型覆写行选中时初始化默认结构,避免 v-model 绑定到 undefined 子字段

View File

@@ -25,7 +25,7 @@
</el-form-item>
</div>
<el-form-item label="overrides">
<OverrideEditor v-model="skill.overrides" :skill-options="skillOptions" :buff-options="buffOptions" />
<OverrideEditor v-model="skill.overrides" :skill-options="skillOptions" :buff-options="buffOptions" :base-skill-uuid="skill.uuid" />
</el-form-item>
<!-- 分档编辑:大招(is-ult)全档编辑;普攻(basic-lv2)仅 lv2 强化档(客户端独立技能图标消费) -->
@@ -47,7 +47,7 @@
</el-form-item>
</div>
<el-form-item label="overrides">
<OverrideEditor v-model="e.overrides" :skill-options="skillOptions" :buff-options="buffOptions" />
<OverrideEditor v-model="e.overrides" :skill-options="skillOptions" :buff-options="buffOptions" :base-skill-uuid="skill.uuid" />
</el-form-item>
<el-form-item label="描述">
<el-input v-model="e.desc" type="textarea" :rows="2" />

View File

@@ -23,7 +23,7 @@
</el-form-item>
</div>
<el-form-item label="overrides">
<OverrideEditor v-model="entry.overrides" :skill-options="skillOptions" :buff-options="buffOptions" />
<OverrideEditor v-model="entry.overrides" :skill-options="skillOptions" :buff-options="buffOptions" :base-skill-uuid="Number(sid)" />
</el-form-item>
<el-form-item label="描述">
<el-input v-model="entry.desc" type="textarea" :rows="2" />

View File

@@ -135,8 +135,8 @@ export const OVERRIDE_KEYS = [
{ key: 'call_hero', label: 'call_hero 召唤英雄id' },
{ key: 'revive_hp', label: 'revive_hp 复活回血比例(%)' },
{ key: 'revive_count', label: 'revive_count 复活次数上限' },
// 对象型覆写:命中后概率触发额外技能(技能级触发器),含 s_uuid/chance/overrides 子字段
{ key: 'proc_skill', label: 'proc_skill 命中概率触发技能', type: 'object' },
// 对象型覆写:命中后概率触发主动技能(大招/触发技),含 s_uuid/chance/overrides 子字段
{ key: 'proc_skill', label: 'proc_skill 命中概率触发主动技能', type: 'object' },
// 字符串型覆写:value 为图标ID(uicons 图集帧名),非数值
{ key: 'icon', label: 'icon 英雄语境图标(覆写 SkillSet.icon)', type: 'string' },
]