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.
 
 
 
 

167 lines
5.5 KiB

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);
}
}