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.
 
 
 
 

145 lines
5.3 KiB

import { GEvent } from 'db://assets/mx/module/event/GEvent';
import { createMembershipSet, membershipAdd, membershipFromArray, membershipDedupeArray, type MembershipSet, type ReadonlyMembershipSet } from '../../../../mx/tools/MembershipSet';
import MTools from 'db://assets/mx/tools/MTools';
import { ChapterNewPlayTypeEnum, IParatroopsProperty } from '../../../game/ChapterPlayTypes';
import { BossWarnType } from '../../../ui/BattleGame/BossWarnSpine';
import { cloneParatroopsConfig } from '../MechanismConfigClone';
import type { IBattleMechanism, MechanismContext } from '../IBattleMechanism';
import { calcMiddleRowRange } from '../MechanismSpawnArea';
import { isCombatPhase, spawnParatroopAtGrid } from '../MechanismServices';
import type { MapGrid } from '../../map/legacy/MapGrid';
/**
* 飞禽空投(Playmode 14 / 19)
*
* 配表语义(Playmode.par1):
* - `monsterId,numMin,numMax` 可重复多组
* - 每次空投:每组各自随机数量并全部生成(不是只随机一种怪)
*/
export class ParatroopsMechanism implements IBattleMechanism {
readonly typeId: ChapterNewPlayTypeEnum;
private _ctx: MechanismContext | null = null;
private _config: IParatroopsProperty | null = null;
private _eventIndex = 0;
private _checkTimer = 0;
constructor(typeId: ChapterNewPlayTypeEnum = ChapterNewPlayTypeEnum.Paratroops) {
this.typeId = typeId;
}
init(ctx: MechanismContext, config: unknown): void {
this._ctx = ctx;
this._config = cloneParatroopsConfig(config as IParatroopsProperty);
this._eventIndex = 0;
this._checkTimer = 0;
const cfg = this._config;
}
tick(dt: number): void {
if (!this._ctx || !this._config || !isCombatPhase(this._ctx)) return;
this._checkTimer += dt;
if (this._checkTimer < 1) return;
this._checkTimer = 0;
this._tryLaunch();
}
destroy(): void {
this._ctx = null;
this._config = null;
}
private _tryLaunch(): void {
const ctx = this._ctx;
const cfg = this._config;
if (!ctx || !cfg) return;
while (this._eventIndex < cfg.timeArr.length && ctx.battleTime >= cfg.timeArr[this._eventIndex]) {
if (!this._launchOnce(this._eventIndex)) {
// 落点暂不可用:保留 eventIndex,下一秒重试
break;
}
this._eventIndex++;
}
}
private _launchOnce(eventIndex: number): boolean {
const ctx = this._ctx;
const cfg = this._config;
if (!ctx || !cfg) return false;
const options = cfg.monsterOptions ?? [];
if (options.length === 0) {
console.error('[ParatroopsMechanism] monsterOptions 为空,跳过 event=', eventIndex);
return true;
}
const map = ctx.runtime.Map;
if (!map) return false;
// 每组怪物各自 roll 数量(策划:两个 id + 两个数量区间都要生效)
const batches: { monsterId: number; count: number }[] = [];
let total = 0;
for (const opt of options) {
const id = Number(opt.monsterId) || 0;
if (id <= 0) continue;
const lo = Math.max(0, Number(opt.numMin) || 0);
const hi = Math.max(lo, Number(opt.numMax) || lo);
const count = MTools.getRandomValue(lo, hi);
if (count <= 0) continue;
batches.push({ monsterId: id, count });
total += count;
}
if (total <= 0) {
console.log('[ParatroopsMechanism] 本波 roll 数量为 0,跳过 event=', eventIndex);
return true;
}
const rowRange = calcMiddleRowRange(map.mapRows);
// 飞禽空投:仍用全图随机可行走落点,不参与「怪物最多区域」规则
let grids = map.getParatroopLandingGrids(total, ctx.session, rowRange);
if (grids.length < total) {
const used = membershipFromArray(grids.map(g => `${g.Row},${g.Col}`));
const extra = map.getParatroopLandingGrids(total - grids.length, ctx.session);
for (const g of extra) {
const key = `${g.Row},${g.Col}`;
if (used.has(key)) continue;
membershipAdd(used, key);
grids.push(g);
if (grids.length >= total) break;
}
}
if (grids.length === 0) {
console.log(
'[ParatroopsMechanism] 无落点 event=',
eventIndex,
'battleTime=',
ctx.battleTime,
);
return false;
}
GEvent.Ins.emit(BossWarnType.Paratroops);
let spawned = 0;
let gridCursor = 0;
const pickGrid = (): MapGrid => {
if (gridCursor < grids.length) return grids[gridCursor++];
return grids[Math.floor(Math.random() * grids.length)];
};
for (const batch of batches) {
for (let i = 0; i < batch.count; i++) {
if (spawnParatroopAtGrid(ctx, pickGrid(), batch.monsterId) != null) {
spawned++;
}
}
}
if (spawned <= 0) {
console.log('[ParatroopsMechanism] 全部生成失败 event=', eventIndex);
return false;
}
return true;
}
}