The three corner fixes (both presets): riser endpoint slope floors 0.2 -> 0.1 (0.1u + 0.9*smoothstep — climbs decelerate into shelves and accelerate out); summit-spike base floor 0.1 -> 0.05 (0.05u + 0.95*u^4 — no hard skirt under the peaks); SHELF_SPAN_MIN 2 m -> 6 m (pronounced shelves keep a gentle tilt). Knots are preset-parameterized (CurveKnots, explicit parameter — pure, D-035), calibrated from the same pooled batch-04 CDF: COMPACT P65/77/84/88/94/97 -> 65/12/7/4/6/3/3 land split BALANCED P60/73/83/88/96/99 -> 60/13/10/5/8/3/1 Gate: "off"|"v5-compact"|"v5-balanced" (batch-only tri-state; interim default v5-balanced pending the developer's verdict — the winner becomes plain "v5" in a follow-up commit); v1-v4 retired loudly. Assertion runs per preset (8 corners x per-seed spikeMax; the lowered floors stay strictly positive). TCRV v5 extension (+9 B): preset id + K5/K6 — the effective curve is unambiguous from the record; old records parse (version-dispatched); harness compares. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
163 lines
5 KiB
C#
163 lines
5 KiB
C#
|
||
using Godot;
|
||
using Godot.Collections; // Required for Godot's built-in JSON parser
|
||
|
||
namespace IslaApocalypse.Core // Change this if your namespace is different
|
||
{
|
||
public static class ConfigManager
|
||
{
|
||
// Default Fallbacks
|
||
// public static int WorldSeed = 1063685222;
|
||
public static int WorldSeed = (int)GD.Randi(); // if 0 also randomizes
|
||
public static int MapSize = 8192;
|
||
public static float CraterRadius = 400f;
|
||
public static float DensityMultiplier = 1.0f; // <-- Replaces TownCount
|
||
public static int ChunkRadius = 24; // Default chunk size, can be overridden by config
|
||
|
||
// Iteration toggle: skip the ~25-min A* road pass entirely. The blueprint is
|
||
// still exported (road sections present but empty) — an iteration artifact,
|
||
// not a shippable world. Default false.
|
||
public static bool SkipRoads = false;
|
||
|
||
// Sea-level model (D-033, terrain-water task 04): "flat" = one scalar sea
|
||
// level everywhere (SeaLevelValue); "field" = the legacy latitude Lerp
|
||
// (0.26 north .. 0.15 south). Generator-only — the runtime never computes
|
||
// sea level. Default: flat 0.15.
|
||
public static string SeaLevelModel = "flat";
|
||
public static float SeaLevelValue = 0.15f;
|
||
|
||
// Height-redistribution curve (tasks 05–09, graduation M-7): the task-09 taste
|
||
// batch runs a tri-state — "v5-compact" (maximum lowland) vs "v5-balanced"
|
||
// (task-07/08 lineage), both with the corner fixes; "off" is the raw legacy
|
||
// profile. The loser retires after the developer's gate and the winner becomes
|
||
// plain "v5". "v1"–"v4" retired. INTERIM default pending the verdict:
|
||
// v5-balanced.
|
||
public static string TerrainCurve = "v5-balanced";
|
||
|
||
public static void LoadConfig()
|
||
{
|
||
string path = "res://ServerConfig.json";
|
||
|
||
if (!FileAccess.FileExists(path))
|
||
{
|
||
GD.PrintErr("[ConfigManager] ServerConfig.json not found! Defaulting to 8K.");
|
||
return;
|
||
}
|
||
|
||
// Read the file
|
||
using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
|
||
string content = file.GetAsText();
|
||
|
||
// Parse the JSON
|
||
var json = new Json();
|
||
var error = json.Parse(content);
|
||
if (error != Error.Ok)
|
||
{
|
||
GD.PrintErr($"[ConfigManager] JSON Parse Error: {json.GetErrorMessage()}");
|
||
return;
|
||
}
|
||
|
||
var data = (Dictionary)json.Data;
|
||
|
||
// Extract the Seed
|
||
if (data.ContainsKey("WorldSeed"))
|
||
{
|
||
WorldSeed = (int)data["WorldSeed"];
|
||
}
|
||
|
||
// Extract the MapProfile and run your Switch/Case logic!
|
||
string profile = "8K";
|
||
if (data.ContainsKey("MapProfile"))
|
||
{
|
||
profile = (string)data["MapProfile"];
|
||
}
|
||
|
||
// Extract the TownDensity and run your Switch/Case logic!
|
||
string townDensity = "Normal";
|
||
if (data.ContainsKey("TownDensity"))
|
||
{
|
||
townDensity = (string)data["TownDensity"];
|
||
}
|
||
// Extract the ChunkRadius
|
||
if (data.ContainsKey("ChunkRadius")) {
|
||
ChunkRadius = (int)data["ChunkRadius"];
|
||
}
|
||
|
||
// Extract the SkipRoads iteration toggle
|
||
if (data.ContainsKey("SkipRoads"))
|
||
{
|
||
SkipRoads = (bool)data["SkipRoads"];
|
||
}
|
||
|
||
// Extract the sea-level model
|
||
if (data.ContainsKey("SeaLevelModel"))
|
||
{
|
||
string model = (string)data["SeaLevelModel"];
|
||
if (model == "flat" || model == "field")
|
||
SeaLevelModel = model;
|
||
else
|
||
GD.PrintErr($"[ConfigManager] Unknown SeaLevelModel '{model}'. Keeping '{SeaLevelModel}'.");
|
||
}
|
||
if (data.ContainsKey("SeaLevelValue"))
|
||
{
|
||
SeaLevelValue = (float)data["SeaLevelValue"];
|
||
}
|
||
|
||
// Extract the terrain-curve gate
|
||
if (data.ContainsKey("TerrainCurve"))
|
||
{
|
||
string curve = (string)data["TerrainCurve"];
|
||
if (curve == "off" || curve == "v5-compact" || curve == "v5-balanced")
|
||
TerrainCurve = curve;
|
||
else if (curve == "v1" || curve == "v2" || curve == "v3" || curve == "v4")
|
||
GD.PrintErr($"[ConfigManager] TerrainCurve '{curve}' was retired by a later recalibration (v5, task 09). Keeping '{TerrainCurve}' — use \"v5-compact\", \"v5-balanced\" or \"off\".");
|
||
else
|
||
GD.PrintErr($"[ConfigManager] Unknown TerrainCurve '{curve}'. Keeping '{TerrainCurve}'.");
|
||
}
|
||
|
||
switch (profile)
|
||
{
|
||
case "4K":
|
||
MapSize = 4096;
|
||
CraterRadius = 400f; // Scaled down crater
|
||
break;
|
||
case "6K":
|
||
MapSize = 6144;
|
||
CraterRadius = 600f;
|
||
break;
|
||
case "8K":
|
||
MapSize = 8192;
|
||
CraterRadius = 800f; // Your original crater size spread over an 8K map
|
||
break;
|
||
case "10K":
|
||
MapSize = 10240;
|
||
CraterRadius = 1000f;
|
||
break;
|
||
default:
|
||
GD.PrintErr($"[ConfigManager] Unknown MapProfile '{profile}'. Defaulting to 8K.");
|
||
MapSize = 8192;
|
||
CraterRadius = 800f;
|
||
break;
|
||
}
|
||
|
||
switch (townDensity)
|
||
{
|
||
case "Sparse":
|
||
DensityMultiplier = 0.5f;
|
||
break;
|
||
case "Normal":
|
||
DensityMultiplier = 1.0f;
|
||
break;
|
||
case "Dense":
|
||
DensityMultiplier = 2.0f;
|
||
break;
|
||
default:
|
||
GD.PrintErr($"[ConfigManager] Unknown TownDensity '{townDensity}'. Defaulting to Normal.");
|
||
DensityMultiplier = 1.0f;
|
||
break;
|
||
}
|
||
|
||
GD.Print($"[ConfigManager] Successfully loaded {profile} Profile. MapSize set to {MapSize}.");
|
||
}
|
||
}
|
||
}
|