refactor(skill): 重构技能触发逻辑,提取统一工具类

将分散在Hero、Mon、HeroAtkSystem、SCastSystem、MissionComp中的技能触发代码统一封装,集中处理触发次数加成、实体合法性校验与技能事件派发,同时新增SkillTriggerType枚举统一管理技能触发类型,简化业务调用并消除重复模板代码。
This commit is contained in:
panw
2026-05-21 11:10:27 +08:00
parent 3bfdf1639b
commit fc3f4d7375
8 changed files with 192 additions and 90 deletions

View File

@@ -76,4 +76,14 @@ export enum GameEvent {
CardPoolUpgrade = "CardPoolUpgrade",
TriggerSkill = "TriggerSkill", // 瞬间触发施法事件
RemoveSkillBox = "RemoveSkillBox", // 技能盒销毁事件
}
/** 触发技能类型枚举 */
export enum SkillTriggerType {
Call = 'call', // 召唤时触发
Dead = 'dead', // 死亡时触发
FStart = 'fstart', // 战斗开始时触发
FEnd = 'fend', // 战斗结束时触发
Atking = 'atking', // 攻击时触发
Atked = 'atked' // 受击时触发
}

View File

@@ -6,7 +6,8 @@ import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
import { BoxSet, FacSet, FightSet, IndexSet } from "../common/config/GameSet";
import { HeroInfo, HeroPos, resolveFormationTargetX } from "../common/config/heroSet";
import { GameEvent } from "../common/config/GameEvent";
import { GameEvent, SkillTriggerType } from "../common/config/GameEvent";
import { SkillTriggerHelper } from "./SkillTriggerHelper";
import { Attrs} from "../common/config/HeroAttrs";
import { MoveComp } from "./MoveComp";
import { mLogger } from "../common/Logger";
@@ -222,24 +223,7 @@ export class Hero extends ecs.Entity {
}
// 落地后触发 call 技能
if (model && model.call && model.call.length > 0) {
let triggerCount = 1 + HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.SummonCount);
if (model.fac === FacSet.HERO) {
triggerCount += HeroAttrsComp.getTalentValue(TalentType.Summon); // 召唤强化额外次数
}
triggerCount = Math.max(1, Math.floor(triggerCount));
for (let i = 0; i < triggerCount; i++) {
model.call.forEach(uuid => {
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: uuid,
heroAttrs: model,
heroView: view,
triggerType: 'call'
});
});
}
}
SkillTriggerHelper.trigger(SkillTriggerType.Call, model, view);
})
.start();
}

View File

@@ -9,10 +9,11 @@ import { DamageQueueComp, DamageEvent } from "./DamageQueueComp";
import { smc } from "../common/SingletonModuleComp";
import { HeroInfo } from "../common/config/heroSet";
import { oops } from "db://oops-framework/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { GameEvent, SkillTriggerType } from "../common/config/GameEvent";
import { FieldSkillType } from "../common/config/SkillSet";
import { mLogger } from "../common/Logger";
import { SkillTriggerHelper } from "./SkillTriggerHelper";
/** 最终伤害数据接口
* 用于封装一次攻击计算的所有结果数据
@@ -94,18 +95,7 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
/** 检查并触发受击附加技能 (atked) */
private checkAndTriggerAtkedSkills(heroAttrs: HeroAttrsComp, heroView: HeroViewComp) {
if (!heroAttrs.atked || heroAttrs.atked.length === 0) return;
heroAttrs.atked.forEach(atkConfig => {
if (heroAttrs.atked_count > 0 && heroAttrs.atked_count % atkConfig.t_num === 0) {
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: atkConfig.s_uuid,
heroAttrs: heroAttrs,
heroView: heroView,
triggerType: 'atked'
});
}
});
SkillTriggerHelper.trigger(SkillTriggerType.Atked, heroAttrs, heroView);
}
/**
@@ -319,27 +309,9 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
const TAttrsComp = entity.get(HeroAttrsComp);
if (!TAttrsComp) return;
if (TAttrsComp.dead && TAttrsComp.dead.length > 0) {
const view = entity.get(HeroViewComp);
if (view) {
let triggerCount = 1 + HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.DeadCount);
triggerCount = Math.max(1, Math.floor(triggerCount));
for (let i = 0; i < triggerCount; i++) {
TAttrsComp.dead.forEach((uuid: number) => {
if (TAttrsComp.fac === FacSet.HERO) {
// 【评分系统 - 防御分】统计死亡触发技能的生效次数
smc.vmdata.scores.dead_trigger_count++;
}
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: uuid,
heroAttrs: TAttrsComp,
heroView: view,
triggerType: 'dead'
});
});
}
}
const view = entity.get(HeroViewComp);
if (view) {
SkillTriggerHelper.trigger(SkillTriggerType.Dead, TAttrsComp, view);
}
}

View File

@@ -8,7 +8,8 @@ import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
import { MoveComp } from "./MoveComp";
import { MonMoveComp } from "./MonMoveComp";
import { GameEvent } from "../common/config/GameEvent";
import { GameEvent, SkillTriggerType } from "../common/config/GameEvent";
import { SkillTriggerHelper } from "./SkillTriggerHelper";
/** 怪物实体:负责怪物对象池复用、属性初始化、入场动画与回收 */
@ecs.register(`Monster`)
export class Monster extends ecs.Entity {
@@ -232,16 +233,7 @@ export class Monster extends ecs.Entity {
}
// 落地后触发 call 技能
if (model.call && model.call.length > 0) {
model.call.forEach((uuid: number) => {
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: uuid,
heroAttrs: model,
heroView: view,
triggerType: 'call'
});
});
}
SkillTriggerHelper.trigger(SkillTriggerType.Call, model, view);
})
.start();
// 维护关卡内怪物数量统计

