import type { BattleSession } from 'db://assets/mx/module/battle'; import type { EntityId } from 'db://assets/mx/module/ecs'; import { AttrId, AttributeComp } from 'db://assets/mx/module/battle/ecs/Components'; import type { Player } from '../../game/Player'; import { TableNames } from '../../game/ConfigTableData'; import { parseEntryBaseAttr } from '../skill/SkillEntryParse'; const LOG = '[GemBattle]'; /** modifier.source 前缀,刷新时整批清除 */ const SOURCE_PREFIX = 'gem_'; /** 五行镶嵌解析结果(局内用) */ export interface IGemBattleResolve { /** 提供技能的宝石 id(0=无) */ skillGemId: number; /** Skill.id(0=无) */ skillId: number; /** 各槽属性条目(含来源槽,便于 source 去重) */ attrs: IGemBattleAttr[]; } /** 单条宝石属性 */ export interface IGemBattleAttr { /** Weapon.attritubes 类型 1~5 */ type: number; /** 万分比值 */ value: number; /** 宝石 id */ gemId: number; /** 槽位 0~3 */ slot: number; } /** * 局内宝石生效:读镶嵌 → 属性 modifier → 技能宝石 id。 * 仅本机玩家;AI 返回空。 */ export class GemBattleEffect { /** * 解析某五行已镶嵌宝石。 * @param element Hero.herotype / 镶嵌 element(1~5) * @param player 部署所属玩家 */ static resolve(element: number, player: Player | null | undefined): IGemBattleResolve { const empty: IGemBattleResolve = { skillGemId: 0, skillId: 0, attrs: [] }; if (!player || !player.isLocal) return empty; const e = Math.floor(Number(element) || 0); if (e < 1 || e > 5) return empty; let slots: number[]; try { slots = gg.gem.getElementSlots(e); } catch (err) { console.warn(LOG, 'getElementSlots 失败', e, err); return empty; } const attrs: IGemBattleAttr[] = []; let skillGemId = 0; let skillId = 0; let skillQuality = -1; for (let slot = 0; slot < slots.length; slot++) { const gemId = Math.floor(Number(slots[slot]) || 0); if (gemId <= 0) continue; const row = gg.data.table.getTableData(TableNames.Weapon, gemId); if (!row) { console.warn(LOG, '宝石配表缺失', gemId, 'element', e, 'slot', slot); continue; } const parsed = parseEntryBaseAttr(String(row.attritubes ?? '')); for (let i = 0; i < parsed.length; i++) { const p = parsed[i]; if (p.type < 1 || p.type > 5) { console.warn(LOG, 'attritubes 类型非法', gemId, p.type, row.attritubes); continue; } if (!(p.value > 0)) { console.warn(LOG, 'attritubes 参数非法', gemId, row.attritubes); continue; } attrs.push({ type: p.type, value: p.value, gemId, slot }); } const sid = Math.floor(Number(row.act_skill) || 0); if (sid <= 0) continue; const q = Math.floor(Number(row.quality) || 0); if (skillId > 0) { console.warn(LOG, '同五行多颗技能宝石,取品质更高者', e, 'prev', skillGemId, 'next', gemId); } if (skillId <= 0 || q > skillQuality || (q === skillQuality && gemId > skillGemId)) { skillGemId = gemId; skillId = sid; skillQuality = q; } } return { skillGemId, skillId, attrs }; } /** * 清除实体上全部宝石属性 modifier。 * @param session 战斗会话 * @param entityId 塔实体 */ static clear(session: BattleSession, entityId: EntityId): void { const attr = session.world.getComponent(entityId, AttributeComp); if (!attr) return; attr.modifiers = attr.modifiers.filter(m => !String(m.source ?? '').startsWith(SOURCE_PREFIX)); } /** * 将解析结果写入属性(先 clear 再挂)。 * @param session 战斗会话 * @param entityId 塔实体 * @param resolved 镶嵌解析 */ static apply(session: BattleSession, entityId: EntityId, resolved: IGemBattleResolve): void { this.clear(session, entityId); if (!resolved.attrs.length) return; if (!session.world.isAlive(entityId)) return; if (!session.world.getComponent(entityId, AttributeComp)) { session.world.addComponent(entityId, new AttributeComp()); } const attrSvc = session.attributes; for (let i = 0; i < resolved.attrs.length; i++) { const a = resolved.attrs[i]; const source = `${SOURCE_PREFIX}${a.gemId}_s${a.slot}_${i}`; const wan = a.value; switch (a.type) { case 1: attrSvc.addModifier(entityId, { attr: AttrId.Attack, op: 'Mul', value: 1 + wan / 10000, source, }); break; case 2: attrSvc.addModifier(entityId, { attr: AttrId.CritRate, op: 'Add', value: wan / 10000, source, }); break; case 3: attrSvc.addModifier(entityId, { attr: AttrId.CritDamage, op: 'Add', value: wan / 10000, source, }); break; case 4: attrSvc.addModifier(entityId, { attr: 'aspd_add', op: 'Add', value: wan, source, }); break; case 5: attrSvc.addModifier(entityId, { attr: 'dmg_mul', op: 'Add', value: wan / 10000, source, }); break; default: console.warn(LOG, '未处理属性类型', a.type, a.gemId); break; } } } }