本次提交完整实现了技能对敌附加debuff的完整流程: 1. 新增TimedDebuffSnapshot数据结构,用于透传debuff配置 2. 在SkillConfig和SkillOverrides中新增debuff相关配置项 3. 实现HeroAtkSystem中debuff的判定与施加逻辑 4. 完善后台配置、前端编辑器的debuff配置支持 5. 调整了buff目标判定逻辑,修复了原有默认目标的语义混淆问题 6. 优化了BuffSystem的调试日志,新增了debuff分类标识 7. 批量更新了现有英雄技能的debuff配置示例
229 lines
7.0 KiB
TypeScript
229 lines
7.0 KiB
TypeScript
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||
import { HeroViewComp } from "./HeroViewComp";
|
||
import { SkillOverrides } from "../common/config/SkillSet";
|
||
|
||
/** 命中后对敌 debuff 快照(由施法技能实体自合并后配置提取并透传,HeroAtkSystem 据此结算) */
|
||
export interface TimedDebuffSnapshot {
|
||
id: number;
|
||
value?: number;
|
||
duration?: number;
|
||
chance?: number;
|
||
}
|
||
|
||
/**
|
||
* ==================== 伤害事件数据 ====================
|
||
*
|
||
* 单个伤害事件的数据结构
|
||
*/
|
||
export interface DamageEvent {
|
||
/** 伤害属性数据 */
|
||
Attrs: any;
|
||
|
||
/** 施法者ID */
|
||
casterEid: number;
|
||
|
||
/** 技能UUID */
|
||
s_uuid: number;
|
||
|
||
/** 伤害创建时间戳(用于排序和调试) */
|
||
timestamp: number;
|
||
|
||
/** 伤害事件ID(用于去重和调试) */
|
||
eventId: string;
|
||
|
||
ext_dmg: number //额外伤害
|
||
dmg_ratio: number//伤害比例
|
||
/**
|
||
* DoT 固定伤害值(>0 时本事件为 DoT tick 结算):
|
||
* 跳过 dmgCount 公式直接用该值,且不走击退/受击动画,其余(暴击/冰冻/击晕/护盾/死亡链)与普攻一致
|
||
*/
|
||
dot_dmg?: number
|
||
/** 命中后概率触发额外技能(由施法技能实体快照自合并后配置,HeroAtkSystem 据此判定;
|
||
* overrides 字段透传给被触发技能,用于自定义伤害/数值) */
|
||
proc_skill?: { s_uuid: number; chance?: number; overrides?: SkillOverrides }
|
||
/** 命中后对敌 debuff(由施法技能实体自合并后配置快照透传;缺省时 HeroAtkSystem 回退读 SkillSet 底座) */
|
||
timed_debuff?: TimedDebuffSnapshot
|
||
}
|
||
|
||
/**
|
||
* ==================== 伤害队列组件 ====================
|
||
*
|
||
* 用途:
|
||
* - 存储一个实体在当前帧收到的所有伤害事件
|
||
* - 支持批量处理,避免同帧多伤害冲突
|
||
* - 提供伤害事件的排序和去重功能
|
||
*/
|
||
@ecs.register('DamageQueue')
|
||
export class DamageQueueComp extends ecs.Comp {
|
||
/** 伤害事件队列 */
|
||
damageEvents: DamageEvent[] = [];
|
||
private readIndex: number = 0;
|
||
|
||
/** 队列创建时间 */
|
||
createTime: number = 0;
|
||
|
||
/** 是否正在处理中 */
|
||
isProcessing: boolean = false;
|
||
|
||
/** 已处理的伤害数量 */
|
||
processedCount: number = 0;
|
||
|
||
reset() {
|
||
this.damageEvents.length = 0;
|
||
this.readIndex = 0;
|
||
this.createTime = 0;
|
||
this.isProcessing = false;
|
||
this.processedCount = 0;
|
||
}
|
||
|
||
/**
|
||
* 添加伤害事件到队列
|
||
*/
|
||
addDamageEvent(attrs: any, casterEid: number, s_uuid: number, ext_dmg: number = 0, dmg_ratio: number = 1, dot_dmg: number = 0, proc_skill?: { s_uuid: number; chance?: number; overrides?: SkillOverrides }, timed_debuff?: TimedDebuffSnapshot): void {
|
||
const timestamp = Date.now();
|
||
|
||
const damageEvent: DamageEvent = {
|
||
Attrs: attrs || null,
|
||
casterEid: casterEid,
|
||
s_uuid: s_uuid,
|
||
timestamp: timestamp,
|
||
eventId: "",
|
||
ext_dmg: ext_dmg,
|
||
dmg_ratio: dmg_ratio,
|
||
dot_dmg: dot_dmg,
|
||
proc_skill: proc_skill,
|
||
timed_debuff: timed_debuff,
|
||
};
|
||
|
||
this.damageEvents.push(damageEvent);
|
||
|
||
// 如果是第一个伤害事件,记录创建时间
|
||
if (this.getRemainingCount() === 1) {
|
||
this.createTime = timestamp;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取下一个待处理的伤害事件
|
||
*/
|
||
getNextDamageEvent(): DamageEvent | null {
|
||
if (this.readIndex >= this.damageEvents.length) return null;
|
||
const event = this.damageEvents[this.readIndex];
|
||
this.readIndex += 1;
|
||
if (this.readIndex >= 32 && this.readIndex * 2 >= this.damageEvents.length) {
|
||
this.damageEvents = this.damageEvents.slice(this.readIndex);
|
||
this.readIndex = 0;
|
||
}
|
||
return event || null;
|
||
}
|
||
|
||
/**
|
||
* 获取队列中剩余的伤害事件数量
|
||
*/
|
||
getRemainingCount(): number {
|
||
return this.damageEvents.length - this.readIndex;
|
||
}
|
||
|
||
/**
|
||
* 检查队列是否为空
|
||
*/
|
||
isEmpty(): boolean {
|
||
return this.readIndex >= this.damageEvents.length;
|
||
}
|
||
|
||
/**
|
||
* 清空队列
|
||
*/
|
||
clear(): void {
|
||
this.damageEvents.length = 0;
|
||
this.readIndex = 0;
|
||
this.processedCount = 0;
|
||
this.isProcessing = false;
|
||
}
|
||
|
||
/**
|
||
* 按时间戳排序伤害事件(可选功能)
|
||
*/
|
||
sortByTimestamp(): void {
|
||
if (this.readIndex > 0) {
|
||
this.damageEvents = this.damageEvents.slice(this.readIndex);
|
||
this.readIndex = 0;
|
||
}
|
||
this.damageEvents.sort((a, b) => a.timestamp - b.timestamp);
|
||
}
|
||
|
||
/**
|
||
* 去除重复的伤害事件(基于eventId)
|
||
*/
|
||
removeDuplicates(): void {
|
||
const seen = new Set<string>();
|
||
if (this.readIndex > 0) {
|
||
this.damageEvents = this.damageEvents.slice(this.readIndex);
|
||
this.readIndex = 0;
|
||
}
|
||
this.damageEvents = this.damageEvents.filter(event => {
|
||
if (seen.has(event.eventId)) {
|
||
return false;
|
||
}
|
||
seen.add(event.eventId);
|
||
return true;
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取队列统计信息(用于调试)
|
||
*/
|
||
getQueueStats(): {
|
||
totalEvents: number;
|
||
processedEvents: number;
|
||
remainingEvents: number;
|
||
isProcessing: boolean;
|
||
createTime: number;
|
||
} {
|
||
return {
|
||
totalEvents: this.processedCount + this.damageEvents.length,
|
||
processedEvents: this.processedCount,
|
||
remainingEvents: this.getRemainingCount(),
|
||
isProcessing: this.isProcessing,
|
||
createTime: this.createTime
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ==================== 伤害队列辅助工具 ====================
|
||
*/
|
||
export class DamageQueueHelper {
|
||
/**
|
||
* 为实体添加伤害事件
|
||
* 如果实体没有伤害队列组件,会自动创建
|
||
*/
|
||
static addDamageToEntity(entity: ecs.Entity, attrs: any, casterEid: number, s_uuid: number, ext_dmg: number = 0, dmg_ratio: number = 1, dot_dmg: number = 0, proc_skill?: { s_uuid: number; chance?: number; overrides?: SkillOverrides }, timed_debuff?: TimedDebuffSnapshot): void {
|
||
let damageQueue = entity.get(DamageQueueComp);
|
||
|
||
// 如果实体没有伤害队列组件,创建一个
|
||
if (!damageQueue) {
|
||
damageQueue = entity.add(DamageQueueComp);
|
||
}
|
||
|
||
// 添加伤害事件到队列
|
||
damageQueue.addDamageEvent(attrs, casterEid, s_uuid, ext_dmg, dmg_ratio, dot_dmg, proc_skill, timed_debuff);
|
||
}
|
||
|
||
/**
|
||
* 检查实体是否有待处理的伤害
|
||
*/
|
||
static hasPendingDamage(entity: ecs.Entity): boolean {
|
||
const damageQueue = entity.get(DamageQueueComp);
|
||
return damageQueue ? !damageQueue.isEmpty() : false;
|
||
}
|
||
|
||
/**
|
||
* 获取实体的伤害队列统计信息
|
||
*/
|
||
static getEntityDamageStats(entity: ecs.Entity): any {
|
||
const damageQueue = entity.get(DamageQueueComp);
|
||
return damageQueue ? damageQueue.getQueueStats() : null;
|
||
}
|
||
}
|