Sloped belts hold their load with moving cleats (t42); sloped filters are downhill shaking screens (t43)

The magic grip is gone: cleats every 8 face cells, 6 grains tall, carry
pockets uphill (about 71 grains/s against 90 flat) and overflow rolls
back. Sloped filters always run downhill; grains travel by gravity and
a shake, and sorting stays exact. Drawing needs another pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 19:47:59 -04:00
co-authored by Claude Opus 5.5
parent 1f8a89db6a
commit 578b55316b
28 changed files with 425 additions and 89 deletions
+22
View File
@@ -698,6 +698,27 @@ with sync_playwright() as p:
check('Wall diagonal defaults back to solid below', ev(f'Array.from({{length:4}}, (_,k)=>blockP[({bby}-12+k)*BW+{bbx}-5+k]).every(c=>c===BP_SLOPE_BL)'))
pg.keyboard.press('Control+z'); settle(60)
# Cleated belts and downhill screens use real drags, previews, picking and R.
for piece_id in ['belt', 'filter']:
check('select ' + piece_id + ' for a diagonal drag', select('3', "piece().id === '" + piece_id + "'"))
ev('money=1e6')
drag(lambda: blk(bbx - 5, bby - 9), lambda: blk(bbx - 2, bby - 12))
code = 'BP_BELT_UP' if piece_id == 'belt' else 'BP_FILTER_UP'
check(piece_id + ' uphill drag lays the expected shape', ev(f'Array.from({{length:4}},(_,k)=>blockP[({bby}-9-k)*BW+{bbx}-5+k]).every(c=>c==={code})'))
if piece_id == 'filter':
check('uphill filter drag runs downhill left', ev(f'Array.from({{length:4}},(_,k)=>blockV[({bby}-9-k)*BW+{bbx}-5+k]).every(v=>v===0)'))
pg.mouse.move(*blk(bbx - 3, bby - 11)); settle(60)
check('screen hover says down-left', ev(f"hoverInfo(({bbx}-3)*4+1,({bby}-11)*4+2).t1.includes('down-left')"))
pg.mouse.click(*blk(bbx - 3, bby - 11), button='middle'); settle(60)
pg.keyboard.press('r'); settle(60)
check('R leaves a picked screen downhill', ev('pieceCode(piece())===BP_FILTER_UP && pieceV(piece())===0'))
pg.keyboard.press('f'); settle(100)
check('screen picker offers only downhill travel', pg.locator('#dlg-body [data-dir]').count() == 0 and 'down-left' in pg.locator('#dlg-body').inner_text())
pg.locator('#dlg-foot').get_by_role('button', name='Cancel', exact=True).click()
pg.keyboard.press('Control+z'); settle(60)
ev('GAME.pieceShape = {}; GAME.pieceDir.filter = 1; GAME.editRun = null')
# Diagonal screws use real mouse placement, preview arrows, R and picking.
check('select screw conveyor', select('3', "piece().id === 'screw'"))
ev('GAME.pieceDir.screw = 0')
@@ -1201,6 +1222,7 @@ with sync_playwright() as p:
point = fp.evaluate(f'(() => {{ const p = worldToScreenPx(testSilo.x + {dx} + 0.5, testSilo.y + {dy} + 0.5); return [p.x * innerWidth / R.canvas.width, p.y * innerWidth / R.canvas.width]; }})()')
fp.mouse.move(*point); fp.wait_for_timeout(200)
want = 'sand 2,000 / 4,000' if amount else 'empty'
fp.wait_for_function(f"$('info').textContent.includes('{('holds 2,000 / 4,000 sand' if amount else 'empty')}')")
check(f'silo {name} hover shows {want}', fp.evaluate(f"hoverInfo(Math.floor(GAME.aim.x), Math.floor(GAME.aim.y)).t1 === 'Silo' && hoverInfo(Math.floor(GAME.aim.x), Math.floor(GAME.aim.y)).contents === '{want}' && $('info').textContent.includes('{('holds 2,000 / 4,000 sand' if amount else 'empty')}')"))
check(f'silo {name} cursor label draws {want} in normal colour', fp.evaluate("""() => {
const calls = [], ctx = R.ctx, text = ctx.fillText;