import type { World } from 'db://assets/mx/module/ecs'; import type { EntityId } from 'db://assets/mx/module/ecs'; import type { ISystem } from 'db://assets/mx/module/ecs'; import { AttrId, AttributeComp, CombatComp, type AttributeModifier, } from '../ecs/Components'; /** 从 CombatComp 读取与 AttrId 对齐的属性 */ function readCombatAttr(combat: CombatComp, attr: string): number | undefined { switch (attr) { case AttrId.Attack: return combat.attack; case AttrId.AttackSpeed: return combat.attackSpeed; case AttrId.Range: return combat.range; case AttrId.CritRate: return combat.critRate; case AttrId.CritDamage: return combat.critDamage; default: return undefined; } } /** 属性系统:base + modifier */ export class AttributeSystem implements ISystem { readonly priority = 10; enabled = true; constructor(private readonly _world: World) {} /** * @param entityId 实体 ID * @param attr 属性名 * @returns 最终属性值(含修改器) */ getAttr(entityId: EntityId, attr: string): number { const world = this._world; if (!world.isAlive(entityId)) return 0; const attrComp = world.getComponent(entityId, AttributeComp); const combat = world.getComponent(entityId, CombatComp); let base = 0; if (attrComp?.base[attr] != null) { base = attrComp.base[attr]; } else if (combat) { const fromCombat = readCombatAttr(combat, attr); if (fromCombat != null) base = fromCombat; } const mods = attrComp?.modifiers.filter(m => m.attr === attr) ?? []; let add = 0; let mul = 1; let post = 0; for (const m of mods) { if (m.op === 'Add') add += m.value; else if (m.op === 'Mul') mul *= m.value; else if (m.op === 'PostMulAdd') post += m.value; else if (m.op === 'Override') return m.value; } return (base + add) * mul + post; } /** 将 CombatComp 同步到 AttributeComp.base */ syncCombatToAttributes(entityId: EntityId): void { const world = this._world; const combat = world.getComponent(entityId, CombatComp); if (!combat) return; let attr = world.getComponent(entityId, AttributeComp); if (!attr) { attr = new AttributeComp(); world.addComponent(entityId, attr); } attr.base[AttrId.Attack] = combat.attack; attr.base[AttrId.AttackSpeed] = combat.attackSpeed; attr.base[AttrId.Range] = combat.range; attr.base[AttrId.CritRate] = combat.critRate; attr.base[AttrId.CritDamage] = combat.critDamage; } /** * @param entityId 实体 ID * @param mod 属性修改器 */ addModifier(entityId: EntityId, mod: AttributeModifier): void { const world = this._world; if (!world.isAlive(entityId)) return; let attr = world.getComponent(entityId, AttributeComp); if (!attr) { attr = new AttributeComp(); world.addComponent(entityId, attr); } // 同 attr + 同 group 的临时正面 modifier 只保留 1 个:移除旧的 if (mod.group) { attr.modifiers = attr.modifiers.filter(m => !(m.attr === mod.attr && m.group === mod.group)); } attr.modifiers.push(mod); } /** * 按 group 移除属性 modifier(次数型 buff 耗尽后清理加成用) * @param entityId 实体 ID * @param group modifier group 标识 */ removeModifiersByGroup(entityId: EntityId, group: string): void { const attr = this._world.getComponent(entityId, AttributeComp); if (!attr) return; attr.modifiers = attr.modifiers.filter(m => m.group !== group); } /** @param dt 帧间隔(秒),递减限时修改器 */ tick(dt: number): void { this._world.view(AttributeComp).forEach((_id, attr) => { const mods = attr.modifiers; let write = 0; for (let i = 0; i < mods.length; i++) { const m = mods[i]; if (m.remain == null) { mods[write++] = m; continue; } m.remain -= dt; if (m.remain > 0) mods[write++] = m; } mods.length = write; }); } /** * 取最终攻速:base × (1 + sum(aspd_add/10000)),受 combat.maxAttackSpeed 上限。 * 支持 Override(最快攻速 buff 强制值,仍受 aspd_min 上限)。 * aspd_add modifier 的 value 存原始万分比值(如 2000=20%)。 */ getFinalAttackSpeed(entityId: EntityId): number { const combat = this._world.getComponent(entityId, CombatComp); const attr = this._world.getComponent(entityId, AttributeComp); // Override(最快攻速):取最后一个 Override 值 let override: number | null = null; if (attr) { for (const m of attr.modifiers) { if (m.attr === AttrId.AttackSpeed && m.op === 'Override') override = m.value; } } const cap = combat?.maxAttackSpeed ?? 0; if (override != null) { // 最快攻速:强制值仍受 aspd_min 上限 return Math.max(0.01, cap > 0 ? Math.min(override, cap) : override); } let base = combat?.attackSpeed ?? attr?.base[AttrId.AttackSpeed] ?? 1; if (attr) { let addRatio = 0; for (const m of attr.modifiers) { if (m.attr === 'aspd_add' && m.op === 'Add') addRatio += m.value / 10000; } base *= (1 + addRatio); } if (cap > 0) base = Math.min(base, cap); return Math.max(0.01, base); } /** * 取最终 CD:baseCd / (1 + sum(cd_add/10000))。 * cd_add modifier 的 value 存原始万分比值(如 3000=30% 减CD)。 */ getFinalCooldown(entityId: EntityId, baseCd: number): number { const attr = this._world.getComponent(entityId, AttributeComp); let addRatio = 0; if (attr) { for (const m of attr.modifiers) { if (m.attr === 'cd_add' && m.op === 'Add') addRatio += m.value / 10000; } } return addRatio > 0 ? baseCd / (1 + addRatio) : baseCd; } }