Identify ores with the cursor: pinged veins, ores-only label, vein outline (t77)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -1215,7 +1215,7 @@ with sync_playwright() as p:
|
||||
fp.wait_for_function('GAME.running')
|
||||
# Silo feedback uses real pointer positions; drawing checks sample an isolated canvas.
|
||||
fp.evaluate("""() => {
|
||||
GAME.paused = true; SET.fog = false; SET.look = 'pixel'; SET.zoom = 8; SET.cursorLabel = true;
|
||||
GAME.paused = true; SET.fog = false; SET.look = 'pixel'; SET.zoom = 8; SET.cursorLabel = 'all';
|
||||
SET.cameraLead = false; SET.cameraSmooth = 0; resizeRender();
|
||||
for (let y = 80; y < 140; y++) for (let x = 80; x < 160; x++) { setCell(y * W + x, EMPTY, 20, 0); bgm[y * W + x] = 0; }
|
||||
window.testSilo = addMachine('silo', 28, 25, 0);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// Ore identification, cached connected veins, ping timing, settings and label lifetime.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { load, suite, flatWorld } = require('./harness');
|
||||
const { run } = load();
|
||||
const { check, done } = suite();
|
||||
run(`const localStorage = { getItem: () => '{}', setItem() {} };`);
|
||||
for (const f of ['ui/state.js', 'ui/hud.js', 'ui/cursor.js', 'ui/info.js', 'render/overlays.js']) run(fs.readFileSync(path.join(__dirname, '../src', f), 'utf8'));
|
||||
flatWorld(run, 160, 160, 150);
|
||||
for (const m of ['CUV', 'CUORE', 'COALV', 'COAL', 'FEV', 'FEORE', 'COPPER'])
|
||||
check(m + ' is ore-like', run(`oreLike(${m}, null)`));
|
||||
check('sand and stone are not ore-like', run('!oreLike(SAND, null) && !oreLike(STONE, null)'));
|
||||
check('any hovered bar is ore-like', run('oreLike(EMPTY, { m: BARGL })'));
|
||||
check('default labels are ores only', run("SET.cursorLabel === 'ores'"));
|
||||
for (const [saved, expected] of [[true, 'ores'], [false, 'off'], ['all', 'all']]) {
|
||||
run(`localStorage.getItem = () => '${JSON.stringify({cursorLabel: saved, sv: 2})}'; loadSettings();`);
|
||||
check('saved ' + saved + ' becomes ' + expected, run(`SET.cursorLabel === '${expected}'`));
|
||||
}
|
||||
check('bit text names the first tier that can drill it', run("BIT_TIERS.every(b => { const m = Array.from(HARD).findIndex(h => h === b.hard); return m < 0 || bitNeeded(m) === 'needs ' + b.name.toLowerCase(); })"));
|
||||
check('undrillable material keeps its warning', run("bitNeeded(Array.from(HARD).findIndex(h => h > 6)) === 'cannot be drilled'"));
|
||||
run(`
|
||||
SET.fog = false; GAME.tool = 0;
|
||||
setCell(20 * W + 20, CUV, 22, F_NAT);
|
||||
setCell(20 * W + 21, CUV, 22, F_NAT);
|
||||
setCell(21 * W + 21, CUV, 22, F_NAT);
|
||||
setCell(21 * W + 20, FEV, 22, F_NAT);
|
||||
setCell(20 * W + 22, CUV, 22, 0);
|
||||
setCell(22 * W + 22, CUV, 22, F_NAT);
|
||||
const v = hoveredVein(20, 20);
|
||||
`);
|
||||
check('fill follows four neighbours of the same natural material', run('v.cells.size === 3 && v.edges.length === 8'));
|
||||
check('moving within the same vein reuses cells and edges', run('hoveredVein(21, 21) === v'));
|
||||
check('leaving the vein removes the outline', run('hoveredVein(22, 20) === null'));
|
||||
check('a disconnected vein gets a new cache', run('hoveredVein(22, 22) !== v'));
|
||||
run(`for (let y = 30; y < 130; y++) for (let x = 0; x < 100; x++) setCell(y * W + x, CUV, 22, F_NAT);`);
|
||||
check('a large vein stops at 6000 cells', run('cursorVein(30 * W).cells.size === 6000'));
|
||||
run(`setCell(W - 1, CUV, 22, F_NAT); setCell(W, CUV, 22, F_NAT);`);
|
||||
check('fill does not wrap across world edges', run('cursorVein(W - 1).cells.size === 1'));
|
||||
run(`setCell(20 * W + 20, FEV, 22, F_NAT);`);
|
||||
check('hover details and warning name the needed bit', run("hoverInfo(20, 20).t2.includes(bitNeeded(FEV)) && hoverInfo(20, 20).warn === bitNeeded(FEV)"));
|
||||
run(`
|
||||
SET.fog = true; fog.fill(0);
|
||||
const realNow = performance.now.bind(performance); let hoverNow = 1000;
|
||||
performance.now = () => hoverNow;
|
||||
GAME.ping = { x: 4, y: 20, t0: 1000, cells: [20 * W + 20] };
|
||||
`);
|
||||
check('fog hides a vein before the ping reaches it', run("hoverInfo(20, 20).t1 === 'Unexplored' && !hoveredVein(20, 20)"));
|
||||
run('hoverNow = 1100;');
|
||||
check('a ping names a hidden vein at arrival', run("hoverInfo(20, 20).t1 === MD[FEV].name && hoverInfo(20, 20).t2.includes('found by the ping') && !!hoveredVein(20, 20)"));
|
||||
run('const pingSet = GAME.ping.cellSet; hoverNow = 3099;');
|
||||
check('ping cells are cached through the fade', run('pingVisible(20, 20) && GAME.ping.cellSet === pingSet && !pingVisible(21, 20)'));
|
||||
run('hoverNow = 3100;');
|
||||
check('fog hides the vein again at the end of the fade', run("hoverInfo(20, 20).t1 === 'Unexplored' && !hoveredVein(20, 20)"));
|
||||
run('performance.now = realNow;');
|
||||
check('the name stays fully visible for two seconds', run("cursorLabelAlpha('copper', 100) === 1 && cursorLabelAlpha('copper', 2100) === 1"));
|
||||
check('the name fades over 400 ms', run("cursorLabelAlpha('copper', 2300) === 0.5 && cursorLabelAlpha('copper', 2500) === 0"));
|
||||
check('a different thing brings the label back', run("cursorLabelAlpha('iron', 2600) === 1"));
|
||||
done();
|
||||
@@ -76,6 +76,58 @@ with sync_playwright() as p:
|
||||
check('built solids get no loose-grain outline', ground['built'][0] == ground['built'][1], ground['built'])
|
||||
check('smooth: a thin darker seam separates ground and rubble', ground['smooth'][0] < ground['smooth'][1] * 0.9, ground['smooth'])
|
||||
check('smooth: packed rubble keeps pebble shading', ground['texture'] > 30, ground['texture'])
|
||||
# Cursor labels and outlines use a paused scene and the real drawing functions.
|
||||
cursor = ev("""() => {
|
||||
SET.look='pixel'; SET.cursorLabel='ores'; SET.fog=false; SET.zoom=8;
|
||||
resizeRender(); GAME.tool=0; GAME.mouse.in=true; GAME.usingPad=false;
|
||||
bot.x=118; bot.y=114; snapCamera(); player.lens=false; R.lens=false;
|
||||
for(let y=110;y<130;y++)for(let x=110;x<140;x++) {
|
||||
const i=y*W+x; setCell(i,STONE,22,F_NAT); part[i]=0; mach[i]=0; occ[i]=0; bgm[i]=0;
|
||||
blockP[(y>>2)*BW+(x>>2)]=0;
|
||||
}
|
||||
for(let y=118;y<126;y++)for(let x=120;x<132;x++)
|
||||
if(x+y>239 && x+y<255)setCell(y*W+x,CUV,22,F_NAT);
|
||||
setCell(116*W+116,SAND,22,0);
|
||||
GAME.aim={x:124,y:122}; cursorLabelKey=null;
|
||||
const ctx=R.ctx,text=ctx.fillText,stroke=ctx.stroke,now=performance.now.bind(performance);
|
||||
let time=1000,calls=[],strokes=0;
|
||||
performance.now=()=>time;
|
||||
ctx.fillText=function(t){calls.push([t,this.globalAlpha,this.fillStyle]);};
|
||||
ctx.stroke=function(){strokes++;};
|
||||
const label=()=>{calls=[];drawCursorLabel(500,300,1);return calls.slice();};
|
||||
try {
|
||||
GAME.aim={x:116,y:116};const sand=label();
|
||||
GAME.aim={x:124,y:122};const copper=label();drawHoveredVein();const outlined=strokes;
|
||||
const vein=cursorVeinCache;
|
||||
GAME.aim={x:125,y:122};time=3200;const fading=label();
|
||||
const reused=cursorVeinCache===vein;
|
||||
time=3400;const faded=label();
|
||||
setCell(116*W+117,FEV,22,F_NAT); GAME.aim={x:117,y:116};
|
||||
const hard=label();time=6000;const warning=label();
|
||||
GAME.aim={x:124,y:122}; SET.fog=true;fog.fill(0);
|
||||
GAME.ping={x:124,y:122,t0:6000,r:48,cells:[122*W+124]};
|
||||
const ping=hoverInfo(124,122),pingLabel=label();renderInfo();
|
||||
const panel=$('info').textContent;
|
||||
time=8000;const hidden=hoverInfo(124,122).t1;
|
||||
GAME.ping=null;SET.fog=false; SET.cursorLabel='off';
|
||||
const off=label();strokes=0;drawHoveredVein();
|
||||
return {sand,copper,outlined,reused,fading,faded,hard,warning,ping,pingLabel,panel,hidden,off,offStrokes:strokes};
|
||||
} finally {ctx.fillText=text;ctx.stroke=stroke;performance.now=now;}
|
||||
}""")
|
||||
check('ores mode skips sand and draws the copper vein name', not cursor['sand'] and any(t[0] == 'Copper ore vein' for t in cursor['copper']), cursor['copper'])
|
||||
check('hovering a vein draws an outline and reuses it within the vein', cursor['outlined'] > 0 and cursor['reused'])
|
||||
check('the name fades then disappears while staying on the vein', any(0 < t[1] < 1 for t in cursor['fading']) and not cursor['faded'], cursor['fading'])
|
||||
check('the needed bit stays red after the name fades', any(t[0].startswith('needs ') and t[1] == 1 and t[2] == '#ef6a55' for t in cursor['warning']), cursor['warning'])
|
||||
check('a pinged vein under fog names itself in the label and corner panel', cursor['ping']['t1'] == 'Copper ore vein' and 'found by the ping' in cursor['panel'] and any('found by the ping' in t[0] for t in cursor['pingLabel']), cursor['pingLabel'])
|
||||
check('a faded ping hides the vein again', cursor['hidden'] == 'Unexplored')
|
||||
check('off hides both label and vein outline', not cursor['off'] and cursor['offStrokes'] == 0)
|
||||
for look in ['pixel', 'smooth']:
|
||||
ev("""look => {
|
||||
SET.look=look; SM.scale=1; SET.cursorLabel='ores'; GAME.aim={x:124,y:122}; cursorLabelKey=null;
|
||||
$('cards').replaceChildren(); renderInfo(); drawWorld(); drawHoveredVein(); drawBot();
|
||||
const p=worldToScreenPx(124,122); drawCursorLabel(p.x,p.y,1);
|
||||
}""", look)
|
||||
pg.screenshot(path=str(root / 'shots' / ('t77-vein.png' if look == 'pixel' else 't77-vein-smooth.png')))
|
||||
wall = pg.evaluate('''() => {
|
||||
GAME.paused = true; R.lens = false; SET.fog = false;
|
||||
SET.lighting = 'off'; SET.glow = 'off'; SET.grain = false;
|
||||
|
||||
Reference in New Issue
Block a user