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/ai/match/AiMatchService.ts

108 lines
4.8 KiB

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