import { BattleEventType, type BattleEvent } from 'db://assets/mx/module/battle'; import MTools from 'db://assets/mx/tools/MTools'; import { ChapterNewPlayTypeEnum, IBloodMoonProperty } from '../../../game/ChapterPlayTypes'; import { BattleAudio } from '../../audio/BattleAudio'; import type { IBattleMechanism, MechanismContext } from '../IBattleMechanism'; import { applyBloodMoonAccelerate, applyBloodMoonAccelerateToAll, clearBloodMoonAccelerateFromAll, pickRandomDigGrid, pickRandomSpawnGrid, playBloodMoonVisual, } from '../MechanismServices'; /** * 黄风怪 / 血月加速机制(Playmode 15) * * 时间语义: * - starttime:首次尝试出现 * - 演出结束后才上加速 * - duration:加速持续(从施法完成起算) * - cd:加速结束后到下次出现的间隔 */ export class BloodMoonMechanism implements IBattleMechanism { readonly typeId = ChapterNewPlayTypeEnum.BloodMoon; private _ctx: MechanismContext | null = null; private _config: IBloodMoonProperty | null = null; private _checkTimer = 0; private _active = false; private _visualPlaying = false; private _castApplied = false; private _nextOpenTime = 0; private _cycleCloseTime = 0; private _intervalTime = 0; private _spawnListener: ((e: BattleEvent) => void) | null = null; init(ctx: MechanismContext, config: unknown): void { this._ctx = ctx; const src = config as IBloodMoonProperty; this._config = { ...src, durationRange: src.durationRange?.slice() ?? [5, 10], openTimeArr: src.openTimeArr?.slice() ?? [], closeTimeArr: src.closeTimeArr?.slice() ?? [], }; this._intervalTime = Math.max(0, Number(src.intervalTime) || 0); this._nextOpenTime = Math.max(0, Number(src.startTime) || 0); this._checkTimer = 0; this._active = false; this._visualPlaying = false; this._castApplied = false; this._cycleCloseTime = 0; this._spawnListener = (e) => { if (e.type !== BattleEventType.UnitSpawned) return; if (!this._active || !this._ctx || !this._config) return; applyBloodMoonAccelerate( this._ctx, e.entity, this._config.accelerateSpeed, this._remainingSec(), ); }; ctx.session.events.on(BattleEventType.UnitSpawned, this._spawnListener); } tick(dt: number): void { if (!this._ctx || !this._config) return; this._checkTimer += dt; if (this._checkTimer < 0.2) return; this._checkTimer = 0; this._checkSchedule(); } destroy(): void { if (this._ctx && this._spawnListener) { this._ctx.session.events.off(BattleEventType.UnitSpawned, this._spawnListener); } if (this._ctx && this._active) { clearBloodMoonAccelerateFromAll(this._ctx); } this._active = false; this._visualPlaying = false; this._spawnListener = null; this._ctx = null; this._config = null; } private _checkSchedule(): void { const ctx = this._ctx; const cfg = this._config; if (!ctx || !cfg) return; if (this._cycleCloseTime > 0 && ctx.battleTime >= this._cycleCloseTime) { this._triggerClose(); } if ( !this._visualPlaying && !this._active && ctx.battleTime >= this._nextOpenTime ) { this._tryTriggerOpen(); } } private _tryTriggerOpen(): void { const ctx = this._ctx; const cfg = this._config; if (!ctx || !cfg) return; const grid = pickRandomDigGrid(ctx) ?? pickRandomSpawnGrid(ctx); if (!grid) { console.log('[BloodMoonMechanism] 无可用落点,稍后重试 battleTime=', ctx.battleTime); return; } BattleAudio.playBloodMoonCast(); this._visualPlaying = true; this._castApplied = false; const started = playBloodMoonVisual(ctx, grid, () => { this._applyCastEffect(); }); if (!started) { this._visualPlaying = false; console.log('[BloodMoonMechanism] 黄风怪表现创建失败'); } } private _applyCastEffect(): void { if (this._castApplied) return; this._castApplied = true; this._visualPlaying = false; if (!this._ctx || !this._config) return; const dur = this._rollDuration(this._config); this._active = true; this._cycleCloseTime = this._ctx.battleTime + dur; this._nextOpenTime = this._cycleCloseTime + this._intervalTime; applyBloodMoonAccelerateToAll(this._ctx, this._config.accelerateSpeed, dur); BattleAudio.playWindBlow(); } private _rollDuration(cfg: IBloodMoonProperty): number { const lo = Math.max(0.05, Number(cfg.durationRange?.[0]) || 1); const hi = Math.max(lo, Number(cfg.durationRange?.[1]) || lo); return Math.max(0.05, MTools.getRandomValue(lo, hi)); } private _triggerClose(): void { const ctx = this._ctx; if (!ctx) return; this._active = false; this._visualPlaying = false; this._castApplied = false; this._cycleCloseTime = 0; clearBloodMoonAccelerateFromAll(ctx); } private _remainingSec(): number { const ctx = this._ctx; if (!ctx || this._cycleCloseTime <= 0) return 0; return Math.max(0.05, this._cycleCloseTime - ctx.battleTime); } }