|
|
|
|
|
import { TableNames } from '../../../game/ConfigTableData';
|
|
|
|
|
|
import { aiLog } from '../runtime/AiLog';
|
|
|
|
|
|
import { BattleBuffSlotType } from '../../../manager/battleBuff/BattleBuffTypes';
|
|
|
|
|
|
|
|
|
|
|
|
/** 局内 AI 可抽 functiontype 下限(人参果 type=3/ft=1 等局外即时排除) */
|
|
|
|
|
|
const AI_BUFF_FT_MIN = 2;
|
|
|
|
|
|
/** 局内 AI 可抽 functiontype 上限 */
|
|
|
|
|
|
const AI_BUFF_FT_MAX = 27;
|
|
|
|
|
|
|
|
|
|
|
|
/** Battlebuff 加权不重复抽取(需求 4.4) */
|
|
|
|
|
|
export class AiBattlebuffPicker {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 按 type 与 weight 加权抽取不重复 buff id。
|
|
|
|
|
|
* 仅 `type=1|2` 且 `functiontype∈[2,27]`;**排除 type=3**(人参果等局外即时)。
|
|
|
|
|
|
* @param buffType 1=主动,2=被动
|
|
|
|
|
|
* @param count 目标个数
|
|
|
|
|
|
* @param seed 随机种子
|
|
|
|
|
|
*/
|
|
|
|
|
|
pick(buffType: 1 | 2, count: number, seed: number): number[] {
|
|
|
|
|
|
const need = Math.max(0, Math.floor(Number(count) || 0));
|
|
|
|
|
|
if (need <= 0) return [];
|
|
|
|
|
|
|
|
|
|
|
|
const list = gg.data.table.getTableList<ITableBattlebuff>(TableNames.Battlebuff) ?? [];
|
|
|
|
|
|
const ids: number[] = [];
|
|
|
|
|
|
const weights: number[] = [];
|
|
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
|
|
const row = list[i];
|
|
|
|
|
|
const slotType = Math.floor(Number(row.type) || 0);
|
|
|
|
|
|
if (slotType === BattleBuffSlotType.Auto) continue;
|
|
|
|
|
|
if (slotType !== buffType) continue;
|
|
|
|
|
|
const ft = Math.floor(Number(row.functiontype) || 0);
|
|
|
|
|
|
if (ft < AI_BUFF_FT_MIN || ft > AI_BUFF_FT_MAX) continue;
|
|
|
|
|
|
const w = Math.max(0, Math.floor(Number(row.weight) || 0));
|
|
|
|
|
|
if (w <= 0) continue;
|
|
|
|
|
|
const id = Math.floor(Number(row.id) || 0);
|
|
|
|
|
|
if (id <= 0) continue;
|
|
|
|
|
|
ids.push(id);
|
|
|
|
|
|
weights.push(w);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ids.length < need) {
|
|
|
|
|
|
aiLog(`[AiBattlebuffPicker] candidate shortage buffType=${buffType} need=${need} have=${ids.length}`);
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const picked: number[] = [];
|
|
|
|
|
|
let rng = (Math.floor(Number(seed) || 0) >>> 0) || 1;
|
|
|
|
|
|
for (let n = 0; n < need; n++) {
|
|
|
|
|
|
let total = 0;
|
|
|
|
|
|
for (let i = 0; i < weights.length; i++) total += weights[i];
|
|
|
|
|
|
if (total <= 0) {
|
|
|
|
|
|
aiLog(`[AiBattlebuffPicker] remaining weight 0 buffType=${buffType} picked=${picked.join(',')}`);
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
rng = (rng * 1664525 + 1013904223) >>> 0;
|
|
|
|
|
|
let roll = (rng / 4294967296) * total;
|
|
|
|
|
|
let choose = -1;
|
|
|
|
|
|
for (let i = 0; i < weights.length; i++) {
|
|
|
|
|
|
roll -= weights[i];
|
|
|
|
|
|
if (roll < 0) {
|
|
|
|
|
|
choose = i;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (choose < 0) choose = weights.length - 1;
|
|
|
|
|
|
picked.push(ids[choose]);
|
|
|
|
|
|
ids.splice(choose, 1);
|
|
|
|
|
|
weights.splice(choose, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
return picked;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|