(engine: VisualNovelEngine, statement: unknown, shouldAdvance = true)
| 340 | * if it couldn't be run correctly. |
| 341 | */ |
| 342 | export async function run (engine: VisualNovelEngine, statement: unknown, shouldAdvance = true): Promise<{ advance: boolean }> { |
| 343 | // Capture current position at the start to detect if another advance happened |
| 344 | // during async operations (e.g., user click during Notification's willApply) |
| 345 | const initialStep = engine.state ('step') as number; |
| 346 | const initialLabel = engine.state ('label') as string; |
| 347 | |
| 348 | const actions = engine.actions (); |
| 349 | const before: Promise<void>[] = actions.map (action => action.beforeRun ({ advance: shouldAdvance })); |
| 350 | |
| 351 | await Promise.all (before); |
| 352 | |
| 353 | // Don't allow null as a valid statement |
| 354 | if (statement === null) { |
| 355 | engine.debug.trace (); |
| 356 | engine.debug.groupEnd (); |
| 357 | throw new Error ('Statement was null.'); |
| 358 | } |
| 359 | |
| 360 | engine.debug.debug ('Preparing Action', statement); |
| 361 | |
| 362 | if (typeof statement === 'function') { |
| 363 | engine.debug.groupCollapsed (`Run Cycle [JS Function]`); |
| 364 | |
| 365 | // Block the game while the function is being run |
| 366 | engine.global ('block', true); |
| 367 | |
| 368 | // Run the function asynchronously and after it has run, unblock |
| 369 | // the game so it can continue. |
| 370 | try { |
| 371 | const returnValue = await Util.callAsync (statement as (...args: unknown[]) => unknown, engine); |
| 372 | |
| 373 | engine.global ('block', false); |
| 374 | |
| 375 | engine.debug.groupEnd (); |
| 376 | |
| 377 | if (shouldAdvance && returnValue !== false) { |
| 378 | // Only advance if we're still on the same step - another action might have |
| 379 | // already advanced the game (e.g., user clicked during async function) |
| 380 | const currentStep = engine.state ('step') as number; |
| 381 | const currentLabel = engine.state ('label') as string; |
| 382 | if (currentStep === initialStep && currentLabel === initialLabel) { |
| 383 | // TODO: Do we need to return here? We don't do it in the other run methods. |
| 384 | return engine.next ().then (() => ({ advance: true })); |
| 385 | } else { |
| 386 | engine.debug.debug ('Skipping auto-advance: game already advanced by another action'); |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | return Promise.resolve ({ advance: false }); |
| 391 | } catch (e: unknown) { |
| 392 | const error: FancyErrorProps = { |
| 393 | 'Label': String(engine.state ('label')), |
| 394 | 'Step': Number(engine.state ('step')), |
| 395 | 'Help': { |
| 396 | '_': 'Check the code for your function, there may be additional information in the console.', |
| 397 | } |
| 398 | }; |
| 399 |
no test coverage detected