import { BattleBuffFunctionType, BattleBuffSlotType } from '../../manager/battleBuff/BattleBuffTypes'; import { BattleGameSkillDragKind, isBattleBuffDragRecycleToPeach, resolveBattleGameSkillClickKind, resolveBattleGameSkillDragKind, } from '../../manager/battleBuff/BattleBuffActiveSkillUtil'; import { parseBattleBuffRow } from '../../manager/BattleBuffParse'; import { DailyShopDataManager } from '../../manager/DailyShopDataManager'; import { DailyShopKind, type DailyShopParsed } from '../../manager/DailyShopTypes'; import { TableNames } from '../../game/ConfigTableData'; import type { ProjectBattleHandle } from '../../battle/runtime/BattleRuntime'; /** 主动 skill 槽数量 */ const SKILL_SLOT_COUNT = 2; /** 七十二变不可随机到的主动 buff id*/ const RANDOM_ACTIVE_BANNED_BUFF_IDS: readonly number[] = []; /** 战斗 rng 最小接口(七十二变 roll) */ type BattleRollRng = { int(min: number, max: number): number }; /** 随机池条目:buff id + 权重 */ type RollPoolEntry = { id: number; weight: number }; /** 单槽七十二变状态 */ type RandomActiveSlotState = { /** 携带的七十二变 buff id */ baseBuffId: number; /** 当前随机展示 buff id;0=显示本体(CD 中) */ rolledBuffId: number; }; /** * 七十二变(functiontype=19)局内宿主。 * CD 结束随机主动 buff;用完恢复本体并重新进入 CD。 */ export class BattleGameRandomActiveSkillHost { private static _inst: BattleGameRandomActiveSkillHost | null = null; private _slots: (RandomActiveSlotState | null)[] = []; /** 单例 */ static get(): BattleGameRandomActiveSkillHost { if (!BattleGameRandomActiveSkillHost._inst) { BattleGameRandomActiveSkillHost._inst = new BattleGameRandomActiveSkillHost(); } return BattleGameRandomActiveSkillHost._inst; } /** 战斗结束重置 */ reset(): void { this._slots.length = 0; } /** 是否七十二变 buff 行 */ isRandomActiveRow(row: ITableBattlebuff | null | undefined): boolean { if (!row) return false; return Number(row.type) === BattleBuffSlotType.Active && Number(row.functiontype) === BattleBuffFunctionType.RandomActive; } /** 槽位是否为七十二变携带 */ isRandomActiveSlot(slotIndex: number): boolean { return this._slots[slotIndex] != null; } /** 当前是否展示随机出的主动 buff(可施放) */ isShowingRolledBuff(slotIndex: number): boolean { const state = this._slots[slotIndex]; return state != null && state.rolledBuffId > 0; } /** 绑定槽位携带 buff(refresh 时) */ syncSlot(slotIndex: number, baseBuffId: number, row: ITableBattlebuff | null | undefined): void { if (slotIndex < 0 || slotIndex >= SKILL_SLOT_COUNT) return; if (!this.isRandomActiveRow(row)) { this._slots[slotIndex] = null; return; } const id = Math.floor(Number(baseBuffId) || 0); if (id <= 0) { this._slots[slotIndex] = null; return; } const prev = this._slots[slotIndex]; const rolledBuffId = prev != null && prev.baseBuffId === id ? prev.rolledBuffId : 0; this._slots[slotIndex] = { baseBuffId: id, rolledBuffId }; } /** 解析槽位当前应走的 skill 拖拽类型(roll 后按变化 buff) */ resolveCastDragKind(slotIndex: number, baseParsed: DailyShopParsed | null): BattleGameSkillDragKind | null { const cast = this.resolveDisplayParsed(slotIndex, baseParsed); if (!cast) return null; return resolveBattleGameSkillDragKind(cast.row as ITableBattlebuff); } /** * 解析槽位当前应展示的 buff 解析结果。 * @param slotIndex skill 槽 * @param baseParsed 携带 buff 解析 */ resolveDisplayParsed(slotIndex: number, baseParsed: DailyShopParsed | null): DailyShopParsed | null { if (!baseParsed) return null; const state = this._slots[slotIndex]; if (!state) return baseParsed; if (state.rolledBuffId > 0) { const rolled = DailyShopDataManager.getParsed(state.rolledBuffId) ?? BattleGameRandomActiveSkillHost._parseRollBuffParsed(state.rolledBuffId); if (!rolled) { console.log('[BattleGameRandomActive] 随机 buff 缺失', state.rolledBuffId); return baseParsed; } return rolled; } return baseParsed; } /** * CD 结束:随机一个可局内施放的主动 buff。 * @returns 是否成功 roll */ tryRollOnCooldownReady(slotIndex: number, battle: ProjectBattleHandle | null): boolean { const state = this._slots[slotIndex]; if (!state || state.rolledBuffId > 0) return false; const excludeIds = BattleGameRandomActiveSkillHost._collectCarriedActiveBuffIds(); const picked = BattleGameRandomActiveSkillHost.pickRollBuffIdFromPool( excludeIds, battle?.session?.rng ?? null, ); if (picked <= 0) { console.log('[BattleGameRandomActive] roll 失败', slotIndex, excludeIds); return false; } state.rolledBuffId = picked; return true; } /** * 从可随机池按 weight 权重选取 buff id;GM 固定 id 优先且须在池内。 * @param excludeIds 排除 id * @param rng 战斗 rng;GM 固定时可省略 */ static pickRollBuffIdFromPool(excludeIds: readonly number[], rng: BattleRollRng | null): number { const pool = BattleGameRandomActiveSkillHost._buildRollPool(excludeIds); const fixId = Math.floor(Number(gg.game.gmRandomActiveFixBuffId) || 0); if (fixId > 0) { for (let i = 0; i < pool.length; i++) { if (pool[i].id === fixId) return fixId; } console.log('[BattleGameRandomActive] GM 固定 roll 不在可用池', fixId, pool); return 0; } if (pool.length <= 0) return 0; if (!rng) { console.log('[BattleGameRandomActive] 无 battle rng'); return 0; } let total = 0; for (let i = 0; i < pool.length; i++) total += pool[i].weight; if (total <= 0) return 0; let roll = rng.int(0, total - 1); for (let i = 0; i < pool.length; i++) { roll -= pool[i].weight; if (roll < 0) return pool[i].id; } return pool[pool.length - 1].id; } /** 随机 buff 施放成功:恢复本体展示(由 View 开 CD) */ onRolledBuffConsumed(slotIndex: number): void { const state = this._slots[slotIndex]; if (!state) return; state.rolledBuffId = 0; } /** 收集全部主动槽携带 buff id(不可随机到自身或其它槽) */ private static _collectCarriedActiveBuffIds(): number[] { const slotIds = DailyShopDataManager.getActiveSlotIds(); const out: number[] = []; for (let i = 0; i < SKILL_SLOT_COUNT; i++) { const id = Math.floor(Number(slotIds[i]) || 0); if (id > 0) out.push(id); } return out; } /** 可随机主动 buff 池(type=1、局内可施放、weight>0;排除已携带 id) */ private static _buildRollPool(excludeIds: readonly number[]): RollPoolEntry[] { const list = gg.data.table.getTableList(TableNames.Battlebuff) ?? []; const exclude = excludeIds.slice(); const out: RollPoolEntry[] = []; for (let i = 0; i < list.length; i++) { const row = list[i]; const id = Math.floor(Number(row.id) || 0); if (id <= 0) continue; if (exclude.indexOf(id) >= 0) continue; if (RANDOM_ACTIVE_BANNED_BUFF_IDS.indexOf(id) >= 0) continue; if (!BattleGameRandomActiveSkillHost._isRollCandidate(row)) continue; const weight = Math.max(0, Math.floor(Number(row.weight) || 0)); if (weight <= 0) continue; out.push({ id, weight }); } return out; } /** 从配表解析 roll 目标 buff(不依赖是否已携带) */ private static _parseRollBuffParsed(buffId: number): DailyShopParsed | null { const id = Math.floor(Number(buffId) || 0); if (id <= 0) return null; const row = gg.data.table.getTableData(TableNames.Battlebuff, id); if (!row) return null; return parseBattleBuffRow(row); } /** 是否可进入随机池 */ private static _isRollCandidate(row: ITableBattlebuff): boolean { const parsed = parseBattleBuffRow(row); if (!parsed || parsed.kind !== DailyShopKind.Active) return false; if (Number(row.functiontype) === BattleBuffFunctionType.RandomActive) return false; if (resolveBattleGameSkillDragKind(row) != null) return true; if (resolveBattleGameSkillClickKind(row) != null) return true; return isBattleBuffDragRecycleToPeach(row); } } /** 获取七十二变宿主 */ export function getBattleGameRandomActiveSkillHost(): BattleGameRandomActiveSkillHost { return BattleGameRandomActiveSkillHost.get(); }