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.
199 lines
7.4 KiB
199 lines
7.4 KiB
import type { BattleSession } from 'db://assets/mx/module/battle';
|
|
import { AttrId, TeamComp, BattleTeamId } from 'db://assets/mx/module/battle';
|
|
import type { Player } from '../../game/Player';
|
|
import { getTowerLoadoutService } from '../combat/TowerLoadoutService';
|
|
import {
|
|
isGlobalSkillEntry,
|
|
sumSelectedBaseAttrWan,
|
|
wanToWeightMul,
|
|
} from '../skill/GlobalSkillEffects';
|
|
import { TableNames } from '../../game/ConfigTableData';
|
|
|
|
const TAG = '[GlobalSkillDebug]';
|
|
const STYLE = 'color:#67C23A;font-weight:bold';
|
|
|
|
/** 默认关闭;需要验数时 setGlobalSkillDebugEnabled(true) */
|
|
let _enabled = false;
|
|
|
|
export function setGlobalSkillDebugEnabled(enabled: boolean): void {
|
|
_enabled = enabled;
|
|
console.log(`%c${TAG} ${enabled ? '已开启' : '已关闭'}`, STYLE);
|
|
}
|
|
|
|
export function isGlobalSkillDebugEnabled(): boolean {
|
|
return _enabled;
|
|
}
|
|
|
|
function _log(message: string, ...args: unknown[]): void {
|
|
if (!_enabled) return;
|
|
let extra = '';
|
|
for (let i = 0; i < args.length; i++) {
|
|
const a = args[i];
|
|
if (a !== null && typeof a === 'object') continue;
|
|
extra += ` ${String(a)}`;
|
|
}
|
|
console.log(`%c${TAG} ${message}${extra}`, STYLE);
|
|
}
|
|
|
|
export interface GlobalSkillSnapshot {
|
|
skillIds: number[];
|
|
skillBrief: string[];
|
|
/** base_attr 各类型万分比合计 */
|
|
attrWan: Record<string, number>;
|
|
/** 已部署塔战斗参数 */
|
|
towers: Array<{
|
|
entityId: number;
|
|
weapon1Id: number;
|
|
attack: number;
|
|
critRate: number;
|
|
critDamage: number;
|
|
baseCd: number;
|
|
finalCd: number;
|
|
}>;
|
|
/** type8 减速后移速乘数 */
|
|
monsterSlowMul: number;
|
|
/** type9 紫权重乘数 */
|
|
purpleWeightMul: number;
|
|
/** type10 英雄刷新权重乘数 */
|
|
heroWeightMul: number;
|
|
/** type11 格子刷新权重乘数 */
|
|
gridWeightMul: number;
|
|
}
|
|
|
|
const ATTR_LABELS: Record<number, string> = {
|
|
1: '全局伤害',
|
|
3: '全局暴击',
|
|
4: '全局暴伤',
|
|
5: '全局冷却',
|
|
8: '怪物减速',
|
|
9: '技能概率(紫)',
|
|
10: '英雄概率',
|
|
11: '格子概率',
|
|
};
|
|
|
|
function _attrWanMap(skills: readonly ITableSkill[]): Record<string, number> {
|
|
const out: Record<string, number> = {};
|
|
for (const type of [1, 3, 4, 5, 8, 9, 10, 11]) {
|
|
const wan = sumSelectedBaseAttrWan(skills, type);
|
|
if (wan > 0) {
|
|
out[`${type}:${ATTR_LABELS[type] ?? type}`] = wan;
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function _towerRows(session: BattleSession, playerId: number): GlobalSkillSnapshot['towers'] {
|
|
const world = session.world;
|
|
const attrs = session.attributes;
|
|
const loadoutSvc = getTowerLoadoutService();
|
|
const rows: GlobalSkillSnapshot['towers'] = [];
|
|
|
|
world.forEachAlive((id) => {
|
|
const team = world.getComponent(id, TeamComp);
|
|
if (!team || team.ownerId !== playerId || BattleTeamId.isEnemyTeam(team.team)) return;
|
|
const loadout = loadoutSvc.getLoadout(id);
|
|
if (!loadout) return;
|
|
const w1 = gg.data.table.getTableData<ITableHero>(TableNames.Hero, loadout.weapon1Id);
|
|
const baseCd = 0;
|
|
rows.push({
|
|
entityId: id as number,
|
|
weapon1Id: loadout.weapon1Id,
|
|
attack: attrs.getAttr(id, AttrId.Attack),
|
|
critRate: attrs.getAttr(id, AttrId.CritRate),
|
|
critDamage: attrs.getAttr(id, AttrId.CritDamage),
|
|
baseCd,
|
|
finalCd: baseCd > 0 ? attrs.getFinalCooldown(id, baseCd) : 0,
|
|
});
|
|
});
|
|
return rows;
|
|
}
|
|
|
|
/** 采集当前已选词条 + 塔属性 + 全局倍率快照 */
|
|
export function captureGlobalSkillSnapshot(
|
|
session: BattleSession,
|
|
player: Player,
|
|
): GlobalSkillSnapshot {
|
|
const skills = player.selectedSkills;
|
|
const slowWan = sumSelectedBaseAttrWan(skills, 8);
|
|
return {
|
|
skillIds: skills.map((s) => s.id),
|
|
skillBrief: skills.map((s) => `${s.id}:${s.name}${isGlobalSkillEntry(s) ? '(全局)' : ''}`),
|
|
attrWan: _attrWanMap(skills),
|
|
towers: _towerRows(session, player.playerId),
|
|
monsterSlowMul: Math.max(0, 1 - slowWan / 10000),
|
|
purpleWeightMul: wanToWeightMul(sumSelectedBaseAttrWan(skills, 9)),
|
|
heroWeightMul: wanToWeightMul(sumSelectedBaseAttrWan(skills, 10)),
|
|
gridWeightMul: wanToWeightMul(sumSelectedBaseAttrWan(skills, 11)),
|
|
};
|
|
}
|
|
|
|
function _fmtTower(t: GlobalSkillSnapshot['towers'][0]): string {
|
|
return `塔#${t.entityId} W1=${t.weapon1Id} atk=${t.attack.toFixed(1)} cri=${(t.critRate * 100).toFixed(1)}% criD=${(t.critDamage * 100).toFixed(1)}% cd=${t.baseCd.toFixed(2)}→${t.finalCd.toFixed(2)}`;
|
|
}
|
|
|
|
function _diffTowers(
|
|
before: GlobalSkillSnapshot['towers'],
|
|
after: GlobalSkillSnapshot['towers'],
|
|
): void {
|
|
const beforeMap = new Map(before.map((t) => [t.entityId, t]));
|
|
for (const a of after) {
|
|
const b = beforeMap.get(a.entityId);
|
|
if (!b) {
|
|
_log(` [新塔] ${_fmtTower(a)}`);
|
|
continue;
|
|
}
|
|
const changed =
|
|
Math.abs(a.attack - b.attack) > 0.01
|
|
|| Math.abs(a.critRate - b.critRate) > 1e-6
|
|
|| Math.abs(a.critDamage - b.critDamage) > 1e-6
|
|
|| Math.abs(a.finalCd - b.finalCd) > 1e-4;
|
|
if (!changed) continue;
|
|
_log(
|
|
` 塔#${a.entityId} W1=${a.weapon1Id}`,
|
|
`\n atk ${b.attack.toFixed(1)} → ${a.attack.toFixed(1)}`,
|
|
`\n cri ${(b.critRate * 100).toFixed(1)}% → ${(a.critRate * 100).toFixed(1)}%`,
|
|
`\n criD ${(b.critDamage * 100).toFixed(1)}% → ${(a.critDamage * 100).toFixed(1)}%`,
|
|
`\n cd ${b.finalCd.toFixed(2)} → ${a.finalCd.toFixed(2)} (base ${a.baseCd.toFixed(2)})`,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 打印选技能前后对比。
|
|
* 开关:setGlobalSkillDebugEnabled(true)
|
|
*/
|
|
export function logGlobalSkillPickCompare(
|
|
before: GlobalSkillSnapshot,
|
|
after: GlobalSkillSnapshot,
|
|
picked: readonly ITableSkill[],
|
|
): void {
|
|
if (!_enabled) return;
|
|
_log('──────── 选技能前后对比 ────────');
|
|
_log('本次选择:', picked.map((s) => `${s.id}:${s.name} base_attr=${s.base_attr || '-'}`).join(' | '));
|
|
_log('已选列表 前:', before.skillBrief.join(', ') || '(空)');
|
|
_log('已选列表 后:', after.skillBrief.join(', ') || '(空)');
|
|
_log(`base_attr万分比 前: ${JSON.stringify(before.attrWan)}`);
|
|
_log(`base_attr万分比 后: ${JSON.stringify(after.attrWan)}`);
|
|
_log(
|
|
'全局倍率',
|
|
`\n 怪物移速 ${before.monsterSlowMul.toFixed(4)} → ${after.monsterSlowMul.toFixed(4)}`,
|
|
`\n 紫权重× ${before.purpleWeightMul.toFixed(4)} → ${after.purpleWeightMul.toFixed(4)}`,
|
|
`\n 英雄权重× ${before.heroWeightMul.toFixed(4)} → ${after.heroWeightMul.toFixed(4)}`,
|
|
`\n 格子权重× ${before.gridWeightMul.toFixed(4)} → ${after.gridWeightMul.toFixed(4)}`,
|
|
);
|
|
if (before.towers.length === 0 && after.towers.length === 0) {
|
|
_log('场上无已部署塔(全局属性将在部署后物化)');
|
|
} else {
|
|
_log('塔属性变化:');
|
|
_diffTowers(before.towers, after.towers);
|
|
}
|
|
_log('────────────────────────────');
|
|
}
|
|
|
|
// 控制台:setGlobalSkillDebugEnabled(true) / false
|
|
try {
|
|
(globalThis as { setGlobalSkillDebugEnabled?: typeof setGlobalSkillDebugEnabled })
|
|
.setGlobalSkillDebugEnabled = setGlobalSkillDebugEnabled;
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
|