actions reactored. multi-tick action support
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
import { ScriptAction, type ScriptCoroutineContext, type V3 } from "../../types";
|
||||
|
||||
|
||||
export class ApplyImpulseScriptAction extends ScriptAction {
|
||||
|
||||
private targetPosition: V3;
|
||||
private amplitude: number;
|
||||
|
||||
constructor(coroutine: ScriptCoroutineContext, targetPosition: V3, amplitude: number) {
|
||||
super(coroutine);
|
||||
this.targetPosition = targetPosition;
|
||||
this.amplitude = amplitude;
|
||||
}
|
||||
|
||||
public tick(_deltaTime: number): boolean {
|
||||
const rb = this.coroutine.thisObject.rb();
|
||||
if (!rb)
|
||||
return true;
|
||||
|
||||
const { x: px, y: py, z: pz } = rb.translation();
|
||||
const dx = this.targetPosition[0] - px;
|
||||
const dy = this.targetPosition[1] - py;
|
||||
const dz = this.targetPosition[2] - pz;
|
||||
const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
if (len > 1e-6) {
|
||||
const scale = (this.amplitude * 100) / len;
|
||||
rb.applyImpulse({ x: dx * scale, y: dy * scale, z: dz * scale }, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './waitConditionAction';
|
||||
export * from './waitFramesAction';
|
||||
export * from './waitSecondsAction';
|
||||
export * from './applyImpulseAction';
|
||||
@@ -0,0 +1,15 @@
|
||||
import { ScriptAction, type ScriptCoroutineContext } from "../../types";
|
||||
|
||||
export class WaitConditionScriptAction extends ScriptAction {
|
||||
|
||||
private condition: () => boolean;
|
||||
|
||||
constructor(coroutine: ScriptCoroutineContext, condition: () => boolean) {
|
||||
super(coroutine);
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public tick(_deltaTime: number): boolean {
|
||||
return this.condition();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ScriptAction, type ScriptCoroutineContext } from "../../types";
|
||||
|
||||
export class WaitFramesScriptAction extends ScriptAction {
|
||||
|
||||
private remaining: number;
|
||||
|
||||
constructor(coroutine: ScriptCoroutineContext, frames: number) {
|
||||
super(coroutine);
|
||||
this.remaining = frames;
|
||||
}
|
||||
|
||||
public tick(_deltaTime: number): boolean {
|
||||
this.remaining--;
|
||||
return this.remaining <= 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { ScriptAction, type ScriptCoroutineContext } from "../../types";
|
||||
|
||||
export class WaitSecondsScriptAction extends ScriptAction {
|
||||
|
||||
private remaining: number;
|
||||
|
||||
constructor(coroutine: ScriptCoroutineContext, seconds: number) {
|
||||
super(coroutine);
|
||||
this.remaining = seconds;
|
||||
}
|
||||
|
||||
public tick(deltaTime: number): boolean {
|
||||
this.remaining -= deltaTime; // TODO should only consume as much time as needed and pass the remainder to next actions
|
||||
return this.remaining <= 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import type { ScriptAction, ScriptCoroutine } from "../types";
|
||||
|
||||
export class ScriptCoroutines {
|
||||
private items: { coroutine: ScriptCoroutine, currentAction?: ScriptAction }[] = [];
|
||||
|
||||
public push(coroutines: ScriptCoroutine[]) {
|
||||
this.items.push(
|
||||
...coroutines
|
||||
.map((c) => ({ coroutine: c })),
|
||||
);
|
||||
}
|
||||
|
||||
public tick(deltaTime: number): void {
|
||||
// execute coroutines one by one and remove the ones that have finished
|
||||
this.items = this.items
|
||||
.filter((c) => !this.tickItem(c, deltaTime));
|
||||
}
|
||||
|
||||
private tickItem(item: { coroutine: ScriptCoroutine, currentAction?: ScriptAction }, deltaTime: number): boolean {
|
||||
|
||||
if (!item.currentAction) {
|
||||
try {
|
||||
const yieldResult = item.coroutine.next();
|
||||
if (yieldResult.done)
|
||||
return true; // coroutine exhausted
|
||||
item.currentAction = yieldResult.value;
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
return true; // coroutine failed to yield an action or return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const done = item.currentAction.tick(deltaTime);
|
||||
if (done)
|
||||
item.currentAction = undefined;
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
item.currentAction = undefined;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { ScriptContext, ScriptCoroutineContext, ScriptGameEventHandler } from "../types";
|
||||
import type { ScriptCoroutines } from "./coroutines";
|
||||
|
||||
export type ScriptCoroutineGameEventHandler = {
|
||||
handler: ScriptGameEventHandler;
|
||||
coroutine: ScriptCoroutineContext;
|
||||
}
|
||||
|
||||
export class ScriptGameEventBus {
|
||||
private coroutines: ScriptCoroutines;
|
||||
private handlers: Record<string, ScriptCoroutineGameEventHandler[]> = {};
|
||||
|
||||
constructor(coroutines: ScriptCoroutines) {
|
||||
this.coroutines = coroutines;
|
||||
}
|
||||
|
||||
on(event: string, handler: ScriptGameEventHandler, coroutine: ScriptCoroutineContext): void {
|
||||
console.log('subscribing to ' + event, handler, coroutine);
|
||||
let set = this.handlers[event];
|
||||
if (!set) {
|
||||
set = [];
|
||||
this.handlers[event] = set;
|
||||
}
|
||||
|
||||
if (!set.some((h) => h.handler === handler))
|
||||
set.push({ handler, coroutine });
|
||||
}
|
||||
|
||||
off(event: string, handler: ScriptGameEventHandler): void {
|
||||
console.log('unubscribing from ' + event, handler);
|
||||
const handlers = this.handlers[event];
|
||||
if (!handlers)
|
||||
return;
|
||||
|
||||
this.handlers[event] = handlers.filter((h) => h.handler !== handler);
|
||||
}
|
||||
|
||||
emit(event: string, context: Omit<ScriptContext, keyof ScriptCoroutineContext>): void {
|
||||
console.log('emitting ' + event);
|
||||
|
||||
const coroutines = this.handlers[event]
|
||||
?.map((h) => {
|
||||
console.log(`passing ${event} to ${h.handler}(${JSON.stringify(context)}, ${JSON.stringify(h.coroutine)})`);
|
||||
return h.handler({ ...context, ...h.coroutine });
|
||||
});
|
||||
|
||||
console.log(coroutines);
|
||||
|
||||
if (coroutines)
|
||||
this.coroutines.push(coroutines);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user