Files
pixelheros/assets/script/game/hero/SkillTriggerHelper.ts
walkpan 88d7bdae47 feat(skill): 支持技能参数自定义覆盖
本次修改实现同技能不同角色的差异化技能效果:
1. 新增SkillOverrides接口与mergeSkillParams工具函数,用于合并基础技能配置和角色覆盖参数
2. 更新英雄配置、属性组件、触发辅助系统与施法系统以适配该机制
3. 为盾骑士、医师添加示例差异化配置,验证功能可行性
4. 整理技能配置,删除冗余重复的旧技能条目
5. 新增技能重构设计计划文档,替换旧的迁移计划文档
2026-05-23 12:11:00 +08:00

156 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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, SkillOverrides } from "../common/config/SkillSet";
import { TalentType } from "../common/config/TalentSet";
import { smc } from "../common/SingletonModuleComp";
import { FieldSkillHelper } from "./FieldSkillHelper";
/**
* 特殊触发技能的统一中介者 (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 += FieldSkillHelper.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 += FieldSkillHelper.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, atkConfig.overrides);
}
});
}
/**
* 处理受击附加技能
* 判定逻辑:依据配置中的 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, atkConfig.overrides);
}
});
}
/**
* 通用的数组型触发器处理(适用于 FStart / FEnd 等无需额外判定的简单列表触发)
*/
private static handleArrayTrigger(configs: {s_uuid: number, t_num: number, overrides?: SkillOverrides}[] | undefined, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) {
if (!configs || configs.length === 0) return;
this.dispatchArray(configs, model, view, type);
}
/**
* 批量派发技能事件
*/
private static dispatchArray(configs: {s_uuid: number, t_num?: number, overrides?: SkillOverrides}[], model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType) {
configs.forEach(config => this.dispatchSingle(config.s_uuid, model, view, type, config.overrides));
}
/**
* 单一技能事件派发底层接口
* 事件发出后,将由 SCastSystem 的 forceCastTriggerSkill 监听拦截并执行无视CD的强制施法
*/
private static dispatchSingle(s_uuid: number, model: HeroAttrsComp, view: HeroViewComp, type: SkillTriggerType, overrides?: SkillOverrides) {
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: s_uuid,
heroAttrs: model,
heroView: view,
triggerType: type,
overrides: overrides
});
}
}