actions reactored. multi-tick action support
This commit is contained in:
@@ -1,39 +1,18 @@
|
||||
import type { Game, RuntimeGameScene, RuntimeScene, World } from "../types";
|
||||
import type { GameEventContext, GameEventHandler, GeneratorEventHandler, InternalGameEventBus } from "../types/runtime/gameBus";
|
||||
import { toJS } from "mobx";
|
||||
import type { Game, RuntimeGameObjectInstance, RuntimeGameScene, RuntimeScene, ScriptAction, ScriptCoroutineContext, World } from "../types";
|
||||
import { clone } from "../utils";
|
||||
import { populateRuntimeScene } from "../utils/runtime";
|
||||
import { ObjectApi } from "../utils/runtime/objectApi";
|
||||
import type { GameScheduler, YieldCommand } from "./gameScheduler";
|
||||
import { ScriptGameEventBus } from "../scripts/eventBus";
|
||||
import * as ScriptActions from "../scripts/actions";
|
||||
import type { ScriptCoroutines } from "../scripts/coroutines";
|
||||
|
||||
export class GameEventBus {
|
||||
private handlers = new Map<string, GameEventHandler[]>();
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// type ScriptActionHelperFunction<T extends abstract new (...args: any[]) => ScriptAction> =
|
||||
// (...args: ConstructorParameters<T>) => InstanceType<T>;
|
||||
|
||||
on(event: string, handler: GameEventHandler): void {
|
||||
console.log('subscribing to ' + event, handler);
|
||||
let set = this.handlers.get(event);
|
||||
if (!set) {
|
||||
set = [];
|
||||
this.handlers.set(event, set);
|
||||
}
|
||||
if (!set.includes(handler))
|
||||
set.push(handler);
|
||||
}
|
||||
|
||||
off(event: string, handler: GameEventHandler): void {
|
||||
console.log('unubscribing from ' + event, handler);
|
||||
const handlers = this.handlers.get(event);
|
||||
if (!handlers)
|
||||
return;
|
||||
|
||||
this.handlers.set(event, handlers.filter((h) => h !== handler));
|
||||
}
|
||||
|
||||
emit(event: string, context: GameEventContext): void {
|
||||
console.log('emitting ' + event);
|
||||
this.handlers.get(event)
|
||||
?.forEach((handler) => handler(context));
|
||||
}
|
||||
}
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// const scriptActionHelperFunction = <T extends new (...args: any[]) => ScriptAction>(Cls: T): ScriptActionHelperFunction<T> =>
|
||||
// (...args) => new Cls(...args) as InstanceType<T>;
|
||||
|
||||
export class GameFactory {
|
||||
|
||||
@@ -66,47 +45,60 @@ export class GameFactory {
|
||||
public static evalGameScripts(
|
||||
world: World,
|
||||
scene: RuntimeGameScene,
|
||||
eventBus: GameEventBus,
|
||||
scheduler: GameScheduler,
|
||||
): void {
|
||||
coroutines: ScriptCoroutines,
|
||||
): ScriptGameEventBus {
|
||||
|
||||
const handlerWrappers = new WeakMap<GeneratorEventHandler, GameEventHandler>();
|
||||
const eventBus = new ScriptGameEventBus(coroutines);
|
||||
|
||||
const wait = (seconds: number): YieldCommand => ({ type: 'waitSeconds', seconds });
|
||||
const waitFrames = (frames: number): YieldCommand => ({ type: 'waitFrames', frames });
|
||||
const waitUntil = (condition: () => boolean): YieldCommand => ({ type: 'waitUntil', condition });
|
||||
|
||||
const internalBus: InternalGameEventBus = {
|
||||
onGameEvent(event, genFn) {
|
||||
const wrapper: GameEventHandler = (ctx) => scheduler.startCoroutine(genFn(ctx) as Generator<YieldCommand, void, void>);
|
||||
handlerWrappers.set(genFn, wrapper);
|
||||
eventBus.on(event, wrapper);
|
||||
},
|
||||
offGameEvent(event, genFn) {
|
||||
const wrapper = handlerWrappers.get(genFn);
|
||||
if (wrapper) {
|
||||
eventBus.off(event, wrapper);
|
||||
handlerWrappers.delete(genFn);
|
||||
}
|
||||
},
|
||||
const scriptHelpers = {
|
||||
// convert all ScriptActions imported to a structure factory(coroutine):
|
||||
// {
|
||||
// WaitSeconds: (seconds) => new WaitSecondsScriptAction(coroutine, seconds),
|
||||
// ...
|
||||
// }
|
||||
actions: (context: ScriptCoroutineContext) => Object.fromEntries(
|
||||
Object.entries(ScriptActions)
|
||||
.map(([name, Cls]) => [
|
||||
name.replace(/ScriptAction$/, ''),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(...args: any[]) => new (Cls as any)(context, ...args),
|
||||
])
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
) as Record<string, (...args: any[]) => ScriptAction>,
|
||||
};
|
||||
|
||||
try {
|
||||
Object.values(world.objectTypes)
|
||||
.filter((ot) => ot.javascript)
|
||||
.forEach((ot) => {
|
||||
const gameScript = eval(`(function gameScript({onGameEvent, offGameEvent, wait, waitFrames, waitUntil}, {api, object, objectType}) {${ot.javascript}})`);
|
||||
Object.values(world.objectTypes)
|
||||
.filter((ot) => ot.javascript)
|
||||
.forEach((ot) => {
|
||||
try {
|
||||
const javascript = `(function gameScript(eventBus, helpers, thisObject, thisObjectType) {${ot.javascript}})`;
|
||||
const gameScript = eval(javascript);
|
||||
|
||||
Object.values(scene.objects)
|
||||
.filter((o) => o.typeId == ot.id)
|
||||
.map((liveObj) => {
|
||||
const snapshot = toJS(liveObj) as RuntimeGameObjectInstance;
|
||||
snapshot.rb = () => liveObj.rb();
|
||||
return snapshot;
|
||||
})
|
||||
.forEach((o) => {
|
||||
const api = new ObjectApi(o, ot, world, scene);
|
||||
gameScript({ ...internalBus, wait, waitFrames, waitUntil }, { api, object: o, objectType: ot });
|
||||
try {
|
||||
const localScriptHelpers = {
|
||||
...scriptHelpers,
|
||||
actions: scriptHelpers.actions({ thisObject: o, thisObjectType: ot }),
|
||||
};
|
||||
gameScript(eventBus, localScriptHelpers, o, ot);
|
||||
}
|
||||
catch (err) {
|
||||
console.log('Error running object instance script:\n' + err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
console.log('Error running game script:\n' + err);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.log('Error evaluating object type script:\n' + err);
|
||||
}
|
||||
});
|
||||
|
||||
return eventBus;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user