using System.Text; namespace IslaApocalypse.Tools { /// Which offshore-islet system pass 1 runs. → . public enum OffshoreMode { /// No islets. The shelf is gated separately (TerrainGenConfig.CoastShelf). Off, /// /// ⭐ chat2/05 stage 1 — the reference's probabilistic layer, verbatim: one noise field, one /// calibrated threshold, the faithful blob, the faithful zone mask. Sparse, no corners, no /// floor. THE CONTROL. /// Faithful, /// /// ⭐ chat2/05 stage 2 — the loose-guaranteed hybrid: a seeded floor of ≥N north / ≥S south /// islands (min-separated, positions varying per seed, never fixed zones) PLUS the organic /// layer on top, reshaped small / low / flat / rigid, south-weighted, corners allowed. /// Hybrid, } /// /// ⭐ EVERY OFFSHORE-ISLET DIAL, IN ONE OBJECT — config-gated and isolated, per the developer's /// standing modularity concern. Nothing above hardcodes an island /// specific; the whole system is this object + IslandFalloff + OffshorePass. /// /// ═══ TWO PRESETS, AND WHY BOTH EXIST ═══ /// /// the reference's constants, verbatim. The control in every batch. /// the reshape — the deliverable. /// /// The reshape does not EDIT the faithful constants; it sets different values on the same /// dials. So Faithful() stays bit-reproducible however far the hybrid is tuned. /// /// ═══ THE TWO PROTECTIONS THAT ARE NOT DIALS ═══ /// /// (the moat) and (the "actually offshore" test) /// are exposed here because every knob is, but they are the MAIN-ISLAND AND LAKE PROTECTIONS and /// the presets do not move them. They are what makes "no island can bridge to shore" and "no /// island in a lake or the crater bay" true by construction — and OffshorePass applies /// them to the SEEDED stamps as well, so a guaranteed island cannot be guaranteed into the wrong /// place. /// public sealed class OffshoreSettings { public OffshoreMode Mode = OffshoreMode.Off; // ---- the organic (noise) layer ---------------------------------------- /// Run the noise layer at all. Off ⇒ only the seeded floor (the `floor_only` contrast). public bool Organic = true; /// Islet noise frequency, periods per map width. Higher ⇒ SMALLER blobs. Reference 14. public float FreqPerMapWidth = IslandFalloff.OFFSHORE_FREQ_ISLANDS; /// Islet noise seed offset. Reference 7607. public int SeedOffset = IslandFalloff.OFFSHORE_SEED_OFFSET; /// /// Organic density — the fraction of the noise field's ACTUAL sampled distribution that /// clears the threshold — per hemisphere. Equal ⇒ the reference's single dial. South > /// north ⇒ the developer's "weighted south". Reference 0.02 / 0.02. /// public float DensityNorth = 0.02f; public float DensitySouth = 0.02f; /// /// Half-width of the smooth threshold blend across the hemisphere midline, as a fraction of /// the map. ⚠ Without it a north/south density difference would cut any organic island that /// straddles the midline along a dead-straight line. 0 ⇒ hard step (never wanted). /// public float HemisphereBlendHalfWidth = 0.05f; /// Islet crest, metres above sea, PRE-CURVE. Reference 34. The curve's toe squashes it lower. public float CrestM = IslandFalloff.OFFSHORE_ISLAND_H_M; /// Fraction of a blob's excess over threshold that saturates. LOWER ⇒ flatter. Reference 0.45. public float CoreFraction = IslandFalloff.OFFSHORE_CORE; /// Crest-to-sea edge sharpening exponent. 1 ⇒ the faithful smoothstep. Higher ⇒ crisper shore. public float EdgeSharpness = 1f; /// /// ⚠ THE DEBRIS GUARD. An organic island smaller than this fraction of the map's area is /// noise debris — a local maximum that barely cleared the threshold and surfaced a cap of a /// few cells — and is reverted to seabed and untagged. 0 ⇒ off (the reference had no such /// rule and its "~585 px blobs" comment was about the NOISE wavelength, not the caps). /// Stated as a fraction so the rule is scale-free: 3e-5 is ~126 cells at 2048, ~2,000 cells /// (a ~50 m islet) at 8192. /// public float MinIslandAreaFrac = 0f; // ---- the zone mask: protections + the Trench bound -------------------- /// ⭐ THE MOAT. Reference 14 m. The presets do not move it. public float MinDepthM = IslandFalloff.OFFSHORE_MIN_DEPTH_M; public float DepthFeatherM = IslandFalloff.OFFSHORE_DEPTH_FEATHER_M; /// ⭐ THE "ACTUALLY OFFSHORE" TEST on the pre-Trench falloff. Reference 0.72. The presets do not move it. public float MinFalloff = IslandFalloff.OFFSHORE_MIN_FALLOFF; public float FalloffFeather = IslandFalloff.OFFSHORE_FALLOFF_FEATHER; /// /// The outer bound, as a fraction of the half-span (map-anchored, like the Trench). Zone /// fades from INNER to zero at OUTER. Reference 0.78 / 0.86 keeps islets well off the /// Trench ramp (which starts at 0.90) and out of the corners. The reshape pushes both OUT so /// islands populate the corners and may sit over the outer edge — the developer accepts /// islands clipped by the map edge. OUTER < 1 keeps every island's centre on the playable /// map. /// public float TrenchInner = IslandFalloff.OFFSHORE_TRENCH_INNER; public float TrenchOuter = IslandFalloff.OFFSHORE_TRENCH_OUTER; // ---- the seeded floor (Hybrid only) ----------------------------------- /// ⭐ The guaranteed minimum island count per hemisphere. 0 / 0 ⇒ no floor (Faithful). public int FloorNorth = 0; public int FloorSouth = 0; /// Stamp radius, fraction of the map. Small and low is the target. public float StampRadiusFrac = 0.012f; /// Fraction of the stamp radius that is flat top. Higher ⇒ flatter, crisper shore. public float StampCoreFrac = 0.60f; /// /// Radial jitter on the stamp's rim, as a fraction of the radius, from a fine noise field: /// the outline wanders ±this much around the circle. 0 ⇒ a compass-drawn disc (the first /// probe plate: rigid, but visibly geometric). 0.25 keeps the crisp shore and the flat top /// while the coastline stops looking stamped. /// public float StampEdgeJitter = 0f; /// Minimum centre-to-centre separation between seeded islands, fraction of the map. public float SeparationFrac = 0.08f; /// /// Minimum clear water between a seeded stamp's rim and ANY existing land (mainland or /// organic), fraction of the map. This is what makes every seeded island its own connected /// component by construction — and therefore what makes the floor COUNT hold, not just the /// number of stamps. /// public float LandGapFrac = 0.010f; /// The placement RNG's seed offset. A SEED offset; positions vary per world seed. public int PlacementSeedOffset = 7703; /// Rejection-sampling budget per hemisphere before the pass REFUSES (loudly) rather than under-delivers. public int MaxPlacementAttempts = 40000; // ---- presets ---------------------------------------------------------- /// The reference, verbatim. No floor, single density, original footprint/crest/mask. public static OffshoreSettings Faithful() => new OffshoreSettings { Mode = OffshoreMode.Faithful, Organic = true, FreqPerMapWidth = IslandFalloff.OFFSHORE_FREQ_ISLANDS, SeedOffset = IslandFalloff.OFFSHORE_SEED_OFFSET, DensityNorth = 0.02f, DensitySouth = 0.02f, // ConfigManager.OffshoreIslandDensity CrestM = IslandFalloff.OFFSHORE_ISLAND_H_M, CoreFraction = IslandFalloff.OFFSHORE_CORE, EdgeSharpness = 1f, TrenchInner = IslandFalloff.OFFSHORE_TRENCH_INNER, TrenchOuter = IslandFalloff.OFFSHORE_TRENCH_OUTER, FloorNorth = 0, FloorSouth = 0, }; /// /// ⭐ The reshape: small / low / flatter / more rigid, loose-guaranteed (≥2 N, ≥4 S), organic /// extras weighted south, corners and the outer edge allowed. /// public static OffshoreSettings Hybrid() => new OffshoreSettings { Mode = OffshoreMode.Hybrid, Organic = true, // ⚠ Chosen by SWEEP, not by feel (chat2/05 scratch): at freq 24 / 0.010 / 0.025 the field // produced 183 organic blobs of which 174 were debris. freq 16 with these densities gives // the floor plus ~+1–2 N / +6–8 S genuine extras of a few hundred cells each at 2048. FreqPerMapWidth = 16f, // a little smaller than the reference's 14 DensityNorth = 0.007f, DensitySouth = 0.012f, // weighted south CrestM = 24f, // lower than the reference's 34 (pre-curve) CoreFraction = 0.25f, // flatter than 0.45 EdgeSharpness = 2.5f, // crisper shore than the faithful smoothstep MinIslandAreaFrac = 3e-5f, // no noise debris TrenchInner = 0.90f, TrenchOuter = 0.97f, // corners + outer edge allowed; centre stays on-map FloorNorth = 2, FloorSouth = 4, StampRadiusFrac = 0.012f, StampCoreFrac = 0.60f, StampEdgeJitter = 0.25f, SeparationFrac = 0.08f, LandGapFrac = 0.010f, }; public OffshoreSettings Clone() => (OffshoreSettings)MemberwiseClone(); public string Describe() { if (Mode == OffshoreMode.Off) return "offshore OFF"; var sb = new StringBuilder(); sb.Append($"{Mode}: organic {(Organic ? "on" : "OFF")} freq {FreqPerMapWidth:F0}/map density N {DensityNorth:F3} S {DensitySouth:F3} · "); sb.Append($"crest {CrestM:F0} m core {CoreFraction:F2} sharp {EdgeSharpness:F1} minArea {MinIslandAreaFrac:G2} · "); sb.Append($"moat {MinDepthM:F0}+{DepthFeatherM:F0} m falloff {MinFalloff:F2}+{FalloffFeather:F2} trench {TrenchInner:F2}→{TrenchOuter:F2}"); if (FloorNorth > 0 || FloorSouth > 0) sb.Append($" · floor N{FloorNorth} S{FloorSouth} r {StampRadiusFrac:F3} core {StampCoreFrac:F2} sep {SeparationFrac:F2} gap {LandGapFrac:F3}"); return sb.ToString(); } } }