Guard pond accessors before attach; caught driving the live game
pondMass/pondCentroid/drainPondAt/dumpPond threw when this.water didn't exist yet — Lane A's HUD reads pondMass() every frame, including in the forecast/prep phases before the sail is rigged. Node tests always attach first so they never hit it; the live game did on the first frame. Guarded + a pre-attach assert so it can't regress. Verified in the assembled game: a flat quad ponds 780 kg (belly sagging below ground), dumps on break, and the merged browser selftest is 219/0/0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b7f93d6486
commit
670443eea0
@ -582,6 +582,7 @@ export class SailRig {
|
|||||||
|
|
||||||
/** Total water on the sail, kg. Lane A's "SAIL PONDING — get the broom" number. */
|
/** Total water on the sail, kg. Lane A's "SAIL PONDING — get the broom" number. */
|
||||||
pondMass() {
|
pondMass() {
|
||||||
|
if (!this.water) return 0; // the HUD may read this before the sail is rigged
|
||||||
let m = 0;
|
let m = 0;
|
||||||
for (let n = 0; n < this.water.length; n++) m += this.water[n];
|
for (let n = 0; n < this.water.length; n++) m += this.water[n];
|
||||||
return m;
|
return m;
|
||||||
@ -593,6 +594,7 @@ export class SailRig {
|
|||||||
* @returns {{x:number,y:number,z:number,mass:number,node:number}|null}
|
* @returns {{x:number,y:number,z:number,mass:number,node:number}|null}
|
||||||
*/
|
*/
|
||||||
pondCentroid() {
|
pondCentroid() {
|
||||||
|
if (!this.water) return null;
|
||||||
let m = 0, x = 0, y = 0, z = 0, node = -1, hw = 0;
|
let m = 0, x = 0, y = 0, z = 0, node = -1, hw = 0;
|
||||||
for (let n = 0; n < this.water.length; n++) {
|
for (let n = 0; n < this.water.length; n++) {
|
||||||
const q = this.water[n];
|
const q = this.water[n];
|
||||||
|
|||||||
@ -879,6 +879,17 @@ test('ponding: the broom SAVES a flat rig that water would have killed', () => {
|
|||||||
return `kept 4/4 by sweeping the belly; unswept the same rig loses corners to water`;
|
return `kept 4/4 by sweeping the belly; unswept the same rig loses corners to water`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('ponding: pond accessors are safe before the sail is rigged', () => {
|
||||||
|
// Caught live: Lane A's HUD reads pondMass() every frame, including before the
|
||||||
|
// player has rigged anything — and this.water doesn't exist until attach().
|
||||||
|
const bare = new SailRig({ anchors: makeAnchors(HEIGHTS_FLAT) });
|
||||||
|
assert(bare.pondMass() === 0, 'pondMass threw / was non-zero on an unrigged sail');
|
||||||
|
assert(bare.pondCentroid() === null, 'pondCentroid should be null on an unrigged sail');
|
||||||
|
assert(bare.drainPondAt(0, SIM_DT) === 0, 'drainPondAt should no-op on an unrigged sail');
|
||||||
|
assert(bare.dumpPond() === 0, 'dumpPond should no-op on an unrigged sail');
|
||||||
|
return 'pondMass/centroid/drain/dump all safe pre-attach';
|
||||||
|
});
|
||||||
|
|
||||||
test('ponding: rain that the router swallows cannot silently pass', () => {
|
test('ponding: rain that the router swallows cannot silently pass', () => {
|
||||||
// The integrator caught the wind router dropping the rain API this sprint,
|
// The integrator caught the wind router dropping the rain API this sprint,
|
||||||
// which would have made every test above pass while ponding did nothing in the
|
// which would have made every test above pass while ponding did nothing in the
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user