feat: 实现药水卡限时强化功能,支持自定义数值覆写

1. 新增Buff数值覆写机制,支持药水卡自定义buff效果数值
2. 重构属性计算逻辑,支持读取药水卡传递的覆写值
3. 新增多组限时强化Buff与药水卡配置
4. 优化UI数值显示,实时展示最终属性值
5. 新增多维度调试日志方便排查问题
This commit is contained in:
panFD
2026-07-23 23:00:49 +08:00
parent 2876108970
commit ac0891ccdd
12 changed files with 205 additions and 50 deletions

View File

@@ -15,12 +15,15 @@ export class ActiveBuff {
source_ap_snapshot: number;
/** 周期效果累计时间(秒) */
tick_acc: number = 0;
/** 修饰数值覆写(来自药水卡 buff_value优先于 BuffList 配置值undefined 表示用配置值 */
value_override?: number;
constructor(cfg_id: number, dur: number, src_uuid: number, src_ap: number) {
constructor(cfg_id: number, dur: number, src_uuid: number, src_ap: number, value_override?: number) {
this.config_id = cfg_id;
this.remaining = dur;
this.source_uuid = src_uuid;
this.source_ap_snapshot = src_ap;
this.value_override = value_override;
}
}

View File

@@ -12,9 +12,11 @@ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ec
import { BuffCategory, BuffList } from "../common/config/BuffSet";
import { BuffComp, ActiveBuff } from "./BuffComp";
/** applyBuff 的覆盖参数(预留按需覆写持续时间) */
/** applyBuff 的覆盖参数(预留按需覆写持续时间 / 修饰数值 */
export interface BuffApplyOverrides {
duration?: number;
/** 覆写计时 buff 的修饰数值modifiers.value / tick.damage_or_heal由药水卡 buff_value 自定义档位 */
value?: number;
}
class BuffManagerImpl {
@@ -34,7 +36,11 @@ class BuffManagerImpl {
*/
applyBuff(target: ecs.Entity, buffId: number, sourceUuid: number = 0, sourceAp: number = 0, overrides?: BuffApplyOverrides): void {
const cfg = BuffList[buffId];
if (!cfg) return;
if (!cfg) {
console.log(`[BuffManager] applyBuff: buffId=${buffId} 配置不存在,跳过`);
return;
}
console.log(`[BuffManager] applyBuff: buff=${cfg.name}(${buffId}) target_eid=${target.eid} duration=${overrides?.duration ?? cfg.duration} value_override=${overrides?.value} source_ap=${sourceAp}`);
const buffComp = this.ensureBuffComp(target);
const duration = overrides?.duration ?? cfg.duration;
@@ -45,7 +51,7 @@ class BuffManagerImpl {
buffComp.buffs.set(buffId, layers);
}
const newLayer = new ActiveBuff(buffId, duration, sourceUuid, sourceAp);
const newLayer = new ActiveBuff(buffId, duration, sourceUuid, sourceAp, overrides?.value);
if (cfg.max_stack === 0 || layers.length < cfg.max_stack) {
layers.push(newLayer);

View File

@@ -60,6 +60,7 @@ export class BuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
if (cfg.duration > 0 && layer.remaining <= 0) {
layers.splice(i, 1);
anyChanged = true;
console.log(`[BuffSystem] buff 到期移除: ${cfg.name}(${buffId}) eid=${e.eid} 剩余层数=${layers.length}`);
}
}
@@ -90,8 +91,10 @@ export class BuffSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
while (layer.tick_acc >= tick.interval) {
layer.tick_acc -= tick.interval;
// 按 ap 缩放后取整floor(dmg * (1 + ap_snapshot * scale_pct / 10000))
// damage_or_heal 优先取药水卡覆写值
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(tick.damage_or_heal * scale);
const amount = Math.floor(baseAmount * scale);
attrs.add_hp(amount);
}
}

View File

@@ -351,14 +351,17 @@ export class HeroAttrsComp extends ecs.Comp {
if (!cfg || !cfg.modifiers) return;
// 每层独立贡献,模拟叠层放大效果
for (let i = 0; i < layers.length; i++) {
const layerVal = layers[i].value_override; // 药水卡覆写值优先
for (const mod of cfg.modifiers) {
if (mod.attr !== attr) continue;
const value = layerVal !== undefined ? layerVal : mod.value;
console.log(`[HeroAttrs] aggregateTimedMods 命中: ${this.hero_name} buff=${cfg.name} attr=${attr} op=${mod.op} value=${value} remaining=${layers[i].remaining.toFixed(1)}`);
if (mod.op === ModOp.Flat) {
result.flatSum += mod.value;
result.flatSum += value;
} else {
// PercentAdd 与 PercentMul 暂统一按加法百分比聚合;
// PercentMul 真正的独立乘区尚未启用TODO: 启用时需改造为多乘区连乘
result.pctSum += mod.value;
result.pctSum += value;
}
}
}
@@ -373,7 +376,11 @@ export class HeroAttrsComp extends ecs.Comp {
public getFinalAp(): number {
const runtimeAp = this.getRuntimeAp(this.ap);
const mods = this.aggregateTimedMods(Attrs.ap);
return (runtimeAp + mods.flatSum) * (1 + mods.pctSum / 100);
const final = (runtimeAp + mods.flatSum) * (1 + mods.pctSum / 100);
if (mods.flatSum !== 0 || mods.pctSum !== 0) {
console.log(`[HeroAttrs] getFinalAp: ${this.hero_name} base=${this.ap} runtime=${runtimeAp.toFixed(1)} flat=${mods.flatSum} pct=${mods.pctSum} final=${final.toFixed(1)}`);
}
return final;
}
/**
@@ -472,7 +479,9 @@ export class HeroAttrsComp extends ecs.Comp {
const skill = this.skills[skillId];
if (!skill) return 0;
if (skill.cd <= 0) return 0;
const speedBonus = this.getRuntimeAttackSpeedBonus();
// 攻速 = 驻场加成 + 计时性 buff 修饰speed 属性的 flat/pct 均按攻速百分点处理)
const timedMods = this.aggregateTimedMods(Attrs.speed);
const speedBonus = this.getRuntimeAttackSpeedBonus() + timedMods.flatSum + timedMods.pctSum;
if (speedBonus <= 0) return skill.cd;
const speedRate = 1 + speedBonus / 100;
return Math.max(HeroAttrsComp.minAttackCd, skill.cd / speedRate);

View File

@@ -142,10 +142,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
}
console.log("[SCastSystem] forceCastCardSkill: casting skill", s_uuid, "castTimes", castTimes, "targetPos", targetPos);
console.log("[SCastSystem] forceCastCardSkill: casting skill", s_uuid, "castTimes", castTimes, "targetPos", targetPos, "isFriendly", isFriendly, "buff_value", config.buff_value, "timed_buff_id", config.timed_buff_id);
for (let i = 0; i < castTimes; i++) {
if (isFriendly) {
const friendlyTargets = this.resolveFriendlyTargets(targetEids, FacSet.HERO);
console.log("[SCastSystem] friendlyTargets count =", friendlyTargets.length);
if (friendlyTargets.length === 0) continue;
this.applyFriendlySkillEffects(s_uuid, cardLv, config, null as any, mockAttrs, friendlyTargets, spawnPos);
} else {
@@ -455,12 +456,11 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
}
}
private applyActualFriendlyEffect(target: HeroViewComp, kind: SkillKind, sAp: number, _cAttrsComp: HeroAttrsComp, config: SkillConfig, sUp: any, _skillLv: number = 1) {
if (!target.ent) return;
const model = target.ent.get(HeroAttrsComp);
if (!model || model.is_dead) return;
console.log(`[SCastSystem] applyActualFriendlyEffect: skill=${config.uuid} kind=${kind} buff_type=${config.buff_type} timed_buff_id=${config.timed_buff_id} buff_value=${config.buff_value} target=${model.hero_name}`);
if (config.endAnm && config.endAnm !== "") {
target.playEnd(config.endAnm);
@@ -493,7 +493,8 @@ export class SCastSystem extends ecs.ComblockSystem implements ecs.ISystemUpdate
target.ent,
config.timed_buff_id,
_cAttrsComp.hero_uuid,
_cAttrsComp.ap
_cAttrsComp.ap,
config.buff_value !== undefined ? { value: config.buff_value } : undefined
);
return;
}