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/runtime/AiPassiveCdHost.ts

160 lines
5.0 KiB

3 weeks ago
import type { DailyShopParsed } from '../../../manager/DailyShopTypes';
import { BattleBuffCarryStore } from '../../../manager/battleBuff/BattleBuffCarryStore';
import {
isBattleBuffPassiveGoldMine,
3 weeks ago
passiveBuffUsesBattleGameCd,
readBattleBuffPassiveCdSec,
} from '../../../manager/battleBuff/BattleBuffPassiveSkillUtil';
import { BattleBuffFunctionType } from '../../../manager/battleBuff/BattleBuffTypes';
import type { ProjectBattleHandle } from '../../runtime/BattleRuntime';
import { aiOp, aiWarn } from './AiLog';
import { AiHeadlessBagHost } from './AiHeadlessBagHost';
/** 单槽 CD 被动状态 */
type AiPassiveCdSlot = {
/** 携带解析结果 */
parsed: DailyShopParsed;
/** CD 总秒 */
cdTotalSec: number;
/** CD 剩余秒 */
cdLeftSec: number;
/** 本轮 CD 结束是否已尝试触发 */
triggerConsumed: boolean;
};
/** AI CD 被动 UI 快照 */
export type AiPassiveCdUiSlot = {
/** buff id */
buffId: number;
/** CD 剩余秒 */
cdLeftSec: number;
/** CD 总秒 */
cdTotalSec: number;
};
/**
* AI 局内 CD 被动:金矿(14) CD 满 +1 金币。
3 weeks ago
*/
export class AiPassiveCdHost {
private _battle: ProjectBattleHandle | null = null;
private readonly _slots: AiPassiveCdSlot[] = [];
/**
* 绑定并按 CarryStore 重建 CD 槽。
* @param _bagHost 无头背包(金矿不入 bag)
3 weeks ago
* @param battle 战斗
* @param _onBoardChanged 盘面变更(金矿不用)
3 weeks ago
*/
bind(_bagHost: AiHeadlessBagHost, battle: ProjectBattleHandle, _onBoardChanged: () => void): void {
3 weeks ago
this.dispose();
this._battle = battle;
this._rebuildSlots();
}
/** 解除绑定 */
dispose(): void {
this._slots.length = 0;
this._battle = null;
}
/**
* 帧更新 CD。
* @param dtSec 帧间隔
*/
tick(dtSec: number): void {
if (!this._battle || this._slots.length <= 0) return;
3 weeks ago
const dt = Math.max(0, Number(dtSec) || 0);
for (let i = 0; i < this._slots.length; i++) {
this._tickSlot(this._slots[i], dt);
3 weeks ago
}
}
/**
* CD 被动 UI 快照。
* @returns 槽列表
*/
snapshotUiSlots(): AiPassiveCdUiSlot[] {
const out: AiPassiveCdUiSlot[] = [];
for (let i = 0; i < this._slots.length; i++) {
const slot = this._slots[i];
out.push({
buffId: slot.parsed.id,
cdLeftSec: slot.cdLeftSec,
cdTotalSec: slot.cdTotalSec,
});
}
return out;
}
/** 从 CarryStore 重建有 CD 的被动槽 */
private _rebuildSlots(): void {
this._slots.length = 0;
const passives = BattleBuffCarryStore.getPassives(AiHeadlessBagHost.AI_PLAYER_ID);
for (let i = 0; i < passives.length; i++) {
const parsed = passives[i];
const row = parsed.row as ITableBattlebuff;
const ft = Math.floor(Number(row.functiontype) || 0);
if (!passiveBuffUsesBattleGameCd(ft)) continue;
if (!isBattleBuffPassiveGoldMine(row)) {
3 weeks ago
aiWarn(`AI CD 被动未接线 functiontype=${ft} id=${parsed.id} name=${parsed.name}`);
continue;
}
const cdSec = Math.max(0, Math.floor(Number(parsed.cd) || 0) || readBattleBuffPassiveCdSec(row));
this._slots.push({
parsed,
cdTotalSec: cdSec,
cdLeftSec: cdSec,
triggerConsumed: false,
});
aiOp(`AI CD被动槽#${this._slots.length - 1} id=${parsed.id} ${parsed.name} ft=${ft} cd=${cdSec}s`);
}
}
/**
* 单槽倒计时与触发。
* @param slot 槽
* @param dt 帧间隔
*/
private _tickSlot(slot: AiPassiveCdSlot, dt: number): void {
3 weeks ago
if (slot.cdLeftSec > 0) {
slot.cdLeftSec = Math.max(0, slot.cdLeftSec - dt);
return;
}
if (slot.triggerConsumed) return;
slot.triggerConsumed = true;
this._onGoldMineReady(slot);
3 weeks ago
}
/**
* 金矿 CD 结束:AI +1 金币并重开 CD。
3 weeks ago
* @param slot 槽
*/
private _onGoldMineReady(slot: AiPassiveCdSlot): void {
const battle = this._battle;
if (!battle) {
aiWarn('AI 金矿失败:battle 未就绪');
this._restartCd(slot);
return;
}
3 weeks ago
const row = slot.parsed.row as ITableBattlebuff;
const ft = Math.floor(Number(row.functiontype) || 0);
if (ft !== BattleBuffFunctionType.GoldMine) {
aiWarn(`AI 金矿 ft 异常 id=${slot.parsed.id} ft=${ft}`);
this._restartCd(slot);
return;
3 weeks ago
}
battle.addPlayerGold(AiHeadlessBagHost.AI_PLAYER_ID, 1);
aiOp(`AI 金矿 +1 coin player=${AiHeadlessBagHost.AI_PLAYER_ID}`);
3 weeks ago
this._restartCd(slot);
}
/**
* 重启 CD。
* @param slot 槽
*/
private _restartCd(slot: AiPassiveCdSlot): void {
slot.cdLeftSec = slot.cdTotalSec;
slot.triggerConsumed = false;
}
}