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.
108 lines
4.8 KiB
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;
|
|
}
|
|
}
|
|
|