View File

@@ -9,7 +9,8 @@ import { HeroDisVal, HeroInfo, HType } from "../common/config/heroSet";
import { Attrs } from "../common/config/HeroAttrs";
import { BoxSet, FacSet, FightSet } from "../common/config/GameSet";
import { oops } from "db://oops-framework/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { GameEvent, SkillTriggerType } from "../common/config/GameEvent";
import { SkillTriggerHelper } from "./SkillTriggerHelper";
/**
* ==================== 自动施法系统 ====================
@@ -338,18 +339,7 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
/** 检查并触发攻击附加技能 (atking) */
private checkAndTriggerAtkingSkills(heroAttrs: HeroAttrsComp, heroView: HeroViewComp) {
if (!heroAttrs.atking || heroAttrs.atking.length === 0) return;
heroAttrs.atking.forEach(atkConfig => {
if (heroAttrs.atk_count > 0 && heroAttrs.atk_count % atkConfig.t_num === 0) {
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: atkConfig.s_uuid,
heroAttrs: heroAttrs,
heroView: heroView,
triggerType: 'atking'
});
}
});
SkillTriggerHelper.trigger(SkillTriggerType.Atking, heroAttrs, heroView);
}
private resolveRepeatCastTargetPos(targetPos: Vec3 | null, castIndex: number): Vec3 | null {

View File

@@ -0,0 +1,153 @@
import { oops } from "db://oops-framework/core/Oops";
import { GameEvent, SkillTriggerType } from "../common/config/GameEvent";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
import { FacSet } from "../common/config/GameSet";
import { FieldSkillType } from "../common/config/SkillSet";
import { TalentType } from "../common/config/TalentSet";
import { smc } from "../common/SingletonModuleComp";
/**
* 特殊触发技能的统一中介者 (Mediator)
*
* 核心职责:
* 1. 统一管理英雄/怪物的各类生命周期触发技能(召唤、死亡、战前、战后、攻受击)。
* 2. 集中处理触发次数的计算(如:局内羁绊加成、局外天赋加成)。
* 3. 作为唯一出口,通过事件总线向 SCastSystem 派发 GameEvent.TriggerSkill 事件。
*
* 优势:
* - 极大地解耦了具体业务组件Hero/Mon/HeroAtkSystem/MissionComp与技能系统。
* - 避免了原先散落在各处的判定逻辑和冗余的事件派发代码。
*/
export class SkillTriggerHelper {
/**
* 统一的技能触发入口
* 根据传入的触发类型,自动分发到对应的处理函数中执行判定和加成逻辑。
*
* @param type 触发类型枚举 (SkillTriggerType)
* @param model 施法者的属性数据组件 (HeroAttrsComp)
* @param view 施法者的视图表现组件 (HeroViewComp)
*/
public static trigger(type: SkillTriggerType, model: HeroAttrsComp, view: HeroViewComp) {
// 健壮性检查:若实体或节点已被销毁,直接阻断,防止空指针报错
if (!model || !view || !view.node || !view.node.isValid) return;
switch (type) {
case SkillTriggerType.Call:
this.handleCall(model, view);
break;
case SkillTriggerType.Dead:
this.handleDead(model, view);
break;
case SkillTriggerType.FStart:
this.handleArrayTrigger(model.fstart, model, view, type);
break;
case SkillTriggerType.FEnd:
this.handleArrayTrigger(model.fend, model, view, type);
break;
case SkillTriggerType.Atking:
this.handleAtking(model, view);
break;
case SkillTriggerType.Atked:
this.handleAtked(model, view);
break;
}
}
/**
* 处理召唤(落地)触发技能
* 支持受【召唤强化羁绊 (SummonCount)】和【召唤强化天赋 (TalentType.Summon)】的多次触发加成。
*/
private static handleCall(model: HeroAttrsComp, view: HeroViewComp) {
if (!model.call || model.call.length === 0) return;
let triggerCount = 1;
// 仅英雄享受加成,怪物始终只触发 1 次
if (model.fac === FacSet.HERO) {
triggerCount += HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.SummonCount);
triggerCount += HeroAttrsComp.getTalentValue(TalentType.Summon);
}
triggerCount = Math.max(1, Math.floor(triggerCount)); // 确保最少触发 1 次
for (let i = 0; i < triggerCount; i++) {
this.dispatchArray(model.call, model, view, SkillTriggerType.Call);
}
}
/**
* 处理死亡触发技能
* 支持受【亡语强化羁绊 (DeadCount)】的多次触发加成。
*/
private static handleDead(model: HeroAttrsComp, view: HeroViewComp) {
if (!model.dead || model.dead.length === 0) return;
let triggerCount = 1;
if (model.fac === FacSet.HERO) {
triggerCount += HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.DeadCount);
// 【局内战绩评分系统】统计死亡触发技能生效次数(用于局后防守评分结算)
smc.vmdata.scores.dead_trigger_count += model.dead.length;
}
triggerCount = Math.max(1, Math.floor(triggerCount));
for (let i = 0; i < triggerCount; i++) {
this.dispatchArray(model.dead, model, view, SkillTriggerType.Dead);
}
}
/**
* 处理攻击附加技能
* 判定逻辑:依据配置中的 t_num触发所需普攻次数对比当前的普攻累计次数取模判定。
*/
private static handleAtking(model: HeroAttrsComp, view: HeroViewComp) {
if (!model.atking || model.atking.length === 0) return;
model.atking.forEach(atkConfig => {
// atk_count 代表已进行的普攻次数。当其余数刚好整除配置阈值时触发。
if (model.atk_count > 0 && model.atk_count % atkConfig.t_num === 0) {
this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atking);
}
});
}
/**
* 处理受击附加技能
* 判定逻辑:依据配置中的 t_num触发所需受击次数对比当前的受击累计次数取模判定。
*/
private static handleAtked(model: HeroAttrsComp, view: HeroViewComp) {
if (!model.atked || model.atked.length === 0) return;
model.atked.forEach(atkConfig => {
// atked_count 代表已承受的受击次数。当其余数刚好整除配置阈值时触发。
if (model.atked_count > 0 && model.atked_count % atkConfig.t_num === 0) {
this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atked);
}
});
}
/**
* 通用的数组型触发器处理(适用于 FStart / FEnd 等无需额外判定的简单列表触发)
*/
private static handleArrayTrigger(uuids: number[] | undefined, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) {
if (!uuids || uuids.length === 0) return;
this.dispatchArray(uuids, model, view, type);
}
/**
* 批量派发技能事件
*/
private static dispatchArray(uuids: number[], model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) {
uuids.forEach(uuid => this.dispatchSingle(uuid, model, view, type));
}
/**
* 单一技能事件派发底层接口
* 事件发出后,将由 SCastSystem 的 forceCastTriggerSkill 监听拦截并执行无视CD的强制施法
*/
private static dispatchSingle(s_uuid: number, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) {
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: s_uuid,
heroAttrs: model,
heroView: view,
triggerType: type
});
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "7a37f6b6-1d1a-4f37-a390-9353253f3fac",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -33,8 +33,9 @@ import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/modu
import { smc } from "../common/SingletonModuleComp";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { GameEvent } from "../common/config/GameEvent";
import { GameEvent, SkillTriggerType } from "../common/config/GameEvent";
import { HeroViewComp } from "../hero/HeroViewComp";
import { SkillTriggerHelper } from "../hero/SkillTriggerHelper";
import { UIID } from "../common/config/GameUIConfig";
import { SkillView } from "../skill/SkillView";
import { FacSet, FightSet } from "../common/config/GameSet";
@@ -588,18 +589,9 @@ export class MissionComp extends CCComp {
const attrs = entity.get(HeroAttrsComp);
const view = entity.get(HeroViewComp);
if (!attrs || !view || attrs.is_dead || attrs.fac !== FacSet.HERO) return;
const skillUuids = isStart ? attrs.fstart : attrs.fend;
if (skillUuids && skillUuids.length > 0) {
for (let i = 0; i < triggerCount; i++) {
skillUuids.forEach(uuid => {
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: uuid,
heroAttrs: attrs,
heroView: view,
triggerType: isStart ? 'fstart' : 'fend'
});
});
}
// 触发战斗开始/结束技能
for (let i = 0; i < triggerCount; i++) {
SkillTriggerHelper.trigger(isStart ? SkillTriggerType.FStart : SkillTriggerType.FEnd, attrs, view);
}
});
}