import { TableNames } from '../../game/ConfigTableData'; import type { UnitDefId } from 'db://assets/mx/module/battle'; import { logChapterMonsterHpCalc } from '../debug/MonsterHpDebugLog'; import type { ProjectLevelMonsterConfig } from './LevelMonsterConfig'; /** * 最终 HP 额外倍率:max(1, 局外紫色及以上武器最高等级 / 5) */ export function calcSpecialReplicaFinalHpScale(): number { const purpleMaxLv = Math.max(1, gg.data.project.getMaxLevelPurpleWeapon()); return Math.max(1, purpleMaxLv / 5); } /** 按关卡配表解析 hp_multiple */ export function resolveHpMultiple(chapterHpMultiple: number): number { return chapterHpMultiple > 0 ? chapterHpMultiple : 1; } /** 读取 Monster1 系数:monster_group 定列,波次序号 waveIndex 定行 */ export function getMonster2Factor(monsterGroup: number, difficultyRow: number): number { const row = gg.data.table.getTableData(TableNames.Monster1, difficultyRow); if (!row) { console.log('[MonsterHpCalculator] Monster2 row missing', difficultyRow); return 1; } const key = `diffcult${monsterGroup}` as keyof ITableMonster1; const value = row[key]; return typeof value === 'number' && value > 0 ? value : 1; } /** 旧逻辑:按战斗时间查 Monster2 行(time <= row.id 的首行) */ export function getMonster2FactorByBattleTime(monsterGroup: number, battleTime: number): number { const list = gg.data.table.getTableList(TableNames.Monster1) ?? []; for (const row of list) { if (battleTime <= row.id) { const key = `diffcult${monsterGroup}` as keyof ITableMonster1; const value = row[key]; return typeof value === 'number' && value > 0 ? value : 1; } } return 1; } /** * 战力修正系数: * - 推荐/玩家 >= 1:max(1, ratio ^ power_para) * - 推荐/玩家 < 1:max(0.5, ratio ^ power_para2) */ export function calcPowerHpCoeff( recommendPower: number, userPower: number, powerPara: number, powerPara2: number = 0, ): number { const user = Math.max(1, userPower); const recommend = Math.max(0, recommendPower); const ratio = recommend / user; if (ratio >= 1) { if (powerPara <= 0) return 1; return Math.max(1, Math.pow(ratio, powerPara)); } if (powerPara2 <= 0) return 1; return Math.max(0.5, Math.pow(ratio, powerPara2)); } /** 段位对应 weapon_para(Level 表暂无该字段时返回 1) */ export function getWeaponParaFromMaxChapter(rankId: number): number { if (!(rankId > 0)) { console.log('[MonsterHpCalculator] rankId 无效', rankId); return 1; } const levelRow = gg.data.table.getTableData(TableNames.Level, rankId); const v = (levelRow as { weapon_para?: number } | null)?.weapon_para; return typeof v === 'number' && v > 0 ? v : 1; } export function calcPowerHpCoeffForLevel( _levelId: number, powerPara: number, powerPara2: number = 0, ): number { const power = gg.data.getCurPower(); return calcPowerHpCoeff(power, power, powerPara, powerPara2); } import { finitePositiveHp } from '../../../mx/tools/FiniteHp'; /** * 策划 / 旧项目血量公式: * floor(monster_hp × hp_multiple × port_hp × basehp × 战力修正 × weapon_para) */ export function calcChapterMonsterHp( monsterId: number, port: number, difficultyRow: number, config: ProjectLevelMonsterConfig, ): number { const monster = gg.data.table.getTableData(TableNames.Monster, monsterId); const monsterHp = finitePositiveHp( (monster as ITableMonster & { HP?: number })?.HP ?? monster?.hp, 1, ); const portHp = finitePositiveHp(config.portHpMult[port - 1], 1); const baseHp = getMonster2Factor(config.monsterGroup, difficultyRow); const powerCoeff = calcPowerHpCoeffForLevel( config.levelId, config.powerPara, config.powerPara2, ); const hpMultiple = finitePositiveHp(config.hpMultiple, 1); const weaponPara = finitePositiveHp(config.weaponPara, 1); const raw = monsterHp * hpMultiple * portHp * baseHp * powerCoeff * weaponPara; if (!Number.isFinite(raw)) { console.error(`[MonsterHpCalculator] invalid hp raw monsterId=${monsterId} port=${port} difficultyRow=${difficultyRow} monsterHp=${monsterHp} hpMultiple=${hpMultiple} portHp=${portHp} baseHp=${baseHp} powerCoeff=${powerCoeff} weaponPara=${weaponPara}`); } let finalHp = finitePositiveHp(Math.floor(raw), 1); const scale = finitePositiveHp(config.specialReplicaHpScale, 1); if (scale !== 1) { finalHp = finitePositiveHp(Math.floor(finalHp * scale), finalHp); } logChapterMonsterHpCalc({ monsterId, port, difficultyRow, config, monsterHp, portHp, baseHp, powerCoeff, hpMultiple, weaponPara, raw, finalHp, }); return finalHp; } /** 旧 MonsterDataManager 兼容:返回 basehp × 战力修正(不含 monster_hp / hp_multiple / weapon_para) */ export function calcMonsterHpBaseByBattleTime( monsterGroup: number, battleTime: number, recommendPower: number, powerPara: number, powerPara2: number = 0, ): number { const m2 = getMonster2FactorByBattleTime(monsterGroup, battleTime); const powerCoeff = calcPowerHpCoeff(recommendPower, gg.data.getCurPower(), powerPara, powerPara2); return m2 * powerCoeff; } export function makeMonsterUnitDefId(monsterId: number, port: number, difficultyRow: number): UnitDefId { return `monster_${monsterId}_p${port}_r${difficultyRow}`; } export function parseMonsterUnitDefId(unitDefId: UnitDefId): { monsterId: number; port: number; difficultyRow: number; } | null { const m = /^monster_(\d+)_p(\d+)_r(\d+)$/.exec(unitDefId); if (!m) return null; return { monsterId: parseInt(m[1], 10), port: parseInt(m[2], 10), difficultyRow: parseInt(m[3], 10), }; } export function parseLegacyMonsterId(unitDefId: UnitDefId): number { const m = /^monster_(\d+)$/.exec(unitDefId); return m ? parseInt(m[1], 10) : 0; }