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/ui/BattleGame/BattleGameRandomActiveSkill...

232 lines
9.1 KiB

3 weeks ago
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<ITableBattlebuff>(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<ITableBattlebuff>(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();
}