import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { test } from "node:test"; import { makeEngine, until } from "./helpers/renpy-engine.mjs"; const original = process.env.PGAMES_MIST_RUNTIME; const runtime = original ? JSON.parse(readFileSync(original, "utf8")) : null; const click = (element) => element.onclick({ stopPropagation() {}, preventDefault() {} }); const enter = (engine, label) => engine.restart(() => engine.guardedJump(label)); test("MIST's actual raise-hand loop accepts screen input and resumes after a save", { skip: !runtime }, async () => { const { engine, root } = makeEngine(runtime); try { engine.newStore(); engine.store.keysqte = false; engine.store.last_pressed = ""; engine.store.player_name = "Player"; enter(engine, "raisehand_label"); await until(() => engine.screens.interactionReturn && root.querySelector(".pg-simgbtn")); for (let i = 0; i < 5; i++) { click(root.querySelector(".pg-simgbtn")); await until(() => engine.screens.interactionReturn); } const position = engine.store.object_pos; assert.ok(position < 600 && position > 50); const save = structuredClone(engine.snapshotState()); engine.restart(() => engine.applySave(save)); await until(() => engine.screens.interactionReturn); assert.equal(engine.store.object_pos, position); for (let i = 0; i < 30 && engine.store.object_pos > 50; i++) { click(root.querySelector(".pg-simgbtn")); await until(() => engine.screens.interactionReturn || engine.store.object_pos === 50); } assert.equal(engine.store.object_pos, 50); } finally { engine.destroy(); } }); test("MIST's actual QTE timer reaches the loss branch", { skip: !runtime }, async () => { const { engine } = makeEngine(runtime); try { engine.newStore(); Object.assign(engine.store, { timer_on: true, keysqte: false, object_pos: 301, lose_pos: 300, finish_pos: 1600, last_pressed: "" }); engine.screens.show("game_scr"); enter(engine, "game_loop"); await until(() => engine.store.timer_on === false); assert.ok(engine.store.object_pos <= 300); assert.equal(engine.cur.label, "qte_lose"); } finally { engine.destroy(); } }); test("MIST's actual QTE accepts alternating keys and reaches the win branch", { skip: !runtime }, async () => { const { engine, root } = makeEngine(runtime); try { engine.newStore(); Object.assign(engine.store, { timer_on: true, keysqte: false, object_pos: 1590, lose_pos: 300, finish_pos: 1600, last_pressed: "q" }); engine.screens.show("game_scr"); enter(engine, "game_loop"); await until(() => engine.screens.interactionReturn && root.querySelectorAll(".pg-simgbtn").length === 2); click(root.querySelectorAll(".pg-simgbtn")[1]); await until(() => engine.store.timer_on === false); assert.ok(engine.store.object_pos >= 1600); assert.equal(engine.cur.label, "qte_win"); } finally { engine.destroy(); } });