diff --git a/web/world/js/sail.js b/web/world/js/sail.js index a3705cc..5edec71 100644 --- a/web/world/js/sail.js +++ b/web/world/js/sail.js @@ -582,6 +582,7 @@ export class SailRig { /** Total water on the sail, kg. Lane A's "SAIL PONDING — get the broom" number. */ pondMass() { + if (!this.water) return 0; // the HUD may read this before the sail is rigged let m = 0; for (let n = 0; n < this.water.length; n++) m += this.water[n]; return m; @@ -593,6 +594,7 @@ export class SailRig { * @returns {{x:number,y:number,z:number,mass:number,node:number}|null} */ pondCentroid() { + if (!this.water) return null; let m = 0, x = 0, y = 0, z = 0, node = -1, hw = 0; for (let n = 0; n < this.water.length; n++) { const q = this.water[n]; diff --git a/web/world/js/sail.selftest.js b/web/world/js/sail.selftest.js index 880bea1..dc66050 100644 --- a/web/world/js/sail.selftest.js +++ b/web/world/js/sail.selftest.js @@ -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`; }); +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', () => { // 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