feat: 实现DoT灼烧技能与debuff图标系统
本次提交完成了以下核心改动: 1. 调整fb-1~fb-6技能预制件的攻击偏移坐标,优化技能弹道位置 2. 新增DoT伤害机制,支持周期伤害/治疗的独立结算 3. 为Buff系统添加施法者属性快照,让DoT暴击、控制判定更准确 4. 新增顶栏debuff图标显示功能,支持灼烧等负面状态可视化 5. 为破晓先知等英雄替换为新的灼烧火球技能6104 6. 修复fb-2预制件的配置字段顺序与多余属性问题
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -251,8 +251,8 @@
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"atk_x": 20,
|
||||
"atk_y": 35,
|
||||
"runType": 0,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
|
||||
@@ -181,7 +181,7 @@
|
||||
},
|
||||
"_type": 0,
|
||||
"_fillType": 0,
|
||||
"_sizeMode": 1,
|
||||
"_sizeMode": 0,
|
||||
"_fillCenter": {
|
||||
"__type__": "cc.Vec2",
|
||||
"x": 0,
|
||||
@@ -389,11 +389,12 @@
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"_id": "",
|
||||
"runType": 0,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
"time": 0
|
||||
"time": 0,
|
||||
"distance": 0,
|
||||
"_id": ""
|
||||
},
|
||||
{
|
||||
"__type__": "cc.CompPrefabInfo",
|
||||
@@ -508,4 +509,4 @@
|
||||
"instance": null,
|
||||
"targetOverrides": null
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -251,8 +251,8 @@
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"atk_x": 20,
|
||||
"atk_y": 30,
|
||||
"runType": 0,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
|
||||
@@ -251,8 +251,8 @@
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"atk_x": 20,
|
||||
"atk_y": 30,
|
||||
"runType": 0,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
|
||||
@@ -251,8 +251,8 @@
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"atk_x": 20,
|
||||
"atk_y": 30,
|
||||
"runType": 0,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
|
||||
@@ -251,8 +251,8 @@
|
||||
"__prefab": {
|
||||
"__id__": 11
|
||||
},
|
||||
"atk_x": 0,
|
||||
"atk_y": 0,
|
||||
"atk_x": 20,
|
||||
"atk_y": 30,
|
||||
"runType": 0,
|
||||
"endType": 2,
|
||||
"speed": 720,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -311,6 +311,12 @@ export const SkillSet: Record<number, SkillConfig> = {
|
||||
DTType: DTType.single, ap: 100, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
RType: RType.linear, EType: EType.collision, info: "造成攻击力100%的伤害, 高阶技能",
|
||||
},
|
||||
// DoT 技能:命中附加灼烧(7002,BuffList 的 Debuff DoT 底座),tick 伤害受施法者 ap 缩放
|
||||
6104: {
|
||||
uuid: 6104, name: "灼热火球", sp_name: "fb-1", icon: "Stat_FireDamage", TGroup: TGroup.Enemy, readyAnm: "", endAnm: "", act: "atk",
|
||||
DTType: DTType.single, ap: 80, hit_count: 2, hitcd: 0.3, speed: 720, with: 90, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
RType: RType.bezier, EType: EType.collision, timed_buff_id: 7002, info: "造成攻击力80%的伤害, 命中附加灼烧(5秒内持续掉血)",
|
||||
},
|
||||
6201: {
|
||||
uuid: 6201, name: "陨石术", sp_name: "fb-3", icon: "Stat_Tripleshot", TGroup: TGroup.Enemy, readyAnm: "blues", endAnm: "", act: "max",
|
||||
DTType: DTType.aoe_grid, stun: 0, ap: 150, hit_count: 6, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.remote,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { v3 } from "cc"
|
||||
import { v3 } from "cc"
|
||||
import { BoxSet, FacSet, FightSet } from "./GameSet"
|
||||
import { SkillOverrides, TGroup, SkillSet, FieldSkillSet, SkillConfig, SkillKind, FieldSkillConfig, FieldSkillType, mergeSkillParams } from "./SkillSet"
|
||||
import { BuffList, BuffConfig, ModOp } from "./BuffSet"
|
||||
@@ -424,7 +424,7 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
// 5004 破晓先知 — fstart 全队攻击
|
||||
5004: {
|
||||
uuid: 5004, name: "破晓先知", path: "hm1", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 143, ap: 32,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
skills: { 6104: { uuid: 6104, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
fstart: {
|
||||
6401: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } },
|
||||
@@ -437,7 +437,7 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
// 5005 战歌法师 — field 攻击光环
|
||||
5005: {
|
||||
uuid: 5005, name: "战歌法师", path: "hm2", fac: FacSet.HERO, card_lv: 1, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 143, ap: 32,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
skills: { 6104: { uuid: 6104, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
field: [
|
||||
{ lv: 1, uuids: [{ uuid: 7002, value: 0.1 }] },
|
||||
{ lv: 3, uuids: [{ uuid: 7002, value: 0.2 }] },
|
||||
@@ -490,7 +490,7 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
// 5104 圣光祭司 — fstart 全队护盾(更高数值)
|
||||
5104: {
|
||||
uuid: 5104, name: "圣光祭司", path: "hm3", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 287, ap: 64,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
skills: { 6104: { uuid: 6104, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
fstart: {
|
||||
6301: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 3 } },
|
||||
@@ -503,7 +503,7 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
// 5105 风怒先知 — field 攻速光环
|
||||
5105: {
|
||||
uuid: 5105, name: "风怒先知", path: "hm1", fac: FacSet.HERO, card_lv: 2, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 287, ap: 64,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
skills: { 6104: { uuid: 6104, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
field: [
|
||||
{ lv: 1, uuids: [{ uuid: 7006, value: 0.1 }] },
|
||||
{ lv: 3, uuids: [{ uuid: 7006, value: 0.2 }] },
|
||||
@@ -569,7 +569,7 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
// 5204 晨光贤者 — fstart 全队暴击(范围扩大)
|
||||
5204: {
|
||||
uuid: 5204, name: "晨光贤者", path: "hm2", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 430, ap: 96,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
skills: { 6104: { uuid: 6104, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
fstart: {
|
||||
6403: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, timed_buff_id: 7012, buff_value: 5, buff_duration: 5 } },
|
||||
@@ -582,7 +582,7 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
// 5205 精准术士 — field 暴击光环(范围扩大)
|
||||
5205: {
|
||||
uuid: 5205, name: "精准术士", path: "hm3", fac: FacSet.HERO, card_lv: 3, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 430, ap: 96,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
skills: { 6104: { uuid: 6104, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
field: [
|
||||
{ lv: 1, uuids: [{ uuid: 7004, value: 0.1 }] },
|
||||
{ lv: 3, uuids: [{ uuid: 7004, value: 0.2 }] },
|
||||
@@ -640,7 +640,7 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
// 5304 战吼术士 — fstart 全队攻击+生命(双效果)
|
||||
5304: {
|
||||
uuid: 5304, name: "战吼术士", path: "hm1", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 573, ap: 128,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
skills: { 6104: { uuid: 6104, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
fstart: {
|
||||
6401: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } },
|
||||
@@ -658,7 +658,7 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
// 5305 领域贤者 — field 暴伤+攻速光环(双效果)
|
||||
5305: {
|
||||
uuid: 5305, name: "领域贤者", path: "hm2", fac: FacSet.HERO, card_lv: 4, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Support, hp: 573, ap: 128,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
skills: { 6104: { uuid: 6104, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
field: [
|
||||
{ lv: 1, uuids: [{ uuid: 7005, value: 0.2 }, { uuid: 7006, value: 0.1 }] },
|
||||
{ lv: 3, uuids: [{ uuid: 7005, value: 0.4 }, { uuid: 7006, value: 0.2 }] },
|
||||
@@ -731,7 +731,7 @@ export const HeroInfo: Record<number, heroInfo> = {
|
||||
// 5404 涅槃贤者 — fstart 护盾+攻击 + revive(双技能)
|
||||
5404: {
|
||||
uuid: 5404, name: "涅槃贤者", path: "hm3", fac: FacSet.HERO, card_lv: 5, lv: 1, type: HType.Long, grow_type: HGrowType.Mage, role: HRole.Mage, hp: 717, ap: 160,
|
||||
skills: { 6013: { uuid: 6013, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
skills: { 6104: { uuid: 6104, lv: 1, cd: AtkSpeedSet[AtkSpeedLv.Slow].cd, ccd: 0 } },
|
||||
fstart: {
|
||||
6301: [
|
||||
{ lv: 1, s_lv: SkillLv.Lv1, t_num: 1, overrides: { TGroup: TGroup.Team, ap: 5 } },
|
||||
|
||||
@@ -13,17 +13,23 @@ export class ActiveBuff {
|
||||
source_uuid: number;
|
||||
/** 施法者攻击力快照(用于 DoT/HoT 的 ap 缩放) */
|
||||
source_ap_snapshot: number;
|
||||
/**
|
||||
* 施法者完整属性快照(DoT tick 判定暴击/冰冻/击晕用)。
|
||||
* 结构同 DamageEvent.Attrs;仅 DoT 类 buff 需要,普通 modifier buff 为 undefined。
|
||||
*/
|
||||
source_attrs_snapshot?: any;
|
||||
/** 周期效果累计时间(秒) */
|
||||
tick_acc: number = 0;
|
||||
/** 修饰数值覆写(来自药水卡 buff_value),优先于 BuffList 配置值;undefined 表示用配置值 */
|
||||
value_override?: number;
|
||||
|
||||
constructor(cfg_id: number, dur: number, src_uuid: number, src_ap: number, value_override?: number) {
|
||||
constructor(cfg_id: number, dur: number, src_uuid: number, src_ap: number, value_override?: number, src_attrs?: any) {
|
||||
this.config_id = cfg_id;
|
||||
this.remaining = dur;
|
||||
this.source_uuid = src_uuid;
|
||||
this.source_ap_snapshot = src_ap;
|
||||
this.value_override = value_override;
|
||||
this.source_attrs_snapshot = src_attrs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ export interface BuffApplyOverrides {
|
||||
duration?: number;
|
||||
/** 覆写计时 buff 的修饰数值(modifiers.value / tick.damage_or_heal),由药水卡 buff_value 自定义档位 */
|
||||
value?: number;
|
||||
/** 施法者完整属性快照(DoT tick 判定暴击/冰冻/击晕用),结构同 DamageEvent.Attrs */
|
||||
source_attrs?: any;
|
||||
}
|
||||
|
||||
class BuffManagerImpl {
|
||||
@@ -55,7 +57,7 @@ class BuffManagerImpl {
|
||||
buffComp.buffs.set(buffId, layers);
|
||||
}
|
||||
|
||||
const newLayer = new ActiveBuff(buffId, duration, sourceUuid, sourceAp, overrides?.value);
|
||||
const newLayer = new ActiveBuff(buffId, duration, sourceUuid, sourceAp, overrides?.value, overrides?.source_attrs);
|
||||
|
||||
if (cfg.max_stack === 0 || layers.length < cfg.max_stack) {
|
||||
layers.push(newLayer);
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Timer } from "db://oops-framework/core/common/timer/Timer";
|
||||
import { BuffList } from "../common/config/BuffSet";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { BuffComp, ActiveBuff } from "./BuffComp";
|
||||
import { DamageQueueHelper } from "./DamageQueueComp";
|
||||
import { mLogger } from "../common/Logger";
|
||||
|
||||
/** Buff 模块调试日志开关 */
|
||||
@@ -52,7 +53,7 @@ export class BuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
|
||||
// 周期效果结算(先于到期判断,避免到期帧丢失末次 tick)
|
||||
if (cfg.tick && attrsComp) {
|
||||
this.processTick(layer, cfg.tick, attrsComp);
|
||||
this.processTick(e, buffId, layer, cfg.tick, attrsComp);
|
||||
}
|
||||
|
||||
// 永久 buff(duration<=0)不递减 remaining
|
||||
@@ -84,11 +85,17 @@ export class BuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
* 处理单层的周期效果(DoT/HoT)。
|
||||
* 累计 tick_acc 到 interval 时结算一次伤害/治疗。
|
||||
*
|
||||
* DoT(负值):转为 DamageEvent 入队,由 HeroAtkSystem.doAttack 走完整结算链
|
||||
* (护盾抵扣 / 暴击 / 冰冻 / 击晕 / 飘字 / 死亡链),Attrs 取 buff 层的施法者属性快照;
|
||||
* HoT(正值):保持直接回血,不走伤害链。
|
||||
*
|
||||
* @param target 被作用者实体(DoT 入队用)
|
||||
* @param buffId buff 配置 id(DoT 事件借 s_uuid 字段携带,仅作日志追溯)
|
||||
* @param layer 当前 buff 层实例
|
||||
* @param tick 周期效果配置
|
||||
* @param attrs 被作用者属性组件(调用 add_hp)
|
||||
* @param attrs 被作用者属性组件
|
||||
*/
|
||||
private processTick(layer: ActiveBuff, tick: { interval: number; damage_or_heal: number; scale_by_ap_pct: number }, attrs: HeroAttrsComp): void {
|
||||
private processTick(target: ecs.Entity, buffId: number, layer: ActiveBuff, tick: { interval: number; damage_or_heal: number; scale_by_ap_pct: number }, attrs: HeroAttrsComp): void {
|
||||
// interval 非正值守卫,避免死循环
|
||||
if (tick.interval <= 0) return;
|
||||
layer.tick_acc += TICK_STEP;
|
||||
@@ -99,7 +106,12 @@ export class BuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
|
||||
const baseAmount = layer.value_override !== undefined ? layer.value_override : tick.damage_or_heal;
|
||||
const scale = 1 + layer.source_ap_snapshot * tick.scale_by_ap_pct / 10000;
|
||||
const amount = Math.floor(baseAmount * scale);
|
||||
attrs.add_hp(amount);
|
||||
if (amount < 0) {
|
||||
// DoT 走伤害队列:dot_dmg 传正值,doAttack 内识别并走固定伤害分支
|
||||
DamageQueueHelper.addDamageToEntity(target, layer.source_attrs_snapshot ?? null, layer.source_uuid, buffId, 0, 1, -amount);
|
||||
} else if (amount > 0) {
|
||||
attrs.add_hp(amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ export interface DamageEvent {
|
||||
|
||||
ext_dmg:number //额外伤害
|
||||
dmg_ratio:number//伤害比例
|
||||
/**
|
||||
* DoT 固定伤害值(>0 时本事件为 DoT tick 结算):
|
||||
* 跳过 dmgCount 公式直接用该值,且不走击退/受击动画,其余(暴击/冰冻/击晕/护盾/死亡链)与普攻一致
|
||||
*/
|
||||
dot_dmg?:number
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +65,7 @@ export class DamageQueueComp extends ecs.Comp {
|
||||
/**
|
||||
* 添加伤害事件到队列
|
||||
*/
|
||||
addDamageEvent(attrs: any, casterEid: number, s_uuid: number,ext_dmg:number=0,dmg_ratio:number=1): void {
|
||||
addDamageEvent(attrs: any, casterEid: number, s_uuid: number,ext_dmg:number=0,dmg_ratio:number=1,dot_dmg:number=0): void {
|
||||
const timestamp = Date.now();
|
||||
|
||||
const damageEvent: DamageEvent = {
|
||||
@@ -71,6 +76,7 @@ export class DamageQueueComp extends ecs.Comp {
|
||||
eventId: "",
|
||||
ext_dmg:ext_dmg,
|
||||
dmg_ratio:dmg_ratio,
|
||||
dot_dmg:dot_dmg,
|
||||
};
|
||||
|
||||
this.damageEvents.push(damageEvent);
|
||||
@@ -176,7 +182,7 @@ export class DamageQueueHelper {
|
||||
* 为实体添加伤害事件
|
||||
* 如果实体没有伤害队列组件,会自动创建
|
||||
*/
|
||||
static addDamageToEntity(entity: ecs.Entity, attrs: any, casterEid: number, s_uuid: number,ext_dmg:number=0,dmg_ratio:number=1): void {
|
||||
static addDamageToEntity(entity: ecs.Entity, attrs: any, casterEid: number, s_uuid: number,ext_dmg:number=0,dmg_ratio:number=1,dot_dmg:number=0): void {
|
||||
let damageQueue = entity.get(DamageQueueComp);
|
||||
|
||||
// 如果实体没有伤害队列组件,创建一个
|
||||
@@ -185,7 +191,7 @@ export class DamageQueueHelper {
|
||||
}
|
||||
|
||||
// 添加伤害事件到队列
|
||||
damageQueue.addDamageEvent(attrs, casterEid, s_uuid,ext_dmg,dmg_ratio);
|
||||
damageQueue.addDamageEvent(attrs, casterEid, s_uuid,ext_dmg,dmg_ratio,dot_dmg);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,6 +18,7 @@ import { SkillTriggerHelper } from "./SkillTriggerHelper";
|
||||
import { getMonsterGoldDrop } from "../map/RogueConfig";
|
||||
import { MissionEconomy } from "../map/MissionEconomy";
|
||||
import { MoveComp } from "./MoveComp";
|
||||
import { buffManager, BuffApplyOverrides } from "./BuffManager";
|
||||
|
||||
/** 最终伤害数据接口
|
||||
* 用于封装一次攻击计算的所有结果数据
|
||||
@@ -119,10 +120,14 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
if (!TAttrsComp || TAttrsComp.is_dead || TAttrsComp.is_reviving) return reDate;
|
||||
|
||||
const casterEid = damageEvent.casterEid;
|
||||
// DoT 事件:dot_dmg>0 时伤害为固定 tick 值,无对应 SkillSet 配置(s_uuid 位置是 buffId)
|
||||
const isDot = (damageEvent.dot_dmg ?? 0) > 0;
|
||||
// DoT 事件的 Attrs 来自 ActiveBuff.source_attrs_snapshot,可能为空,兜底空对象避免取属性报错
|
||||
if (isDot && !damageEvent.Attrs) damageEvent.Attrs = {};
|
||||
|
||||
// 获取技能配置
|
||||
const skillConf = SkillSet[damageEvent.s_uuid];
|
||||
if (!skillConf) return reDate;
|
||||
if (!skillConf && !isDot) return reDate;
|
||||
|
||||
// 触发被攻击事件
|
||||
this.onAttacked(target);
|
||||
@@ -132,8 +137,8 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
const criticalChance = (damageEvent.Attrs[Attrs.critical] || 0) - TAttrsComp.getFinalCriticalRes();
|
||||
const isCrit = this.checkChance(criticalChance);
|
||||
|
||||
// 计算基础伤害
|
||||
let damage = this.dmgCount(damageEvent, TAttrsComp);
|
||||
// 计算基础伤害:DoT 直接用固定 tick 值,普攻走 dmgCount 公式
|
||||
let damage = isDot ? (damageEvent.dot_dmg ?? 0) : this.dmgCount(damageEvent, TAttrsComp);
|
||||
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', " dmgCount", damage)
|
||||
if (isCrit) {
|
||||
@@ -172,9 +177,11 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
}
|
||||
}
|
||||
|
||||
// 受击计数:只有实际造成伤害才 +1(被格挡不计数)
|
||||
TAttrsComp.atked_count++;
|
||||
this.checkAndTriggerAtkedSkills(TAttrsComp, targetView);
|
||||
// 受击计数:只有实际造成伤害才 +1(被格挡不计数);DoT 不计受击、不触发受击技
|
||||
if (!isDot) {
|
||||
TAttrsComp.atked_count++;
|
||||
this.checkAndTriggerAtkedSkills(TAttrsComp, targetView);
|
||||
}
|
||||
|
||||
mLogger.log(this.debugMode, 'HeroAtkSystem', ` 英雄${TAttrsComp.hero_name} (uuid: ${TAttrsComp.hero_uuid}) 受到 eid:${casterEid} 的 伤害 ${damage},${isCrit ? "暴击" : "普通"}攻击,技能ID ${damageEvent.s_uuid}`);
|
||||
|
||||
@@ -186,24 +193,29 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
const stunChance = (damageEvent.Attrs[Attrs.stun_chance] || 0) - TAttrsComp.getFinalStunRes();
|
||||
const isStun = !TAttrsComp.isStun() && this.checkChance(stunChance);
|
||||
|
||||
// 击退:受击默认后退,击退属性不再作为概率,而是转换为后退距离的增量
|
||||
// 击退增量 = 施法者击退强化(原击退概率)- 被攻击者击退抗性 + 击退距离强化
|
||||
const knockbackBonus = (damageEvent.Attrs[Attrs.knockback_chance] || 0) - (TAttrsComp.getFinalKnockbackRes() || 0);
|
||||
const knockbackExtra = Math.max(0, knockbackBonus + (damageEvent.Attrs[Attrs.knockback_distance] || 0));
|
||||
|
||||
// ✅ 触发视图层表现(伤害数字、受击动画、冰冻、击晕、击退)
|
||||
if (targetView) {
|
||||
targetView.do_atked(damage, isCrit, damageEvent.s_uuid, knockbackExtra);
|
||||
targetView.playEnd(skillConf.endAnm);
|
||||
if (isDot) {
|
||||
// DoT:只飘伤害数字 + 刷新血条,不播受击动画/音效/击退
|
||||
targetView.showDamage(damage, isCrit);
|
||||
} else {
|
||||
// 击退:受击默认后退,击退属性不再作为概率,而是转换为后退距离的增量
|
||||
// 击退增量 = 施法者击退强化(原击退概率)- 被攻击者击退抗性 + 击退距离强化
|
||||
const knockbackBonus = (damageEvent.Attrs[Attrs.knockback_chance] || 0) - (TAttrsComp.getFinalKnockbackRes() || 0);
|
||||
const knockbackExtra = Math.max(0, knockbackBonus + (damageEvent.Attrs[Attrs.knockback_distance] || 0));
|
||||
|
||||
// 击退位移下沉到 MoveSystem:直接写入 knockbackVelocity,由 MoveSystem 每帧衰减并叠加位移
|
||||
const moveComp = target.get(MoveComp);
|
||||
if (moveComp) {
|
||||
const dist = FightSet.BACK_RANG + knockbackExtra;
|
||||
const isHero = TAttrsComp.fac === FacSet.HERO;
|
||||
// 击退初速度:0.1 秒时间常数衰减,总位移约等于 dist
|
||||
const knockbackSpeed = dist * 16;
|
||||
moveComp.knockbackVelocity = isHero ? -knockbackSpeed : knockbackSpeed;
|
||||
targetView.do_atked(damage, isCrit, damageEvent.s_uuid, knockbackExtra);
|
||||
targetView.playEnd(skillConf.endAnm);
|
||||
|
||||
// 击退位移下沉到 MoveSystem:直接写入 knockbackVelocity,由 MoveSystem 每帧衰减并叠加位移
|
||||
const moveComp = target.get(MoveComp);
|
||||
if (moveComp) {
|
||||
const dist = FightSet.BACK_RANG + knockbackExtra;
|
||||
const isHero = TAttrsComp.fac === FacSet.HERO;
|
||||
// 击退初速度:0.1 秒时间常数衰减,总位移约等于 dist
|
||||
const knockbackSpeed = dist * 16;
|
||||
moveComp.knockbackVelocity = isHero ? -knockbackSpeed : knockbackSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFrost) {
|
||||
@@ -222,6 +234,15 @@ export class HeroAtkSystem extends ecs.ComblockSystem implements ecs.ISystemUpda
|
||||
}
|
||||
}
|
||||
|
||||
// 命中附带 debuff(如灼烧 DoT):仅普攻路径,复用友方 timed_buff_id 机制;
|
||||
// 施法者 ap/完整属性快照取自伤害事件,供 BuffSystem tick 缩放与暴击/冰冻/击晕判定
|
||||
if (!isDot && skillConf.timed_buff_id) {
|
||||
const buffOverrides: BuffApplyOverrides = { source_attrs: damageEvent.Attrs };
|
||||
if (skillConf.buff_value !== undefined) buffOverrides.value = skillConf.buff_value;
|
||||
if (skillConf.buff_duration !== undefined) buffOverrides.duration = skillConf.buff_duration;
|
||||
buffManager.applyBuff(target, skillConf.timed_buff_id, casterEid, damageEvent.Attrs[Attrs.ap] || 0, buffOverrides);
|
||||
}
|
||||
|
||||
|
||||
// 检查死亡
|
||||
if (TAttrsComp.hp <= 0) {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { _decorator, ProgressBar, Sprite, UIOpacity, Vec3, v3, tween, Tween, Color, clamp } from "cc";
|
||||
import { _decorator, ProgressBar, Sprite, UIOpacity, Vec3, v3, tween, Tween, Color, clamp, Node } from "cc";
|
||||
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
|
||||
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
|
||||
import { HeroAttrsComp } from "./HeroAttrsComp";
|
||||
import { BuffComp } from "./BuffComp";
|
||||
import { BuffList, BuffCategory } from "../common/config/BuffSet";
|
||||
import { FacSet, FightSet } from "../common/config/GameSet";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
@@ -35,6 +37,10 @@ export class HeroTopBarComp extends CCComp {
|
||||
// ==================== 内部引用 ====================
|
||||
private topOpacity: UIOpacity = null!;
|
||||
private basePos: Vec3 = v3();
|
||||
/** debuff 容器节点(top 下的 debuff 子节点,按名字管理各 debuff 图标) */
|
||||
private debuffNode: Node = null!;
|
||||
/** 当前点亮的 debuff 图标节点名集合(用于 diff,避免每帧重复 set active) */
|
||||
private activeDebuffNames: Set<string> = new Set();
|
||||
|
||||
// ==================== 配置 ====================
|
||||
private hp_height: number = 90;
|
||||
@@ -87,6 +93,15 @@ export class HeroTopBarComp extends CCComp {
|
||||
this.node.getChildByName("lv")!.active = false;
|
||||
this.node.active = true;
|
||||
|
||||
// debuff 图标容器(无该节点的旧 prefab 容错为 null)
|
||||
this.debuffNode = this.node.getChildByName("debuff")!;
|
||||
this.activeDebuffNames.clear();
|
||||
if (this.debuffNode) {
|
||||
for (const child of this.debuffNode.children) {
|
||||
child.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.setTopBarOpacity(false);
|
||||
this.hpShow();
|
||||
this.shieldShow(model.shield);
|
||||
@@ -120,6 +135,51 @@ export class HeroTopBarComp extends CCComp {
|
||||
this.shieldProgressBar.node.active = shield > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* buffId → debuff 图标节点名映射(节点名为 top.prefab debuff 容器下的子节点名)。
|
||||
* 仅伤害类 debuff 需要图标;未映射的 buff 不显示。
|
||||
*/
|
||||
private static readonly DEBUFF_ICON_MAP: Record<number, string> = {
|
||||
7002: "FireDamage", // 灼烧
|
||||
};
|
||||
|
||||
/**
|
||||
* 刷新 debuff 图标显隐:按 BuffComp 中当前生效的 debuff 种类点亮对应图标。
|
||||
* 由 HeroViewComp 在 dirty_buffs 脏标记时调用;与上次状态做 diff,避免重复 set active。
|
||||
* @param buffComp 目标的 buff 组件(无 BuffComp 时全部隐藏)
|
||||
*/
|
||||
public debuffShow(buffComp: BuffComp | null): void {
|
||||
if (!this.debuffNode) return;
|
||||
|
||||
// 收集当前生效的、有图标映射的 debuff 节点名
|
||||
const wantActive = new Set<string>();
|
||||
if (buffComp) {
|
||||
for (const [buffId, layers] of buffComp.buffs) {
|
||||
if (layers.length === 0) continue;
|
||||
const cfg = BuffList[buffId];
|
||||
// 只显示伤害类 debuff(tick 负值)与控制类 debuff;普通 Buff 不占用 debuff 栏
|
||||
if (!cfg || cfg.category === BuffCategory.Buff) continue;
|
||||
const nodeName = HeroTopBarComp.DEBUFF_ICON_MAP[buffId];
|
||||
if (nodeName) wantActive.add(nodeName);
|
||||
}
|
||||
}
|
||||
|
||||
// diff:先隐藏不再需要的,再点亮新增的
|
||||
for (const name of this.activeDebuffNames) {
|
||||
if (!wantActive.has(name)) {
|
||||
const child = this.debuffNode.getChildByName(name);
|
||||
if (child) child.active = false;
|
||||
}
|
||||
}
|
||||
for (const name of wantActive) {
|
||||
if (!this.activeDebuffNames.has(name)) {
|
||||
const child = this.debuffNode.getChildByName(name);
|
||||
if (child) child.active = true;
|
||||
}
|
||||
}
|
||||
this.activeDebuffNames = wantActive;
|
||||
}
|
||||
|
||||
/** 激活顶栏(高亮 + 延迟恢复闲置透明度) */
|
||||
public activateTopBar(): void {
|
||||
this.setTopBarOpacity(true);
|
||||
@@ -165,6 +225,12 @@ export class HeroTopBarComp extends CCComp {
|
||||
/** 对象池回收 / 组件重置时调用 */
|
||||
public reset(): void {
|
||||
this.unscheduleAllCallbacks();
|
||||
this.activeDebuffNames.clear();
|
||||
if (this.debuffNode && this.debuffNode.isValid) {
|
||||
for (const child of this.debuffNode.children) {
|
||||
child.active = false;
|
||||
}
|
||||
}
|
||||
if (this.node && this.node.isValid) {
|
||||
Tween.stopAllByTarget(this.node);
|
||||
this.node.setPosition(this.basePos);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/modu
|
||||
import { mLogger } from "../common/Logger";
|
||||
import { HeroSpine } from "./HeroSpine";
|
||||
import { HeroTopBarComp } from "./HeroTopBarComp";
|
||||
import { BuffComp } from "./BuffComp";
|
||||
import { BoxSet, FacSet, FightSet, NumberFormatter, TooltipTypes } from "../common/config/GameSet";
|
||||
import { smc } from "../common/SingletonModuleComp";
|
||||
import { SkillSet, } from "../common/config/SkillSet";
|
||||
@@ -139,6 +140,13 @@ export class HeroViewComp extends CCComp {
|
||||
this.topBar.shieldShow(this.model.shield);
|
||||
this.model.dirty_shield = false;
|
||||
}
|
||||
|
||||
// ✅ buff 变更脏标记:刷新 debuff 图标显隐(灼烧等)
|
||||
const buffComp = this.ent.has(BuffComp) ? this.ent.get(BuffComp) : null;
|
||||
if (buffComp && buffComp.dirty_buffs) {
|
||||
this.topBar.debuffShow(buffComp);
|
||||
buffComp.dirty_buffs = false;
|
||||
}
|
||||
}
|
||||
|
||||
public cd_show() {
|
||||
|
||||
Reference in New Issue
Block a user