fix(cockpit): a coarse read never takes a river bridge back to the sea

Before the first full-detail water read lands (it can take many seconds
from cold tiles), a refresh's loaded-tile read with one low probe still
dropped a river height to the geoid. Before that landing, loaded-tile reads
may now find raised water but only a full-detail read lowers it.

CURRENT-STATE also says what the earlier fixes changed: C stays the CCTV
shortcut when the bridge cannot be entered, voice entry is refused on a
bridge, and the ridden ship's trail is hidden with its sprite.
This commit is contained in:
Sameh Khamis committed 2026-11-08 14:09:18 -08:00
1 parent 5c937e67e1
commit 80a33ba52c
3 files changed
+44 -9

No files matched your search

+24
View File
@@ -1350,6 +1350,30 @@ test('a river height stands through reads that sample nothing or one coarse prob
);
});
test('before any full read lands, a coarse loaded read keeps the river it found', async (t) => {
const clock = manualClock(t, START_MS);
const sea = await bridgeAt(t, clock);
frames(sea.cockpit, clock, sea.shots, 60);
const seaEyeM = eyeHeightM(sea.shots.at(-1));
sea.cockpit.exit();
clock.set(START_MS);
const record = { ...RECORD, lastPositionEpoch: START_MS / 1000 };
const ship = fixture(t, { record });
// The full-detail read is cold and never lands in this test.
ship.viewer.scene.sampleHeightMostDetailed = () => new Promise(() => {});
ship.viewer.scene.sampleHeight = () => 27;
ship.cockpit.enter();
await settle();
ship.cockpit.update();
frames(ship.cockpit, clock, ship.shots, 60);
near(eyeHeightM(ship.shots.at(-1)) - seaEyeM, 27, 'on the river', 0.5);
// A refresh meets one low probe on coarse tiles.
ship.viewer.scene.sampleHeight = () => 0.5;
frames(ship.cockpit, clock, ship.shots, 240);
near(eyeHeightM(ship.shots.at(-1)) - seaEyeM, 27, 'still on the river', 0.5);
});
test('a client clock minutes fast does not age the bridge into a stale feed', async (t) => {
// The feed's newest fix is this ship's; the client clock runs 200 s
// ahead of the feed and received the refresh just now.
+10 -4
View File
@@ -110,9 +110,10 @@ export function update() {
* every frame until it settles): while one is still running, a refresh
* reads the loaded tiles instead, and the slow read applies whenever it
* lands. Once a full-detail read has answered, only full-detail reads
* change the height: a loaded-tile read stands in only until the first
* lands, so one coarse probe cannot drop a river bridge to the sea, and a
* read that sampled nothing keeps the height it had.
* change the height; before then a loaded-tile read may find raised water
* but never takes a raised height back to the sea. One coarse probe cannot
* drop a river bridge to the sea, and a read that sampled nothing keeps
* the height it had.
* @param {{latitude: number, longitude: number}} [at] Where to read the
* water: the dead-reckoned position the camera rides.
* @returns {number} Ellipsoidal metres.
@@ -145,7 +146,12 @@ function vesselSurfaceM(info, nowMs, at = info) {
read.sampledMs = nowMs;
if (read.inFlight && !read.landed) {
const loadedM = loadedWaterlineM(scene, probes, seaM);
if (Number.isFinite(loadedM)) read.heightM = loadedM;
// Taking a river back to the sea is the full-detail read's to say.
const lowers =
Number.isFinite(read.heightM) &&
read.heightM > seaM &&
!(loadedM > seaM);
if (Number.isFinite(loadedM) && !lowers) read.heightM = loadedM;
}
}
if (!read.inFlight) readWaterline.call(this, read, scene, probes, seaM);