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