diff --git a/assets/script/game/common/config/SkillSet.ts b/assets/script/game/common/config/SkillSet.ts index 312b7b89..466cb168 100644 --- a/assets/script/game/common/config/SkillSet.ts +++ b/assets/script/game/common/config/SkillSet.ts @@ -166,6 +166,7 @@ export interface SkillConfig { bck_chance?: number, // 击退概率(百分点;未配置视为 0 不击退,可由 heroSet overrides 覆写) buff_type?: Attrs, // Buff 类型 (单一职责) timed_buff_id?: number, // 计时性 buff 配置 id(指向 BuffList),存在时走 buffManager 而非永久 add_* + shield_duration?: number, // 护盾持续时间(秒),仅 kind=Shield 生效;缺省/<=0 表示永久护盾 buff_value?: number, // 覆写计时 buff 的修饰数值(modifiers.value / tick.damage_or_heal),由药水卡自定义档位 buff_duration?: number, // 覆写计时 buff 的持续时间(秒),不配置则用 BuffList 默认值 buff_chance?: number, // 计时 buff 施加概率(百分点,缺省 100) @@ -205,6 +206,7 @@ export interface SkillOverrides { bck_chance?: number; buff_type?: Attrs; timed_buff_id?: number; + shield_duration?: number; buff_value?: number; buff_duration?: number; buff_chance?: number; @@ -384,8 +386,8 @@ export const SkillSet: Record = { //============================= ====== 辅助技能 回合技能 ====== ========================== 6301: { uuid: 6301, name: "护盾", sp_name: "buff_wind", icon: "28-Electric-Wave", TGroup: TGroup.Self, readyAnm: "up_blue", endAnm: "", act: "atk", - DTType: DTType.single, kind: SkillKind.Shield, ap: 30, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support, - RType: RType.fixed, EType: EType.animationEnd, info: "获得施法者最大生命30%的护盾", + DTType: DTType.single, kind: SkillKind.Shield, ap: 30, hit_count: 1, hitcd: 0.2, speed: 720, with: 0, ready: 0.2, EAnm: 0, DAnm: "", IType: IType.support, shield_duration: 7, + RType: RType.fixed, EType: EType.animationEnd, info: "获得施法者最大生命30%的护盾, 持续7秒", }, 6302: { uuid: 6302, name: "治疗", sp_name: "buff_wind", icon: "g_priest_zldy_skill", TGroup: TGroup.Team, readyAnm: "up_green", endAnm: "", act: "atk", diff --git a/assets/script/game/common/config/heroSet.ts b/assets/script/game/common/config/heroSet.ts index 1980de40..97c63d51 100644 --- a/assets/script/game/common/config/heroSet.ts +++ b/assets/script/game/common/config/heroSet.ts @@ -549,7 +549,7 @@ export const HeroInfo: Record = { { lv: 2, s_lv: SkillLv.Lv2, overrides: {"ap":120}, desc: "普通攻击 造成 120% 伤害" }, ], }, - 6301: { uuid: 6301, name: "魔法盾", lv: 1, cd: 8, ccd: 0, overrides: {"ap":30}, desc: "{target}获得生命最大值{ap}%的护盾", + 6301: { uuid: 6301, name: "魔法盾", lv: 1, cd: 10, ccd: 0, overrides: {"ap":30}, desc: "{target}获得生命最大值{ap}%的护盾", lv_entries: [ { lv: 8, s_lv: SkillLv.Lv2, overrides: {"ap":60}, desc: "{target}获得生命最大值{ap}%的护盾" }, { lv: 16, s_lv: SkillLv.Lv3, overrides: {"ap":120}, desc: "{target}获得生命最大值{ap}%的护盾" }, diff --git a/assets/script/game/hero/BuffSystem.ts b/assets/script/game/hero/BuffSystem.ts index 323be5fc..0f1ed0e4 100644 --- a/assets/script/game/hero/BuffSystem.ts +++ b/assets/script/game/hero/BuffSystem.ts @@ -28,18 +28,34 @@ const TICK_STEP = 0.1; export class BuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate { private timer = new Timer(TICK_STEP); + // HeroAttrsComp 可选,用于 DoT/HoT 的血量结算与护盾倒计时 + private processShieldTimer(attrsComp: HeroAttrsComp | null): void { + if (!attrsComp) return; + // 永久护盾(duration<=0)不参与倒计时 + if (attrsComp.shield_duration <= 0) return; + attrsComp.shield_duration -= TICK_STEP; + if (attrsComp.shield_duration <= 0) { + attrsComp.clear_shield(); + mLogger.log(DEBUG, "BuffSystem", `护盾到期: ${attrsComp.hero_name}(eid:${attrsComp.ent?.eid ?? "-"}) 护盾已清空`); + } + } + + // filter 改为 allOf(HeroAttrsComp):护盾倒计时挂在 HeroAttrsComp 上, + // 若按 BuffComp 过滤,无 buff 的实体不会被遍历,护盾到期清空会失效 filter(): ecs.IMatcher { - return ecs.allOf(BuffComp); + return ecs.allOf(HeroAttrsComp); } update(e: ecs.Entity): void { if (!this.timer.update(this.dt)) return; - const buffComp = e.get(BuffComp); - if (!buffComp) return; + // HeroAttrsComp 必有(filter 保证),用于 DoT/HoT 血量结算与护盾倒计时 + const attrsComp = e.get(HeroAttrsComp); + this.processShieldTimer(attrsComp); - // HeroAttrsComp 可选,用于 DoT/HoT 的血量结算 - const attrsComp = e.has(HeroAttrsComp) ? e.get(HeroAttrsComp) : null; + // BuffComp 可选:无 buff 时仅做护盾倒计时 + const buffComp = e.has(BuffComp) ? e.get(BuffComp) : null; + if (!buffComp) return; // ---- 处理所有 buff 层 ---- let anyChanged = false; diff --git a/assets/script/game/hero/HeroAttrsComp.ts b/assets/script/game/hero/HeroAttrsComp.ts index 3da2816d..5cec81e4 100644 --- a/assets/script/game/hero/HeroAttrsComp.ts +++ b/assets/script/game/hero/HeroAttrsComp.ts @@ -42,6 +42,8 @@ export class HeroAttrsComp extends ecs.Comp { shield: number = 0; // 当前护盾 /** 护盾获得时的快照值,作为护盾进度条分母(护盾为覆盖语义,分母不能随扣盾变化) */ shield_max: number = 0; + /** 护盾剩余持续时间(秒),<=0 表示永久护盾;由 BuffSystem 定时递减,归零时清空护盾 */ + shield_duration: number = 0; defense: number = 0; // 防御(除法递减模型减伤,见 getFinalDefense) // ==================== 攻击属性 (补充) ==================== @@ -168,13 +170,27 @@ export class HeroAttrsComp extends ecs.Comp { * * Why 覆盖而非累加:新护盾直接替换旧护盾,避免多源护盾无限叠加导致数值膨胀。 * @param value 新的护盾值(<=0 时视为清空护盾) + * @param duration 护盾持续时间(秒),<=0 或不传表示永久护盾;到期由 BuffSystem 清空 */ - add_shield(value: number) { + add_shield(value: number, duration: number = 0) { this.shield = Math.max(0, Math.floor(value)); // 覆盖为新护盾时刷新快照分母;清空护盾时分母归零 this.shield_max = this.shield; + this.shield_duration = Math.max(0, duration); this.dirty_shield = true; // 标记护盾需要更新 } + + /** + * 清空护盾(护盾到期或外部强制驱散时调用)。 + * 直接置零并打脏标记,视图层据此隐藏护盾条与特效。 + */ + clear_shield() { + if (this.shield <= 0) return; + this.shield = 0; + this.shield_max = 0; + this.shield_duration = 0; + this.dirty_shield = true; + } add_hp_max(value: number) { this.hp_max += value this.hp += value @@ -701,6 +717,7 @@ export class HeroAttrsComp extends ecs.Comp { this.dis = 100; this.shield = 0; this.shield_max = 0; + this.shield_duration = 0; this.defense = 0; // 重置新增属性 diff --git a/assets/script/game/hero/SCastSystem.ts b/assets/script/game/hero/SCastSystem.ts index 54d6c57f..1fd4cabb 100644 --- a/assets/script/game/hero/SCastSystem.ts +++ b/assets/script/game/hero/SCastSystem.ts @@ -575,7 +575,8 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate } else if (kind === SkillKind.Shield && sAp !== 0) { // 护盾值 = ap% × 施法者最终最大生命(getFinalHpMax 含驻场光环与计时 buff 加成) const addShield = Math.max(0, Math.floor(_cAttrsComp.getFinalHpMax() * sAp / 100)); - model.add_shield(addShield); + // shield_duration 缺省/<=0 表示永久护盾 + model.add_shield(addShield, config.shield_duration ?? 0); } if (config.buff_type !== undefined) {