- 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)
55 lines
2.6 KiB
JavaScript
55 lines
2.6 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} });
|
|
await page.goto('http://127.0.0.1:5199', { waitUntil:'domcontentloaded' });
|
|
await page.waitForSelector('.title-screen');
|
|
await page.waitForTimeout(1500);
|
|
|
|
function analyzeCanvas(sel){
|
|
return page.evaluate((sel)=>{
|
|
const c=document.querySelector(sel);
|
|
const g=c.getContext('2d');
|
|
const d=g.getImageData(0,0,c.width,c.height).data;
|
|
let sum=0,sum2=0,n=0; const buckets={};
|
|
for(let i=0;i<d.length;i+=4*97){
|
|
const v=(d[i]+d[i+1]+d[i+2])/3;
|
|
sum+=v;sum2+=v*v;n++;
|
|
const b=Math.floor(v/32); buckets[b]=(buckets[b]??0)+1;
|
|
}
|
|
const mean=sum/n, sd=Math.sqrt(Math.max(0,sum2/n-mean*mean));
|
|
return {w:c.width,h:c.height,mean:Math.round(mean),sd:Math.round(sd),buckets};
|
|
},sel);
|
|
}
|
|
|
|
console.log('TITLE canvas:', JSON.stringify(await analyzeCanvas('#title-canvas')));
|
|
// body font/bg sanity
|
|
console.log('body bg:', await page.evaluate(()=>getComputedStyle(document.body).backgroundColor));
|
|
console.log('logo font-size:', await page.evaluate(()=>getComputedStyle(document.querySelector('.logo')).fontSize));
|
|
|
|
await page.click('text=NEW RUN'); await page.fill('.seed-input','999'); await page.click('text=BEGIN DAY 1');
|
|
await page.waitForSelector('.game-layout');
|
|
await page.waitForTimeout(1500);
|
|
console.log('GAME canvas:', JSON.stringify(await analyzeCanvas('#scene')));
|
|
|
|
// layout geometry: no zero-size majors, actionbar buttons count
|
|
const geo = await page.evaluate(()=>{
|
|
const r=s=>{const e=document.querySelector(s); if(!e) return null; const b=e.getBoundingClientRect(); return [Math.round(b.width),Math.round(b.height)];};
|
|
return {
|
|
scene:r('#scene'), left:r('.panel.left'), right:r('.panel.right'),
|
|
actions:[...document.querySelectorAll('.action')].length,
|
|
survCards:document.querySelectorAll('.surv-card').length,
|
|
resChips:document.querySelectorAll('.res-chip').length,
|
|
apPips:document.querySelectorAll('.ap-pip').length,
|
|
svgPorts:document.querySelectorAll('.surv-card svg').length,
|
|
};
|
|
});
|
|
console.log('geometry:',JSON.stringify(geo));
|
|
// portrait svg content sanity
|
|
const svgInfo = await page.evaluate(()=>{
|
|
const s=document.querySelector('.surv-card svg');
|
|
return s?{paths:s.querySelectorAll('path,ellipse,circle,rect').length,len:s.innerHTML.length}:null;
|
|
});
|
|
console.log('portrait:',JSON.stringify(svgInfo));
|
|
await browser.close();
|
|
console.log('VISUAL CHECK DONE');
|