using Godot; using IslaApocalypse.Core; namespace IslaApocalypse.Tools { /// /// ⭐⭐ THE CROWN JEWEL — the terrain noise configuration, ported and PINNED. /// → D-050 ("port, don't re-derive"), `Design - Rewrite - Extraction Manifest.md`. /// /// ═══ WHAT THE REFERENCE ACTUALLY SET, AND WHAT IT DID NOT ═══ /// /// The reference (MapGenerator._Ready ~:141-144) set exactly THREE properties: /// /// _noise = new FastNoiseLite(); /// _noise.Seed = ConfigManager.WorldSeed == 0 ? (int)GD.Randi() : ConfigManager.WorldSeed; /// _noise.NoiseType = FastNoiseLite.NoiseTypeEnum.Simplex; /// _noise.Frequency = 0.004f / scaleFactor; /// /// That is all four lines. There is no fifth. Fractal type, octaves, gain, lacunarity and /// weighted-strength were NEVER SET anywhere in that codebase — so the island's entire fractal /// character was Godot 4.7.1's `FastNoiseLite` CONSTRUCTOR DEFAULTS, and nothing recorded what /// they were. (chat1/00 §2.1, §4.1.) /// /// ═══ ⚠⚠ WHY EVERY VALUE IS PINNED EXPLICITLY BELOW ═══ /// /// Because a default is not a decision, and this project no longer rides one. The terrain must /// not change when the engine version changes, and it must not change when this code is ported /// off Godot's FastNoiseLite to a C++ or hand-rolled implementation — which D-049 explicitly /// anticipates ("C++-ready seams... port path-by-path when settled and hot"). An unpinned /// default would move the island silently, and nothing in the source would say what moved. /// /// Measured on Godot 4.7.2.stable.mono (Tools/Scenes/NoiseDefaultsProbe.tscn): /// FractalType Fbm · Octaves 5 · Gain 0.5 · Lacunarity 2.0 · WeightedStrength 0.0 /// These MATCH the Godot 4 documented defaults the reference rode on 4.7.1 — so pinning them /// reproduces the reference look exactly, with no delta to reconcile. /// /// ⚠ Note NoiseType is the one place the reference did NOT ride the default: the constructor /// default is SimplexSmooth, and the reference explicitly chose Simplex. Different noise. /// /// Run NoiseDefaultsProbe after any engine upgrade. It cannot change the terrain — this file /// pins it — but it makes a drifting default visible instead of silent. /// public static class TerrainNoise { // ---- the pinned fractal character (was 4.7.1 constructor defaults) ---- public const FastNoiseLite.NoiseTypeEnum PinnedNoiseType = FastNoiseLite.NoiseTypeEnum.Simplex; public const FastNoiseLite.FractalTypeEnum PinnedFractalType = FastNoiseLite.FractalTypeEnum.Fbm; public const int PinnedOctaves = 5; public const float PinnedGain = 0.5f; public const float PinnedLacunarity = 2.0f; public const float PinnedWeightedStrength = 0.0f; /// /// The base frequency, stated at the 1024 baseline. The reference wrote /// 0.004f / scaleFactor; here the division is 's /// job, so the literal never reaches a call site. → `Design - Tooling - Scaling Discipline.md`. /// public const float BaselineFrequency = 0.004f; /// /// Build the one terrain noise field. ⚠ ONE INSTANCE, deliberately. /// /// The reference sampled the latitude wobble, the coastline edge roughness and the base /// height from the SAME `_noise` object — three correlated slices of one field, at three /// different coordinate transforms. That correlation is part of the look, so the port keeps /// one instance rather than "tidying" it into three seeded fields. /// public static FastNoiseLite Create(int seed, GenerationScale scale) { var noise = new FastNoiseLite(); // --- ported verbatim from the reference --- noise.Seed = seed; noise.NoiseType = PinnedNoiseType; noise.Frequency = scale.NoiseFrequency(BaselineFrequency); // = 0.004f / scaleFactor // --- PINNED: the reference left these to the engine. We do not. --- noise.FractalType = PinnedFractalType; noise.FractalOctaves = PinnedOctaves; noise.FractalGain = PinnedGain; noise.FractalLacunarity = PinnedLacunarity; noise.FractalWeightedStrength = PinnedWeightedStrength; return noise; } /// /// A MODULATION field — the curve's bench/plateau/strength anchors and the detail pass's /// relief/edge fields. Ported from the reference's MapGenerator.MakeModulationNoise /// (~:1041-1048). /// /// ═══ ⚠ DECORRELATION IS BY SEED, NOT BY COORDINATE OFFSET ═══ /// /// The reference offset these fields from each other with Seed = resolvedSeed + offset /// (7101 / 7207 / 7303 / 7409 / 7507 / 7607 / 9271) and then sampled every one of them at the /// bare (x, y). There is no GetNoise2D(x + 1000, …) anywhere in this path. /// /// So the raw-pixel-offset hazard warns about — the one that /// bit the latitude wobble in pass 1 — does not apply here, and there was nothing to /// normalize on the port. Recorded explicitly because "we checked and it was fine" is /// only worth anything if someone wrote down that they checked. (chat2/01.) /// /// ⚠ FREQUENCY IS STATED PER MAP WIDTH, not at the 1024 baseline — the reference wrote /// periodsPerIsland / MapSize, which is already size-independent. → /// . /// /// ⚠ THE FRACTAL PROPERTIES ARE PINNED HERE TOO. The reference left them to the engine on /// these fields exactly as it did on the base noise, so the same argument applies: a default /// is not a decision, and an engine upgrade must not move the island. The pinned values are /// the ones measured on 4.7.2 and match what 4.7.1 supplied, so pinning reproduces the /// reference with no delta. /// /// The run's resolved seed — the offset is added here, not by the caller. /// The field's decorrelation offset (e.g. CurveAnchors.BenchSeedOffset). /// How many undulations across the island. public static FastNoiseLite CreateModulation(int seed, int seedOffset, float periodsPerMapWidth, GenerationScale scale) { var noise = new FastNoiseLite(); // --- ported verbatim from the reference --- noise.Seed = seed + seedOffset; // deterministic from the resolved seed noise.NoiseType = PinnedNoiseType; noise.Frequency = scale.NoiseFrequencyPerMapWidth(periodsPerMapWidth); // --- PINNED: the reference left these to the engine here too. We do not. --- noise.FractalType = PinnedFractalType; noise.FractalOctaves = PinnedOctaves; noise.FractalGain = PinnedGain; noise.FractalLacunarity = PinnedLacunarity; noise.FractalWeightedStrength = PinnedWeightedStrength; return noise; } /// The pinned configuration as one line, for a run header. public static string Describe(int seed, GenerationScale scale) => $"Simplex · Fbm · octaves {PinnedOctaves} · gain {PinnedGain} · lacunarity {PinnedLacunarity} · " + $"weighted {PinnedWeightedStrength} · freq {scale.NoiseFrequency(BaselineFrequency):G6} " + $"(= {BaselineFrequency} / {scale.ScaleFactor:F3}) · seed {seed}"; } }