Fog of war: wider fades and a grainy edge instead of stepped lines (t17)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -237,6 +237,8 @@ above, including:
|
|||||||
- **Fog of war also reveals the ground around the bot (t17, 2026-10-24).** About 20 grains in every
|
- **Fog of war also reveals the ground around the bot (t17, 2026-10-24).** About 20 grains in every
|
||||||
direction, through rock, fully clear with a soft edge. Line of sight alone hid everything inside
|
direction, through rock, fully clear with a soft edge. Line of sight alone hid everything inside
|
||||||
the ground, so the owner couldn't see what was around them or where to dig.
|
the ground, so the owner couldn't see what was around them or where to dig.
|
||||||
|
As tuned: clear to 12 grains, fading out by 24, with a grainy edge; the owner found the first
|
||||||
|
version's narrow fade too sharp.
|
||||||
|
|
||||||
## Ideas not built yet
|
## Ideas not built yet
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -221,10 +221,10 @@ World state: the per-cell typed arrays, alloc(), ambient temperature by depth, w
|
|||||||
- functions: emit, bagCap, discover, alloc, depthU, depthY, computeAmbient, fillNoise, wakeXY, wakeHeatXY, wakeIdx, wakeHeatIdx
|
- functions: emit, bagCap, discover, alloc, depthU, depthY, computeAmbient, fillNoise, wakeXY, wakeHeatXY, wakeIdx, wakeHeatIdx
|
||||||
- variables: W, H, N, CW, CH, NCH, mat, temp, life, flags, stamp, part, mach, digp, noiseB, airS, netId, isPow, occ, surfY, dirtY, graniteY, basaltY, magmaY, roofY, AMB, worldSeed, surfBase, underD, shallowD, lapse, BW, BH, NB, blockP, blockV, ents, bgm, flyers, ghostBlocks, ghostMolds, awake, hot, hotLvl, tick, tickStamp, machines, molds, nets, pcells, powerDirty, beltL, beltR, tubeLists, brokenMachines, ghostMachines, ghostBlocked, EV, bot, bag, bagTotal, player, seen, ledger, stats
|
- variables: W, H, N, CW, CH, NCH, mat, temp, life, flags, stamp, part, mach, digp, noiseB, airS, netId, isPow, occ, surfY, dirtY, graniteY, basaltY, magmaY, roofY, AMB, worldSeed, surfBase, underD, shallowD, lapse, BW, BH, NB, blockP, blockV, ents, bgm, flyers, ghostBlocks, ghostMolds, awake, hot, hotLvl, tick, tickStamp, machines, molds, nets, pcells, powerDirty, beltL, beltR, tubeLists, brokenMachines, ghostMachines, ghostBlocked, EV, bot, bag, bagTotal, player, seen, ledger, stats
|
||||||
|
|
||||||
### src/sim/fog.js (46 lines)
|
### src/sim/fog.js (51 lines)
|
||||||
Exploration only: never changes grains or the random stream.
|
Exploration only: never changes grains or the random stream.
|
||||||
- functions: fogAlloc, fogMark, fogSeen, fogInit, fogLook, fogRevealAround, fogRevealAll
|
- functions: fogAlloc, fogMark, fogSeen, fogInit, fogLook, fogRevealAround, fogRevealAll
|
||||||
- variables: FOG, FOG_SENSE, fog, FW, FH
|
- variables: FOG, FOG_SENSE, FOG_SENSE_FADE, fog, FW, FH
|
||||||
|
|
||||||
### src/sim/cells.js (176 lines)
|
### src/sim/cells.js (176 lines)
|
||||||
Cell helpers (setCell, move, enter) and the falling-sand movement of powders, liquids and gases (simMove).
|
Cell helpers (setCell, move, enter) and the falling-sand movement of powders, liquids and gases (simMove).
|
||||||
@@ -343,7 +343,7 @@ Drawing (src/render/): browser only. This file: the renderer state R, colour tab
|
|||||||
Propagated coloured light from glowing and burning cells.
|
Propagated coloured light from glowing and burning cells.
|
||||||
- functions: computeLight
|
- functions: computeLight
|
||||||
|
|
||||||
### src/render/world.js (297 lines)
|
### src/render/world.js (299 lines)
|
||||||
Scale colour without changing the smooth-look class.
|
Scale colour without changing the smooth-look class.
|
||||||
- functions: fogColor, drawWorld
|
- functions: fogColor, drawWorld
|
||||||
- variables: TUBE_CLASS
|
- variables: TUBE_CLASS
|
||||||
|
|||||||
@@ -4,6 +4,18 @@ 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-24: t17 follow-up: softer fog edges (Claude Code, Codex)
|
||||||
|
|
||||||
|
- The owner found the edges sharp and stepped. `src/sim/fog.js`: sense ring 24 grains with a
|
||||||
|
12-grain fade (`FOG_SENSE_FADE`), rays fade 8 grains into rock and over the last 16 of range,
|
||||||
|
surface fade 16, `fogRevealAround` gets a 6-grain soft edge. `src/render/world.js`: partly seen
|
||||||
|
grains get ±40 of per-grain noise (`noiseB`), so edges look grainy, not stepped.
|
||||||
|
- `test/t_fog.js`: a point in the fade band is between 0 and 128. `docs/systems/fog.md` updated.
|
||||||
|
Screenshots `shots/fog-edge-{before,after}{,-nolight}.png` compared.
|
||||||
|
- Tests: all pass except `t_smooth` half-resolution, which failed 1 of 3 runs with the change and
|
||||||
|
passed 2 of 2 without, at load 6–12; that check runs with fog off, so the drawing change does
|
||||||
|
not run there. Fog drawing 0.43 → 0.45 ms.
|
||||||
|
|
||||||
## 2026-10-24: t17 Fog of war reveals the ground around the bot (Claude Code, Codex)
|
## 2026-10-24: t17 Fog of war reveals the ground around the bot (Claude Code, Codex)
|
||||||
|
|
||||||
- `src/sim/fog.js`: `FOG_SENSE = 20`; `fogLook` also reveals every fog cell within 20 grains,
|
- `src/sim/fog.js`: `FOG_SENSE = 20`; `fogLook` also reveals every fog cell within 20 grains,
|
||||||
|
|||||||
+11
-7
@@ -7,26 +7,30 @@ surface start seen. Exploration never changes the simulation.
|
|||||||
|
|
||||||
`src/sim/fog.js` keeps a `Uint8Array` at half the grain resolution (`FOG = 2`).
|
`src/sim/fog.js` keeps a `Uint8Array` at half the grain resolution (`FOG = 2`).
|
||||||
Values run from 0 (unseen) to 255 (seen) and only increase. `alloc` allocates it with
|
Values run from 0 (unseen) to 255 (seen) and only increase. `alloc` allocates it with
|
||||||
the other world arrays. `fogInit` reveals above `surfY` and fades eight grains below it.
|
the other world arrays. `fogInit` reveals above `surfY` and fades 16 grains below it.
|
||||||
|
|
||||||
Every four ticks, 256 rays from the bot's centre reveal up to 48 grains. The last eight
|
Every four ticks, 256 rays from the bot's centre reveal up to 48 grains. The last 16
|
||||||
grains fade out. Solid and powder stop sight after a four-grain fade into their face;
|
grains fade out. Solid and powder stop sight after an eight-grain fade into their face;
|
||||||
air, gas and liquid pass sight. Bars and machine voids do not block it.
|
air, gas and liquid pass sight. Bars and machine voids do not block it.
|
||||||
|
|
||||||
Each look also senses the ground around the bot through rock: every fog cell within
|
Each look also senses the ground around the bot through rock: every fog cell within
|
||||||
`FOG_SENSE` (20 grains) of the bot is revealed, fully clear out to 16 grains and fading to
|
`FOG_SENSE` (24 grains) of the bot is revealed, fully clear out to 12 grains and fading over
|
||||||
nothing at 20, so the player can see ore and caves next to their tunnel and choose where to dig.
|
the outer 12 grains to nothing at 24, so the player can see ore and caves next to their tunnel
|
||||||
|
and choose where to dig.
|
||||||
|
|
||||||
## Saving
|
## Saving
|
||||||
|
|
||||||
The fog buffer is saved and exported with the other typed arrays, without a save version
|
The fog buffer is saved and exported with the other typed arrays, without a save version
|
||||||
change. Older saves rebuild the surface, reveal a radius of 12 grains around built blocks,
|
change. Older saves rebuild the surface, reveal a radius of 12 grains around built blocks,
|
||||||
machine footprints and molds, then look from the bot. Loading and generating also look
|
machine footprints and molds, fully clear inside six grains with a six-grain fade to the
|
||||||
|
radius edge, then look from the bot. Loading and generating also look
|
||||||
immediately from the bot.
|
immediately from the bot.
|
||||||
|
|
||||||
## Drawing and setting
|
## Drawing and setting
|
||||||
|
|
||||||
Both looks and the thermal lens sample fog bilinearly. Seen grains use the usual drawing;
|
Both looks and the thermal lens sample fog bilinearly. Partial values get stable per-grain
|
||||||
|
noise of about ±40, clamped to 1–255, to break up stepped edges. Unseen and fully seen
|
||||||
|
grains are unchanged. Seen grains use the usual drawing;
|
||||||
unseen grains use the out-of-world colour and smooth-look buffers, hiding their shapes.
|
unseen grains use the out-of-world colour and smooth-look buffers, hiding their shapes.
|
||||||
Partial values dim foreground, background and trees. Bloom sources need at least 128;
|
Partial values dim foreground, background and trees. Bloom sources need at least 128;
|
||||||
bars, flyers, rain and particles need at least 64. Hover info and the cursor label say
|
bars, flyers, rain and particles need at least 64. Hover info and the cursor label say
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ function drawWorld() {
|
|||||||
const a = fog[fr + j], b = fog[fr + j1], c = fog[fn + j], d = fog[fn + j1];
|
const a = fog[fr + j], b = fog[fr + j1], c = fog[fn + j], d = fog[fn + j1];
|
||||||
if ((a & b & c & d) !== 255) fv = (a + (b - a) * t) * (1 - ft) + (c + (d - c) * t) * ft;
|
if ((a & b & c & d) !== 255) fv = (a + (b - a) * t) * (1 - ft) + (c + (d - c) * t) * ft;
|
||||||
if (!fv) { px[row + bx] = OUT; if (sm) { bpx[row + bx] = 0x000a0a0c; tpx[row + bx] = 0; fpx[row + bx] = 0xff; } continue; }
|
if (!fv) { px[row + bx] = OUT; if (sm) { bpx[row + bx] = 0x000a0a0c; tpx[row + bx] = 0; fpx[row + bx] = 0xff; } continue; }
|
||||||
|
// Stable grain noise breaks up the half-resolution fog's stepped edges.
|
||||||
|
if (fv < 255) fv = Math.max(1, Math.min(255, fv + (noiseB[yw + x] - 128) * (40 / 128)));
|
||||||
}
|
}
|
||||||
const i = yw + x, m = mat[i], nz = noiseB[i] - 128;
|
const i = yw + x, m = mat[i], nz = noiseB[i] - 128;
|
||||||
const T = temp[i];
|
const T = temp[i];
|
||||||
|
|||||||
+12
-7
@@ -1,6 +1,7 @@
|
|||||||
// Exploration only: never changes grains or the random stream.
|
// Exploration only: never changes grains or the random stream.
|
||||||
const FOG = 2;
|
const FOG = 2;
|
||||||
const FOG_SENSE = 20; // Reveal nearby ground through rock, in grains.
|
const FOG_SENSE = 24; // Reveal nearby ground through rock, in grains.
|
||||||
|
const FOG_SENSE_FADE = 12;
|
||||||
let fog, FW, FH;
|
let fog, FW, FH;
|
||||||
function fogAlloc() { FW = Math.ceil(W / FOG); FH = Math.ceil(H / FOG); fog = new Uint8Array(FW * FH); }
|
function fogAlloc() { FW = Math.ceil(W / FOG); FH = Math.ceil(H / FOG); fog = new Uint8Array(FW * FH); }
|
||||||
function fogMark(x, y, v) {
|
function fogMark(x, y, v) {
|
||||||
@@ -13,7 +14,7 @@ function fogSeen(x, y) {
|
|||||||
return fog[Math.floor(y / FOG) * FW + Math.floor(x / FOG)];
|
return fog[Math.floor(y / FOG) * FW + Math.floor(x / FOG)];
|
||||||
}
|
}
|
||||||
function fogInit() {
|
function fogInit() {
|
||||||
for (let x = 0; x < W; x++) for (let y = 0; y < Math.min(H, surfY[x] + 8); y++) fogMark(x, y, y <= surfY[x] ? 255 : (surfY[x] + 8 - y) * 255 / 8);
|
for (let x = 0; x < W; x++) for (let y = 0; y < Math.min(H, surfY[x] + 16); y++) fogMark(x, y, y <= surfY[x] ? 255 : (surfY[x] + 16 - y) * 255 / 16);
|
||||||
}
|
}
|
||||||
function fogLook(cx, cy) {
|
function fogLook(cx, cy) {
|
||||||
for (let a = 0; a < 256; a++) {
|
for (let a = 0; a < 256; a++) {
|
||||||
@@ -24,23 +25,27 @@ function fogLook(cx, cy) {
|
|||||||
if (x < 0 || y < 0 || x >= W || y >= H) break;
|
if (x < 0 || y < 0 || x >= W || y >= H) break;
|
||||||
const ph = PH[mat[y * W + x]];
|
const ph = PH[mat[y * W + x]];
|
||||||
if (hit < 0 && (ph === PH_SOLID || ph === PH_POWDER)) hit = d;
|
if (hit < 0 && (ph === PH_SOLID || ph === PH_POWDER)) hit = d;
|
||||||
const v = Math.min(255, (48 - d) * 255 / 8, hit < 0 ? 255 : (4 - d + hit) * 255 / 4);
|
const v = Math.min(255, (48 - d) * 255 / 16, hit < 0 ? 255 : (8 - d + hit) * 255 / 8);
|
||||||
if (v <= 0) break;
|
if (v <= 0) break;
|
||||||
fogMark(x, y, v);
|
fogMark(x, y, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// one fog cell at a time, measured from its centre; full inside FOG_SENSE - 4, fading to the edge
|
// one fog cell at a time, measured from its centre; full inside 12 grains, fading over the outer 12 grains
|
||||||
const R2 = FOG_SENSE * FOG_SENSE, in2 = (FOG_SENSE - 4) ** 2;
|
const R2 = FOG_SENSE * FOG_SENSE, in2 = (FOG_SENSE - FOG_SENSE_FADE) ** 2;
|
||||||
const fy0 = Math.max(0, Math.floor((cy - FOG_SENSE) / FOG)), fy1 = Math.min(FH - 1, Math.floor((cy + FOG_SENSE) / FOG));
|
const fy0 = Math.max(0, Math.floor((cy - FOG_SENSE) / FOG)), fy1 = Math.min(FH - 1, Math.floor((cy + FOG_SENSE) / FOG));
|
||||||
const fx0 = Math.max(0, Math.floor((cx - FOG_SENSE) / FOG)), fx1 = Math.min(FW - 1, Math.floor((cx + FOG_SENSE) / FOG));
|
const fx0 = Math.max(0, Math.floor((cx - FOG_SENSE) / FOG)), fx1 = Math.min(FW - 1, Math.floor((cx + FOG_SENSE) / FOG));
|
||||||
for (let fy = fy0; fy <= fy1; fy++) for (let fx = fx0; fx <= fx1; fx++) {
|
for (let fy = fy0; fy <= fy1; fy++) for (let fx = fx0; fx <= fx1; fx++) {
|
||||||
const dx = (fx + 0.5) * FOG - cx, dy = (fy + 0.5) * FOG - cy, d2 = dx * dx + dy * dy, i = fy * FW + fx;
|
const dx = (fx + 0.5) * FOG - cx, dy = (fy + 0.5) * FOG - cy, d2 = dx * dx + dy * dy, i = fy * FW + fx;
|
||||||
if (d2 >= R2) continue;
|
if (d2 >= R2) continue;
|
||||||
const v = d2 <= in2 ? 255 : (FOG_SENSE - Math.sqrt(d2)) * 255 / 4;
|
const v = d2 <= in2 ? 255 : (FOG_SENSE - Math.sqrt(d2)) * 255 / FOG_SENSE_FADE;
|
||||||
if (v > fog[i]) fog[i] = v;
|
if (v > fog[i]) fog[i] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function fogRevealAround(x, y, r) {
|
function fogRevealAround(x, y, r) {
|
||||||
for (let yy = Math.max(0, Math.floor(y - r)); yy <= Math.min(H - 1, y + r); yy++) for (let xx = Math.max(0, Math.floor(x - r)); xx <= Math.min(W - 1, x + r); xx++) if ((xx - x) ** 2 + (yy - y) ** 2 <= r * r) fogMark(xx, yy, 255);
|
for (let yy = Math.max(0, Math.floor(y - r)); yy <= Math.min(H - 1, y + r); yy++) for (let xx = Math.max(0, Math.floor(x - r)); xx <= Math.min(W - 1, x + r); xx++) {
|
||||||
|
const d2 = (xx - x) ** 2 + (yy - y) ** 2;
|
||||||
|
if (d2 >= r * r) continue;
|
||||||
|
fogMark(xx, yy, r >= 6 && d2 <= (r - 6) ** 2 ? 255 : Math.min(255, (r - Math.sqrt(d2)) * 255 / 6));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function fogRevealAll() { fog.fill(255); }
|
function fogRevealAll() { fog.fill(255); }
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ check('open pocket is visible', run('fogSeen(82, 104) >= 200'));
|
|||||||
check('sense test points are rock', run('mat[117 * W + 62] === STONE && mat[122 * W + 62] === STONE'));
|
check('sense test points are rock', run('mat[117 * W + 62] === STONE && mat[122 * W + 62] === STONE'));
|
||||||
check('nearby rock is clearly seen through the floor', run('fogSeen(62, 117) >= 200'));
|
check('nearby rock is clearly seen through the floor', run('fogSeen(62, 117) >= 200'));
|
||||||
check('rock near the sense edge is seen', run('fogSeen(62, 122) > 0'));
|
check('rock near the sense edge is seen', run('fogSeen(62, 122) > 0'));
|
||||||
|
check('outer sense band softly reveals rock', run('mat[124 * W + 70] === STONE && fogSeen(70, 124) > 0 && fogSeen(70, 124) < 128'));
|
||||||
check('rock hides separate pocket', run('fogSeen(62, 130) === 0'));
|
check('rock hides separate pocket', run('fogSeen(62, 130) === 0'));
|
||||||
check('one look has limited range', run('fogSeen(124, 104) === 0'));
|
check('one look has limited range', run('fogSeen(124, 104) === 0'));
|
||||||
check('fog only increases', run('(()=>{const f = fog.slice(); fogLook(90,104); return f.every((v,i)=>fog[i]>=v)})()'));
|
check('fog only increases', run('(()=>{const f = fog.slice(); fogLook(90,104); return f.every((v,i)=>fog[i]>=v)})()'));
|
||||||
|
|||||||
Reference in New Issue
Block a user