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.
87 lines
2.9 KiB
87 lines
2.9 KiB
import type { BattleEvent, BattleEventListener } from './BattleEvents';
|
|
import { BATTLE_EVENT_ALL, BattleEventType, type BattleEventSubscription } from './BattleEventType';
|
|
import { createMembershipSet, membershipAdd, membershipForEach, type MembershipSet } from '../../../tools/MembershipSet';
|
|
|
|
/**
|
|
* 战斗事件总线
|
|
* - 逻辑层 emit,表现层 / UI on 订阅
|
|
* - 默认帧末 flush,减少同帧多次刷新
|
|
*/
|
|
export class BattleEventBus {
|
|
private _listeners = new Map<BattleEventSubscription, MembershipSet<BattleEventListener>>();
|
|
private _pending: BattleEvent[] = [];
|
|
private _immediate = false;
|
|
/** 当前正在派发的事件(监听重入 flush 时压栈恢复) */
|
|
private _event: BattleEvent | null = null;
|
|
|
|
/** 单个监听:抛错不影响同帧其余监听(含 UnitSpawned 出怪表现) */
|
|
private readonly _callListener = (fn: BattleEventListener): void => {
|
|
const event = this._event;
|
|
if (!event) return;
|
|
try {
|
|
fn(event);
|
|
} catch (err) {
|
|
console.error('[BattleEventBus] listener', event.type, err);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param type 事件类型或通配符
|
|
* @param listener 回调
|
|
*/
|
|
on(type: BattleEventSubscription, listener: BattleEventListener): void {
|
|
if (!this._listeners.has(type)) {
|
|
this._listeners.set(type, createMembershipSet());
|
|
}
|
|
membershipAdd(this._listeners.get(type)!, listener);
|
|
}
|
|
|
|
/**
|
|
* @param type 事件类型或通配符
|
|
* @param listener 回调
|
|
*/
|
|
off(type: BattleEventSubscription, listener: BattleEventListener): void {
|
|
this._listeners.get(type)?.delete(listener);
|
|
}
|
|
|
|
/**
|
|
* @param event 战斗事件
|
|
* @param immediate 是否立即派发(跳过帧末批处理)
|
|
*/
|
|
emit(event: BattleEvent, immediate = false): void {
|
|
if (immediate || this._immediate) {
|
|
this._dispatch(event);
|
|
} else {
|
|
this._pending.push(event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 派发当前批次。派发中新入队的事件留到下次 flush。
|
|
* 派发中再次 flush 只处理新批次,不得改外层下标。
|
|
*/
|
|
flush(): void {
|
|
const batch = this._pending;
|
|
const n = batch.length;
|
|
if (n === 0) return;
|
|
this._pending = [];
|
|
for (let i = 0; i < n; i++) {
|
|
this._dispatch(batch[i]);
|
|
}
|
|
}
|
|
|
|
/** @param enabled 是否启用立即派发模式 */
|
|
setImmediateMode(enabled: boolean): void {
|
|
this._immediate = enabled;
|
|
}
|
|
|
|
private _dispatch(event: BattleEvent): void {
|
|
const prev = this._event;
|
|
this._event = event;
|
|
const specific = this._listeners.get(event.type);
|
|
if (specific) membershipForEach(specific, this._callListener);
|
|
const all = this._listeners.get(BATTLE_EVENT_ALL);
|
|
if (all) membershipForEach(all, this._callListener);
|
|
this._event = prev;
|
|
}
|
|
}
|
|
|