Build range follows the screen; out-of-range replace ghosts replace (t64)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 16:41:32 -04:00
co-authored by Claude Opus 5.5
parent 6bc4fd5928
commit ef4ba6d24a
11 changed files with 77 additions and 16 deletions
+1 -1
View File
@@ -209,7 +209,7 @@ function blockRect(bx0, by0, bx1, by1) {
for (let y = Math.min(by0, by1); y <= Math.max(by0, by1); y++) for (let x = Math.min(bx0, bx1); x <= Math.max(bx0, bx1); x++) out.push([x, y]);
return out;
}
function blockInReach(bx, by) { return inReach(bx * BS + BS / 2, by * BS + BS / 2); }
function blockInReach(bx, by) { return inBuildRange(bx * BS + BS / 2, by * BS + BS / 2); }
// Is block b already exactly this piece (same kind, variant and, for a filter, settings)?
function blockIs(b, code, v, extra) {
if (blockP[b] !== code) return false;
+13
View File
@@ -1,5 +1,18 @@
// Tool helpers: reach, brushes, and plain digging.
// ---------- Tools ----------
// Building reaches about as far as the screen shows (t64): the view box the UI sets each frame
// (setBuildRange), at least REACH from its centre and at most BUILD_MAX; anything beyond becomes
// a blueprint ghost. With no view (tests, headless), a REACH box around the bot.
const BUILD_MAX = 200;
let buildBox = null;
function setBuildRange(cx, cy, w, h) {
const hw = Math.max(REACH, Math.min(BUILD_MAX, w / 2)), hh = Math.max(REACH, Math.min(BUILD_MAX, h / 2));
if (!buildBox) buildBox = { cx, cy, hw, hh }; else { buildBox.cx = cx; buildBox.cy = cy; buildBox.hw = hw; buildBox.hh = hh; }
}
function inBuildRange(x, y) {
if (!buildBox) { const dx = x - (bot.x + bot.w / 2), dy = y - (bot.y + bot.h / 2); return Math.abs(dx) <= REACH && Math.abs(dy) <= REACH; }
return Math.abs(x - buildBox.cx) <= buildBox.hw && Math.abs(y - buildBox.cy) <= buildBox.hh;
}
function inReach(x, y) { const dx = x - (bot.x + bot.w / 2), dy = y - (bot.y + bot.h / 2); return dx * dx + dy * dy <= REACH * REACH; }
function forBrush(cx, cy, r, fn) {
const r2 = (r + 0.5) * (r + 0.5);
+9 -3
View File
@@ -54,13 +54,19 @@ function ghostTick0() {
while (budget > 0 && checks-- > 0 && ghostBlocks.size) {
let nx = ghostIter.next();
if (nx.done) { ghostIter = ghostBlocks.entries(); nx = ghostIter.next(); if (nx.done) break; }
const [b, [code, v, e]] = nx.value;
const [b, [code, v, e, swap]] = nx.value;
const bx = b % BW, by = (b / BW) | 0;
if (blockP[b] === code && blockV[b] === (isFilter(code) ? v & 128 : v)) {
if (isFilter(code) && e && !sameFilter(filterInfo(b), cleanFilterCfg(e))) setFilter(b, e); // same piece, new settings
ghostBlocks.delete(b); ghostBlocked.delete(b); continue;
}
if (!blockInReach(bx, by)) continue;
// a replacing ghost takes down what is there (refunded) and builds itself, like a replace in reach
if (swap && blockP[b]) {
if (blockP[b] === code && isFilter(code)) { blockV[b] = filterVariant(code, v); setFilter(b, e); filtersDirty = true; ghostBlocks.delete(b); ghostBlocked.delete(b); budget--; continue; }
if (!canAfford(pieceCost(code, v), 1)) { ghostMissing++; continue; }
removeBlock(bx, by);
}
const err = canPlaceBlock(bx, by, code);
if (err) { if (err.startsWith('Needs')) ghostBlocked.add(b); continue; }
if (!canAfford(pieceCost(code, v), 1)) { ghostMissing++; continue; }
@@ -70,7 +76,7 @@ function ghostTick0() {
}
for (let k = ghostMachines.length - 1; k >= 0 && budget > 0; k--) {
const g = ghostMachines[k], P = PIECE_BY_ID[g.type];
if (!inReach(g.bx * BS + P.w / 2, g.by * BS + P.h / 2)) continue;
if (!inBuildRange(g.bx * BS + P.w / 2, g.by * BS + P.h / 2)) continue;
if (canPlaceMachine(g.type, g.bx, g.by)) continue;
if (!canAfford(P.cost, 1)) { ghostMissing++; continue; }
placeMachineAt(g.type, g.dir, g.bx, g.by); ghostMachines.splice(k, 1); budget--;
@@ -78,7 +84,7 @@ function ghostTick0() {
}
for (let k = ghostMolds.length - 1; k >= 0 && budget > 0; k--) {
const g = ghostMolds[k], md = MOLDS[g.kind];
if (!inReach(g.bx * BS + md.w / 2, g.by * BS + md.h / 2)) continue;
if (!inBuildRange(g.bx * BS + md.w / 2, g.by * BS + md.h / 2)) continue;
if (canPlaceMold(g.kind, g.bx, g.by)) continue;
if (!canAfford(wallCost(g.wall, md.walls), 1)) { ghostMissing++; continue; }
addMold(g.kind, g.bx, g.by, g.wall, false); ghostMolds.splice(k, 1); budget--;