"Doesn't connect" marks on transport ends that would join one block off (t67 phase 4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+14
-14
@@ -281,10 +281,10 @@ Pipes, pumps and outlets: each joined line holds one liquid as a count, not as g
|
||||
Per-tick machine work: steam engines, fans, belts, filters, screw conveyors (machinesTick).
|
||||
- functions: engineIntake, fanBlow, machinesTick
|
||||
|
||||
### src/sim/joins.js (73 lines)
|
||||
### src/sim/joins.js (125 lines)
|
||||
Travel-directed transport ends. Precomputed probes; world/plan queries allocate nothing. End indices stay geometric: left/right, or up/down for vertical screws.
|
||||
- functions: pieceEnds, joinCandidate, endJoin, pieceJoins
|
||||
- variables: JOIN_ENDS
|
||||
- functions: pieceEnds, joinCandidate, endJoin, pieceJoins, endAt, joinIsDrop, joinSnap, nearMiss
|
||||
- variables: JOIN_ENDS, NO_PIECE
|
||||
|
||||
### src/sim/conveyors.js (211 lines)
|
||||
How belts, filters and screw conveyors move what is on or in them. What rests on a belt or filter rides along: the whole stack standing on the carrying surface steps one cell toward d together (belts run front of the line first, so the way ahead is clear). If a powder grain is blocked, the load above it stands still and the belt slips underneath. A filter's own material drops through it instead, unless it is already full.
|
||||
@@ -388,7 +388,7 @@ Drawing (src/render/): browser only. This file: the renderer state R, colour tab
|
||||
Propagated coloured light from glowing and burning cells.
|
||||
- functions: builtForLight, looseForLight, computeLight
|
||||
|
||||
### src/render/slope-belts.js (169 lines)
|
||||
### src/render/slope-belts.js (167 lines)
|
||||
Drawing only: the face stays on its simulation cells; the band hangs below it.
|
||||
- functions: slopeBeltConnections, flatBeltCaps, prepareSlopeBelts, slopeBeltSegment, slopeBeltCorner, slopeBeltShape, slopeBeltAt, slopeBeltColor, screenLoaded, slopeWallShape, slopeWallAt, slopeDetailAt, slopePreviewCleat
|
||||
- variables: slopeBeltJoins, slopeBeltHalo, beltCaps, slopeAcross, slopeAlong, slopeDistance, slopeWallOwner
|
||||
@@ -398,7 +398,7 @@ Screw casing geometry shared by both looks, ghosts and placement previews. smoot
|
||||
- functions: diagonalScrewJoins, straightScrewJoins, diagonalScrewShape, diagonalScrewBlock
|
||||
- variables: SCREW_CLASS, diagonalJoins, diagonalAcross, diagonalAlong, diagonalCap
|
||||
|
||||
### src/render/world.js (405 lines)
|
||||
### src/render/world.js (404 lines)
|
||||
Scale colour without changing the smooth-look class.
|
||||
- functions: fogColor, drawWorld
|
||||
|
||||
@@ -406,16 +406,16 @@ Scale colour without changing the smooth-look class.
|
||||
Machine overlays: fans, engines, silo and the rest, drawn on top of the world.
|
||||
- functions: cellRect, drawHazardRims, drawMachines, drawProcBlocks, drawSilo, drawSiloPixel, drawSiloWindow, drawRackBlocks
|
||||
|
||||
### src/render/overlays.js (229 lines)
|
||||
### src/render/overlays.js (251 lines)
|
||||
Other overlays: floating text, rain and snow, flow arrows, filter tags, molds, the bot, particles.
|
||||
- functions: spawnFloater, drawRain, drawFlowArrows, drawRunOutline, drawFilterTags, drawFloaters, moldLabel, drawMolds, drawBot, spawnDebris, spawnSpark, spawnSuck, drawParticles
|
||||
- functions: spawnFloater, drawRain, drawFlowArrows, drawJoinMark, drawJoinMarks, drawRunOutline, drawFilterTags, drawFloaters, moldLabel, drawMolds, drawBot, spawnDebris, spawnSpark, spawnSuck, drawParticles
|
||||
|
||||
### src/render/smooth.js (99 lines)
|
||||
Smooth look: WebGL setup, texture uploads, drawing and adaptive resolution.
|
||||
- functions: smCompile, smoothAvailable, smUpload, smoothDraw, smoothPace
|
||||
- variables: SM, smBytes
|
||||
|
||||
### src/render/slope-belt-shader.js (126 lines)
|
||||
### src/render/slope-belt-shader.js (111 lines)
|
||||
Smooth slope shading, kept beside the shared CPU geometry in slope-belts.js.
|
||||
- variables: SM_SLOPE_HELPERS, SM_SLOPE_DRAW
|
||||
|
||||
@@ -480,9 +480,9 @@ The build menu along the bottom: categories, piece tiles and their icons.
|
||||
- functions: pieceIcon, catOf, selectBuildCat, cycleBuildCat, buildVariantChips, renderBuildBar, setupBuildBar
|
||||
- variables: BUILD_CATS, BUILD_ORDER, PIECE_ICONS, lastBuildKey, lastBuildMenu
|
||||
|
||||
### src/ui/selection.js (122 lines)
|
||||
### src/ui/selection.js (133 lines)
|
||||
Tool selection: cycling, drill tuning, piece variants and costs.
|
||||
- functions: pieceSize, pieceUsesWall, setDrillWidth, tuneUnderCursor, cycleItem, cycleWall, rotate, toggleBlow, pickFilterUnderCursor, toggleFilterMat, setBrush, pieceV, pieceExtra, filterBlockAt, chipCost, pieceCostNow, pieceCode, turnSlope
|
||||
- functions: pieceSize, pieceUsesWall, setDrillWidth, tuneUnderCursor, cycleItem, cycleWall, rotate, toggleBlow, pickFilterUnderCursor, toggleFilterMat, setBrush, pieceV, dragPlan, pieceExtra, filterBlockAt, chipCost, pieceCostNow, pieceCode, turnSlope
|
||||
|
||||
### src/ui/selection-pickers.js (61 lines)
|
||||
Selection pickers: filter rules, filter settings and wall materials. What a filter can hold: loose things and bars you have found, plus anything it already holds
|
||||
@@ -493,7 +493,7 @@ Selection panels: current tool card and bag materials.
|
||||
- functions: renderSelCard, renderBag
|
||||
- variables: lastBagKey
|
||||
|
||||
### src/ui/info.js (195 lines)
|
||||
### src/ui/info.js (200 lines)
|
||||
Discovery cards, hover info (I) and the cursor label.
|
||||
- functions: showCard, howText, discoveryCard, reactionsOf, hoverInfo, renderInfo, drawCursorLabel, pipeContentsText
|
||||
|
||||
@@ -506,7 +506,7 @@ Keyboard and mouse listeners.
|
||||
- functions: isTyping
|
||||
- variables: canvas
|
||||
|
||||
### src/ui/actions.js (244 lines)
|
||||
### src/ui/actions.js (242 lines)
|
||||
What input does: overlays, F, pick, copy / cut / paste, undo, building and tool use each frame. Which overlays are on, shown as a coloured frame round the screen and a named badge
|
||||
- functions: updateOverlayUI, toggleLens, doInteract, pickUnderCursor, aimBlock, stampOrigin, selModeHeld, startPaste, stopSelectAndPaste, plural, piecesIn, doUndo, endRmbRecord, dragDirFor, blockLabel, primaryPress, primaryRelease, secondaryPress, toolTick
|
||||
- variables: ovShown
|
||||
@@ -516,7 +516,7 @@ Gamepad input.
|
||||
- functions: padPressed, pollPad
|
||||
- variables: PAD
|
||||
|
||||
### src/ui/previews.js (240 lines)
|
||||
### src/ui/previews.js (241 lines)
|
||||
Cursor previews: shared grid helpers, air streaks and tool preview dispatch.
|
||||
- functions: blockRectPx, drawGrid, previewColor, drawAirStreaks, drawReachCircle, drawPreviews, previewBlock
|
||||
|
||||
@@ -539,7 +539,7 @@ Shared world colours, minimap and full map.
|
||||
- functions: resetMap, mapArea, updateMap, drawMap, mapFit, drawFullMap, openMap, zoomMap, zoomMinimap
|
||||
- variables: MAP_S, MAP
|
||||
|
||||
### src/ui/loop.js (135 lines)
|
||||
### src/ui/loop.js (136 lines)
|
||||
The frame loop and boot.
|
||||
- functions: scanVisible, frame, boot
|
||||
- variables: lastT, acc, lastRender, frameMs, simMs, fpsSm, ambientT
|
||||
|
||||
@@ -4,6 +4,19 @@ 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
|
||||
open or not verified. Keep each entry short; the git history has the details.
|
||||
|
||||
## 2026-10-27: t67 phase 4, "doesn't connect" marks (Claude Code)
|
||||
|
||||
- `nearMiss(bx, by, end, planned)` in `src/sim/joins.js`: an end with no join that would join
|
||||
seamlessly, with a diagonal piece involved, one block up or down in empty space. Drop joins
|
||||
count as connected; straight beside straight is never marked. It checks the moved block
|
||||
through a small override view of `planned`, so ghost maps are never copied.
|
||||
- Marks: a red ring with a slash at the end, on built pieces and ghosts (`drawJoinMarks` in
|
||||
`src/render/overlays.js`, called every frame from `src/ui/loop.js`) and at the ends of a drag
|
||||
preview (`src/ui/previews.js`). Hover says "doesn't connect: place it one block higher/lower"
|
||||
(`src/ui/info.js`). This covers t58's mark for belts and the same cases for screws.
|
||||
- Tests: 7 nearMiss checks in `test/t_joins.js`; `python3 test/run_all.py --browser` all passed.
|
||||
`docs/PROJECT_MAP.md` regenerated.
|
||||
|
||||
## 2026-10-27: t67 phase 3 shared drag placement, and snapping (Claude Code)
|
||||
|
||||
- `dragPlan(P, a, b, shift)` in `src/ui/selection.js` gives code, direction, variant, blocks and
|
||||
|
||||
@@ -53,6 +53,28 @@ function drawFlowArrows() {
|
||||
}
|
||||
ctx.restore();
|
||||
}
|
||||
// "Doesn't connect": a red ring with a slash at a transport end that would join one block up or
|
||||
// down (nearMiss), on built pieces, ghosts and the drag preview alike.
|
||||
function drawJoinMark(bx, by, end, code, v) {
|
||||
const e = pieceEnds(code, v)[end], ctx = R.ctx, r = Math.max(3, BS * R.zp * 0.24);
|
||||
const c = worldToScreenPx(bx * BS + BS / 2 + e.dx * BS / 2, by * BS + BS / 2 + e.dy * BS / 2);
|
||||
ctx.save();
|
||||
ctx.fillStyle = 'rgba(14,12,11,0.7)'; ctx.beginPath(); ctx.arc(c.x, c.y, r * 1.2, 0, Math.PI * 2); ctx.fill();
|
||||
ctx.strokeStyle = '#ef6a55'; ctx.lineWidth = Math.max(1.2, r * 0.3);
|
||||
ctx.beginPath(); ctx.arc(c.x, c.y, r * 0.75, 0, Math.PI * 2); ctx.stroke();
|
||||
ctx.beginPath(); ctx.moveTo(c.x - r * 0.53, c.y + r * 0.53); ctx.lineTo(c.x + r * 0.53, c.y - r * 0.53); ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
function drawJoinMarks() {
|
||||
const v = viewCells();
|
||||
const bx0 = Math.max(0, Math.floor(R.camX / BS)), bx1 = Math.min(BW - 1, Math.floor((R.camX + v.w) / BS));
|
||||
const by0 = Math.max(0, Math.floor(R.camY / BS)), by1 = Math.min(BH - 1, Math.floor((R.camY + v.h) / BS));
|
||||
for (let by = by0; by <= by1; by++) for (let bx = bx0; bx <= bx1; bx++) {
|
||||
const b = by * BW + bx, ghost = ghostBlocks.get(b), code = ghost ? ghost[0] : blockP[b], vv = ghost ? ghost[1] : blockV[b];
|
||||
if (!code || !pieceEnds(code, vv).length) continue;
|
||||
for (let end = 0; end < 2; end++) if (nearMiss(bx, by, end, ghostBlocks.size ? ghostBlocks : null)) drawJoinMark(bx, by, end, code, vv);
|
||||
}
|
||||
}
|
||||
// Outline a run of blocks: strong while it is being edited, faint when pointed at
|
||||
function drawRunOutline(run, strong) {
|
||||
if (!run || !run.length) return;
|
||||
|
||||
@@ -103,3 +103,23 @@ function joinSnap(code, v, cells) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// "Doesn't connect": an end with no join that would join seamlessly, with a diagonal piece
|
||||
// involved, if its block sat one higher (-1) or lower (+1) in empty space. 0 when no near miss.
|
||||
// A drop join counts as connected. The moved block is tried through a small override view of
|
||||
// `planned`, so ghost maps are never copied.
|
||||
const NO_PIECE = [0, 0];
|
||||
function nearMiss(bx, by, end, planned = null) {
|
||||
const b = by * BW + bx, p = planned && planned.get(b), code = p ? p[0] : blockP[b], v = p ? p[1] : blockV[b];
|
||||
const own = pieceEnds(code, v);
|
||||
if (!own[end] || endJoin(bx, by, end, planned) >= 0) return 0;
|
||||
const entry = [code, v];
|
||||
for (const dy of [-1, 1]) {
|
||||
const y = by + dy, nb = y * BW + bx;
|
||||
if (y < 0 || y >= BH || blockP[nb] || (planned && planned.get(nb))) continue;
|
||||
const view = { get: k => k === b ? NO_PIECE : k === nb ? entry : planned ? planned.get(k) : undefined };
|
||||
view.has = k => view.get(k) !== undefined;
|
||||
const j = endJoin(bx, y, end, view);
|
||||
if (j >= 0 && !joinIsDrop(bx, y, end, j, view) && (own[end].diagonal || endAt(j >> 1, j & 1, view).diagonal)) return dy;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -132,6 +132,11 @@ function hoverInfo(ax, ay) {
|
||||
if (b === BP_OUTLET) t2.push(v & 4 ? 'closed · press F' : 'open · F closes it');
|
||||
}
|
||||
else if (isWall(b) && PH[m] === PH_SOLID && !(flags[i] & F_NAT)) t1 = `${MD[m].name} ${isSlope(b) ? 'sloped wall' : 'block'}`;
|
||||
const jb = ((ay / BS) | 0) * BW + ((ax / BS) | 0);
|
||||
for (let end = 0; end < 2 && !id && !bar; end++) {
|
||||
const miss = nearMiss(jb % BW, (jb / BW) | 0, end);
|
||||
if (miss) { t2.push(`doesn't connect: place it one block ${miss < 0 ? 'higher' : 'lower'}`); warn = warn || "doesn't connect"; break; }
|
||||
}
|
||||
const n = nets[netId[i]];
|
||||
if (n && isPow[i] && !POWER_FREE) t2.push(`network ${n.supply.toFixed(1)} supply / ${n.demand.toFixed(1)} demand`);
|
||||
if (player.lens && !bar) t2.push(fmtT(temp[i]));
|
||||
|
||||
@@ -71,6 +71,7 @@ function frame(now) {
|
||||
const hb = GAME.mouse.in || GAME.usingPad ? filterBlockAt(Math.floor(GAME.aim.x), Math.floor(GAME.aim.y)) : -1;
|
||||
if (GAME.editRun) drawRunOutline(GAME.editRun, true); else if (hb >= 0) drawRunOutline(filterRun(hb), false);
|
||||
if (GAME.infoOverlay) drawFlowArrows();
|
||||
drawJoinMarks();
|
||||
drawFilterTags(!!GAME.infoOverlay, GAME.editRun ? GAME.editRun[0] : hb);
|
||||
drawFloaters();
|
||||
drawRain();
|
||||
|
||||
@@ -129,6 +129,9 @@ function drawPreviews() {
|
||||
chev(); ctx.strokeStyle = 'rgba(255,224,160,0.98)'; ctx.lineWidth = Math.max(1.2, zp * 0.45); chev();
|
||||
}
|
||||
}
|
||||
// ends of the line that would join one block up or down get the "doesn't connect" mark
|
||||
for (const [bx, by] of cells.length > 1 ? [cells[0], cells[cells.length - 1]] : cells)
|
||||
for (let end = 0; end < 2; end++) if (nearMiss(bx, by, end, planned)) drawJoinMark(bx, by, end, code, v);
|
||||
if (GAME.drag && cells.length > 1) {
|
||||
const label = `${cells.length} × ${costText(pieceCostNow(P), 1)}`;
|
||||
ctx.font = `600 12px ui-monospace, monospace`; ctx.fillStyle = 'rgba(14,12,11,0.85)';
|
||||
|
||||
@@ -187,4 +187,20 @@ check('a downhill screw started one block right and one lower than a screw end s
|
||||
check('a screw that already joins stays put',
|
||||
snap('placeBlock(BP_SCREW,1,10,10,true)', 'BP_SCREW', 5, [[11,10],[12,11]]) === 0);
|
||||
check('pieces with no join ends never snap', snap('', 'BP_PIPE', 0, [[11,10]]) === 0);
|
||||
// "Doesn't connect" marks (t67 phase 4): nearMiss says which way one block would make the join.
|
||||
const miss = (setup, expr) => { flatWorld(run, 96, 96, 92); return run(`(()=>{${setup};return ${expr};})()`); };
|
||||
check('a flat belt level with a downhill belt low end is marked: place it one lower (t58)',
|
||||
miss('placeBlock(BP_BELT_DOWN,1,10,10,true);placeBlock(BP_BELT_DOWN,1,11,11,true);placeBlock(BP_BELT,1,12,11,true)', 'nearMiss(12,11,0)') === 1);
|
||||
check('a flat belt one lower at that low end is joined and unmarked',
|
||||
miss('placeBlock(BP_BELT_DOWN,1,10,10,true);placeBlock(BP_BELT_DOWN,1,11,11,true);placeBlock(BP_BELT,1,12,12,true)', 'endJoin(12,12,0)>=0 && nearMiss(12,12,0)===0 && nearMiss(11,11,1)===0'));
|
||||
check('a screw end diagonally off a downhill screw start is marked on both pieces',
|
||||
miss('placeBlock(BP_SCREW,1,10,10,true);placeBlock(BP_SCREW,5,11,11,true)', '[nearMiss(10,10,1),nearMiss(11,11,0)].join()') === '1,-1');
|
||||
check('a drop join (flat feeding an uphill low end on its row) counts as connected',
|
||||
miss('placeBlock(BP_BELT,1,10,10,true);placeBlock(BP_BELT_UP,1,11,10,true)', 'nearMiss(10,10,1)===0 && nearMiss(11,10,0)===0'));
|
||||
check('straight belts one row apart are never marked',
|
||||
miss('placeBlock(BP_BELT,1,10,10,true);placeBlock(BP_BELT,1,11,11,true)', 'nearMiss(10,10,1)===0 && nearMiss(11,11,0)===0'));
|
||||
check('no mark when the block that would fix it is taken',
|
||||
miss('placeBlock(BP_BELT_DOWN,1,10,10,true);placeBlock(BP_BELT_DOWN,1,11,11,true);placeBlock(BP_BELT,1,12,11,true);placeBlock(BP_WALL,RSTONE,12,12,true)', 'nearMiss(12,11,0)') === 0);
|
||||
check('ghosts are checked through their planned map without copying it',
|
||||
miss('ghostBlocks.set(10*BW+10,[BP_SCREW,1]);ghostBlocks.set(11*BW+11,[BP_SCREW,5])', 'nearMiss(10,10,1,ghostBlocks)===1 && ghostBlocks.size===2'));
|
||||
done();
|
||||
|
||||
Reference in New Issue
Block a user