You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AdLike2048/assets/script/battle/combat/GemBattleEffect.ts

161 lines
6.0 KiB

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<ITableWeapon>(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;
}
}
}
}