Build tool: compact top-left card again; status row moved off the build bar
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+6
-2
@@ -147,8 +147,9 @@ above, including:
|
|||||||
wall material and filter settings. G no longer picks, as the owner requested (it is now Replace).
|
wall material and filter settings. G no longer picks, as the owner requested (it is now Replace).
|
||||||
- **The build menu** is a bar of tabs and icon tiles along the bottom: Structure, Moving,
|
- **The build menu** is a bar of tabs and icon tiles along the bottom: Structure, Moving,
|
||||||
Liquids, Machines, Molds. Hover a tile for its full description, cost and placement help;
|
Liquids, Machines, Molds. Hover a tile for its full description, cost and placement help;
|
||||||
gamepad players see the selected tile’s popup. The top-left card is hidden while building,
|
gamepad players see the selected tile’s popup. While building, the top-left card shows a
|
||||||
with variants, ghosts and status kept in the bar so the world stays clear.
|
compact summary (name, cost, variant chips, Replace, ghosts, how to place, status); the full
|
||||||
|
description is in the hover popup.
|
||||||
- **No length limit when dragging.** Blocks out of the bot's reach become ghosts that it builds
|
- **No length limit when dragging.** Blocks out of the bot's reach become ghosts that it builds
|
||||||
when it gets there.
|
when it gets there.
|
||||||
- **Pipes are thin:** sand, water and anything else loose passes them. Pumps and outlets are
|
- **Pipes are thin:** sand, water and anything else loose passes them. Pumps and outlets are
|
||||||
@@ -168,6 +169,9 @@ above, including:
|
|||||||
- **Replace is held G, not Ctrl.** The owner hit the browser's Ctrl+W (close tab) and Ctrl+D
|
- **Replace is held G, not Ctrl.** The owner hit the browser's Ctrl+W (close tab) and Ctrl+D
|
||||||
(bookmark) while replacing and flying with WASD, and the game ignored WASD while Ctrl was held.
|
(bookmark) while replacing and flying with WASD, and the game ignored WASD while Ctrl was held.
|
||||||
|
|
||||||
|
- **The build tool has a compact top-left card** like the other tools. The owner asked for it
|
||||||
|
back after it was hidden; the long description stays in the hover popup.
|
||||||
|
|
||||||
## Ideas not built yet
|
## Ideas not built yet
|
||||||
|
|
||||||
- **Concrete**, as a stronger wall material from rubble.
|
- **Concrete**, as a stronger wall material from rubble.
|
||||||
|
|||||||
+3
-3
@@ -392,12 +392,12 @@ The notebook (N): milestones, market, wallet, blueprints, controls.
|
|||||||
The tool hotbar and what is selected.
|
The tool hotbar and what is selected.
|
||||||
- functions: renderHotbar, toolId, steering, modShift, grainAt, selectTool, bagMaterials, mixList, ensureDropMat, piece
|
- functions: renderHotbar, toolId, steering, modShift, grainAt, selectTool, bagMaterials, mixList, ensureDropMat, piece
|
||||||
|
|
||||||
### src/ui/build-menu.js (104 lines)
|
### src/ui/build-menu.js (101 lines)
|
||||||
The build menu along the bottom: categories, piece tiles and their icons.
|
The build menu along the bottom: categories, piece tiles and their icons.
|
||||||
- functions: pieceIcon, catOf, selectBuildCat, cycleBuildCat, renderBuildBar, setupBuildBar
|
- functions: pieceIcon, catOf, selectBuildCat, cycleBuildCat, buildVariantChips, renderBuildBar, setupBuildBar
|
||||||
- variables: BUILD_CATS, BUILD_ORDER, PIECE_ICONS, lastBuildKey, lastBuildMenu
|
- variables: BUILD_CATS, BUILD_ORDER, PIECE_ICONS, lastBuildKey, lastBuildMenu
|
||||||
|
|
||||||
### src/ui/selection.js (228 lines)
|
### src/ui/selection.js (234 lines)
|
||||||
The selected tool and piece: R / Q / E, drill width, filter picker, the selection card, the bag panel.
|
The selected tool and piece: R / Q / E, drill width, filter picker, the selection card, the bag panel.
|
||||||
- functions: pieceUsesWall, setDrillWidth, tuneUnderCursor, cycleItem, cycleWall, rotate, toggleCollect, pickFilterUnderCursor, toggleFilterMat, toggleCollectUnderCursor, setBrush, pieceV, pieceExtra, filterChoices, matList, filterParts, filterSentence, applyFilterRun, openFilterPicker, filterBlockAt, chipCost, pieceCostNow, renderSelCard, renderBag
|
- functions: pieceUsesWall, setDrillWidth, tuneUnderCursor, cycleItem, cycleWall, rotate, toggleCollect, pickFilterUnderCursor, toggleFilterMat, toggleCollectUnderCursor, setBrush, pieceV, pieceExtra, filterChoices, matList, filterParts, filterSentence, applyFilterRun, openFilterPicker, filterBlockAt, chipCost, pieceCostNow, renderSelCard, renderBag
|
||||||
- variables: lastBagKey
|
- variables: lastBagKey
|
||||||
|
|||||||
@@ -4,6 +4,22 @@ What agents have done, newest first. Add an entry for every piece of work: the d
|
|||||||
note or request it was for, what changed, the files touched, the tests run, and anything left
|
note or request it was for, what changed, the files touched, the tests run, and anything left
|
||||||
open or not verified. Keep each entry short; the git history has the details.
|
open or not verified. Keep each entry short; the git history has the details.
|
||||||
|
|
||||||
|
## 2026-10-23: compact build card top-left (Claude Code, with Codex)
|
||||||
|
|
||||||
|
Note: "build tool has no info box like the other tools." The owner chose a compact card.
|
||||||
|
|
||||||
|
- `src/ui/selection.js`: the build tool has a card again: Q, piece name, cost (red if short), E;
|
||||||
|
variant chips, Replace · G and ghosts waiting; one how-to line; the status line. No long
|
||||||
|
description; that stays in the hover popup.
|
||||||
|
- `src/ui/build-menu.js`: the `.bb-status` row is gone (its contents are on the card); the
|
||||||
|
variant chips are shared through `buildVariantChips(P)`. `src/styles/style.css`,
|
||||||
|
`docs/DESIGN.md`, `docs/PROJECT_MAP.md`.
|
||||||
|
- `test/play.py`: the card is visible while building with name and cost and without the
|
||||||
|
description; status and Replace · G are read from the card; no `.bb-status` (122 checks pass).
|
||||||
|
All 11 headless suites pass; `t_smooth.py`'s half-resolution check is still flaky under load.
|
||||||
|
- Shots: `shots/build-card-*` (belt and wall, both looks, 1280×800 and 900×700) look right.
|
||||||
|
- Open: at 900px wide the bottom-left hover panel still sits partly under the build bar.
|
||||||
|
|
||||||
## 2026-10-23: Replace is held G, not Ctrl (Claude Code, with Codex)
|
## 2026-10-23: Replace is held G, not Ctrl (Claude Code, with Codex)
|
||||||
|
|
||||||
Note: "i see the issue was ctrl+w closing the tab… it happens for the bookmark hotkey too. maybe
|
Note: "i see the issue was ctrl+w closing the tab… it happens for the bookmark hotkey too. maybe
|
||||||
|
|||||||
@@ -63,8 +63,6 @@ canvas#game.nocursor { cursor: none; }
|
|||||||
.bb-tile.on svg { color: var(--melt-hot); }
|
.bb-tile.on svg { color: var(--melt-hot); }
|
||||||
#buildbar { position: relative; }
|
#buildbar { position: relative; }
|
||||||
.bb-menu { display: flex; flex-direction: column; gap: 6px; }
|
.bb-menu { display: flex; flex-direction: column; gap: 6px; }
|
||||||
.bb-status { display: flex; align-items: center; gap: 4px; min-height: 1.4em; overflow-x: auto; }
|
|
||||||
.bb-status .sw { flex-shrink: 0; }
|
|
||||||
#bb-pop { position: absolute; bottom: calc(100% + 8px); width: min(calc(300px * var(--ui)), calc(100vw - 32px)); box-sizing: border-box; padding: calc(10px * var(--ui)); display: flex; flex-direction: column; gap: 8px; background: var(--plate); border: 1px solid var(--soot-3); border-radius: 5px; box-shadow: 0 4px 18px #0008; color: var(--bone); pointer-events: none; }
|
#bb-pop { position: absolute; bottom: calc(100% + 8px); width: min(calc(300px * var(--ui)), calc(100vw - 32px)); box-sizing: border-box; padding: calc(10px * var(--ui)); display: flex; flex-direction: column; gap: 8px; background: var(--plate); border: 1px solid var(--soot-3); border-radius: 5px; box-shadow: 0 4px 18px #0008; color: var(--bone); pointer-events: none; }
|
||||||
.bb-title { display: flex; align-items: center; gap: 8px; }
|
.bb-title { display: flex; align-items: center; gap: 8px; }
|
||||||
.bb-title svg { width: calc(26px * var(--ui)); height: calc(26px * var(--ui)); color: var(--melt-hot); flex-shrink: 0; }
|
.bb-title svg { width: calc(26px * var(--ui)); height: calc(26px * var(--ui)); color: var(--melt-hot); flex-shrink: 0; }
|
||||||
|
|||||||
+14
-17
@@ -40,6 +40,18 @@ function cycleBuildCat(d) {
|
|||||||
const c = GAME.buildCat != null ? GAME.buildCat : catOf(piece().id);
|
const c = GAME.buildCat != null ? GAME.buildCat : catOf(piece().id);
|
||||||
selectBuildCat((c + d + BUILD_CATS.length) % BUILD_CATS.length);
|
selectBuildCat((c + d + BUILD_CATS.length) % BUILD_CATS.length);
|
||||||
}
|
}
|
||||||
|
function buildVariantChips(P) {
|
||||||
|
const dir = P.dirs ? P.dirs[GAME.pieceDir[P.id] || 0] : null;
|
||||||
|
// while dragging a line of belts, filters or tubes, the direction is the drag's
|
||||||
|
const dragging = GAME.drag && GAME.dragDir >= 0 && P.dirs;
|
||||||
|
const glyph = d => ({ left: '←', right: '→', up: '↑', down: '↓' })[d] || '';
|
||||||
|
const dirChip = !P.dirs ? '' : dragging ? `<span class="chip ok">${glyph(P.dirs[GAME.dragDir])} ${esc(P.dirs[GAME.dragDir])} · from your drag</span>` : `<span class="chip hot">${glyph(dir)} ${esc(dir)} · R</span>`;
|
||||||
|
const fc = GAME.filterCfg;
|
||||||
|
const sws = ms => ms.slice(0, 6).map(m => `<span class="sw" style="display:inline-block;vertical-align:-2px;width:9px;height:9px;margin-right:2px;background:${rgbOf(m)}"></span>`).join('') + (ms.length > 6 ? `+${ms.length - 6}` : '');
|
||||||
|
return pieceUsesWall(P) ? `<span class="chip hot"><span class="sw" style="display:inline-block;vertical-align:-2px;width:9px;height:9px;margin-right:4px;background:${rgbOf(WALL_OF[GAME.wallMat])}"></span>${esc(MD[WALL_OF[GAME.wallMat]].name.toLowerCase())} · R</span>`
|
||||||
|
: P.filter ? `<span class="chip hot">${fc.inv ? 'carries' : 'drops'} ${fc.m.length ? sws(fc.m) : 'nothing'} · F</span>${dirChip}`
|
||||||
|
: dir ? dirChip : '';
|
||||||
|
}
|
||||||
function renderBuildBar() {
|
function renderBuildBar() {
|
||||||
const bar = $('buildbar'); if (!bar) return;
|
const bar = $('buildbar'); if (!bar) return;
|
||||||
const show = GAME.running && toolId() === 'build' && !GAME.paste && !GAME.selDrag && !selModeHeld();
|
const show = GAME.running && toolId() === 'build' && !GAME.paste && !GAME.selDrag && !selModeHeld();
|
||||||
@@ -47,10 +59,8 @@ function renderBuildBar() {
|
|||||||
if (!show) { lastBuildKey = ''; return; }
|
if (!show) { lastBuildKey = ''; return; }
|
||||||
const P = piece(), cat = GAME.buildCat != null ? GAME.buildCat : catOf(P.id), hov = GAME.bbHover;
|
const P = piece(), cat = GAME.buildCat != null ? GAME.buildCat : catOf(P.id), hov = GAME.bbHover;
|
||||||
const ids = BUILD_CATS[cat][1], tip = PIECE_BY_ID[GAME.usingPad ? P.id : hov && ids.includes(hov) ? hov : ''];
|
const ids = BUILD_CATS[cat][1], tip = PIECE_BY_ID[GAME.usingPad ? P.id : hov && ids.includes(hov) ? hov : ''];
|
||||||
const status = performance.now() - GAME.statusT < 3200 ? GAME.status : '';
|
|
||||||
const ng = ghostBlocks.size + ghostMachines.length + ghostMolds.length;
|
|
||||||
const afford = ids.map(id => canAfford(pieceCostNow(PIECE_BY_ID[id]), 1) ? 1 : 0).join('');
|
const afford = ids.map(id => canAfford(pieceCostNow(PIECE_BY_ID[id]), 1) ? 1 : 0).join('');
|
||||||
const key = [cat, P.id, tip && tip.id, afford, GAME.wallMat, Math.round(money), GAME.replace, GAME.pieceDir[P.id], !!GAME.drag, GAME.dragDir, GAME.filterCfg.inv, GAME.filterCfg.m.join('.'), ng, ghostMissing, status, GAME.statusKind, innerWidth, innerHeight, SET.uiScale].join('|');
|
const key = [cat, P.id, tip && tip.id, afford, GAME.wallMat, Math.round(money), GAME.pieceDir[P.id], GAME.filterCfg.inv, GAME.filterCfg.m.join('.'), innerWidth, innerHeight, SET.uiScale].join('|');
|
||||||
if (key === lastBuildKey) return;
|
if (key === lastBuildKey) return;
|
||||||
lastBuildKey = key;
|
lastBuildKey = key;
|
||||||
let h = '<div class="bb-head">' + BUILD_CATS.map(([name], k) => `<button class="bb-tab${k === cat ? ' on' : ''}" data-cat="${k}">${esc(name)}</button>`).join('')
|
let h = '<div class="bb-head">' + BUILD_CATS.map(([name], k) => `<button class="bb-tab${k === cat ? ' on' : ''}" data-cat="${k}">${esc(name)}</button>`).join('')
|
||||||
@@ -61,22 +71,9 @@ function renderBuildBar() {
|
|||||||
h += `<button class="bb-tile${id === P.id ? ' on' : ''}" data-piece="${id}" aria-pressed="${id === P.id}" aria-label="${esc(Q.name)}">${sw}${pieceIcon(id)}<span class="n">${esc(Q.name)}</span><span class="c${ok ? '' : ' bad'}">${esc(costText(cost, 1) || 'free')}</span></button>`;
|
h += `<button class="bb-tile${id === P.id ? ' on' : ''}" data-piece="${id}" aria-pressed="${id === P.id}" aria-label="${esc(Q.name)}">${sw}${pieceIcon(id)}<span class="n">${esc(Q.name)}</span><span class="c${ok ? '' : ' bad'}">${esc(costText(cost, 1) || 'free')}</span></button>`;
|
||||||
}
|
}
|
||||||
h += '</div>';
|
h += '</div>';
|
||||||
if (!bar.querySelector('.bb-menu')) bar.innerHTML = '<div class="bb-menu"></div><div class="bb-status"></div><div id="bb-pop" hidden></div>';
|
if (!bar.querySelector('.bb-menu')) bar.innerHTML = '<div class="bb-menu"></div><div id="bb-pop" hidden></div>';
|
||||||
const menu = bar.querySelector('.bb-menu');
|
const menu = bar.querySelector('.bb-menu');
|
||||||
if (lastBuildMenu !== h) { menu.innerHTML = h; lastBuildMenu = h; }
|
if (lastBuildMenu !== h) { menu.innerHTML = h; lastBuildMenu = h; }
|
||||||
const dir = P.dirs ? P.dirs[GAME.pieceDir[P.id] || 0] : null;
|
|
||||||
// while dragging a line of belts, filters or tubes, the direction is the drag's
|
|
||||||
const dragging = GAME.drag && GAME.dragDir >= 0 && P.dirs;
|
|
||||||
const glyph = d => ({ left: '←', right: '→', up: '↑', down: '↓' })[d] || '';
|
|
||||||
const dirChip = !P.dirs ? '' : dragging ? `<span class="chip ok">${glyph(P.dirs[GAME.dragDir])} ${esc(P.dirs[GAME.dragDir])} · from your drag</span>` : `<span class="chip hot">${glyph(dir)} ${esc(dir)} · R</span>`;
|
|
||||||
const fc = GAME.filterCfg;
|
|
||||||
const sws = ms => ms.slice(0, 6).map(m => `<span class="sw" style="display:inline-block;vertical-align:-2px;width:9px;height:9px;margin-right:2px;background:${rgbOf(m)}"></span>`).join('') + (ms.length > 6 ? `+${ms.length - 6}` : '');
|
|
||||||
const variant = pieceUsesWall(P) ? `<span class="chip hot"><span class="sw" style="display:inline-block;vertical-align:-2px;width:9px;height:9px;margin-right:4px;background:${rgbOf(WALL_OF[GAME.wallMat])}"></span>${esc(MD[WALL_OF[GAME.wallMat]].name.toLowerCase())} · R</span>`
|
|
||||||
: P.filter ? `<span class="chip hot">${fc.inv ? 'carries' : 'drops'} ${fc.m.length ? sws(fc.m) : 'nothing'} · F</span>${dirChip}`
|
|
||||||
: dir ? dirChip : '';
|
|
||||||
bar.querySelector('.bb-status').innerHTML = variant + (GAME.replace ? '<span class="chip hot">Replace · G</span>' : '')
|
|
||||||
+ (ng ? `<span class="chip ok">${ng} ghost pieces waiting${ghostMissing ? ', some short on materials' : ''}.</span>` : '')
|
|
||||||
+ (status ? `<span class="chip ${GAME.statusKind === 'info' ? 'ok' : 'bad'}">${esc(status)}</span>` : '');
|
|
||||||
const pop = $('bb-pop'); pop.hidden = !tip;
|
const pop = $('bb-pop'); pop.hidden = !tip;
|
||||||
if (!tip) return;
|
if (!tip) return;
|
||||||
const size = tip.kind === 'machine' ? `${tip.bw}×${tip.bh} blocks` : tip.kind === 'mold' ? `${MOLDS[tip.mold].bw}×${MOLDS[tip.mold].bh} blocks` : '1 block';
|
const size = tip.kind === 'machine' ? `${tip.bw}×${tip.bh} blocks` : tip.kind === 'mold' ? `${MOLDS[tip.mold].bw}×${MOLDS[tip.mold].bh} blocks` : '1 block';
|
||||||
|
|||||||
+8
-2
@@ -150,8 +150,7 @@ function renderSelCard() {
|
|||||||
let h = '';
|
let h = '';
|
||||||
const status = performance.now() - GAME.statusT < 3200 ? GAME.status : '';
|
const status = performance.now() - GAME.statusT < 3200 ? GAME.status : '';
|
||||||
const sel = GAME.selDrag ? GAME.selDrag.mode : selModeHeld();
|
const sel = GAME.selDrag ? GAME.selDrag.mode : selModeHeld();
|
||||||
c.hidden = t === 'build' && !GAME.paste && !GAME.selDrag && !sel;
|
c.hidden = false;
|
||||||
if (c.hidden) return;
|
|
||||||
const ng = ghostBlocks.size + ghostMachines.length + ghostMolds.length;
|
const ng = ghostBlocks.size + ghostMachines.length + ghostMolds.length;
|
||||||
if (sel === 'cut') {
|
if (sel === 'cut') {
|
||||||
h = `<div class="row"><span class="chip bad">✂ Cut</span><span class="name grow">Hold X and drag a box</span></div>
|
h = `<div class="row"><span class="chip bad">✂ Cut</span><span class="name grow">Hold X and drag a box</span></div>
|
||||||
@@ -169,6 +168,13 @@ function renderSelCard() {
|
|||||||
h = `<div class="row cycle"><kbd>Q</kbd><span class="name grow">Resonance drill</span><span class="chip hot">${pw.name} pulse</span><kbd>E</kbd></div>
|
h = `<div class="row cycle"><kbd>Q</kbd><span class="name grow">Resonance drill</span><span class="chip hot">${pw.name} pulse</span><kbd>E</kbd></div>
|
||||||
<div class="row" style="flex-wrap:wrap;gap:4px"><span class="chip">${esc(b.name.toLowerCase())}</span><span class="chip">hardness ≤ ${b.hard}</span><span class="chip">reach ${drillReach(GAME.drillW)}</span>${tn ? `<span class="chip ok"><span class="sw" style="display:inline-block;vertical-align:-2px;width:9px;height:9px;margin-right:4px;background:${rgbOf(tn)}"></span>tuned: ${esc(MD[tn].name.toLowerCase())} · R</span>` : '<span class="chip">untuned · R tunes</span>'}</div>
|
<div class="row" style="flex-wrap:wrap;gap:4px"><span class="chip">${esc(b.name.toLowerCase())}</span><span class="chip">hardness ≤ ${b.hard}</span><span class="chip">reach ${drillReach(GAME.drillW)}</span>${tn ? `<span class="chip ok"><span class="sw" style="display:inline-block;vertical-align:-2px;width:9px;height:9px;margin-right:4px;background:${rgbOf(tn)}"></span>tuned: ${esc(MD[tn].name.toLowerCase())} · R</span>` : '<span class="chip">untuned · R tunes</span>'}</div>
|
||||||
<div class="sub">Hold to send pulses at the cursor: rock they shake hard enough shatters and falls. <kbd>Q</kbd>/<kbd>E</kbd> width: narrow bores a long tunnel fast, wide clears a big face up close. <kbd>R</kbd> over a material tunes to it, so only that shatters. Built blocks are never harmed.</div>`;
|
<div class="sub">Hold to send pulses at the cursor: rock they shake hard enough shatters and falls. <kbd>Q</kbd>/<kbd>E</kbd> width: narrow bores a long tunnel fast, wide clears a big face up close. <kbd>R</kbd> over a material tunes to it, so only that shatters. Built blocks are never harmed.</div>`;
|
||||||
|
} else if (t === 'build') {
|
||||||
|
const P = piece(), cost = pieceCostNow(P);
|
||||||
|
const chips = buildVariantChips(P) + (GAME.replace ? '<span class="chip hot">Replace · G</span>' : '')
|
||||||
|
+ (ng ? `<span class="chip ok">${ng} ghost pieces waiting${ghostMissing ? ', some short on materials' : ''}.</span>` : '');
|
||||||
|
h = `<div class="row cycle"><kbd>Q</kbd><span class="name grow">${esc(P.name)}</span><span class="chip${canAfford(cost, 1) ? '' : ' bad'}">${esc(costText(cost, 1) || 'free')}</span><kbd>E</kbd></div>`
|
||||||
|
+ (chips ? `<div class="row" style="flex-wrap:wrap;gap:4px">${chips}</div>` : '')
|
||||||
|
+ `<div class="sub">${P.kind === 'block' ? 'Drag for a line, <kbd>Shift</kbd>-drag for a box.' : 'Click to place.'}${P.dirs ? ' <kbd>R</kbd> turns it.' : pieceUsesWall(P) ? ' <kbd>R</kbd> picks the material.' : ''} <kbd>Shift</kbd>+<kbd>Q</kbd>/<kbd>E</kbd> tab.</div>`;
|
||||||
} else if (t === 'vacuum') {
|
} else if (t === 'vacuum') {
|
||||||
ensureDropMat();
|
ensureDropMat();
|
||||||
const m = GAME.dropMat, f = GAME.lmb ? GAME.vacFilter : 0;
|
const m = GAME.dropMat, f = GAME.lmb ? GAME.vacFilter : 0;
|
||||||
|
|||||||
+4
-3
@@ -213,7 +213,8 @@ with sync_playwright() as p:
|
|||||||
ev('GAME.wallMat = RSTONE; hudDirty = true')
|
ev('GAME.wallMat = RSTONE; hudDirty = true')
|
||||||
check('build tool starts on wall blocks', ok)
|
check('build tool starts on wall blocks', ok)
|
||||||
settle(150)
|
settle(150)
|
||||||
check('the build tool hides the top-left card', not pg.is_visible('#selcard'))
|
check('the build tool shows a compact top-left card', pg.is_visible('#selcard') and pg.inner_text('#selcard .name') == ev('piece().name') and ev("costText(pieceCostNow(piece()), 1) || 'free'") in pg.inner_text('#selcard') and ev('piece().desc') not in pg.inner_text('#selcard'))
|
||||||
|
check('the build menu has no status row', pg.locator('#buildbar .bb-status').count() == 0)
|
||||||
check('the build menu has no short description row', pg.locator('.bb-tip').count() == 0)
|
check('the build menu has no short description row', pg.locator('.bb-tip').count() == 0)
|
||||||
for id in ['wall', 'grate']:
|
for id in ['wall', 'grate']:
|
||||||
pg.hover(f'#buildbar [data-piece="{id}"]'); settle(120)
|
pg.hover(f'#buildbar [data-piece="{id}"]'); settle(120)
|
||||||
@@ -228,7 +229,7 @@ with sync_playwright() as p:
|
|||||||
check(f'tool {key} restores the top-left card', pg.is_visible('#selcard'))
|
check(f'tool {key} restores the top-left card', pg.is_visible('#selcard'))
|
||||||
pg.keyboard.press('3'); settle(150)
|
pg.keyboard.press('3'); settle(150)
|
||||||
pg.mouse.click(*cell_to_screen(bx, gy - 20), button='middle'); settle(120)
|
pg.mouse.click(*cell_to_screen(bx, gy - 20), button='middle'); settle(120)
|
||||||
check('empty middle-click pick shows its status in the build bar', 'Nothing built here to pick.' in pg.inner_text('#buildbar .bb-status'))
|
check('empty middle-click pick shows its status in the top-left card', 'Nothing built here to pick.' in pg.inner_text('#selcard'))
|
||||||
c0 = ev('GAME.buildCat != null ? GAME.buildCat : catOf(piece().id)')
|
c0 = ev('GAME.buildCat != null ? GAME.buildCat : catOf(piece().id)')
|
||||||
pg.keyboard.press('Shift+E')
|
pg.keyboard.press('Shift+E')
|
||||||
check('Shift+E switches build tabs and selects a piece in the tab', ev('GAME.buildCat') == (c0 + 1) % ev('BUILD_CATS.length') and ev('BUILD_CATS[GAME.buildCat][1].includes(piece().id)'))
|
check('Shift+E switches build tabs and selects a piece in the tab', ev('GAME.buildCat') == (c0 + 1) % ev('BUILD_CATS.length') and ev('BUILD_CATS[GAME.buildCat][1].includes(piece().id)'))
|
||||||
@@ -345,7 +346,7 @@ with sync_playwright() as p:
|
|||||||
pg.mouse.down(); pg.mouse.up(); settle(100)
|
pg.mouse.down(); pg.mouse.up(); settle(100)
|
||||||
check('a fan on a belt says to hold G', 'Hold G' in ev('GAME.status'), ev('GAME.status'))
|
check('a fan on a belt says to hold G', 'Hold G' in ev('GAME.status'), ev('GAME.status'))
|
||||||
pg.keyboard.down('g'); settle(100); pg.mouse.move(*blk(bbx - 4, bby + 1)); settle(200)
|
pg.keyboard.down('g'); settle(100); pg.mouse.move(*blk(bbx - 4, bby + 1)); settle(200)
|
||||||
check('holding G shows replace mode', 'Replace' in ev("$('buildbar').textContent"))
|
check('holding G shows replace mode', 'Replace · G' in ev("$('selcard').textContent"))
|
||||||
pg.screenshot(path=str(out / '07g-replace-fan.png'))
|
pg.screenshot(path=str(out / '07g-replace-fan.png'))
|
||||||
pg.mouse.down(); pg.mouse.up(); settle(100); pg.keyboard.up('g'); settle(100)
|
pg.mouse.down(); pg.mouse.up(); settle(100); pg.keyboard.up('g'); settle(100)
|
||||||
check('G-click puts the fan in place of the belt', ev(f"machines.some(m => m && m.type === 'fan' && m.x === ({bbx} - 4) * 4 && m.y === ({bby} + 1) * 4)") and ev(f'blockP[({bby} + 1) * BW + {bbx} - 4]') == 0, ev('GAME.status'))
|
check('G-click puts the fan in place of the belt', ev(f"machines.some(m => m && m.type === 'fan' && m.x === ({bbx} - 4) * 4 && m.y === ({bby} + 1) * 4)") and ev(f'blockP[({bby} + 1) * BW + {bbx} - 4]') == 0, ev('GAME.status'))
|
||||||
|
|||||||
Reference in New Issue
Block a user