Compare commits
2 Commits
30748536d0
...
c4b49e4df6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4b49e4df6 | ||
|
|
63423c5378 |
@@ -171,6 +171,15 @@ export interface SkillConfig {
|
||||
call_hero?: number, // 召唤技能召唤英雄id(可选)
|
||||
summon_count?: number, // 单次施法召唤的技能实体个数(未配置时回退 prefab 上 SkillView.summon_count)
|
||||
is_accel?: boolean, // 是否逐渐加速飞行
|
||||
/**
|
||||
* 命中后概率触发额外技能(技能级触发器)。
|
||||
* s_uuid: 被触发的技能 uuid;chance: 触发概率(百分点,缺省 100)。
|
||||
* overrides: 透传给被触发技能的覆写参数(如 ap/hit_count 等),用于自定义被触发技能的伤害/数值;
|
||||
* 与被触发技能自身在 heroSet 中的 overrides 叠加(本次传入优先)。
|
||||
* 触发走 SkillTriggerHelper/forceCastTriggerSkill 通道(无视 CD、无施法前摇),与 atking 同一通道。
|
||||
* 支持在 lv_entries 的 overrides 里按等级覆写(利用 overrides 浅合并机制)。
|
||||
*/
|
||||
proc_skill?: { s_uuid: number; chance?: number; overrides?: SkillOverrides },
|
||||
info: string, // 技能描述
|
||||
}
|
||||
|
||||
@@ -202,6 +211,9 @@ export interface SkillOverrides {
|
||||
revive_hp?: number;
|
||||
/** 复活次数上限,配合 revive_hp 使用(仅 dead 触发有效) */
|
||||
revive_count?: number;
|
||||
/** 命中后概率触发额外技能(技能级触发器,覆写 SkillConfig.proc_skill),支持 lv_entries 按等级覆写;
|
||||
* overrides 字段可自定义被触发技能的伤害/数值(如 ap),透传给 forceCastTriggerSkill */
|
||||
proc_skill?: { s_uuid: number; chance?: number; overrides?: SkillOverrides };
|
||||
/** 英雄语境专属图标(图鉴面板/技能弹窗展示用):配置后覆盖 SkillSet.icon,
|
||||
* 实现"同一底座、不同英雄/档位不同外观"(如 5002 普攻与冲刺斩同 6004 但图标不同) */
|
||||
icon?: string;
|
||||
|
||||
@@ -548,7 +548,7 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
5001: {
|
||||
uuid: 5001, name: "叠甲骑士", path: "hk1", fac: FacSet.HERO, rarity: 1, lv: 1, type: HType.Melee, grow_type: HGrowType.Tank, role: HRole.Tank, hp: HERO_ROLE_BASE[HRole.Tank].hp + 20, ap: HERO_ROLE_BASE[HRole.Tank].ap, def: HERO_ROLE_BASE[HRole.Tank].def + 10,
|
||||
skills: {
|
||||
6004: { uuid: 6004, name: "强化普攻", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: {"ap":100}, desc: "挥剑对{target}造成攻击力{ap}%伤害",
|
||||
6004: { uuid: 6004, name: "强化普攻", lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0, overrides: {"ap":100,"proc_skill":{"s_uuid":6001,"chance":100,"overrides":{"crt":100}}}, desc: "挥剑对{target}造成攻击力{ap}%伤害",
|
||||
lv_entries: [
|
||||
{ lv: 2, s_lv: SkillLv.Lv2, overrides: {"ap":120} },
|
||||
],
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "dae7fb4d-25c3-4c7a-8630-c70e2bac9b3a",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { HeroViewComp } from "./HeroViewComp";
|
||||
import { SkillOverrides } from "../common/config/SkillSet";
|
||||
|
||||
/**
|
||||
* ==================== 伤害事件数据 ====================
|
||||
@@ -29,6 +30,9 @@ export interface DamageEvent {
|
||||
* 跳过 dmgCount 公式直接用该值,且不走击退/受击动画,其余(暴击/冰冻/击晕/护盾/死亡链)与普攻一致
|
||||
*/
|
||||
dot_dmg?:number
|
||||
/** 命中后概率触发额外技能(由施法技能实体快照自合并后配置,HeroAtkSystem 据此判定;
|
||||
* overrides 字段透传给被触发技能,用于自定义伤害/数值) */
|
||||
proc_skill?: { s_uuid: number; chance?: number; overrides?: SkillOverrides }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +69,7 @@ export class DamageQueueComp extends ecs.Comp {
|
||||
/**
|
||||
* 添加伤害事件到队列
|
||||
*/
|
||||
addDamageEvent(attrs: any, casterEid: number, s_uuid: number,ext_dmg:number=0,dmg_ratio:number=1,dot_dmg:number=0): void {
|
||||
addDamageEvent(attrs: any, casterEid: number, s_uuid: number,ext_dmg:number=0,dmg_ratio:number=1,dot_dmg:number=0,proc_skill?: { s_uuid: number; chance?: number; overrides?: SkillOverrides }): void {
|
||||
const timestamp = Date.now();
|
||||
|
||||
const damageEvent: DamageEvent = {
|
||||
@@ -77,6 +81,7 @@ export class DamageQueueComp extends ecs.Comp {
|
||||
ext_dmg:ext_dmg,
|
||||
dmg_ratio:dmg_ratio,
|
||||
dot_dmg:dot_dmg,
|
||||
proc_skill:proc_skill,
|
||||
};
|
||||
|
||||
this.damageEvents.push(damageEvent);
|
||||
@@ -182,7 +187,7 @@ export class DamageQueueHelper {
|
||||
* 为实体添加伤害事件
|
||||
* 如果实体没有伤害队列组件,会自动创建
|
||||
*/
|
||||
static addDamageToEntity(entity: ecs.Entity, attrs: any, casterEid: number, s_uuid: number,ext_dmg:number=0,dmg_ratio:number=1,dot_dmg:number=0): void {
|
||||
static addDamageToEntity(entity: ecs.Entity, attrs: any, casterEid: number, s_uuid: number,ext_dmg:number=0,dmg_ratio:number=1,dot_dmg:number=0,proc_skill?: { s_uuid: number; chance?: number; overrides?: SkillOverrides }): void {
|
||||
let damageQueue = entity.get(DamageQueueComp);
|
||||
|
||||
// 如果实体没有伤害队列组件,创建一个
|
||||
@@ -191,7 +196,7 @@ export class DamageQueueHelper {
|
||||
}
|
||||
|
||||
// 添加伤害事件到队列
|
||||
damageQueue.addDamageEvent(attrs, casterEid, s_uuid,ext_dmg,dmg_ratio,dot_dmg);
|
||||
damageQueue.addDamageEvent(attrs, casterEid, s_uuid,ext_dmg,dmg_ratio,dot_dmg,proc_skill);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -271,6 +271,25 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
}
|
||||
}
|
||||
|
||||
// 命中概率触发额外技能(proc_skill):配置由施法技能实体快照自合并后配置(含 heroSet overrides/lv_entries),
|
||||
// 触发走 SkillTriggerHelper/forceCastTriggerSkill 通道(无视 CD、无施法前摇),与 atking 同一通道。
|
||||
// 防递归:被触发的技能经 forceCastTriggerSkill 不再产生 proc_skill 伤害事件之外的新事件,且不会累加 atk_count。
|
||||
if (!isDot && damageEvent.proc_skill) {
|
||||
const proc = damageEvent.proc_skill;
|
||||
const procChance = proc.chance ?? 100;
|
||||
if (procChance >= 100 || Math.random() * 100 < procChance) {
|
||||
const caster = ecs.getEntityByEid(casterEid);
|
||||
if (caster) {
|
||||
const casterAttrs = caster.get(HeroAttrsComp);
|
||||
const casterView = caster.get(HeroViewComp);
|
||||
if (casterAttrs && casterView && !casterAttrs.is_dead && !casterAttrs.is_reviving) {
|
||||
// proc_skill.overrides 透传给被触发技能(如自定义 ap 伤害),与被触发技能自身 overrides 叠加(本次传入优先)
|
||||
SkillTriggerHelper.dispatchSinglePublic(proc.s_uuid, casterAttrs, casterView, SkillTriggerType.Atking, proc.overrides);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 检查死亡
|
||||
if (TAttrsComp.hp <= 0) {
|
||||
|
||||
@@ -166,4 +166,17 @@ export class SkillTriggerHelper {
|
||||
overrides: overrides
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 公共单技能派发(供 HeroAtkSystem 等命中后触发场景调用)。
|
||||
* 直接复用 TriggerSkill 通道(forceCastTriggerSkill),无视 CD、无施法前摇。
|
||||
* @param s_uuid 被触发的技能 uuid
|
||||
* @param model 施法者属性组件
|
||||
* @param view 施法者视图组件
|
||||
* @param type 触发类型(语义来源,如 SkillTriggerType.Atking)
|
||||
* @param overrides 透传给被触发技能的覆写参数
|
||||
*/
|
||||
public static dispatchSinglePublic(s_uuid: number, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType, overrides?: SkillOverrides) {
|
||||
this.dispatchSingle(s_uuid, model, view, type, overrides);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { BoxSet } from "../common/config/GameSet";
|
||||
import { SkillOverrides } from "../common/config/SkillSet";
|
||||
|
||||
/** 业务层对象 */
|
||||
//技能数据
|
||||
@@ -16,6 +17,9 @@ export class SDataCom extends ecs.Comp {
|
||||
dmg_ratio:number=1 //伤害比例
|
||||
hit_count:number=0 //击中数量
|
||||
max_hit_count:number=0 //最大可命中次数
|
||||
/** 命中后概率触发额外技能(由 Skill.load 从合并后配置快照;DamageEvent 透传给 HeroAtkSystem 判定;
|
||||
* overrides 字段透传给被触发技能,用于自定义伤害/数值) */
|
||||
proc_skill?: { s_uuid: number; chance?: number; overrides?: SkillOverrides };
|
||||
reset() {
|
||||
this.Attrs=null
|
||||
this.group=BoxSet.HERO
|
||||
@@ -27,6 +31,7 @@ export class SDataCom extends ecs.Comp {
|
||||
this.max_hit_count=0
|
||||
this.ext_dmg=0
|
||||
this.dmg_ratio=1
|
||||
this.proc_skill=undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -242,6 +242,8 @@ export class Skill extends ecs.Entity {
|
||||
sDataCom.ext_dmg = ext_dmg
|
||||
sDataCom.hit_count = 0
|
||||
sDataCom.max_hit_count = Math.max(1, sHit)
|
||||
// 快照合并后(基础配置+heroSet overrides/lv_entries)的 proc_skill,命中时由 HeroAtkSystem 判定触发
|
||||
sDataCom.proc_skill = config.proc_skill;
|
||||
SView.init();
|
||||
};
|
||||
|
||||
|
||||
@@ -219,6 +219,8 @@ export class SkillView extends CCComp {
|
||||
this.sData.s_uuid,
|
||||
this.sData.ext_dmg,
|
||||
finalRatio,
|
||||
0,
|
||||
this.sData.proc_skill,
|
||||
);
|
||||
}
|
||||
close_collider() {
|
||||
|
||||
@@ -23,7 +23,14 @@
|
||||
"cd": 1.2,
|
||||
"cdExpr": "AtkSpeedSet[AtkSpeedLv.Slow].cd",
|
||||
"overrides": {
|
||||
"ap": 100
|
||||
"ap": 100,
|
||||
"proc_skill": {
|
||||
"s_uuid": 6001,
|
||||
"chance": 100,
|
||||
"overrides": {
|
||||
"crt": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
"desc": "挥剑对{target}造成攻击力{ap}%伤害",
|
||||
"lv_entries": [
|
||||
|
||||
@@ -114,14 +114,14 @@
|
||||
<!-- 普攻 -->
|
||||
<div class="panel">
|
||||
<h3>普攻 skills[0]</h3>
|
||||
<SkillEditor v-if="basicSkill" :skill="basicSkill" :options="basicSkillOptions" :basic-lv2="true" />
|
||||
<SkillEditor v-if="basicSkill" :skill="basicSkill" :options="basicSkillOptions" :skill-options="allSkills" :basic-lv2="true" />
|
||||
<el-button v-else size="small" type="primary" @click="addBasicSkill">+ 添加普攻</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 大招 -->
|
||||
<div class="panel">
|
||||
<h3>大招 skills[1]</h3>
|
||||
<SkillEditor v-if="ultSkill" :skill="ultSkill" :options="ultSkillOptions" :is-ult="true" />
|
||||
<SkillEditor v-if="ultSkill" :skill="ultSkill" :options="ultSkillOptions" :skill-options="allSkills" :is-ult="true" />
|
||||
<el-button v-else size="small" type="primary" @click="addUltSkill">+ 添加大招</el-button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="override-editor">
|
||||
<div v-for="(item, idx) in rows" :key="idx" class="override-row">
|
||||
<el-select v-model="item.key" placeholder="字段" size="small" style="width: 220px" filterable>
|
||||
<el-select v-model="item.key" placeholder="字段" size="small" style="width: 220px" filterable @change="onKeyChange(item)">
|
||||
<el-option v-for="k in availableKeys(idx)" :key="k.key" :label="k.label" :value="k.key" />
|
||||
</el-select>
|
||||
<!-- 字符串型覆写(icon):输入帧名,提供常用候选可筛选 -->
|
||||
@@ -17,6 +17,33 @@
|
||||
>
|
||||
<el-option v-for="opt in SKILL_ICON_OPTIONS" :key="opt" :label="opt" :value="opt" />
|
||||
</el-select>
|
||||
<!-- 对象型覆写(proc_skill):命中后概率触发额外技能,结构化编辑 s_uuid/chance/overrides -->
|
||||
<template v-else-if="isObjectKey(item.key)">
|
||||
<div class="proc-skill-editor">
|
||||
<el-select
|
||||
v-model="item.value.s_uuid"
|
||||
placeholder="被触发技能 uuid"
|
||||
size="small"
|
||||
style="width: 220px"
|
||||
filterable
|
||||
>
|
||||
<el-option v-for="s in skillOptions" :key="s.uuid" :label="`${s.uuid} ${s.name}`" :value="s.uuid" />
|
||||
</el-select>
|
||||
<el-input-number
|
||||
v-model="item.value.chance"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:controls="false"
|
||||
size="small"
|
||||
placeholder="概率%"
|
||||
style="width: 90px"
|
||||
/>
|
||||
<div class="proc-skill-overrides">
|
||||
<span class="proc-skill-label">被触发技能覆写:</span>
|
||||
<OverrideEditor v-model="item.value.overrides" :skill-options="skillOptions" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<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>
|
||||
@@ -28,25 +55,43 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import { OVERRIDE_KEYS, SKILL_ICON_OPTIONS } from '../constants'
|
||||
|
||||
const props = defineProps({ modelValue: { type: Object, default: () => ({}) } })
|
||||
const props = defineProps({
|
||||
modelValue: { type: Object, default: () => ({}) },
|
||||
// 全部技能候选(proc_skill.s_uuid 下拉用),由 App 经 SkillEditor 透传
|
||||
skillOptions: { type: Array, default: () => [] },
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
// 本地行数组,允许 key 为空
|
||||
const rows = ref(Object.entries(props.modelValue || {}).map(([key, value]) => ({ key, value })))
|
||||
|
||||
// 外部 modelValue 变化时同步(如切换英雄)
|
||||
// 该行是否满足输出条件(proc_skill 需有合法 s_uuid,其余仅需有 key)
|
||||
function isRowOutput(it) {
|
||||
if (!it.key) return false
|
||||
if (it.key === 'proc_skill') return !!it.value?.s_uuid
|
||||
return true
|
||||
}
|
||||
|
||||
// 外部 modelValue 变化时同步(如切换英雄):
|
||||
// 仅当外部值与"当前可输出的行"不一致时才覆盖;proc_skill 编辑中(s_uuid 未填)的行不输出但需保留,
|
||||
// 否则会因 modelValue 缺少 proc_skill 而把用户正在编辑的行整个清掉(表现为"添加无效")
|
||||
watch(() => props.modelValue, (v) => {
|
||||
const next = Object.entries(v || {}).map(([key, value]) => ({ key, value }))
|
||||
// 简单比较,避免输入时循环
|
||||
if (JSON.stringify(next) !== JSON.stringify(rows.value.filter(r => r.key))) {
|
||||
rows.value = next
|
||||
const outputRows = rows.value.filter(isRowOutput)
|
||||
if (JSON.stringify(next) !== JSON.stringify(outputRows)) {
|
||||
// 保留编辑中(key 已选但未达输出条件)的行,追加在外部同步结果之后
|
||||
const editing = rows.value.filter((r) => r.key && !isRowOutput(r))
|
||||
rows.value = [...next, ...editing]
|
||||
}
|
||||
}, { deep: true })
|
||||
|
||||
// 每次行变化都尝试同步(过滤空 key)
|
||||
// 每次行变化都尝试同步(过滤空 key;proc_skill 需有合法 s_uuid 才输出)
|
||||
watch(rows, (arr) => {
|
||||
const obj = {}
|
||||
for (const it of arr) if (it.key) obj[it.key] = it.value
|
||||
for (const it of arr) {
|
||||
if (!isRowOutput(it)) continue
|
||||
obj[it.key] = it.value
|
||||
}
|
||||
emit('update:modelValue', obj)
|
||||
}, { deep: true })
|
||||
|
||||
@@ -61,10 +106,20 @@ function isStringKey(key) {
|
||||
return OVERRIDE_KEYS.find((k) => k.key === key)?.type === 'string'
|
||||
}
|
||||
|
||||
// 该覆写字段为枚举型时返回候选列表(如 buff_target),否则返回 null 走数值输入
|
||||
function enumOptions(key) {
|
||||
const def = OVERRIDE_KEYS.find((k) => k.key === key)
|
||||
return def?.type === 'enum' ? def.options : null
|
||||
// 该覆写字段是否为对象型(proc_skill:{s_uuid, chance, overrides})
|
||||
function isObjectKey(key) {
|
||||
return OVERRIDE_KEYS.find((k) => k.key === key)?.type === 'object'
|
||||
}
|
||||
|
||||
// 对象型覆写行选中时初始化默认结构,避免 v-model 绑定到 undefined 子字段
|
||||
function onKeyChange(item) {
|
||||
if (isObjectKey(item.key)) {
|
||||
item.value = { s_uuid: undefined, chance: 100, overrides: {} }
|
||||
} else if (isStringKey(item.key)) {
|
||||
item.value = ''
|
||||
} else {
|
||||
item.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
function add() {
|
||||
@@ -77,4 +132,8 @@ function remove(idx) {
|
||||
|
||||
<style scoped>
|
||||
.override-row { display: flex; gap: 8px; margin-bottom: 6px; align-items: center; }
|
||||
/* proc_skill 结构化编辑:占满整行,子字段横排,内嵌覆写编辑器缩进展示 */
|
||||
.proc-skill-editor { display: flex; gap: 8px; align-items: flex-start; flex-wrap: wrap; flex: 1; }
|
||||
.proc-skill-overrides { width: 100%; padding-left: 12px; border-left: 2px solid var(--el-border-color); }
|
||||
.proc-skill-label { font-size: 12px; color: var(--el-text-color-secondary); }
|
||||
</style>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="overrides">
|
||||
<OverrideEditor v-model="skill.overrides" />
|
||||
<OverrideEditor v-model="skill.overrides" :skill-options="skillOptions" />
|
||||
</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" />
|
||||
<OverrideEditor v-model="e.overrides" :skill-options="skillOptions" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="e.desc" type="textarea" :rows="2" />
|
||||
@@ -70,6 +70,8 @@ import OverrideEditor from './OverrideEditor.vue'
|
||||
const props = defineProps({
|
||||
skill: { type: Object, required: true },
|
||||
options: { type: Array, default: () => [] },
|
||||
// 全部技能候选(proc_skill.s_uuid 下拉用),由 App 透传
|
||||
skillOptions: { type: Array, default: () => [] },
|
||||
isUlt: { type: Boolean, default: false },
|
||||
// 普攻 lv2 强化档模式:仅允许配置单个 lv2 档(lv=2 / s_lv=2 锁定,禁用增删)
|
||||
basicLv2: { type: Boolean, default: false },
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="overrides">
|
||||
<OverrideEditor v-model="entry.overrides" />
|
||||
<OverrideEditor v-model="entry.overrides" :skill-options="skillOptions" />
|
||||
</el-form-item>
|
||||
<el-form-item label="描述">
|
||||
<el-input v-model="entry.desc" type="textarea" :rows="2" />
|
||||
|
||||
@@ -124,6 +124,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' },
|
||||
// 字符串型覆写:value 为图标ID(uicons 图集帧名),非数值
|
||||
{ key: 'icon', label: 'icon 英雄语境图标(覆写 SkillSet.icon)', type: 'string' },
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user