From 3bbf4a7eb6bb86f2e6874a5f8aefdfa7144cc4ba Mon Sep 17 00:00:00 2001 From: panFD Date: Mon, 14 Sep 2026 16:59:47 +0800 Subject: [PATCH] feat(skill): add round end skill trigger system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增回合结束(FEnd)类型的技能触发机制,包括: 1. 新增SkillTriggerType.FEnd枚举与相关配置映射 2. 为英雄、怪物属性组件添加回合结束技能的运行时解析与存储 3. 实现关卡节间流转时触发全队英雄回合结束技能的逻辑 4. 完善战力计算、技能图标检索等配套支持逻辑 --- assets/script/game/common/config/heroSet.ts | 18 +++++++++++++++--- assets/script/game/hero/Hero.ts | 1 + assets/script/game/hero/HeroAttrsComp.ts | 5 +++++ assets/script/game/hero/Mon.ts | 1 + assets/script/game/hero/SkillTriggerHelper.ts | 3 +++ assets/script/game/map/MissionComp.ts | 18 ++++++++++++++++++ 6 files changed, 43 insertions(+), 3 deletions(-) diff --git a/assets/script/game/common/config/heroSet.ts b/assets/script/game/common/config/heroSet.ts index 97c63d51..769c5955 100644 --- a/assets/script/game/common/config/heroSet.ts +++ b/assets/script/game/common/config/heroSet.ts @@ -199,6 +199,7 @@ export enum SkillTriggerType { Call = "call", // 召唤后触发 降临 Dead = "dead", // 死亡后触发 遗志 FStart = "fstart", // 战斗开始 先手 + FEnd = "fend", // 回合结束 收官 Field = "field", // 驻场 光环 Atking = "atking", // 攻击后触发 追击 Atked = "atked", // 受击后触发 反击 @@ -209,6 +210,7 @@ export const SkillTriggerName = { [SkillTriggerType.Call]: "召唤", [SkillTriggerType.Dead]: "死亡后", [SkillTriggerType.FStart]: "回合开始", + [SkillTriggerType.FEnd]: "回合结束", [SkillTriggerType.Field]: "光环", [SkillTriggerType.Atking]: "攻击", [SkillTriggerType.Atked]: "受伤", @@ -219,6 +221,7 @@ export const SkillTriggerDesc = { [SkillTriggerType.Call]: "召唤时", [SkillTriggerType.Dead]: "死亡时", [SkillTriggerType.FStart]: "战斗开始时", + [SkillTriggerType.FEnd]: "回合结束时", [SkillTriggerType.Field]: "光环", [SkillTriggerType.Atking]: "攻击n次", [SkillTriggerType.Atked]: "受伤n次", @@ -351,7 +354,7 @@ export function getHeroSkillIcon(hero: heroInfo | undefined, skillUuid: number, return undefined; } // 自动定位:再各触发组,最后光环 - for (const type of [SkillTriggerType.Call, SkillTriggerType.Dead, SkillTriggerType.FStart, SkillTriggerType.Atking, SkillTriggerType.Atked]) { + for (const type of [SkillTriggerType.Call, SkillTriggerType.Dead, SkillTriggerType.FStart, SkillTriggerType.FEnd, SkillTriggerType.Atking, SkillTriggerType.Atked]) { const grouped = hero[type] as TriggerGrouped | undefined; const entry = grouped?.[skillUuid]?.[0]; if (entry) return resolveTriggerSkillIcon(entry, skillUuid); @@ -395,6 +398,7 @@ export interface heroInfo { [SkillTriggerType.Call]?: TriggerGrouped; // 召唤后触发的技能配置(按 s_uuid 分组,每组按 lv 升序) [SkillTriggerType.Dead]?: TriggerGrouped; // 死亡后触发的技能配置 [SkillTriggerType.FStart]?: TriggerGrouped; // 战斗开始时释放的技能配置 + [SkillTriggerType.FEnd]?: TriggerGrouped; // 回合结束时释放的技能配置 [SkillTriggerType.Field]?: LvFieldEntry[]; // 驻场光环(按 lv 升序,运行时取 ≤英雄等级 的最大档) [SkillTriggerType.Atking]?: TriggerGrouped; // 普通攻击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的普攻次数 [SkillTriggerType.Atked]?: TriggerGrouped; // 受击后触发的技能配置,s_uuid: 技能id, t_num: 触发所需的受击次数 @@ -1235,6 +1239,7 @@ export const HERO_POWER_WEIGHTS = { W_atking: 1.0, W_atked: 0.8, W_fstart: 1.0, + W_fend: 1.0, W_dead: 0.6, W_field_alive: 1.0, W_revive: 1.0, @@ -1407,6 +1412,7 @@ function getTriggerCount( case SkillTriggerType.Atked: return getHitCount(seconds) / t_num; case SkillTriggerType.FStart: + case SkillTriggerType.FEnd: return getWaveCount(seconds); case SkillTriggerType.Dead: // 死亡触发次数 = 回合数 × (1 + 最大复活次数),复活从 dead 配置中提取 @@ -1548,6 +1554,7 @@ export interface HeroPowerBreakdown { atking: number; atked: number; fstart: number; + fend: number; dead: number; field: number; revive: number; @@ -1596,6 +1603,9 @@ function calcTriggerPowerDetailed( case SkillTriggerType.FStart: weight = weights.W_fstart; break; + case SkillTriggerType.FEnd: + weight = weights.W_fend; + break; case SkillTriggerType.Dead: weight = weights.W_dead; break; @@ -1693,6 +1703,7 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco const atking = hero.atking ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.atking, targetLv), hero, targetLv, SkillTriggerType.Atking, seconds) : { total: 0, items: [] as TriggerSkillEval[] }; const atked = hero.atked ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.atked, targetLv), hero, targetLv, SkillTriggerType.Atked, seconds) : { total: 0, items: [] as TriggerSkillEval[] }; const fstart = hero.fstart ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.fstart, targetLv), hero, targetLv, SkillTriggerType.FStart, seconds) : { total: 0, items: [] as TriggerSkillEval[] }; + const fend = hero.fend ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.fend, targetLv), hero, targetLv, SkillTriggerType.FEnd, seconds) : { total: 0, items: [] as TriggerSkillEval[] }; const dead = hero.dead ? calcTriggerPowerDetailed(resolveTriggerByLv(hero.dead, targetLv), hero, targetLv, SkillTriggerType.Dead, seconds) : { total: 0, items: [] as TriggerSkillEval[] }; const fieldEntries = resolveFieldByLv(hero.field, targetLv); const field = fieldEntries.length > 0 ? calcFieldPowerDetailed(fieldEntries, hero, targetLv, seconds) : { total: 0, items: [] as FieldSkillEval[] }; @@ -1708,7 +1719,7 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco } } - const total = hpValue + dpsValue + atking.total + atked.total + fstart.total + dead.total + field.total + reviveValue; + const total = hpValue + dpsValue + atking.total + atked.total + fstart.total + fend.total + dead.total + field.total + reviveValue; return { total, @@ -1718,11 +1729,12 @@ export function calcHeroPowerDetailed(hero: heroInfo, targetLv: number = 1, seco atking: atking.total, atked: atked.total, fstart: fstart.total, + fend: fend.total, dead: dead.total, field: field.total, revive: reviveValue, }, - triggerSkills: [...atking.items, ...atked.items, ...fstart.items, ...dead.items], + triggerSkills: [...atking.items, ...atked.items, ...fstart.items, ...fend.items, ...dead.items], fieldSkills: field.items, normalAttack, }; diff --git a/assets/script/game/hero/Hero.ts b/assets/script/game/hero/Hero.ts index eb99cdc3..ce043367 100644 --- a/assets/script/game/hero/Hero.ts +++ b/assets/script/game/hero/Hero.ts @@ -177,6 +177,7 @@ export class Hero extends ecs.Entity { model.runtime_call = resolveTriggerByLv(model.call, hero_lv); model.runtime_dead = resolveTriggerByLv(model.dead, hero_lv); model.runtime_fstart = resolveTriggerByLv(model.fstart, hero_lv); + model.runtime_fend = resolveTriggerByLv(model.fend, hero_lv); model.runtime_atking = resolveTriggerByLv(model.atking, hero_lv); model.runtime_atked = resolveTriggerByLv(model.atked, hero_lv); model.runtime_field = resolveFieldByLv(model.field, hero_lv); diff --git a/assets/script/game/hero/HeroAttrsComp.ts b/assets/script/game/hero/HeroAttrsComp.ts index 5cec81e4..89f3d89a 100644 --- a/assets/script/game/hero/HeroAttrsComp.ts +++ b/assets/script/game/hero/HeroAttrsComp.ts @@ -53,6 +53,7 @@ export class HeroAttrsComp extends ecs.Comp { [SkillTriggerType.Call]?: TriggerGrouped; [SkillTriggerType.Dead]?: TriggerGrouped; [SkillTriggerType.FStart]?: TriggerGrouped; + [SkillTriggerType.FEnd]?: TriggerGrouped; [SkillTriggerType.Atking]?: TriggerGrouped; [SkillTriggerType.Atked]?: TriggerGrouped; /** 驻场光环(按 lv 解锁,存活期间全局生效) */ @@ -71,6 +72,8 @@ export class HeroAttrsComp extends ecs.Comp { runtime_dead?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; /** 当前等级下生效的战前触发条目 */ runtime_fstart?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; + /** 当前等级下生效的回合结束触发条目 */ + runtime_fend?: { s_uuid: number; t_num: number; overrides?: SkillOverrides }[]; /** 当前等级下生效的驻场光环词条列表 */ runtime_field?: FieldEntry[]; @@ -725,6 +728,7 @@ export class HeroAttrsComp extends ecs.Comp { this.call = undefined; this.dead = undefined; this.fstart = undefined; + this.fend = undefined; this.atking = undefined; this.atked = undefined; this.field = undefined; @@ -734,6 +738,7 @@ export class HeroAttrsComp extends ecs.Comp { this.runtime_call = undefined; this.runtime_dead = undefined; this.runtime_fstart = undefined; + this.runtime_fend = undefined; this.runtime_field = undefined; this.critical = 0; this.critical_res = 0; diff --git a/assets/script/game/hero/Mon.ts b/assets/script/game/hero/Mon.ts index c0ff9914..46923b78 100644 --- a/assets/script/game/hero/Mon.ts +++ b/assets/script/game/hero/Mon.ts @@ -241,6 +241,7 @@ export class Monster extends ecs.Entity { model.runtime_call = resolveTriggerByLv(model.call, mon_lv); model.runtime_dead = resolveTriggerByLv(model.dead, mon_lv); model.runtime_fstart = resolveTriggerByLv(model.fstart, mon_lv); + model.runtime_fend = resolveTriggerByLv(model.fend, mon_lv); model.runtime_atking = resolveTriggerByLv(model.atking, mon_lv); model.runtime_atked = resolveTriggerByLv(model.atked, mon_lv); // 复活已纳入 dead 触发组,无需单独解析 revive diff --git a/assets/script/game/hero/SkillTriggerHelper.ts b/assets/script/game/hero/SkillTriggerHelper.ts index 4aaf02c1..fe47db99 100644 --- a/assets/script/game/hero/SkillTriggerHelper.ts +++ b/assets/script/game/hero/SkillTriggerHelper.ts @@ -44,6 +44,9 @@ export class SkillTriggerHelper { case SkillTriggerType.FStart: this.handleArrayTrigger(model.runtime_fstart, model, view, type); break; + case SkillTriggerType.FEnd: + this.handleArrayTrigger(model.runtime_fend, model, view, type); + break; case SkillTriggerType.Atking: this.handleAtking(model, view); break; diff --git a/assets/script/game/map/MissionComp.ts b/assets/script/game/map/MissionComp.ts index 43535ea1..73a356fc 100644 --- a/assets/script/game/map/MissionComp.ts +++ b/assets/script/game/map/MissionComp.ts @@ -565,6 +565,10 @@ export class MissionComp extends CCComp { // 通关判定:Boss 节(末节)胜利即视为本关通关 smc.vmdata.scores.passed_wave_20 = true; + + // 触发回合结束技能(fend):仅节间流转时触发; + // 末节结算/失败时 pause 已置位,跳过避免结算瞬间白播施法表现 + this.triggerHeroWaveEndSkills(); } // 战斗结束阶段,按 FightSet.WAVE_HEAL_RATE 恢复所有英雄血量 @@ -676,6 +680,20 @@ export class MissionComp extends CCComp { }); } + /** + * 触发英雄的回合结束技能(fend) + * 每回合(节)切入 BattleEnd 时对全队存活英雄各触发 1 次(无光环加成), + * 先于节间回血 healAllHeroes 结算,保证治疗/护盾类 fend 能吃到节末血线 + */ + private triggerHeroWaveEndSkills() { + ecs.query(this.heroAttrsMatcher).forEach(entity => { + const attrs = entity.get(HeroAttrsComp); + const view = entity.get(HeroViewComp); + if (!attrs || !view || attrs.is_dead || attrs.fac !== FacSet.HERO) return; + SkillTriggerHelper.trigger(SkillTriggerType.FEnd, attrs, view); + }); + } + /** * 战斗结束阶段治疗所有英雄(节间回血),按 FightSet.WAVE_HEAL_RATE 恢复最大生命值比例 */