/* Feature screenshots: taming at dusk, lake raider, wall assault */ 'use strict'; const fs = require('fs'); const path = require('path'); const { chromium } = require('/tmp/node_modules/playwright-core'); const EXE = '/root/.cache/ms-playwright/chromium_headless_shell-1148/chrome-linux/headless_shell'; const URL = 'http://127.0.0.1:8933/index.html'; const OUT = path.join(__dirname, '..', 'shots'); (async () => { const browser = await chromium.launch({ executablePath: EXE, args: ['--no-sandbox'] }); const page = await browser.newPage({ viewport: { width: 1440, height: 860 } }); const errors = []; page.on('pageerror', e => errors.push(String(e))); await page.goto(URL); await page.waitForTimeout(700); // start a normal game await page.evaluate(() => { document.querySelector('[data-diff="normal"]').click(); }); await page.waitForTimeout(500); // ---- diorama 1: dusk base with Primal Pen, tamed raptor, tamer at work ---- await page.evaluate(() => { const s = RTS.sim.state(); s.res.gold += 9000; s.res.wood += 9000; s.res.stone += 9000; const hq = RTS.sim.hq(); const put = (id, dx, dy) => { const x = Math.round(hq.x + dx), y = Math.round(hq.y + dy); if (!RTS.sim.canPlace(id, x, y).ok) return null; const r = RTS.sim.place(id, x, y); if (!r.ok) return null; r.b.done = true; r.b.progress = 1; r.b.hp = r.b.maxHp; return r.b; }; const tryPut = (id, spots) => { for (const [dx, dy] of spots) { const b = put(id, dx, dy); if (b) return b; } return null; }; // a tidy little colony put('generator', -4, -3); put('generator', 4, 3); put('house', -3, 2); put('house', 3, -3); put('house', -5, 1); put('farm', 2, 4); put('forester', -6, -2); put('quarry', 6, -1); put('watchtower', -2, -5); put('watchtower', 5, 1); put('watchtower', 0, 5); put('watchtower', -6, 4); put('barracks', 1, -5); window.__pen = tryPut('primalpen', [[-2, 3], [-3, 3], [-2, 4], [2, -4], [4, -1]]); // rangers on guard for (let i = 0; i < 5; i++) { const u = RTS.entities.makeUnit('ranger', hq.x + (i - 2) * 1.2, hq.y + 2.5); s.units.push(u); } const tamer = RTS.entities.makeUnit('tamer', (window.__pen ? window.__pen.x : hq.x) + 1.5, (window.__pen ? window.__pen.y : hq.y) + 1.8); s.units.push(tamer); // a weakened raptor right next to him → capture starts on its own const wild = RTS.entities.makeDino('raptor', tamer.x + 1.2, tamer.y + 0.4, 'roam'); wild.hp = wild.maxHp * 0.2; s.dinos.push(wild); // an already-loyal trike guarding the gate side const pet = RTS.entities.makeDino('trike', hq.x + 4.5, hq.y + 4, 'roam'); pet.tamed = true; pet.mode = 'pet'; pet.homeX = pet.x; pet.homeY = pet.y; pet.hp = pet.maxHp * 0.85; s.dinos.push(pet); // golden hour s.time = Math.floor(RTS.CONFIG.WORLD.DAY_LENGTH * 0.52); }); // let the capture channel run while rendering await page.evaluate(() => { for (let i = 0; i < 30 * 2; i++) RTS.sim.tick(1 / 30); }); await page.waitForTimeout(400); await page.screenshot({ path: OUT + '/taming.png' }); // ---- diorama 2: suchomimus rising from a lake ---- await page.evaluate(() => { const s = RTS.sim.state(); const W = RTS.CONFIG.WORLD.W, H = RTS.CONFIG.WORLD.H; // find a lake tile reasonably near the visible area let spot = null; outer: for (let y = 6; y < H - 6; y++) for (let x = 6; x < W - 6; x++) if (s.world.tiles.terrain[y * W + x] === 3) { spot = { x: x + .5, y: y + .5 }; break outer; } if (spot) { // a colonist on the shore keeps the fog revealed (updateFog stamps units) const shore = { x: spot.x + 1.5, y: spot.y - 1 }; s.units.push(RTS.entities.makeUnit('colonist', shore.x, shore.y)); const d = RTS.entities.makeDino('sucho', spot.x - 1, spot.y + 0.5, 'final'); d.aggro = true; s.dinos.push(d); RTS.render.cam.x = (spot.x + shore.x) / 2; RTS.render.cam.y = (spot.y + shore.y) / 2; RTS.render.cam.zoom = 1.3; s.shakeT = 0; // keep the shot steady } s.time = Math.floor(RTS.CONFIG.WORLD.DAY_LENGTH * 0.30); // daylight }); await page.waitForTimeout(600); await page.screenshot({ path: OUT + '/lake.png' }); // ---- diorama 3: full assault on the walls ---- await page.evaluate(() => { const s = RTS.sim.state(); const hq = RTS.sim.hq(); // wall arc between the horde and the colony for (let k = -4; k <= 4; k++) { if (k === 0) continue; const x = Math.round(hq.x + 7), y = Math.round(hq.y + k * 1.15); if (RTS.sim.canPlace('wall', x, y).ok) { const r = RTS.sim.place('wall', x, y); if (r.ok) { r.b.done = true; r.b.progress = 1; } } } // horde: mixed ground force + air support const comp = { raptor: 8, compy: 12, trike: 2, dilo: 3 }; let i = 0; for (const type in comp) for (let k = 0; k < comp[type]; k++) { const d = RTS.entities.makeDino(type, hq.x + 13 + (i % 4), hq.y - 6 + (i % 13), 'final'); d.aggro = true; i++; s.dinos.push(d); } for (let k = 0; k < 6; k++) { const d = RTS.entities.makeDino('ptera', hq.x + 9 + (k % 3), hq.y - 4 + k * 1.4, 'final'); d.aggro = true; s.dinos.push(d); } s.time = Math.floor(RTS.CONFIG.WORLD.DAY_LENGTH * 0.62); // night falls on battle RTS.render.cam.x = hq.x + 5; RTS.render.cam.y = hq.y; s.warnT = 0; }); // simulate ~4s so projectiles fly and muzzle flashes show await page.evaluate(() => { for (let k = 0; k < 30 * 4; k++) RTS.sim.tick(1 / 30); }); await page.waitForTimeout(300); await page.screenshot({ path: OUT + '/battle2.png' }); console.log('screenshots written:', ['taming.png', 'lake.png', 'battle2.png'].map(f => 'shots/' + f).join(', ')); console.log('console errors:', errors.length ? errors : 'none'); await browser.close(); process.exit(errors.length ? 1 : 0); })().catch(e => { console.error(e); process.exit(1); });