Replace is held G instead of Ctrl (Ctrl+W/Ctrl+D were hit while flying)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+5
-3
@@ -81,7 +81,7 @@ Details and numbers live in the code and in `docs/systems/`.
|
|||||||
leave one open block below and a belt under that carries them off. F is optional upkeep: it
|
leave one open block below and a belt under that carries them off. F is optional upkeep: it
|
||||||
cleans the trough, chips off slag and rakes the furnace grate. Short stubs stay for the next
|
cleans the trough, chips off slag and rakes the furnace grate. Short stubs stay for the next
|
||||||
firing.
|
firing.
|
||||||
- Build actions: drag for lines, Shift-drag for rectangles, Ctrl to replace, hold C / X and
|
- Build actions: drag for lines, Shift-drag for rectangles, hold G to replace, hold C / X and
|
||||||
drag to copy / cut, V to paste, Ctrl+Z to undo, middle mouse to pick,
|
drag to copy / cut, V to paste, Ctrl+Z to undo, middle mouse to pick,
|
||||||
Shift+Q/E to switch build tabs.
|
Shift+Q/E to switch build tabs.
|
||||||
- **Economy:**
|
- **Economy:**
|
||||||
@@ -123,7 +123,7 @@ above, including:
|
|||||||
- **Copy and cut** are held keys, not a tool. Hold C or X and drag a box; it acts on release.
|
- **Copy and cut** are held keys, not a tool. Hold C or X and drag a box; it acts on release.
|
||||||
Copy and cut look different. Cut is destructive, but it can be pasted back or undone.
|
Copy and cut look different. Cut is destructive, but it can be pasted back or undone.
|
||||||
- **Ctrl+Z undoes building** (up to 80 steps). The bot building ghosts is never a step of its own.
|
- **Ctrl+Z undoes building** (up to 80 steps). The bot building ghosts is never a step of its own.
|
||||||
- **Holding Ctrl** while placing replaces what is built there.
|
- **Holding Ctrl** while placing replaces what is built there (moved to G on 2026-10-23).
|
||||||
- **Fans melt like copper (1085°C).** Put the fan outside the furnace, blowing in through a grate.
|
- **Fans melt like copper (1085°C).** Put the fan outside the furnace, blowing in through a grate.
|
||||||
The game warns when a fan gets close to melting.
|
The game warns when a fan gets close to melting.
|
||||||
- **The vacuum takes only ticked materials** when anything is ticked, even if the drag starts on
|
- **The vacuum takes only ticked materials** when anything is ticked, even if the drag starts on
|
||||||
@@ -144,7 +144,7 @@ above, including:
|
|||||||
the load above it stands still and the belt slips underneath; the top no longer slides over
|
the load above it stands still and the belt slips underneath; the top no longer slides over
|
||||||
a stuck bottom. Filters still let their chosen materials drop through.
|
a stuck bottom. Filters still let their chosen materials drop through.
|
||||||
- **Pick** (middle mouse) selects the piece under the cursor with its direction,
|
- **Pick** (middle mouse) selects the piece under the cursor with its direction,
|
||||||
wall material and filter settings. G is unbound, as the owner requested.
|
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. The top-left card is hidden while building,
|
||||||
@@ -165,6 +165,8 @@ above, including:
|
|||||||
players will do it only when they need more ore rather than more money.
|
players will do it only when they need more ore rather than more money.
|
||||||
- The project moved from a claude.ai artifact to local development with coding agents. It is
|
- The project moved from a claude.ai artifact to local development with coding agents. It is
|
||||||
built from small files, with `NOTES.md` as the owner's to-do inbox.
|
built from small files, with `NOTES.md` as the owner's to-do inbox.
|
||||||
|
- **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.
|
||||||
|
|
||||||
## Ideas not built yet
|
## Ideas not built yet
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -406,7 +406,7 @@ The selected tool and piece: R / Q / E, drill width, filter picker, the selectio
|
|||||||
Discovery cards, hover info (I) and the cursor label.
|
Discovery cards, hover info (I) and the cursor label.
|
||||||
- functions: showCard, howText, discoveryCard, reactionsOf, hoverInfo, renderInfo, drawCursorLabel
|
- functions: showCard, howText, discoveryCard, reactionsOf, hoverInfo, renderInfo, drawCursorLabel
|
||||||
|
|
||||||
### src/ui/input.js (75 lines)
|
### src/ui/input.js (79 lines)
|
||||||
Keyboard and mouse listeners.
|
Keyboard and mouse listeners.
|
||||||
- functions: isTyping
|
- functions: isTyping
|
||||||
- variables: canvas
|
- variables: canvas
|
||||||
|
|||||||
@@ -4,6 +4,21 @@ 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: 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
|
||||||
|
change the replace key?" The owner chose holding G.
|
||||||
|
|
||||||
|
- `src/ui/input.js`: `GAME.ctrl` became `GAME.replace`, set while G is held. Ctrl no longer
|
||||||
|
does anything on its own, so WASD works while replacing. Ctrl+S, Ctrl+Z, Ctrl+wheel and the
|
||||||
|
leave-page prompt are unchanged.
|
||||||
|
- Flag users and text: `previews.js`, `actions.js` ("Hold G to replace it."), `build-menu.js`
|
||||||
|
(Replace · G chip, key hints), `loop.js`, `state.js`, `notebook.js` controls, `docs/DESIGN.md`.
|
||||||
|
- `test/play.py`: the replace checks hold G; a new check flies right with D while G is held
|
||||||
|
(121 checks pass). All 11 headless suites pass. `t_smooth.py`'s half-resolution check failed
|
||||||
|
on the slow fallback again (known, flaky under load).
|
||||||
|
- Open: nothing.
|
||||||
|
|
||||||
## 2026-10-23: Ctrl+W no longer closes the game without asking (Claude Code)
|
## 2026-10-23: Ctrl+W no longer closes the game without asking (Claude Code)
|
||||||
|
|
||||||
Note: "crash: hoplding crtl and pressing W crashes the whole browser."
|
Note: "crash: hoplding crtl and pressing W crashes the whole browser."
|
||||||
|
|||||||
+5
-5
@@ -137,15 +137,15 @@ function primaryPress(shift) {
|
|||||||
let err = '';
|
let err = '';
|
||||||
undoRecord(rec => piecesIn(rec, 'block-') + piecesIn(rec, 'mach-') + piecesIn(rec, 'mold-') ? `replacing with a ${P.name.toLowerCase()}` : `placing a ${P.name.toLowerCase()}`, () => {
|
undoRecord(rec => piecesIn(rec, 'block-') + piecesIn(rec, 'mach-') + piecesIn(rec, 'mold-') ? `replacing with a ${P.name.toLowerCase()}` : `placing a ${P.name.toLowerCase()}`, () => {
|
||||||
err = check() || '';
|
err = check() || '';
|
||||||
// Ctrl: take down what you built in the way (refunded) and put this in its place
|
// G: take down what you built in the way (refunded) and put this in its place
|
||||||
if (err && GAME.ctrl && /already/.test(err)) {
|
if (err && GAME.replace && /already/.test(err)) {
|
||||||
err = footprintBlocker(bx, by, bw, bh);
|
err = footprintBlocker(bx, by, bw, bh);
|
||||||
if (!err && !canAfford(pieceCostNow(P), 1)) err = needText(pieceCostNow(P), 1);
|
if (!err && !canAfford(pieceCostNow(P), 1)) err = needText(pieceCostNow(P), 1);
|
||||||
if (!err) err = clearFootprint(bx, by, bw, bh);
|
if (!err) err = clearFootprint(bx, by, bw, bh);
|
||||||
}
|
}
|
||||||
if (!err) err = place();
|
if (!err) err = place();
|
||||||
});
|
});
|
||||||
if (err) { setStatus(err + (/already/.test(err) ? `${/[.!]$/.test(err) ? '' : '.'} Hold Ctrl to replace it.` : '')); playSfx('error'); } else { playSfx('place'); hudDirty = true; }
|
if (err) { setStatus(err + (/already/.test(err) ? `${/[.!]$/.test(err) ? '' : '.'} Hold G to replace it.` : '')); playSfx('error'); } else { playSfx('place'); hudDirty = true; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,7 @@ function primaryRelease(shift) {
|
|||||||
const near = list.filter(([bx, by]) => blockInReach(bx, by)), far = list.filter(([bx, by]) => !blockInReach(bx, by));
|
const near = list.filter(([bx, by]) => blockInReach(bx, by)), far = list.filter(([bx, by]) => !blockInReach(bx, by));
|
||||||
let ghosts = 0;
|
let ghosts = 0;
|
||||||
const r = undoRecord(rec => blockLabel(P, rec) + (piecesIn(rec, 'paste') ? ' (and its ghosts)' : ''), () => {
|
const r = undoRecord(rec => blockLabel(P, rec) + (piecesIn(rec, 'paste') ? ' (and its ghosts)' : ''), () => {
|
||||||
const res = placeBlocks(P.code, v, near, ex, GAME.ctrl);
|
const res = placeBlocks(P.code, v, near, ex, GAME.replace);
|
||||||
const g = { t: 'paste', blocks: [], machines: [], molds: [] };
|
const g = { t: 'paste', blocks: [], machines: [], molds: [] };
|
||||||
for (const [bx, by] of far) {
|
for (const [bx, by] of far) {
|
||||||
const b = by * BW + bx;
|
const b = by * BW + bx;
|
||||||
@@ -185,7 +185,7 @@ function primaryRelease(shift) {
|
|||||||
if (ghosts) undoNote(g);
|
if (ghosts) undoNote(g);
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
const hint = /already built/.test(r.status) ? `${/[.!]$/.test(r.status) ? '' : '.'} Hold Ctrl to replace it.` : '';
|
const hint = /already built/.test(r.status) ? `${/[.!]$/.test(r.status) ? '' : '.'} Hold G to replace it.` : '';
|
||||||
const gtxt = ghosts ? `${r.n ? `Placed ${plural(r.n, P.name.toLowerCase())}; ` : ''}${plural(ghosts, 'more')} out of reach ${ghosts === 1 ? 'is a ghost' : 'are ghosts'} your bot builds when it gets close.` : '';
|
const gtxt = ghosts ? `${r.n ? `Placed ${plural(r.n, P.name.toLowerCase())}; ` : ''}${plural(ghosts, 'more')} out of reach ${ghosts === 1 ? 'is a ghost' : 'are ghosts'} your bot builds when it gets close.` : '';
|
||||||
if (r.n || ghosts) { playSfx('place'); setStatus(gtxt || (r.status ? r.status + hint : ''), 'info'); } else { setStatus((r.status || 'Nothing placed.') + hint); playSfx('error'); }
|
if (r.n || ghosts) { playSfx('place'); setStatus(gtxt || (r.status ? r.status + hint : ''), 'info'); } else { setStatus((r.status || 'Nothing placed.') + hint); playSfx('error'); }
|
||||||
GAME.drag = null; GAME.dragDir = -1; hudDirty = true;
|
GAME.drag = null; GAME.dragDir = -1; hudDirty = true;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ function renderBuildBar() {
|
|||||||
const status = performance.now() - GAME.statusT < 3200 ? GAME.status : '';
|
const status = performance.now() - GAME.statusT < 3200 ? GAME.status : '';
|
||||||
const ng = ghostBlocks.size + ghostMachines.length + ghostMolds.length;
|
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.ctrl, 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.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('|');
|
||||||
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('')
|
||||||
@@ -74,7 +74,7 @@ function renderBuildBar() {
|
|||||||
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>`
|
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}`
|
: P.filter ? `<span class="chip hot">${fc.inv ? 'carries' : 'drops'} ${fc.m.length ? sws(fc.m) : 'nothing'} · F</span>${dirChip}`
|
||||||
: dir ? dirChip : '';
|
: dir ? dirChip : '';
|
||||||
bar.querySelector('.bb-status').innerHTML = variant + (GAME.ctrl ? '<span class="chip hot">Replace · Ctrl</span>' : '')
|
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>` : '')
|
+ (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>` : '');
|
+ (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;
|
||||||
@@ -83,7 +83,7 @@ function renderBuildBar() {
|
|||||||
pop.innerHTML = `<div class="bb-title">${pieceIcon(tip.id)}<b>${esc(tip.name)}</b></div><div class="bb-chips">${chipCost(pieceCostNow(tip))}<span class="chip">${size}</span>${tip.power ? `<span class="chip">${tip.power} power</span>` : ''}</div>
|
pop.innerHTML = `<div class="bb-title">${pieceIcon(tip.id)}<b>${esc(tip.name)}</b></div><div class="bb-chips">${chipCost(pieceCostNow(tip))}<span class="chip">${size}</span>${tip.power ? `<span class="chip">${tip.power} power</span>` : ''}</div>
|
||||||
<div class="bb-desc">${esc(tip.desc)}</div><div class="bb-place"><b>How to place</b> ${tip.kind === 'block' ? 'Drag for a line, Shift-drag for a rectangle; out of reach becomes ghosts.' : 'Click to place.'}${tip.dirs && tip.kind === 'block' ? ' A dragged line runs the way you drag it; a single click uses <kbd>R</kbd>.' : tip.dirs ? ' <kbd>R</kbd> turns it.' : pieceUsesWall(tip) ? ' <kbd>R</kbd> picks the material.' : ''}</div>
|
<div class="bb-desc">${esc(tip.desc)}</div><div class="bb-place"><b>How to place</b> ${tip.kind === 'block' ? 'Drag for a line, Shift-drag for a rectangle; out of reach becomes ghosts.' : 'Click to place.'}${tip.dirs && tip.kind === 'block' ? ' A dragged line runs the way you drag it; a single click uses <kbd>R</kbd>.' : tip.dirs ? ' <kbd>R</kbd> turns it.' : pieceUsesWall(tip) ? ' <kbd>R</kbd> picks the material.' : ''}</div>
|
||||||
${tip.filter ? `<div class="bb-place">New ones will ${esc(filterSentence(GAME.filterCfg))}. <kbd>F</kbd> sets materials and mode (or click materials in the bag, or <kbd>T</kbd> over one); <kbd>F</kbd> on a placed filter changes its whole row.</div>` : ''}
|
${tip.filter ? `<div class="bb-place">New ones will ${esc(filterSentence(GAME.filterCfg))}. <kbd>F</kbd> sets materials and mode (or click materials in the bag, or <kbd>T</kbd> over one); <kbd>F</kbd> on a placed filter changes its whole row.</div>` : ''}
|
||||||
<div class="bb-place"><kbd>Ctrl</kbd> replace · <kbd>Ctrl</kbd>+<kbd>Z</kbd> undo · right click take back · middle mouse pick · hold <kbd>C</kbd>/<kbd>X</kbd> copy/cut · <kbd>V</kbd> paste</div>`;
|
<div class="bb-place">hold <kbd>G</kbd> replace · <kbd>Ctrl</kbd>+<kbd>Z</kbd> undo · right click take back · middle mouse pick · hold <kbd>C</kbd>/<kbd>X</kbd> copy/cut · <kbd>V</kbd> paste</div>`;
|
||||||
const tile = bar.querySelector(`[data-piece="${tip.id}"]`); if (!tile) { pop.hidden = true; return; }
|
const tile = bar.querySelector(`[data-piece="${tip.id}"]`); if (!tile) { pop.hidden = true; return; }
|
||||||
const r = tile.getBoundingClientRect(), br = bar.getBoundingClientRect(), w = pop.getBoundingClientRect().width;
|
const r = tile.getBoundingClientRect(), br = bar.getBoundingClientRect(), w = pop.getBoundingClientRect().width;
|
||||||
pop.style.left = `${Math.max(8, Math.min(innerWidth - w - 8, r.left + r.width / 2 - w / 2)) - br.left}px`;
|
pop.style.left = `${Math.max(8, Math.min(innerWidth - w - 8, r.left + r.width / 2 - w / 2)) - br.left}px`;
|
||||||
|
|||||||
+6
-6
@@ -11,7 +11,6 @@ window.addEventListener('keydown', e => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!GAME.running) return;
|
if (!GAME.running) return;
|
||||||
if (e.key === 'Control') { GAME.ctrl = true; return; }
|
|
||||||
if (e.ctrlKey || e.metaKey) {
|
if (e.ctrlKey || e.metaKey) {
|
||||||
if (k === 's') { e.preventDefault(); saveWorld('main'); }
|
if (k === 's') { e.preventDefault(); saveWorld('main'); }
|
||||||
else if (k === 'z' && !e.shiftKey) { e.preventDefault(); doUndo(); }
|
else if (k === 'z' && !e.shiftKey) { e.preventDefault(); doUndo(); }
|
||||||
@@ -30,6 +29,7 @@ window.addEventListener('keydown', e => {
|
|||||||
case 'r': rotate(); break;
|
case 'r': rotate(); break;
|
||||||
case 't': if (toolId() === 'vacuum') toggleCollectUnderCursor(); else if (toolId() === 'build' && piece().filter) pickFilterUnderCursor(); break;
|
case 't': if (toolId() === 'vacuum') toggleCollectUnderCursor(); else if (toolId() === 'build' && piece().filter) pickFilterUnderCursor(); break;
|
||||||
case 'f': doInteract(); break;
|
case 'f': doInteract(); break;
|
||||||
|
case 'g': GAME.replace = true; hudDirty = true; break;
|
||||||
case 'h': toggleLens(); break;
|
case 'h': toggleLens(); break;
|
||||||
case 'i': GAME.infoOverlay = !GAME.infoOverlay; playSfx('ui'); toast(GAME.infoOverlay ? 'Info overlay on: what every filter does, and which way belts and tubes run. I hides it.' : 'Info overlay off.'); break;
|
case 'i': GAME.infoOverlay = !GAME.infoOverlay; playSfx('ui'); toast(GAME.infoOverlay ? 'Info overlay on: what every filter does, and which way belts and tubes run. I hides it.' : 'Info overlay off.'); break;
|
||||||
case 'l': bot.lamp = !bot.lamp; playSfx('ui'); break;
|
case 'l': bot.lamp = !bot.lamp; playSfx('ui'); break;
|
||||||
@@ -43,9 +43,9 @@ window.addEventListener('keydown', e => {
|
|||||||
case 'Tab': $('bagpanel').hidden = !$('bagpanel').hidden; break;
|
case 'Tab': $('bagpanel').hidden = !$('bagpanel').hidden; break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
window.addEventListener('keyup', e => { const k = e.key.length === 1 ? e.key.toLowerCase() : e.key; keys.delete(k); if (e.key === 'Control') GAME.ctrl = false; if (k === 'c' || k === 'x') hudDirty = true; });
|
window.addEventListener('keyup', e => { const k = e.key.length === 1 ? e.key.toLowerCase() : e.key; keys.delete(k); if (k === 'g') { GAME.replace = false; hudDirty = true; } if (k === 'c' || k === 'x') hudDirty = true; });
|
||||||
window.addEventListener('blur', () => { keys.clear(); GAME.lmb = GAME.rmb = false; GAME.ctrl = false; GAME.selDrag = null; endRmbRecord(); });
|
window.addEventListener('blur', () => { keys.clear(); GAME.lmb = GAME.rmb = false; GAME.replace = false; GAME.selDrag = null; endRmbRecord(); });
|
||||||
canvas.addEventListener('mousemove', e => { GAME.ctrl = e.ctrlKey; GAME.mouse.x = e.clientX; GAME.mouse.y = e.clientY; GAME.mouse.in = true; if (GAME.usingPad) { GAME.usingPad = false; canvas.classList.remove('nocursor'); } });
|
canvas.addEventListener('mousemove', e => { GAME.mouse.x = e.clientX; GAME.mouse.y = e.clientY; GAME.mouse.in = true; if (GAME.usingPad) { GAME.usingPad = false; canvas.classList.remove('nocursor'); } });
|
||||||
canvas.addEventListener('mouseleave', () => { GAME.mouse.in = false; });
|
canvas.addEventListener('mouseleave', () => { GAME.mouse.in = false; });
|
||||||
canvas.addEventListener('contextmenu', e => e.preventDefault());
|
canvas.addEventListener('contextmenu', e => e.preventDefault());
|
||||||
canvas.addEventListener('mousedown', e => {
|
canvas.addEventListener('mousedown', e => {
|
||||||
@@ -57,7 +57,7 @@ canvas.addEventListener('mousedown', e => {
|
|||||||
else if (e.button === 1) { e.preventDefault(); pickUnderCursor(); }
|
else if (e.button === 1) { e.preventDefault(); pickUnderCursor(); }
|
||||||
});
|
});
|
||||||
window.addEventListener('mouseup', e => {
|
window.addEventListener('mouseup', e => {
|
||||||
if (e.button === 0) { GAME.ctrl = e.ctrlKey; if (GAME.lmb) primaryRelease(modShift()); GAME.lmb = false; }
|
if (e.button === 0) { if (GAME.lmb) primaryRelease(modShift()); GAME.lmb = false; }
|
||||||
else if (e.button === 2) { GAME.rmb = false; endRmbRecord(); }
|
else if (e.button === 2) { GAME.rmb = false; endRmbRecord(); }
|
||||||
});
|
});
|
||||||
canvas.addEventListener('wheel', e => {
|
canvas.addEventListener('wheel', e => {
|
||||||
@@ -74,6 +74,6 @@ document.addEventListener('visibilitychange', () => {
|
|||||||
if (document.hidden) { if (SET.muteUnfocused) AU.ctx.suspend(); }
|
if (document.hidden) { if (SET.muteUnfocused) AU.ctx.suspend(); }
|
||||||
else AU.ctx.resume();
|
else AU.ctx.resume();
|
||||||
});
|
});
|
||||||
// Ctrl+W closes the tab and the page cannot stop it, and Ctrl (replace) + W (fly) is an easy slip:
|
// Ctrl+W closes the tab and the page cannot stop it:
|
||||||
// while a world is open, ask the browser to confirm leaving
|
// while a world is open, ask the browser to confirm leaving
|
||||||
window.addEventListener('beforeunload', e => { if (GAME.running) { e.preventDefault(); e.returnValue = ''; } });
|
window.addEventListener('beforeunload', e => { if (GAME.running) { e.preventDefault(); e.returnValue = ''; } });
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@ function frame(now) {
|
|||||||
drawRain();
|
drawRain();
|
||||||
drawBot();
|
drawBot();
|
||||||
if (!GAME.hidePreviews) drawPreviews(); // tools/shot.py hides them for clean screenshots
|
if (!GAME.hidePreviews) drawPreviews(); // tools/shot.py hides them for clean screenshots
|
||||||
if (GAME.ctrl !== GAME.ctrlShown) { GAME.ctrlShown = GAME.ctrl; hudDirty = true; }
|
if (GAME.replace !== GAME.replaceShown) { GAME.replaceShown = GAME.replace; hudDirty = true; }
|
||||||
renderBuildBar();
|
renderBuildBar();
|
||||||
if (hudDirty || R.frame % 6 === 0) { renderSelCard(); renderBag(); renderWallet(); hudDirty = false; }
|
if (hudDirty || R.frame % 6 === 0) { renderSelCard(); renderBag(); renderWallet(); hudDirty = false; }
|
||||||
if (R.frame % 4 === 0) renderInfo();
|
if (R.frame % 4 === 0) renderInfo();
|
||||||
|
|||||||
+1
-1
@@ -171,7 +171,7 @@ function controlsHtml() {
|
|||||||
['Fly', 'W A S D'], ['Boost while flying', 'Shift'], ['Tool modifier (while not flying)', 'Shift'], ['Use tool', 'Left mouse'], ['Second action', 'Right mouse'],
|
['Fly', 'W A S D'], ['Boost while flying', 'Shift'], ['Tool modifier (while not flying)', 'Shift'], ['Use tool', 'Left mouse'], ['Second action', 'Right mouse'],
|
||||||
['Tools', '1 – 4'], ['Cycle what you blow · piece', 'Q / E'], ['Switch build tab', 'Shift + Q / E'], ['Variant: material · direction · flip', 'R'],
|
['Tools', '1 – 4'], ['Cycle what you blow · piece', 'Q / E'], ['Switch build tab', 'Shift + Q / E'], ['Variant: material · direction · flip', 'R'],
|
||||||
['Tick/untick what is under the cursor (Vacuum)', 'R or T'], ['Nozzle or brush size', '[ ] or Shift + wheel'],
|
['Tick/untick what is under the cursor (Vacuum)', 'R or T'], ['Nozzle or brush size', '[ ] or Shift + wheel'],
|
||||||
['Replace what is built (Build)', 'Hold Ctrl'], ['Copy a box of what you built', 'Hold C, drag'], ['Cut a box (refunded, paste it back)', 'Hold X, drag'], ['Paste again', 'V'], ['Undo building', 'Ctrl + Z'], ['Blueprint library', 'B'], ['Zoom', 'Mouse wheel'], ['Interact (gates, molds, fans, filters)', 'F'], ['Thermal lens', 'H'], ['Info overlay: filters, belt and tube directions', 'I'], ['Bot lamp', 'L'],
|
['Replace what is built (Build)', 'Hold G'], ['Copy a box of what you built', 'Hold C, drag'], ['Cut a box (refunded, paste it back)', 'Hold X, drag'], ['Paste again', 'V'], ['Undo building', 'Ctrl + Z'], ['Blueprint library', 'B'], ['Zoom', 'Mouse wheel'], ['Interact (gates, molds, fans, filters)', 'F'], ['Thermal lens', 'H'], ['Info overlay: filters, belt and tube directions', 'I'], ['Bot lamp', 'L'],
|
||||||
['Pick the piece under the cursor (with its direction and settings)', 'Middle mouse'], ['Notebook', 'N'], ['Clear all blueprint ghosts', 'Delete'], ['Save', 'Ctrl + S'], ['Pause · menu', 'Esc'], ['Mute', 'M'],
|
['Pick the piece under the cursor (with its direction and settings)', 'Middle mouse'], ['Notebook', 'N'], ['Clear all blueprint ghosts', 'Delete'], ['Save', 'Ctrl + S'], ['Pause · menu', 'Esc'], ['Mute', 'M'],
|
||||||
];
|
];
|
||||||
const pad = [
|
const pad = [
|
||||||
|
|||||||
+3
-3
@@ -176,7 +176,7 @@ function drawPreviews() {
|
|||||||
const afford = canAfford(pieceCostNow(P), cells.length);
|
const afford = canAfford(pieceCostNow(P), cells.length);
|
||||||
for (const [bx, by] of cells) {
|
for (const [bx, by] of cells) {
|
||||||
const err = canPlaceBlock(bx, by), rb = by * BW + bx;
|
const err = canPlaceBlock(bx, by), rb = by * BW + bx;
|
||||||
const swap = GAME.ctrl && !!blockP[rb] && /already built/.test(err) && !blockIs(rb, P.code, v, pieceExtra(P));
|
const swap = GAME.replace && !!blockP[rb] && /already built/.test(err) && !blockIs(rb, P.code, v, pieceExtra(P));
|
||||||
const ghost = !blockInReach(bx, by) && (!err || /bar/.test(err)); // out of reach: it will be a ghost
|
const ghost = !blockInReach(bx, by) && (!err || /bar/.test(err)); // out of reach: it will be a ghost
|
||||||
const ok = (!err || swap) && !ghost && afford;
|
const ok = (!err || swap) && !ghost && afford;
|
||||||
const [x, y, w, h] = blockRectPx(bx, by, 1, 1);
|
const [x, y, w, h] = blockRectPx(bx, by, 1, 1);
|
||||||
@@ -217,7 +217,7 @@ function drawPreviews() {
|
|||||||
const bw = P.kind === 'mold' ? MOLDS[P.mold].bw : P.bw, bh = P.kind === 'mold' ? MOLDS[P.mold].bh : P.bh;
|
const bw = P.kind === 'mold' ? MOLDS[P.mold].bw : P.bw, bh = P.kind === 'mold' ? MOLDS[P.mold].bh : P.bh;
|
||||||
const { bx, by } = stampOrigin(bw, bh);
|
const { bx, by } = stampOrigin(bw, bh);
|
||||||
const err = P.kind === 'mold' ? canPlaceMold(P.mold, bx, by) : canPlaceMachine(P.id, bx, by);
|
const err = P.kind === 'mold' ? canPlaceMold(P.mold, bx, by) : canPlaceMachine(P.id, bx, by);
|
||||||
const swap = !!err && GAME.ctrl && /already/.test(err) && !footprintBlocker(bx, by, bw, bh);
|
const swap = !!err && GAME.replace && /already/.test(err) && !footprintBlocker(bx, by, bw, bh);
|
||||||
const ok = (!err || swap) && inReach(bx * BS + bw * BS / 2, by * BS + bh * BS / 2) && canAfford(pieceCostNow(P), 1);
|
const ok = (!err || swap) && inReach(bx * BS + bw * BS / 2, by * BS + bh * BS / 2) && canAfford(pieceCostNow(P), 1);
|
||||||
const [x, y, w, h] = blockRectPx(bx, by, bw, bh);
|
const [x, y, w, h] = blockRectPx(bx, by, bw, bh);
|
||||||
if (swap && ok) { ctx.fillStyle = 'rgba(255,190,90,0.35)'; ctx.fillRect(x, y, w, h); }
|
if (swap && ok) { ctx.fillStyle = 'rgba(255,190,90,0.35)'; ctx.fillRect(x, y, w, h); }
|
||||||
@@ -250,7 +250,7 @@ function drawPreviews() {
|
|||||||
ctx.beginPath(); ctx.moveTo(cx - 10, cy); ctx.lineTo(cx - 4, cy); ctx.moveTo(cx + 4, cy); ctx.lineTo(cx + 10, cy); ctx.moveTo(cx, cy - 10); ctx.lineTo(cx, cy - 4); ctx.moveTo(cx, cy + 4); ctx.lineTo(cx, cy + 10); ctx.stroke();
|
ctx.beginPath(); ctx.moveTo(cx - 10, cy); ctx.lineTo(cx - 4, cy); ctx.moveTo(cx + 4, cy); ctx.lineTo(cx + 10, cy); ctx.moveTo(cx, cy - 10); ctx.lineTo(cx, cy - 4); ctx.moveTo(cx, cy + 4); ctx.lineTo(cx, cy + 10); ctx.stroke();
|
||||||
}
|
}
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
if (t === 'build' && GAME.ctrl && !sel && !pasting) drawTag('Replace', '255,190,90', p.x + BS * zp * 1.2, p.y + BS * zp * 1.4);
|
if (t === 'build' && GAME.replace && !sel && !pasting) drawTag('Replace', '255,190,90', p.x + BS * zp * 1.2, p.y + BS * zp * 1.4);
|
||||||
drawCursorLabel(p.x, p.y, t === 'vacuum' ? GAME.brush[GAME.tool] : t === 'ignite' || t === 'drill' ? 1 : 2);
|
drawCursorLabel(p.x, p.y, t === 'vacuum' ? GAME.brush[GAME.tool] : t === 'ignite' || t === 'drill' ? 1 : 2);
|
||||||
}
|
}
|
||||||
// a small labelled tag drawn on the world (screen pixels)
|
// a small labelled tag drawn on the world (screen pixels)
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ function loadCollect() { try { const a = JSON.parse(localStorage.getItem('deepgr
|
|||||||
const GAME = {
|
const GAME = {
|
||||||
running: false, paused: false, tool: 0, dropMat: 0, matChosen: false, wallMat: RSTONE, filterCfg: { m: [], inv: 0 }, pieceIdx: 0, pieceDir: {},
|
running: false, paused: false, tool: 0, dropMat: 0, matChosen: false, wallMat: RSTONE, filterCfg: { m: [], inv: 0 }, pieceIdx: 0, pieceDir: {},
|
||||||
brush: [3, 2, 0, 0, 0], collect: loadCollect(), tipsOpen: new Set(), cards: [], vacFilter: 0, vacLocked: false, rmbConsumed: false, rmbSeen: new Set(), lookHold: { x: 0, y: 0 },
|
brush: [3, 2, 0, 0, 0], collect: loadCollect(), tipsOpen: new Set(), cards: [], vacFilter: 0, vacLocked: false, rmbConsumed: false, rmbSeen: new Set(), lookHold: { x: 0, y: 0 },
|
||||||
paste: false, selDrag: null, rmbRec: null, dragDir: -1, ctrlShown: false, clip: null, drag: null, aim: { x: 0, y: 0 }, mouse: { x: 0, y: 0, in: false },
|
paste: false, selDrag: null, rmbRec: null, dragDir: -1, replaceShown: false, clip: null, drag: null, aim: { x: 0, y: 0 }, mouse: { x: 0, y: 0, in: false },
|
||||||
lmb: false, rmb: false, blowMix: false, drillW: 1, drillTune: 0, lastDug: 0, padRun: 0, status: '', statusKind: 'err', statusT: 0, usingPad: false, padAim: { x: 12, y: 0 },
|
lmb: false, rmb: false, blowMix: false, drillW: 1, drillTune: 0, lastDug: 0, padRun: 0, status: '', statusKind: 'err', statusT: 0, usingPad: false, padAim: { x: 12, y: 0 },
|
||||||
lastAutosave: 0, playTime: 0, lastHud: 0, worldName: '',
|
lastAutosave: 0, playTime: 0, lastHud: 0, worldName: '',
|
||||||
};
|
};
|
||||||
|
|||||||
+18
-13
@@ -27,7 +27,7 @@ with sync_playwright() as p:
|
|||||||
ev = pg.evaluate
|
ev = pg.evaluate
|
||||||
look = sys.argv[2] if len(sys.argv) > 2 else 'pixel'
|
look = sys.argv[2] if len(sys.argv) > 2 else 'pixel'
|
||||||
check('a new game starts fully zoomed in', ev('SET.zoom') == 12, ev('SET.zoom'))
|
check('a new game starts fully zoomed in', ev('SET.zoom') == 12, ev('SET.zoom'))
|
||||||
# Ctrl+W (Ctrl to replace, W to fly) closes the tab; with a world open the browser must ask first
|
# Ctrl+W closes the tab; with a world open the browser must ask first
|
||||||
leave = "(() => { const e = new Event('beforeunload', { cancelable: true }); dispatchEvent(e); return e.defaultPrevented; })()"
|
leave = "(() => { const e = new Event('beforeunload', { cancelable: true }); dispatchEvent(e); return e.defaultPrevented; })()"
|
||||||
check('leaving the page with a world open asks to confirm', ev(leave))
|
check('leaving the page with a world open asks to confirm', ev(leave))
|
||||||
check('but not on the title screen', not ev(f'(() => {{ GAME.running = false; const r = {leave}; GAME.running = true; return r; }})()'))
|
check('but not on the title screen', not ev(f'(() => {{ GAME.running = false; const r = {leave}; GAME.running = true; return r; }})()'))
|
||||||
@@ -339,16 +339,21 @@ with sync_playwright() as p:
|
|||||||
pg.keyboard.press('Escape'); settle(100)
|
pg.keyboard.press('Escape'); settle(100)
|
||||||
pg.keyboard.press('Control+z'); settle(300)
|
pg.keyboard.press('Control+z'); settle(300)
|
||||||
check('Ctrl+Z puts the cut row back', ev(f'blockP[({bby} + 1) * BW + {bbx} - 4]') == ev('BP_BELT') and ev('money') == m_before, ev('[money, GAME.status]'))
|
check('Ctrl+Z puts the cut row back', ev(f'blockP[({bby} + 1) * BW + {bbx} - 4]') == ev('BP_BELT') and ev('money') == m_before, ev('[money, GAME.status]'))
|
||||||
# Ctrl with a machine: it replaces the belt it is placed on
|
# G with a machine: it replaces the belt it is placed on
|
||||||
ok = select('3', "piece().id === 'fan'")
|
ok = select('3', "piece().id === 'fan'")
|
||||||
pg.mouse.move(*blk(bbx - 4, bby + 1)); settle(400)
|
pg.mouse.move(*blk(bbx - 4, bby + 1)); settle(400)
|
||||||
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 Ctrl', 'Ctrl' 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('Control'); 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 Ctrl shows replace mode', 'Replace' in ev("$('buildbar').textContent"))
|
check('holding G shows replace mode', 'Replace' in ev("$('buildbar').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('Control'); settle(100)
|
pg.mouse.down(); pg.mouse.up(); settle(100); pg.keyboard.up('g'); settle(100)
|
||||||
check('Ctrl-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'))
|
||||||
|
x_before, y_before = ev('[bot.x, bot.y]')
|
||||||
|
pg.keyboard.down('g'); pg.keyboard.down('d'); settle(300)
|
||||||
|
pg.keyboard.up('d'); pg.keyboard.up('g')
|
||||||
|
check('flying with WASD works while G (replace) is held', ev('bot.x') > x_before, ev('bot.x') - x_before)
|
||||||
|
ev(f'bot.x = {x_before}; bot.y = {y_before}; R.snapNext = true;'); settle(200)
|
||||||
# pipes are laid by dragging, like belts
|
# pipes are laid by dragging, like belts
|
||||||
ok = select('3', "piece().id === 'pipe'")
|
ok = select('3', "piece().id === 'pipe'")
|
||||||
check('pipes, pumps, outlets and the refiner are in the build menu', ok and all(x in ev("PIECES.filter(p => !p.hidden).map(p => p.id)") for x in ['pipe', 'pump', 'outlet', 'refiner']))
|
check('pipes, pumps, outlets and the refiner are in the build menu', ok and all(x in ev("PIECES.filter(p => !p.hidden).map(p => p.id)") for x in ['pipe', 'pump', 'outlet', 'refiner']))
|
||||||
@@ -360,7 +365,7 @@ with sync_playwright() as p:
|
|||||||
pg.keyboard.press('Control+z'); settle(200)
|
pg.keyboard.press('Control+z'); settle(200)
|
||||||
check('and Ctrl+Z swaps the belt back', ev(f'blockP[({bby} + 1) * BW + {bbx} - 4]') == ev('BP_BELT') and not ev(f"machines.some(m => m && m.type === 'fan' && m.x === ({bbx} - 4) * 4)"), ev('GAME.status'))
|
check('and Ctrl+Z swaps the belt back', ev(f'blockP[({bby} + 1) * BW + {bbx} - 4]') == ev('BP_BELT') and not ev(f"machines.some(m => m && m.type === 'fan' && m.x === ({bbx} - 4) * 4)"), ev('GAME.status'))
|
||||||
|
|
||||||
# 7b. filter: choose several materials with F, place a line, replace belts with Ctrl, edit the whole line
|
# 7b. filter: choose several materials with F, place a line, replace belts with G, edit the whole line
|
||||||
ev('seen[CUORE] = 1; seen[FEORE] = 1; seen[SAND] = 1; money = 1000; GAME.filterCfg = { m: [], inv: 0 };')
|
ev('seen[CUORE] = 1; seen[FEORE] = 1; seen[SAND] = 1; money = 1000; GAME.filterCfg = { m: [], inv: 0 };')
|
||||||
ok = select('3', "piece().id === 'filter'")
|
ok = select('3', "piece().id === 'filter'")
|
||||||
check('select filter', ok)
|
check('select filter', ok)
|
||||||
@@ -375,16 +380,16 @@ with sync_playwright() as p:
|
|||||||
drag(lambda: blk(bbx - 6, fy), lambda: blk(bbx - 3, fy))
|
drag(lambda: blk(bbx - 6, fy), lambda: blk(bbx - 3, fy))
|
||||||
nf = ev('blockP.reduce((a, v) => a + (v === BP_FILTER), 0)')
|
nf = ev('blockP.reduce((a, v) => a + (v === BP_FILTER), 0)')
|
||||||
check('filters placed by drag, dropping both ores and carrying right', nf == 4 and ev(f'blockV[{fy}*BW+{bbx-5}]') == 128 and ev(f'filterInfo({fy}*BW+{bbx-5}).m.length') == 2, f"{nf} {ev(f'JSON.stringify(filterInfo({fy}*BW+{bbx-5}))')}")
|
check('filters placed by drag, dropping both ores and carrying right', nf == 4 and ev(f'blockV[{fy}*BW+{bbx-5}]') == 128 and ev(f'filterInfo({fy}*BW+{bbx-5}).m.length') == 2, f"{nf} {ev(f'JSON.stringify(filterInfo({fy}*BW+{bbx-5}))')}")
|
||||||
# two belts at the end of the line: plain dragging leaves them, Ctrl-dragging swaps them for filters
|
# two belts at the end of the line: plain dragging leaves them, G-dragging swaps them for filters
|
||||||
ev(f"placeBlock(BP_BELT, 1, {bbx-2}, {fy}, true); placeBlock(BP_BELT, 1, {bbx-1}, {fy}, true);")
|
ev(f"placeBlock(BP_BELT, 1, {bbx-2}, {fy}, true); placeBlock(BP_BELT, 1, {bbx-1}, {fy}, true);")
|
||||||
drag(lambda: blk(bbx - 2, fy), lambda: blk(bbx - 1, fy))
|
drag(lambda: blk(bbx - 2, fy), lambda: blk(bbx - 1, fy))
|
||||||
check('dragging over built belts leaves them and says how to replace', ev(f'blockP[{fy}*BW+{bbx-2}] === BP_BELT') and 'Ctrl' in ev('GAME.status'), ev('GAME.status'))
|
check('dragging over built belts leaves them and says how to replace', ev(f'blockP[{fy}*BW+{bbx-2}] === BP_BELT') and 'Hold G' in ev('GAME.status'), ev('GAME.status'))
|
||||||
pg.keyboard.down('Control'); settle(50)
|
pg.keyboard.down('g'); settle(50)
|
||||||
pg.mouse.move(*blk(bbx - 2, fy)); settle(300)
|
pg.mouse.move(*blk(bbx - 2, fy)); settle(300)
|
||||||
pg.screenshot(path=str(out / '07c-replace-preview.png'))
|
pg.screenshot(path=str(out / '07c-replace-preview.png'))
|
||||||
drag(lambda: blk(bbx - 2, fy), lambda: blk(bbx - 1, fy))
|
drag(lambda: blk(bbx - 2, fy), lambda: blk(bbx - 1, fy))
|
||||||
pg.keyboard.up('Control'); settle(50)
|
pg.keyboard.up('g'); settle(50)
|
||||||
check('Ctrl-dragging replaces them with filters', ev(f'blockP[{fy}*BW+{bbx-2}] === BP_FILTER && blockP[{fy}*BW+{bbx-1}] === BP_FILTER') and ev(f'filterRun({fy}*BW+{bbx-5}).length') == 6, ev('GAME.status'))
|
check('G-dragging replaces them with filters', ev(f'blockP[{fy}*BW+{bbx-2}] === BP_FILTER && blockP[{fy}*BW+{bbx-1}] === BP_FILTER') and ev(f'filterRun({fy}*BW+{bbx-5}).length') == 6, ev('GAME.status'))
|
||||||
pg.mouse.move(*blk(bbx - 5, fy)); settle(200)
|
pg.mouse.move(*blk(bbx - 5, fy)); settle(200)
|
||||||
check('hovering a filter says what it drops and carries', 'copper ore' in ev('JSON.stringify(hoverInfo(Math.floor(GAME.aim.x), Math.floor(GAME.aim.y)))').lower())
|
check('hovering a filter says what it drops and carries', 'copper ore' in ev('JSON.stringify(hoverInfo(Math.floor(GAME.aim.x), Math.floor(GAME.aim.y)))').lower())
|
||||||
ev(f"for (let y = {fy*4 - 10}; y < {fy*4 - 1}; y++) for (let x = {(bbx-6)*4}; x < {(bbx-5)*4 + 2}; x++) setCell(y*W+x, [SAND, CUORE, FEORE][(x+y)%3], 20, 0);")
|
ev(f"for (let y = {fy*4 - 10}; y < {fy*4 - 1}; y++) for (let x = {(bbx-6)*4}; x < {(bbx-5)*4 + 2}; x++) setCell(y*W+x, [SAND, CUORE, FEORE][(x+y)%3], 20, 0);")
|
||||||
|
|||||||
Reference in New Issue
Block a user