Per the task-06 ground-test verdict (floor and 420 m ceiling frozen; v2's ascent read flat-then-wall — 87% of the vertical budget in the last 4% of input): the ascent is rebuilt as a terraced climb. Knots recalibrated from the same pooled batch-04 land CDF at P59/72/82/87/95/98 (K=0.509179/0.604081/0.698485/0.767213/0.930304/ 1.050720), pooled land fractions exactly 59/13/10/5/8/3/2 (orange/red/ foothill-riser/bench/mid-riser/plateau/spike). Segments: frozen toe and rise (storm anchors), smoothstep foothill riser to a near-flat 100 m bench, smoothstep mid riser to the near-flat white plateau at 220 m (relocated from 50 m — mountain towns/snow belong there), per-seed-normalized u4 summit spike to 420 m (both retained from v2), linear tail. Per-seed effective-curve monotonicity assertion retained. TerrainCurve gate: "off"|"v3" (default v3); v1/v2 retired with a loud config error (old blueprints regenerable). TCRV: knot slots carry K1..K4 (K5/K6 are version constants), PlateauLo/Hi carry the two bench anchors; version byte selects the semantics. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
162 lines
4.8 KiB
C#
162 lines
4.8 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/06/07, graduation M-7): "v3" applies
|
|
// the terraced-ascent curve with the per-seed peak spike (HeightCurve.cs);
|
|
// "off" is the raw legacy profile. "v1"/"v2" were retired by their successor
|
|
// recalibrations (old blueprints are regenerable). Biome classification is
|
|
// curve-invariant by construction either way. Default: v3.
|
|
public static string TerrainCurve = "v3";
|
|
|
|
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 == "v3")
|
|
TerrainCurve = curve;
|
|
else if (curve == "v1" || curve == "v2")
|
|
GD.PrintErr($"[ConfigManager] TerrainCurve '{curve}' was retired by a later recalibration (v3, task 07). Keeping '{TerrainCurve}' — use \"v3\" 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}.");
|
|
}
|
|
}
|
|
}
|