64 lines
1.8 KiB
TypeScript
64 lines
1.8 KiB
TypeScript
import { Delegate, UpdateController } from "@24tools/playable_template";
|
||
|
||
export type ScheduledCall = {
|
||
remaining: number;
|
||
callback: () => void;
|
||
cancelled: boolean;
|
||
};
|
||
|
||
/**
|
||
* Відкладені виклики, керовані ігровим тікером (delta з `UpdateController`),
|
||
* а не `window.setTimeout`. Перевага: затримки можна скасувати на teardown і
|
||
* вони не спрацюють після завершення playable.
|
||
*
|
||
* Прим.: коли дійде черга до уніфікації часу (план, п.2) — масштаб `TimeC.TimeScale`
|
||
* додається саме тут, в одному місці.
|
||
*/
|
||
export class TickScheduler {
|
||
private static updateDelegate: Delegate<number> | null = null;
|
||
private static calls = new Set<ScheduledCall>();
|
||
|
||
/** `delaySeconds` — затримка в секундах ігрового часу. */
|
||
static schedule(delaySeconds: number, callback: () => void): ScheduledCall {
|
||
this.ensureUpdate();
|
||
|
||
const call: ScheduledCall = {
|
||
remaining: delaySeconds,
|
||
callback,
|
||
cancelled: false,
|
||
};
|
||
this.calls.add(call);
|
||
return call;
|
||
}
|
||
|
||
static cancel(call: ScheduledCall | null | undefined) {
|
||
if (!call) return;
|
||
call.cancelled = true;
|
||
this.calls.delete(call);
|
||
}
|
||
|
||
private static ensureUpdate() {
|
||
if (this.updateDelegate) return;
|
||
this.updateDelegate = UpdateController.Instance.onUpdate.addDelegate((delta) =>
|
||
this.update(delta),
|
||
);
|
||
}
|
||
|
||
private static update(delta: number) {
|
||
if (this.calls.size === 0) return;
|
||
|
||
for (const call of [...this.calls]) {
|
||
if (call.cancelled) {
|
||
this.calls.delete(call);
|
||
continue;
|
||
}
|
||
|
||
call.remaining -= delta;
|
||
if (call.remaining <= 0) {
|
||
this.calls.delete(call);
|
||
call.callback();
|
||
}
|
||
}
|
||
}
|
||
}
|