// act-gameplay.js — acts.html 上「玩法确实存在」的断言(M6 / issue #21) // // 这是 map-save-load.js 退役后,唯一还需要真浏览器的那部分:证明真地图场景里 // 怪物被生成进了世界、能被打死、掉落能被捡起、K/L 存读档走的是引擎的 tick 路径。 // 纯逻辑部分已经在 tests/engine-save-load.test.ts 与 tests/iso-terrain.test.ts 覆盖。 // // CHROME=/usr/bin/google-chrome // node scripts/browser/inspect-page.mjs "$CHROME" \ // "http://127.0.0.1:5173/acts.html?act=1" scripts/browser/checks/act-gameplay.js (async () => { const sleep = (ms) => new Promise((r) => setTimeout(r, ms)) const press = (code, down) => window.dispatchEvent(new KeyboardEvent(down ? 'keydown' : 'keyup', { code, bubbles: true })) const tap = async (code, ms) => { press(code, true); await sleep(ms); press(code, false); await sleep(60) } const deadline = Date.now() + 120000 while (Date.now() < deadline) { const s = window.__d2webAct if (s && (s.ready || s.error)) break await sleep(200) } const s = window.__d2webAct if (s === undefined) return { error: 'no scene state' } if (s.error) return { error: s.error } await sleep(2000) const spawned = { monstersSpawned: s.monstersSpawned ?? null, monstersAlive: s.monstersAlive ?? null, npcs: s.npcs ?? null, walkable: s.walkable, } // 追着打:四个方向各走一段并连续挥砍,直到有击杀或超时。 const dirs = ['ArrowRight', 'ArrowDown', 'ArrowLeft', 'ArrowUp'] const fightUntil = Date.now() + 40000 let round = 0 while (Date.now() < fightUntil && (window.__d2webAct?.kills ?? 0) === 0) { const dir = dirs[round % dirs.length] press(dir, true) for (let i = 0; i < 6; i += 1) await tap('Space', 180) press(dir, false) await sleep(120) round += 1 } await sleep(400) for (let i = 0; i < 8; i += 1) await tap('KeyE', 120) await sleep(300) const fought = { kills: window.__d2webAct?.kills ?? null, playerHits: window.__d2webAct?.playerHits ?? null, dropsRolled: window.__d2webAct?.dropsRolled ?? null, groundItems: window.__d2webAct?.groundItems ?? null, pickups: window.__d2webAct?.pickups ?? null, gold: window.__d2webAct?.gold ?? null, bagItems: window.__d2webAct?.bagItems ?? null, playerXp: window.__d2webAct?.playerXp ?? null, playerHp: window.__d2webAct?.playerHp ?? null, } await tap('KeyK', 200) await sleep(400) const savedAt = { x: Math.round(window.__d2webAct?.x ?? 0), y: Math.round(window.__d2webAct?.y ?? 0), kills: window.__d2webAct?.kills ?? null } press('ArrowRight', true); await sleep(2000); press('ArrowRight', false) await sleep(400) const drifted = { x: Math.round(window.__d2webAct?.x ?? 0), y: Math.round(window.__d2webAct?.y ?? 0) } await tap('KeyL', 200) await sleep(600) const after = { x: Math.round(window.__d2webAct?.x ?? 0), y: Math.round(window.__d2webAct?.y ?? 0), kills: window.__d2webAct?.kills ?? null } const gl = document.querySelector('#view')?.getContext('webgl2') return { source: s.source, level: s.level, spawned, fought, savedAt, drifted, after, restored: after.x === savedAt.x && after.y === savedAt.y, saves: window.__d2webAct?.saves ?? null, loads: window.__d2webAct?.loads ?? null, saveError: window.__d2webAct?.saveError ?? null, tick: window.__d2webAct?.tick ?? null, glError: gl ? gl.getError() : null, } })()