Diablo2D — Shadows of Tristram: complete browser ARPG

- Isometric canvas renderer (depth-sorted, FOV/fog, additive lighting)
- 3 classes x 20 skills, 4 acts x 4 floors + boss lairs, torment I-X
- Diablo-style loot: rarities, affix tiers, 14 legendaries, vendor, stash
- Rogue camp with 6 NPCs: Charsi/Akara/Kashya/Cain/Gheed/storage
- NPC quest chain (accept -> hunt -> turn in) with rewards & gating
- Procedural WebAudio SFX + generative music, EN/VI localization
- Saves, settings, waypoints, hardcore mode, PWA manifest
- 93-assertion headless suite + browser E2E via CDP
This commit is contained in:
2026-08-23 06:59:36 +00:00
commit fc1fa2d51e
42 changed files with 11784 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
/* CDP runner: attach to running chrome (real time, no virtual time),
poll the drive page status until DONE/ERROR, then report.
usage: node tools/cdp_run.mjs [port] [timeoutSec] */
const port = process.argv[2] || '9333';
const timeoutMs = (parseInt(process.argv[3] || '120', 10)) * 1000;
const targets = await (await fetch(`http://127.0.0.1:${port}/json`)).json();
const page = targets.find(t => t.type === 'page');
if (!page) { console.log('no page'); process.exit(1); }
const ws = new WebSocket(page.webSocketDebuggerUrl);
let id = 0;
const pending = new Map();
function send(method, params = {}) {
return new Promise((res, rej) => {
const mid = ++id;
pending.set(mid, { res, rej });
ws.send(JSON.stringify({ id: mid, method, params }));
});
}
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
if (msg.id && pending.has(msg.id)) {
const { res, rej } = pending.get(msg.id);
pending.delete(msg.id);
msg.error ? rej(new Error(msg.error.message)) : res(msg.result);
}
};
await new Promise(r => { ws.onopen = r; });
async function evalJs(expr) {
const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true });
return r.result ? r.result.value : undefined;
}
const t0 = Date.now();
let last = '';
while (Date.now() - t0 < timeoutMs) {
await new Promise(r => setTimeout(r, 2000));
const s = await evalJs(`(document.getElementById('status')||{}).textContent || ''`);
if (s !== last) { console.log('----\n' + s); last = s; }
if (/DONE|DRIVE_ERROR/.test(s)) break;
}
console.log('\n===== FINAL =====');
console.log(await evalJs(`(document.getElementById('status')||{}).textContent || ''`));
ws.close();