import type { AiMatchBuildInput } from '../types/AiTypes'; import { aiOp } from '../runtime/AiLog'; import type { AiMatchProfile } from './AiMatchProfile'; import { AiBattlebuffPicker } from './AiBattlebuffPicker'; import { RankDataManager } from '../../../manager/RankDataManager'; import { DailyShopDataManager } from '../../../manager/DailyShopDataManager'; import { MatchStreakStats } from '../../../manager/MatchStreakStats'; /** * 开战前匹配:段位星级 + 对手 buff。 * Const 12~20(偏移/连胜败/最大槽)已删:对手 buff 数量与玩家相同(偏移恒 0),槽位用商店代码常量。 */ export class AiMatchService { private readonly _buffPicker = new AiBattlebuffPicker(); /** * 构建本局 AI 对手档案。 * @param input 玩家 buff 数量等入参 * @returns 成功返回档案;配表/权重非法时返回 null */ buildProfile(input: AiMatchBuildInput): AiMatchProfile | null { const playerStars = RankDataManager.getPlayerRankStars(); const playerRankId = RankDataManager.getPlayerRankId(); let starMatch = RankDataManager.resolveOpponentStarMatch(playerStars); if (!starMatch) { console.warn( '[AiMatchService] star match failed,回退玩家段位', `playerStars=${playerStars} playerRankId=${playerRankId}`, ); starMatch = { starOffset: 0, opponentStars: Math.max(1, playerStars), opponentRankId: playerRankId > 0 ? playerRankId : 1, }; } const caps = DailyShopDataManager.getSlotCaps(); const playerActive = Math.max(0, Math.floor(Number(input.playerActiveBuffCount) || 0)); const playerPassive = Math.max(0, Math.floor(Number(input.playerPassiveBuffCount) || 0)); const activeBuffCount = Math.max(0, Math.min(caps.active, playerActive)); const passiveBuffCount = Math.max(0, Math.min(caps.passive, playerPassive)); aiOp( `AI buff 数量=玩家(无偏移)` + ` A=${playerActive}→clamp[0,${caps.active}]=${activeBuffCount}` + ` | P=${playerPassive}→clamp[0,${caps.passive}]=${passiveBuffCount}` + ` | streakW=${MatchStreakStats.getWinStreak()} streakL=${MatchStreakStats.getLoseStreak()}`, ); const seed = input.seed != null ? Math.floor(Number(input.seed) || 0) : ((Date.now() % 100000) ^ (starMatch.opponentStars * 9973)); let activeBuffIds = this._buffPicker.pick(1, activeBuffCount, seed); let passiveBuffIds = this._buffPicker.pick(2, passiveBuffCount, seed ^ 0x9e3779b9); let resolvedActiveCount = activeBuffIds.length; let resolvedPassiveCount = passiveBuffIds.length; if (resolvedActiveCount !== activeBuffCount || resolvedPassiveCount !== passiveBuffCount) { console.warn( '[AiMatchService] buff 抽取不足,按实际数量建档', `active=${activeBuffCount}/${resolvedActiveCount}`, `passive=${passiveBuffCount}/${resolvedPassiveCount}`, ); } aiOp( `AI buff 抽取 seed=${seed}` + ` active[${resolvedActiveCount}]=[${activeBuffIds.join(',')}]` + ` passive[${resolvedPassiveCount}]=[${passiveBuffIds.join(',')}]`, ); const gmPassive = gg.game.gmAiPassiveBuffIds; // Phase1:新表无 AI 主动 buff,强制空 activeBuffIds = []; resolvedActiveCount = 0; if (gmPassive?.length) { passiveBuffIds = gmPassive.slice(); resolvedPassiveCount = passiveBuffIds.length; aiOp(`GM 常驻覆盖 AI 被动 buff=[${passiveBuffIds.join(',')}]`); } const profile: AiMatchProfile = { playerRankId, playerStars, opponentRankId: starMatch.opponentRankId, opponentStars: starMatch.opponentStars, starOffset: starMatch.starOffset, activeBuffCount: 0, passiveBuffCount: resolvedPassiveCount, activeBuffIds: [], passiveBuffIds, seed, }; aiOp( `匹配档案 playerRank=${playerRankId} stars=${playerStars}` + ` -> aiRank=${profile.opponentRankId} stars=${profile.opponentStars}` + ` buffA=${resolvedActiveCount}[${activeBuffIds.join(',')}]` + ` buffP=${resolvedPassiveCount}[${passiveBuffIds.join(',')}]` + ` streakW=${MatchStreakStats.getWinStreak()} streakL=${MatchStreakStats.getLoseStreak()}` + ` caps=${caps.active}/${caps.passive}`, ); return profile; } /** buff 抽取器 */ get buffPicker(): AiBattlebuffPicker { return this._buffPicker; } }