feat(技能): 新增触发技能机制并调整死亡特效颜色

- 在 GameEvent 枚举中添加 TriggerSkill 事件用于技能触发
- 为 Hero 和 Monster 实体添加召唤入场时的 call 技能触发
- 在 HeroAtkSystem 中实现死亡时的 dead 技能触发
- 扩展 SCastSystem 支持强制触发技能(忽略CD和动画前摇)
- 将死亡技能特效颜色从灰色调整为白色以提升视觉效果
This commit is contained in:
walkpan
2026-04-05 22:09:16 +08:00
parent 5a5d849c0b
commit ef07982645
6 changed files with 104 additions and 4 deletions

View File

@@ -158,9 +158,9 @@
"_dstBlendFactor": 4,
"_color": {
"__type__": "cc.Color",
"r": 173,
"g": 170,
"b": 170,
"r": 255,
"g": 255,
"b": 255,
"a": 255
},
"_spriteFrame": {

View File

@@ -73,4 +73,5 @@ export enum GameEvent {
UpdateMissionGet = "UpdateMissionGet",
GlobalAttrChange = "GlobalAttrChange",
CoinAdd = "CoinAdd",
TriggerSkill = "TriggerSkill", // 瞬间触发施法事件
}

View File

@@ -143,6 +143,15 @@ export class Hero extends ecs.Entity {
move.moving = false;
hv.as.idle();
// 触发 call 技能
if (hero.call) {
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: hero.call,
heroAttrs: model,
heroView: hv
});
}
// 依据下落距离自适应入场时长,保证手感稳定
const dropDistance = Math.abs(pos.y - dropToY);
const dropDuration = Math.max(0.18, Math.min(0.38, dropDistance / 1200));

View File

@@ -7,7 +7,9 @@ import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
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 { mLogger } from "../common/Logger";
@@ -250,6 +252,19 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpd
if (!TAttrsComp || TAttrsComp.is_dead) return;
TAttrsComp.is_dead = true;
// 触发死亡技能
const heroInfo = HeroInfo[TAttrsComp.hero_uuid];
if (heroInfo && heroInfo.dead) {
const view = entity.get(HeroViewComp);
if (view) {
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: heroInfo.dead,
heroAttrs: TAttrsComp,
heroView: view
});
}
}
// 触发死亡事件
this.onDeath(entity);

View File

@@ -7,6 +7,7 @@ import { HeroInfo } from "../common/config/heroSet";
import { HeroAttrsComp } from "./HeroAttrsComp";
import { HeroViewComp } from "./HeroViewComp";
import { MoveComp } from "./MoveComp";
import { GameEvent } from "../common/config/GameEvent";
/** 怪物实体:负责怪物对象池复用、属性初始化、入场动画与回收 */
@ecs.register(`Monster`)
export class Monster extends ecs.Entity {
@@ -193,6 +194,15 @@ export class Monster extends ecs.Entity {
move.baseY = dropToY;
move.moving = false;
// 触发 call 技能
if (hero.call) {
oops.message.dispatchEvent(GameEvent.TriggerSkill, {
s_uuid: hero.call,
heroAttrs: model,
heroView: view
});
}
// 依据下落距离自适应入场时长,确保观感一致
const dropDistance = Math.abs(pos.y - dropToY);
const dropDuration = Math.max(0.18, Math.min(0.38, dropDistance / 1200));

View File

@@ -8,6 +8,8 @@ import { smc } from "../common/SingletonModuleComp";
import { HType } from "../common/config/heroSet";
import { Attrs } from "../common/config/HeroAttrs";
import { BoxSet, FightSet } from "../common/config/GameSet";
import { oops } from "db://oops-framework/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
/**
* ==================== 自动施法系统 ====================
@@ -23,7 +25,20 @@ import { BoxSet, FightSet } from "../common/config/GameSet";
*/
@ecs.register('SCastSystem')
export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate {
static instance: SCastSystem | null = null;
debugMode: boolean = false; // 是否启用调试模式
constructor() {
super();
SCastSystem.instance = this;
// 监听触发技能事件
oops.message.on(GameEvent.TriggerSkill, this.onTriggerSkill, this);
}
private onTriggerSkill(event: string, args: { s_uuid: number, heroAttrs: HeroAttrsComp, heroView: HeroViewComp }) {
if (!args || !args.s_uuid || !args.heroAttrs || !args.heroView) return;
this.forceCastTriggerSkill(args.s_uuid, args.heroAttrs, args.heroView);
}
/** 空施法计划:用于“当前无可施法技能”时的统一返回 */
private readonly emptyCastPlan = { skillId: 0, skillLv: 1, isFriendly: false, targetPos: null as Vec3 | null, targetEids: [] as number[] };
/** 近战英雄默认施法射程 */
@@ -71,6 +86,56 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
this.castSkill(castPlan, heroAttrs, heroView);
}
/**
* 强制执行触发技能(召唤/死亡触发)
* 忽略CD、状态、动画前摇直接生效
*/
public forceCastTriggerSkill(s_uuid: number, heroAttrs: HeroAttrsComp, heroView: HeroViewComp) {
if (!smc.mission.in_fight) return;
const config = SkillSet[s_uuid];
if (!config) return;
const skillLv = heroAttrs.getSkillLevel(s_uuid) || 1;
let isFriendly = false;
let targetPos: Vec3 | null = null;
let targetEids: number[] = [];
const selfEid = heroView.ent?.eid;
const type = heroAttrs.type as HType;
const maxRange = this.resolveMaxCastRange(heroAttrs, type);
if (this.isSelfSkill(config.TGroup)) {
isFriendly = true;
if (typeof selfEid === "number") targetEids = [selfEid];
} else if (this.isFriendlySkill(config.TGroup)) {
isFriendly = true;
const includeSelf = config.TGroup === TGroup.Ally;
targetEids = this.collectFriendlyTargetEids(heroAttrs.fac, selfEid, includeSelf);
} else {
const target = this.findNearestEnemyInRange(heroAttrs, heroView, maxRange);
if (target && target.node) {
targetPos = this.resolveEnemyCastTargetPos(config, heroAttrs, heroView, target, maxRange);
}
}
const sUp = SkillUpList[s_uuid] ? SkillUpList[s_uuid] : SkillUpList[1001];
const cNum = Math.min(2, Math.max(0, Math.floor(sUp.num ?? 0)));
const castTimes = 1 + cNum;
for (let i = 0; i < castTimes; i++) {
if (!heroView.node || !heroView.node.isValid) return;
if (isFriendly) {
const friendlyTargets = this.resolveFriendlyTargets(targetEids, heroAttrs.fac);
if (friendlyTargets.length === 0) continue;
this.applyFriendlySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, friendlyTargets, null);
} else {
const enemyTargetPos = this.resolveRepeatCastTargetPos(targetPos, i);
this.applyEnemySkillEffects(s_uuid, skillLv, config, heroView, heroAttrs, enemyTargetPos, i);
}
}
}
/**
* 选择当前应释放的技能。
* 选择顺序:技能候选列表顺序 + 条件过滤CD、目标可达、目标类型匹配