- Seeded procedural runs (survivors, locations, weather, events) - 100-day campaign with 5 phases, scripted story beats, Day-100 finale - 8 endings incl. hidden 'First Light Again' - Data-driven events (people/world/story packs) with callback chains - Relationships, memories, grief, karma/hope systems - Tactical encounters (fight/hide/run/negotiate) with honest odds - 8 upgradeable camp buildings, 13 location templates - Canvas parallax scenes, procedural SVG portraits, weather FX - Synthesized adaptive audio (WebAudio, no assets) - Save slots + autosave (localStorage), shareable seeds - Tests: headless multi-seed simulator + browser E2E (playwright)
73 lines
3.2 KiB
JavaScript
73 lines
3.2 KiB
JavaScript
import { chromium } from 'playwright-core';
|
|
// build a Day-100-ready save with node + tsx first
|
|
const { execSync } = await import('node:child_process');
|
|
const stateJson = execSync(`npx tsx -e "
|
|
import { createNewGame } from './src/engine/state';
|
|
const s = createNewGame(2024);
|
|
s.day = 99; s.ap = 3;
|
|
s.flags.evacPrep = 1; s.flags.heardTruth = 1; s.flags.militaryRep = 3;
|
|
s.res.food = 40; s.res.water = 40; s.res.medicine = 6;
|
|
console.log(JSON.stringify({ v: s.version, ts: Date.now(), state: s }));
|
|
"`, { encoding: 'utf8', cwd: '/root/100days' }).trim();
|
|
|
|
const browser = await chromium.launch({ executablePath: '/root/.cache/ms-playwright/chromium_headless_shell-1148/chrome-linux/headless_shell', args:['--no-sandbox','--disable-gpu','--disable-dev-shm-usage'] });
|
|
const ctx = await browser.newContext({ viewport:{width:1440,height:900} });
|
|
await ctx.addInitScript((save) => {
|
|
localStorage.setItem('d100_save_auto', save);
|
|
}, stateJson);
|
|
const page = await ctx.newPage();
|
|
page.setDefaultTimeout(9000);
|
|
const errors = [];
|
|
page.on('pageerror', e => errors.push(e.message));
|
|
await page.goto('http://127.0.0.1:5199', { waitUntil:'domcontentloaded' });
|
|
await page.waitForSelector('.title-screen');
|
|
await page.click('text=CONTINUE');
|
|
await page.waitForSelector('.game-layout');
|
|
console.log('loaded day:', await page.evaluate(()=>document.querySelector('#hud-day')?.textContent));
|
|
|
|
// end days 99 -> 100 -> finale
|
|
for (let i = 0; i < 10; i++) {
|
|
// drain modals
|
|
for (let k = 0; k < 12; k++) {
|
|
const r = await page.evaluate(() => {
|
|
const ov = document.querySelectorAll('.modal-root .overlay');
|
|
const last = ov[ov.length - 1];
|
|
if (!last) return { none: true };
|
|
const btns = [...last.querySelectorAll('button')].filter(b => !b.disabled && b.offsetParent !== null);
|
|
if (/DAY 100/i.test(last.querySelector('.modal-head h2')?.textContent ?? '')) {
|
|
const muster = btns.find(b => /convoy|muster/i.test(b.textContent));
|
|
const any = muster ?? btns[0];
|
|
any.click();
|
|
return { finale: true, clicked: any.textContent.slice(0, 30) };
|
|
}
|
|
const t = btns.find(b => /CONTINUE|STEEL/i.test(b.textContent))
|
|
|| btns.find(b => b.classList.contains('ev-choice') && !b.classList.contains('locked'))
|
|
|| btns.find(b => b.classList.contains('choice'));
|
|
if (!t) return { stuck: true };
|
|
t.click();
|
|
return { clicked: true };
|
|
});
|
|
if (r.none) break;
|
|
if (r.finale) { console.log('FINALE choice:', r.clicked); continue; }
|
|
if (r.stuck) { console.log('STUCK on modal'); break; }
|
|
await page.waitForTimeout(300);
|
|
}
|
|
const ended = await page.evaluate(() => !!document.querySelector('.ending-modal'));
|
|
if (ended) break;
|
|
const btn = page.locator('#btn-endday');
|
|
if (!(await btn.isEnabled())) break;
|
|
await btn.click();
|
|
await page.waitForTimeout(500);
|
|
}
|
|
await page.waitForTimeout(800);
|
|
const ending = await page.evaluate(() => {
|
|
const el = document.querySelector('.ending-title');
|
|
return el ? el.textContent : null;
|
|
});
|
|
await page.screenshot({ path: 'shots/ending.png' });
|
|
console.log('ENDING SHOWN:', ending);
|
|
console.log(errors.length ? 'PAGE ERRORS: ' + errors.join(' | ') : 'no page errors');
|
|
await browser.close();
|
|
if (!ending || errors.length) process.exit(1);
|
|
console.log('FINALE TEST PASSED');
|