# Tools — the offline generator **Holds:** the world generator and its diagnostics. Everything that produces a blueprint, and nothing that consumes one at play time. **Boundary — the tools wall:** > ### ⚠⚠ TOOLS MAY NOT REFERENCE `Client/`. THE DEPENDENCY RUNS ONE WAY, OR THE WALL IS NOT A WALL. > > No UI, no player controllers, no shaders, no rendering code. `Tools/` may use `Core/`. > > **Two reasons, both from `Design - Tooling - Offline Generation.md`:** a lighter shipped client, > and — less obviously — **not handing players the world-generation logic.** Shipping the generator > lets it be reverse-engineered into an unfair map preview, with the whole island's layout, every > settlement and every route known before anyone sets foot on the beach. > > **Tools emit data; they never write to a live save.** A generator that could touch live server > state is a corruption risk for no benefit. ## What is here ### The generator — pass 1 (Phase 1) **Ported from the reference's `MapGenerator.GenerateTopography` pass-1 loop** at tag `pre-rewrite-reference` (`ab78883`). This is the crown-jewel port: **working code and its tuned constants, carried over verbatim — not re-derived from a design summary** (→ D-050, `Design - Rewrite - Extraction Manifest.md`). | File | What it is | |---|---| | `Scripts/TerrainNoise.cs` | ⭐ The FastNoiseLite config, **every fractal property pinned explicitly** | | `Scripts/Topography.cs` | ⭐⭐ Pass 1 — the six elements, in the reference's execution order | | `Scripts/IslandFalloff.cs` | `SmoothAbs` + `CREST_EPSILON` (pass-1 half of the reference file) | | `Scripts/Pass1Result.cs` | The height field **and the Phase-2 seams** | | `Scripts/TerrainGenConfig.cs` | Config + the per-element ablation toggles | | `Scripts/HeightMapRenderer.cs` | Hypsometric PNG + raw `.f32` dump | | `Scripts/TerrainGenTool.cs` | Batch entry point (ladder + seed batch + `INDEX.md`) | | `Scenes/TerrainGenTool.tscn` | Run this | ```bash Godot_v4.7.2-stable_mono_linux.x86_64 --headless \ --path ~/celerNexus/islaApocalypse-v2 res://Tools/Scenes/TerrainGenTool.tscn ``` `ISLA_MAPSIZE` (default 2048) · `ISLA_SEEDS` (comma-separated, positive) · `ISLA_BATCH` · `ISLA_SKIP_RAW=1` · `ISLA_LADDER=0` **The six ported elements, in order** — the order is load-bearing, not an implementation detail: 1. **wobbled latitude scalar** — `y/MapSize` plus a ±0.1 low-frequency wobble 2. **island falloff/mask** — squircle + ellipse, 50/50, then `Pow(·, 2.5f)` 3. **edge roughness** — `noise(x·2.5, y·2.5)`, modulated by the squircle so it bites only at the rim 4. **southern sinker** — bottom 25%, **before** the power (its effect is superlinear) 5. **the Trench** — map-anchored, additive on both axes, **after** the power 6. **mountain spine** — `SmoothAbs` crest, cubic, southern fade, amplitude `0.6f` > ### ⚠ The latitude scalar is NOT climate temperature. > > The reference called it `temperature` and let biomes read the same array — a large part of how > climate and terrain got fused. Here it is a **stage-1-local latitude field that terrain geometry > reads**, and nothing else. Climate is **stage 3** and classifies finished shape (D-049 §2, D-056). > Do not alias, store, or rename this into a climate map. **Deferred to Phase 2, with the seam already open:** the submarine **coast shelf** and the **offshore islets** (reference ~:621-664). Both act only below sea level and are judged once water renders. `Pass1Result.PreTrenchFalloff` is captured at the exact point they consume, and `Pass1Result.HMaxSeed` is carried for the seed-dependent redistribution curve. **Not here at all:** the curve, erosion, rivers, water bodies, the crater, biomes, roads, the mesher. ### ⚠ The render writes a `Godot.Image` directly — not the capture path The reference captured maps through a SubViewport + a `TextureRect._Draw`, because it composited **vector overlays** (roads, town markers, river polylines) onto the raster. That machinery awaits render frames — which is exactly why unattended runs need `xvfb-run` and why `--headless` hangs on it. **Phase 1 has no overlays.** A heightmap is a raster, so it is written per-pixel into an `Image` and saved: no viewport, no frame awaits, no display. **Do not reintroduce the capture path by habit** — it becomes correct the moment something vector needs compositing, and not before. ### Diagnostics - `Scenes/UserDirProbe.tscn` — confirms this project's `user://` is its own, not the old prototype's. Exits non-zero on a clash. - `Scenes/NoiseDefaultsProbe.tscn` — prints this engine's `FastNoiseLite` constructor defaults and checks them against the recorded baseline. **Run it after any Godot upgrade.** It cannot change the terrain (`TerrainNoise` pins every value); it makes a drifting default *visible* instead of silent. --- ## ⭐ The conventions Phase 1's generator inherits These were **earned, not assumed** — each one came out of the prototype's terrain arc. → `Design - Tooling - Iteration and Batching.md`. ### 1. `xvfb-run` for unattended generation — NOT `--headless` > ### ⚠ `--headless` HANGS on a real generation run. > > The snapshot capture **awaits render frames, and there is no frame loop without a display.** > **Measured, not assumed.** A run left on `--headless` does not fail loudly; it sits there. ```bash xvfb-run -a Godot_v4.7.2-stable_mono_linux.x86_64 --path ~/celerNexus/islaApocalypse-v2 ``` A windowed run on the desktop also invites being closed mid-run by whoever is using the machine. **The one exception is a job that awaits no frames** — like `UserDirProbe`, which is why that one documents `--headless` explicitly: ```bash Godot_v4.7.2-stable_mono_linux.x86_64 --headless \ --path ~/celerNexus/islaApocalypse-v2 res://Tools/Scenes/UserDirProbe.tscn ``` **If a job awaits a frame, it needs `xvfb-run`. When unsure, use `xvfb-run`** — it is correct in both cases and costs nothing. ### 2. Environment overrides — safe BY CODE, not by care Every path a tool reads or writes resolves through `Core/Scripts/ToolingPaths.cs`, and each has an environment override, so a batch **cannot** read over or write to the developer's live files: | Variable | Overrides | Default | |---|---|---| | `ISLA_CONFIG_PATH` | the generation config file | `user://config.json` | | `ISLA_BLUEPRINT_PATH` | blueprints read/written | `user://blueprints` | | `ISLA_OUTPUT_DIR` | generation output (batches live under it) | `user://output` | > ⭐ **The point is that it is enforced by CODE, not by care.** A rule that depends on an executor > remembering it will eventually meet an executor who does not. There is no second way to obtain these paths. Do not add one. ### 3. ⚠ File safety — permanent rules Enforced by `Core/Scripts/FileSafety.cs`, which throws rather than advises. 1. **No deletion in the runtime root or anywhere under `batches/`.** 2. **Intermediates persist** in a `scratch/` subfolder that is **never cleaned.** 3. **The developer's placed files are never touched.** 4. **Any deletion is named explicitly in the run report.** > ⚠ **These came from a real incident:** an executor admitted it had been deleting the developer's > staged test blueprint. It was owned and fixed **in code.** That is why these are rules and not > guidance. ### 4. Batch layout ``` batches/NN_/_/ batches/NN_/INDEX.md batches/NN_/scratch/ ← persistent; never cleaned ``` **A/B comparisons are browsed by a human, and a flat directory of same-named PNGs is not browsable.** The `INDEX.md` is what makes a batch readable a week later. ⚠ **Batches are OUTPUT.** They live under `ISLA_OUTPUT_DIR` (i.e. under `user://`), **not in this repo.** The `batches/` folder here carries the convention, not the data. No batches are run this phase. ### 5. Iteration levers, for when the generator exists - **Skip the road pass for all terrain and water iteration.** In the prototype this cut a run from ~26 min to ~80 s — a **19× cut**, and the difference between iterating on terrain and not iterating on terrain. Blueprints from such a run carry present-but-empty road sections, which is **legitimate, not corrupt.** - **⭐ Build the oracle before the taste-iteration, not after.** Because the classify path was pinned to *uncurved* height, every shaping iteration had a hard automatic correctness check (biome and water maps md5-identical). Five rounds of taste-iteration were safe **because correctness was not being judged by eye.** *Where a future phase has a subjective gate, ask first what the automatic invariant is.* - **Config-gate every shaping change**, with the legacy behaviour surviving on the other side. It keeps changes comparable as an A/B pair, and it means a rejected change costs a flipped default rather than a reverted commit. - **The lever is variants per batch, not speed per pass.** Two Phase B tasks ran ~2 hours each, which looked like the pipeline getting slower. It was not — a single pass stayed at ~2 minutes. The cost was A/B/ablation multiplication. **When a batch is genuinely wide, say so up front.** ### 6. Scaling discipline **Nothing uses a raw pixel number.** Every distance, radius, threshold and noise frequency comes from `Core/Scripts/GenerationScale.cs`. Read its header before adding any constant — including the tightening on **normalized additive noise offsets**, which is a forward-guard for the Phase 1 noise port. → `Design - Tooling - Scaling Discipline.md`.