- 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)
59 lines
2.5 KiB
JavaScript
59 lines
2.5 KiB
JavaScript
import { chromium } from 'playwright-core';
|
|
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 page = await browser.newPage({ viewport:{width:1440,height:900} });
|
|
page.setDefaultTimeout(9000);
|
|
await page.goto('http://127.0.0.1:5199', { waitUntil:'domcontentloaded' });
|
|
await page.waitForSelector('.title-screen');
|
|
await page.waitForTimeout(1200);
|
|
await page.screenshot({ path: 'shots/title.png' });
|
|
|
|
await page.click('text=NEW RUN');
|
|
await page.fill('.seed-input','31337');
|
|
await page.click('text=BEGIN DAY 1');
|
|
await page.waitForSelector('.game-layout');
|
|
await page.waitForTimeout(1500);
|
|
await page.screenshot({ path: 'shots/game.png' });
|
|
|
|
// survivor detail
|
|
await page.locator('.surv-card:not(.dead)').first().click();
|
|
await page.waitForTimeout(400);
|
|
await page.screenshot({ path: 'shots/survivor.png' });
|
|
await page.click('.modal-x');
|
|
|
|
// build menu
|
|
await page.click('.action:has-text("BUILD")');
|
|
await page.waitForTimeout(400);
|
|
await page.screenshot({ path: 'shots/build.png' });
|
|
await page.keyboard.press('Escape').catch(()=>{});
|
|
await page.evaluate(() => document.querySelector('.modal-x')?.click());
|
|
await page.waitForTimeout(300);
|
|
|
|
// force an encounter via hunt a few times for the shot; else map hover
|
|
await page.click('[data-tab="map"]');
|
|
await page.waitForTimeout(200);
|
|
await page.locator('.map-entry').nth(2).hover();
|
|
await page.waitForTimeout(800);
|
|
await page.screenshot({ path: 'shots/map-preview.png' });
|
|
|
|
// end day -> night report shot
|
|
await page.click('#btn-endday');
|
|
await page.waitForTimeout(600);
|
|
for (let i=0;i<6;i++){
|
|
const res = await page.evaluate(()=>{
|
|
const ov=document.querySelectorAll('.modal-root .overlay'); const last=ov[ov.length-1];
|
|
if(!last) return {done:true};
|
|
const btns=[...last.querySelectorAll('button')].filter(b=>!b.disabled&&b.offsetParent!==null);
|
|
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 {done:true};
|
|
const isEvent = !!t.closest('.event-modal, .ev-choice');
|
|
t.click();
|
|
return {done:false, isEvent};
|
|
});
|
|
if (res.done) break;
|
|
if (i===0) await page.waitForTimeout(250);
|
|
await page.screenshot({ path: 'shots/modal.png' }).catch(()=>{});
|
|
await page.waitForTimeout(380);
|
|
}
|
|
await browser.close();
|
|
console.log('shots done');
|