// 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,
}
})()