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/debug/GlobalSkillDebugLog.ts

200 lines
7.3 KiB

3 weeks ago
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('──────── 选技能前后对比 ────────');
1 week ago
_log('本次选择:', picked.map((s) => `${s.id}:${s.name}`).join(' | '));
3 weeks ago
_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 */
}