Fix ocean color change reverting nuke-created water to land (#4343)

## Problem

Changing the ocean/water color in **Graphics settings** repaints the
terrain — and any water tiles created by water nukes (land → water) snap
back to their original land appearance.

## Root cause

`TerrainPass` captures the `terrainBytes` buffer at construction and
reuses it in two places:

- `setOceanColor()` does a **full** terrain texture re-upload from
`terrainBytes` when the ocean color changes.
- `applyTerrainDelta()` applies live land→water nuke conversions, but
only wrote to the **GPU texture** — never back into `terrainBytes`.

So the CPU buffer stayed frozen at the map's original terrain. Changing
the ocean color rebuilt the whole texture from that stale buffer,
reverting every nuke crater to land.

## Fix

Write each delta byte back into `terrainBytes` inside
`applyTerrainDelta()`, so the buffer stays the live source of truth and
full re-uploads reflect conversions.

```ts
this.terrainBytes[ref] = bytes[i];
```

The indexing already lines up — `terrainBytes` is indexed by linear ref
(`y * mapW + x`), the same `ref` the delta loop iterates. The buffer is
only otherwise read once at construction by `RailroadPass`/`TerrainPass`
to seed GPU textures (which copy), so mutating it has no side effects
elsewhere.

## Testing

The WebGL passes have no unit-test harness (they need a live GL
context), so this isn't covered by an automated test. Verified by
reasoning through the data flow; can confirm in-game by nuking land into
water and then changing the ocean color.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Evan
2026-06-18 13:43:09 -07:00
committed by GitHub
parent 117fa43947
commit 58e8a5fabd
@@ -91,6 +91,9 @@ export class TerrainPass {
* nuke). `bytes[i]` is the new terrain byte for `refs[i]` (parallel arrays).
* One 1×1 texSubImage2D per ref — fine for the small bursts a single nuke
* produces.
*
* Also writes back into `terrainBytes` so a later full re-upload (e.g.
* setOceanColor) reflects these conversions instead of reverting them.
*/
applyTerrainDelta(refs: readonly number[], bytes: Uint8Array): void {
if (refs.length === 0) return;
@@ -101,6 +104,7 @@ export class TerrainPass {
const ref = refs[i];
const x = ref % this.mapW;
const y = (ref - x) / this.mapW;
this.terrainBytes[ref] = bytes[i];
encodeTerrainTile(bytes[i], this.pixelScratch, 0, this.oceanColor);
gl.texSubImage2D(
gl.TEXTURE_2D,