islaApocalypse/Core/Scripts/BlueprintFormat.cs
beezm 45fe2ce034 feat: shelf micro-relief + drainage incision (terrain-water task 10)
Two paired detail passes on the locked v5 skeleton, output-heights
only (classify path untouched):

PASS A — shelf micro-relief: +-ShelfReliefAmp (default 3 m) Simplex
skin (seed resolved+7409, ~40 undulations/island ~ 200 m features),
weighted by shelf-ness (full mid-shelf, feathered to zero 30% of a
band half-width into the risers) — both shelves get their rolling
texture back; risers and peaks untouched.

PASS B — drainage incision: D8 steepest-descent routing + height-
ordered flow accumulation over the curved+relieved land
(TerrainDetailPass — pure array machinery, named C++ candidate per
D-035); depth = K*accum^p*slope (K=0.78, p=0.45 concave, cap 30 m),
masked full on risers / 30% on shelves / zero on the toe, above the
plateau top, and within 1.2x CraterRadius (feathered to 1.4x); hard
clamp: carved height >= sea + 1 m. Ordering: curve -> relief ->
incision -> crater carve (the carve stays the final authority; carve
moved to its own pass 2c, bit-identical expression on both maps).
The pass prints its own MEASURED depth distribution.

Gate TerrainDetail "off"|"v1" (default v1; both passes one judged
unit; no-op without the curve) + ShelfReliefAmp dial. New TDTL section
(38 B: version + relief/incision params + seed offset) across
writer/parser/harness — blueprints stay self-describing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 18:08:28 -04:00

76 lines
3.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

namespace IslaApocalypse.Core
{
/// <summary>
/// The single registry of constants for the v2 blueprint container — magic, version,
/// section tags, validation bounds, and the sentinel values used when a v2 file is
/// re-encoded from a v1 source that cannot supply real generation parameters.
///
/// The byte-accurate layout lives in Core/Scripts/BLUEPRINT_FORMAT.md. Writer:
/// BlueprintWriter.WriteV2. Reader: MapDataParser (v2 branch). Tags are FourCC codes
/// stored as little-endian u32, so the raw bytes on disk read as ASCII ("PRMS", "HGTS",
/// ...) in a hex dump.
/// </summary>
public static class BlueprintFormat
{
// "ISLA" as raw bytes 'I','S','L','A' == little-endian u32 0x414C5349.
// Deliberately NOT a length-prefixed .NET string: the v1 header starts with the
// 7-bit length prefix 0x07, so the first byte alone (0x49 vs 0x07) identifies the
// format and the reader can dispatch without ambiguity.
public const uint MAGIC = 0x414C5349;
public const uint VERSION = 2;
// Section tags (FourCC, little-endian). Reader rule: known tag -> parse,
// unknown tag -> skip by length and continue. Never reuse a retired tag value.
public const uint TAG_PARAMS = 0x534D5250; // "PRMS"
public const uint TAG_HEIGHTS = 0x53544748; // "HGTS"
public const uint TAG_BIOMES = 0x4D4F4942; // "BIOM"
public const uint TAG_TOWNS = 0x4E574F54; // "TOWN"
public const uint TAG_ROADS_HIGHWAY = 0x57484452; // "RDHW"
public const uint TAG_ROADS_BRANCH = 0x52424452; // "RDBR"
public const uint TAG_ROADS_RUGGED = 0x47524452; // "RDRG"
public const uint TAG_ROADS_TRAIL = 0x4C544452; // "RDTL"
public const uint TAG_WATER_BODY_IDS = 0x44494257; // "WBID"
public const uint TAG_WATER_BODY_TABLE = 0x42544257; // "WBTB"
public const uint TAG_WATER_SURFACE = 0x46525357; // "WSRF"
public const uint TAG_TERRAIN_CURVE = 0x56524354; // "TCRV"
public const uint TAG_TERRAIN_DETAIL = 0x4C544454; // "TDTL"
// WSRF quantization: u16, 0 reserved as the no-water sentinel. A real level L
// (raw blueprint height units) encodes as 1 + round(L × 32768), so a genuine
// level can never encode to 0. Decodes back via (q 1) / 32768. Covers
// [0 .. ~1.99997] raw at 1/32768 raw resolution ≈ 7.7 mm of world height
// (1 raw unit = 251 m) — far below the 1 m voxel.
public const float WSRF_SCALE = 1f / 32768f;
public static ushort EncodeWaterLevel(float level)
{
return (ushort)Godot.Mathf.Clamp(1 + Godot.Mathf.RoundToInt(level * 32768f), 1, ushort.MaxValue);
}
public static float DecodeWaterLevel(ushort quantized)
{
return (quantized - 1) * WSRF_SCALE;
}
// MapSize sanity bounds, checked before any allocation on read.
public const int MIN_MAP_SIZE = 256;
public const int MAX_MAP_SIZE = 32768;
// Sentinels written into the params section when the source blueprint came from a
// v1 file (a re-encode cannot know the original generation inputs). WorldSeed 0 is
// also the config "randomize" sentinel, which no real resolved seed ever is.
public const int SENTINEL_WORLD_SEED = 0;
public const float SENTINEL_CRATER_RADIUS = -1f;
public const float SENTINEL_DENSITY_MULTIPLIER = -1f;
public const float SENTINEL_IMPACT_CENTER = -1f; // both components
/// <summary>Renders a tag as its 4 ASCII characters, for log messages.</summary>
public static string TagToString(uint tag)
{
return new string(new[] {
(char)(tag & 0xFF), (char)((tag >> 8) & 0xFF),
(char)((tag >> 16) & 0xFF), (char)((tag >> 24) & 0xFF)
});
}
}
}