Files
pixelheros/assets/script/game/hero/SkillTriggerHelper.ts
panFD 707e25e73f refactor: 重构天赋系统与技能触发逻辑
1.  重构天赋系统架构:将天赋加成与触发修正统一收敛到 HeroAttrsComp.applyTalent
2.  新增天赋触发修正字段:talent_t_num_reduce 与 talent_extra_trigger_chance
3.  重构天赋加点逻辑:统一 1 点/级,新增软前置校验
4.  优化天赋UI组件:改为编辑器静态配置驱动,重构层位展示逻辑
5.  修复技能触发判定:在 SkillTriggerHelper 中应用天赋触发修正
6.  调整天赋配置结构:改为 5 层递进解锁,新增 L4/L5 专属触发修正配置
2026-09-19 11:20:55 +08:00

202 lines
9.1 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 } from "../common/config/GameEvent";
import { SkillTriggerType } from "../common/config/heroSet";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
import { FacSet } from "../common/config/GameSet";
import { FieldSkillType, SkillOverrides } from "../common/config/SkillSet";
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.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;
case SkillTriggerType.Atked:
this.handleAtked(model, view);
break;
}
}
/**
* 处理召唤(落地)触发技能
* 支持受【召唤强化羁绊 (SummonCount)】的多次触发加成。
*/
private static handleCall(model: HeroAttrsComp, view: HeroViewComp) {
if (!model.runtime_call || model.runtime_call.length === 0) return;
let triggerCount = 1;
// 仅英雄享受加成,怪物始终只触发 1 次
if (model.fac === FacSet.HERO) {
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.SummonCount);
}
triggerCount = Math.max(1, Math.floor(triggerCount)); // 确保最少触发 1 次
for (let i = 0; i < triggerCount; i++) {
this.dispatchArray(model.runtime_call, model, view, SkillTriggerType.Call);
}
}
/**
* 处理死亡触发技能
* 支持受【亡语强化羁绊 (DeadCount)】的多次触发加成。
*/
private static handleDead(model: HeroAttrsComp, view: HeroViewComp) {
if (!model.runtime_dead || model.runtime_dead.length === 0) return;
let triggerCount = 1;
if (model.fac === FacSet.HERO) {
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.DeadCount);
// 【局内战绩评分系统】统计死亡触发技能生效次数(用于局后防守评分结算)
smc.vmdata.scores.dead_trigger_count += model.runtime_dead.length;
}
triggerCount = Math.max(1, Math.floor(triggerCount));
for (let i = 0; i < triggerCount; i++) {
this.dispatchArray(model.runtime_dead, model, view, SkillTriggerType.Dead);
}
}
/**
* 处理攻击附加技能
* 判定逻辑:依据配置中的 t_num(触发所需普攻次数),对比当前的普攻累计次数取模判定。
* 天赋修正:L4 减少 t_num 阈值(下限 1);L5 按概率额外触发 1 次。
*/
private static handleAtking(model: HeroAttrsComp, view: HeroViewComp) {
if (!model.runtime_atking || model.runtime_atking.length === 0) return;
let triggerCount = 1;
if (model.fac === FacSet.HERO) {
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.AtkCount);
}
triggerCount = Math.max(1, Math.floor(triggerCount));
// L5 天赋:概率额外触发 1 次(与羁绊次数加成独立乘算)
if (model.talent_extra_trigger_chance > 0 && Math.random() * 100 < model.talent_extra_trigger_chance) {
triggerCount += 1;
}
// L4 天赋:减少触发所需次数(下限 1)
const tNumReduce = model.talent_t_num_reduce;
model.runtime_atking.forEach(atkConfig => {
// atk_count 代表已进行的普攻次数。当其余数刚好整除配置阈值时触发。
const tNum = Math.max(1, atkConfig.t_num - tNumReduce);
if (model.atk_count > 0 && model.atk_count % tNum === 0) {
for (let i = 0; i < triggerCount; i++) {
this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atking, atkConfig.overrides);
}
}
});
}
/**
* 处理受击附加技能
* 判定逻辑:依据配置中的 t_num(触发所需受击次数),对比当前的受击累计次数取模判定。
* 天赋修正:L4 减少 t_num 阈值(下限 1);L5 按概率额外触发 1 次。
*/
private static handleAtked(model: HeroAttrsComp, view: HeroViewComp) {
if (!model.runtime_atked || model.runtime_atked.length === 0) return;
let triggerCount = 1;
if (model.fac === FacSet.HERO) {
triggerCount += FieldSkillHelper.getFieldSkillTotalValue(FieldSkillType.BeAtkCount);
}
triggerCount = Math.max(1, Math.floor(triggerCount));
// L5 天赋:概率额外触发 1 次(与羁绊次数加成独立乘算)
if (model.talent_extra_trigger_chance > 0 && Math.random() * 100 < model.talent_extra_trigger_chance) {
triggerCount += 1;
}
// L4 天赋:减少触发所需次数(下限 1)
const tNumReduce = model.talent_t_num_reduce;
model.runtime_atked.forEach(atkConfig => {
// atked_count 代表已承受的受击次数。当其余数刚好整除配置阈值时触发。
const tNum = Math.max(1, atkConfig.t_num - tNumReduce);
if (model.atked_count > 0 && model.atked_count % tNum === 0) {
for (let i = 0; i < triggerCount; i++) {
this.dispatchSingle(atkConfig.s_uuid, model, view, SkillTriggerType.Atked, atkConfig.overrides);
}
}
});
}
/**
* 通用的数组型触发器处理(适用于 FStart 等无需额外判定的简单列表触发)
*/
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
});
}
/**
* 公共单技能派发(供 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);
}
}