Compare commits
No commits in common. "ad541dfbd8c00cd240c099748b6dea085421a81a" and "bb0b4fe4f29a6ea9384d517090eab8441ac8c4e4" have entirely different histories.
ad541dfbd8
...
bb0b4fe4f2
2 changed files with 23 additions and 75 deletions
|
|
@ -7,8 +7,8 @@ namespace IslaApocalypse.Core // Change this if your namespace is different
|
||||||
public static class ConfigManager
|
public static class ConfigManager
|
||||||
{
|
{
|
||||||
// Default Fallbacks
|
// Default Fallbacks
|
||||||
// public static int WorldSeed = 1063685222;
|
public static int WorldSeed = 1063685222;
|
||||||
public static int WorldSeed = (int)GD.Randi(); // if 0 also randomizes
|
// public static int WorldSeed = (int)GD.Randi(); if 0 also randomizes
|
||||||
public static int MapSize = 8192;
|
public static int MapSize = 8192;
|
||||||
public static float CraterRadius = 400f;
|
public static float CraterRadius = 400f;
|
||||||
public static float DensityMultiplier = 1.0f; // <-- Replaces TownCount
|
public static float DensityMultiplier = 1.0f; // <-- Replaces TownCount
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,6 @@ public partial class MapGenerator : TextureRect
|
||||||
public bool IsHighwayNode;
|
public bool IsHighwayNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When generation began, in engine milliseconds. Every progress print is stamped with
|
|
||||||
// the time since this, so the cost of each stage is readable straight off the console.
|
|
||||||
private ulong _pipelineStartMs;
|
|
||||||
|
|
||||||
private FastNoiseLite _noise; // The one and only original detailed noise!
|
private FastNoiseLite _noise; // The one and only original detailed noise!
|
||||||
private Vector2 _impactCenter;
|
private Vector2 _impactCenter;
|
||||||
private float _impactRadius;
|
private float _impactRadius;
|
||||||
|
|
@ -39,8 +35,6 @@ public partial class MapGenerator : TextureRect
|
||||||
|
|
||||||
public override async void _Ready()
|
public override async void _Ready()
|
||||||
{
|
{
|
||||||
_pipelineStartMs = Time.GetTicksMsec();
|
|
||||||
|
|
||||||
// 1. LOAD CONFIGURATION
|
// 1. LOAD CONFIGURATION
|
||||||
ConfigManager.LoadConfig();
|
ConfigManager.LoadConfig();
|
||||||
MapSize = ConfigManager.MapSize;
|
MapSize = ConfigManager.MapSize;
|
||||||
|
|
@ -72,71 +66,27 @@ public partial class MapGenerator : TextureRect
|
||||||
float randomY = (float)GD.RandRange(0.05f, 0.12f);
|
float randomY = (float)GD.RandRange(0.05f, 0.12f);
|
||||||
_impactCenter = new Vector2(MapSize * randomX, MapSize * randomY);
|
_impactCenter = new Vector2(MapSize * randomX, MapSize * randomY);
|
||||||
|
|
||||||
// --- THE GENERATION PIPELINE ---
|
|
||||||
// Each stage prints its elapsed time and drops a snapshot, so you can watch the
|
|
||||||
// world appear in layers instead of waiting for the whole run to judge any of it.
|
|
||||||
// A future water stage slots in as another CaptureStage call at its own boundary.
|
|
||||||
|
|
||||||
GenerateTopography();
|
GenerateTopography();
|
||||||
GD.Print($"{T()} Topography done (height + temperature).");
|
|
||||||
|
|
||||||
CalculateTrueOcean();
|
CalculateTrueOcean();
|
||||||
CalculateMainland();
|
CalculateMainland();
|
||||||
GD.Print($"{T()} Ocean and mainland masks done.");
|
|
||||||
|
|
||||||
AssignBiomesAndDraw();
|
AssignBiomesAndDraw();
|
||||||
GD.Print($"{T()} Biomes done.");
|
|
||||||
await CaptureStage("1_biomes");
|
|
||||||
|
|
||||||
GenerateTowns();
|
GenerateTowns();
|
||||||
GD.Print($"{T()} Towns placed: {_towns.Count}.");
|
|
||||||
await CaptureStage("2_towns");
|
|
||||||
|
|
||||||
// NEW: We await the roads so the engine doesn't freeze!
|
// NEW: We await the roads so the engine doesn't freeze!
|
||||||
await GenerateRoadsAsync();
|
await GenerateRoadsAsync();
|
||||||
|
|
||||||
ExportMapData();
|
ExportMapData();
|
||||||
GD.Print($"{T()} Blueprint exported.");
|
SaveMapSnapshot();
|
||||||
|
|
||||||
await CaptureStage("3_roads");
|
|
||||||
GD.Print($"{T()} GENERATION COMPLETE.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private async void SaveMapSnapshot()
|
||||||
/// Elapsed time since generation started, e.g. "[+12.4s]". Prefixed onto the progress
|
|
||||||
/// prints so the cost of each stage can be read straight off the console — no stopwatch.
|
|
||||||
/// </summary>
|
|
||||||
private string T()
|
|
||||||
{
|
{
|
||||||
double seconds = (Time.GetTicksMsec() - _pipelineStartMs) / 1000.0;
|
// 1. THE FIX: Wait exactly one frame to let Godot finish _Ready() and unlock the Scene Tree!
|
||||||
return $"[+{seconds,6:F1}s]";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Saves a PNG of the map AS IT STANDS RIGHT NOW, tagged with a stage label.
|
|
||||||
///
|
|
||||||
/// Call this at any pipeline boundary. It draws whatever exists at that moment — after
|
|
||||||
/// biomes you get bare terrain, after towns the town markers appear, after roads the
|
|
||||||
/// road network does. Nothing needs to be passed in or turned on; the draw proxy just
|
|
||||||
/// renders the lists as they currently are, so a future stage (water, for instance)
|
|
||||||
/// shows up automatically once it populates its data.
|
|
||||||
///
|
|
||||||
/// ⚠ DO NOT "SIMPLIFY" THE WAITS BELOW. They look like superstition and are not — each
|
|
||||||
/// one is there because removing it produced blank or half-drawn images. The design
|
|
||||||
/// vault records why (01_Design/tooling_notes.md). Symptoms of getting this wrong are
|
|
||||||
/// postage-stamp, black, or road-less captures.
|
|
||||||
/// </summary>
|
|
||||||
private async Task CaptureStage(string label)
|
|
||||||
{
|
|
||||||
// 1. Yield one frame. Godot locks the scene tree while it is processing, and we are
|
|
||||||
// about to add nodes to it. Without this, the AddChild below is unsafe.
|
|
||||||
await ToSignal(GetTree(), "process_frame");
|
await ToSignal(GetTree(), "process_frame");
|
||||||
|
|
||||||
GD.Print($"{T()} Capturing snapshot: {label}...");
|
GD.Print("Starting offscreen map capture...");
|
||||||
|
|
||||||
// 2. Build an invisible, offscreen "monitor" at the map's real size. It must be a
|
// 2. Build the invisible, offscreen 4096 monitor
|
||||||
// SubViewport we size ourselves — rendering through the scene's UI layout makes
|
|
||||||
// Godot shrink the image to fit the physical screen.
|
|
||||||
var offscreenVP = new SubViewport();
|
var offscreenVP = new SubViewport();
|
||||||
offscreenVP.Size = new Vector2I(MapSize, MapSize);
|
offscreenVP.Size = new Vector2I(MapSize, MapSize);
|
||||||
offscreenVP.RenderTargetUpdateMode = SubViewport.UpdateMode.Always;
|
offscreenVP.RenderTargetUpdateMode = SubViewport.UpdateMode.Always;
|
||||||
|
|
@ -145,33 +95,31 @@ public partial class MapGenerator : TextureRect
|
||||||
|
|
||||||
// 3. Stamp the raw terrain texture onto it
|
// 3. Stamp the raw terrain texture onto it
|
||||||
var bgRect = new TextureRect();
|
var bgRect = new TextureRect();
|
||||||
bgRect.Texture = this.Texture;
|
bgRect.Texture = this.Texture;
|
||||||
bgRect.CustomMinimumSize = new Vector2(MapSize, MapSize);
|
bgRect.CustomMinimumSize = new Vector2(MapSize, MapSize);
|
||||||
offscreenVP.AddChild(bgRect);
|
offscreenVP.AddChild(bgRect);
|
||||||
|
|
||||||
// 4. Attach the proxy to draw the roads and towns on top. Reading the viewport's
|
// 4. Attach the proxy to draw the roads and towns on top
|
||||||
// texture alone would miss them — _Draw() output is not part of the base texture.
|
|
||||||
var drawProxy = new MapDrawProxy();
|
var drawProxy = new MapDrawProxy();
|
||||||
drawProxy.Source = this;
|
drawProxy.Source = this;
|
||||||
drawProxy.CustomMinimumSize = new Vector2(MapSize, MapSize);
|
drawProxy.CustomMinimumSize = new Vector2(MapSize, MapSize);
|
||||||
offscreenVP.AddChild(drawProxy);
|
offscreenVP.AddChild(drawProxy);
|
||||||
|
|
||||||
// 5. WAIT FOR THE GPU! Issuing draw calls is not the same as having drawn.
|
// 5. WAIT FOR THE GPU! (Wait 2 render frames to guarantee the framebuffer is painted)
|
||||||
// Two frames — one proved insufficient in practice.
|
|
||||||
await ToSignal(RenderingServer.Singleton, RenderingServer.SignalName.FramePostDraw);
|
await ToSignal(RenderingServer.Singleton, RenderingServer.SignalName.FramePostDraw);
|
||||||
await ToSignal(RenderingServer.Singleton, RenderingServer.SignalName.FramePostDraw);
|
await ToSignal(RenderingServer.Singleton, RenderingServer.SignalName.FramePostDraw);
|
||||||
|
|
||||||
// 6. Pull the image from the invisible monitor and save it!
|
// 6. Pull the image from the invisible monitor and save it!
|
||||||
Image capture = offscreenVP.GetTexture().GetImage();
|
Image capture = offscreenVP.GetTexture().GetImage();
|
||||||
string seedStr = _noise.Seed.ToString();
|
string seedStr = _noise.Seed.ToString();
|
||||||
string fileName = $"user://Map_Seed_{seedStr}_{label}.png";
|
string fileName = $"user://Map_Seed_{seedStr}.png";
|
||||||
|
|
||||||
Error saveResult = capture.SavePng(fileName);
|
Error saveResult = capture.SavePng(fileName);
|
||||||
|
|
||||||
if (saveResult == Error.Ok)
|
if (saveResult == Error.Ok)
|
||||||
GD.Print($"{T()} Map saved: {ProjectSettings.GlobalizePath(fileName)}");
|
GD.Print($"Map saved successfully to: {ProjectSettings.GlobalizePath(fileName)}");
|
||||||
else
|
else
|
||||||
GD.PrintErr($"{T()} Failed to save map. Godot Error code: {saveResult}");
|
GD.PrintErr($"Failed to save map. Godot Error code: {saveResult}");
|
||||||
|
|
||||||
// 7. Delete the invisible monitor to free up RAM
|
// 7. Delete the invisible monitor to free up RAM
|
||||||
offscreenVP.QueueFree();
|
offscreenVP.QueueFree();
|
||||||
|
|
@ -674,7 +622,7 @@ public partial class MapGenerator : TextureRect
|
||||||
// --- LOGISTICS & ROAD GENERATION ---
|
// --- LOGISTICS & ROAD GENERATION ---
|
||||||
private async Task GenerateRoadsAsync()
|
private async Task GenerateRoadsAsync()
|
||||||
{
|
{
|
||||||
GD.Print($"{T()} [A*] Starting Logistics Pathfinding for {MapSize}x{MapSize} world...");
|
GD.Print($"[A*] Starting Logistics Pathfinding for {MapSize}x{MapSize} world...");
|
||||||
_highwayPaths.Clear(); _branchPaths.Clear(); _ruggedPaths.Clear(); _trailPaths.Clear();
|
_highwayPaths.Clear(); _branchPaths.Clear(); _ruggedPaths.Clear(); _trailPaths.Clear();
|
||||||
|
|
||||||
AStarGrid2D astar = new AStarGrid2D();
|
AStarGrid2D astar = new AStarGrid2D();
|
||||||
|
|
@ -697,7 +645,7 @@ public partial class MapGenerator : TextureRect
|
||||||
return angleA.CompareTo(angleB);
|
return angleA.CompareTo(angleB);
|
||||||
});
|
});
|
||||||
|
|
||||||
GD.Print($"{T()} [A*] 1/4: Plotting the {highwayNodes.Count}-Node Continental Loop...");
|
GD.Print($"[A*] 1/4: Plotting the {highwayNodes.Count}-Node Continental Loop...");
|
||||||
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); // BREATHE
|
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); // BREATHE
|
||||||
|
|
||||||
if (highwayNodes.Count >= 3) {
|
if (highwayNodes.Count >= 3) {
|
||||||
|
|
@ -720,7 +668,7 @@ public partial class MapGenerator : TextureRect
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 2. THE MOUNTAIN BOSS BRANCH ---
|
// --- 2. THE MOUNTAIN BOSS BRANCH ---
|
||||||
GD.Print($"{T()} [A*] 2/4: Connecting the Snow Boss...");
|
GD.Print("[A*] 2/4: Connecting the Snow Boss...");
|
||||||
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); // BREATHE
|
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); // BREATHE
|
||||||
|
|
||||||
List<TownData> connectedTowns = new List<TownData>(highwayNodes);
|
List<TownData> connectedTowns = new List<TownData>(highwayNodes);
|
||||||
|
|
@ -752,7 +700,7 @@ public partial class MapGenerator : TextureRect
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 3 & 4. COUNTY ROADS (The Daisy-Chain Protocol) ---
|
// --- 3 & 4. COUNTY ROADS (The Daisy-Chain Protocol) ---
|
||||||
GD.Print($"{T()} [A*] 3/4 & 4/4: Weaving County Roads (Daisy-Chaining)...");
|
GD.Print("[A*] 3/4 & 4/4: Weaving County Roads (Daisy-Chaining)...");
|
||||||
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); // BREATHE
|
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); // BREATHE
|
||||||
|
|
||||||
List<TownData> unconnectedTowns = _towns.FindAll(t => !connectedTowns.Contains(t) && t.Tier != TownTier.IslandLoot);
|
List<TownData> unconnectedTowns = _towns.FindAll(t => !connectedTowns.Contains(t) && t.Tier != TownTier.IslandLoot);
|
||||||
|
|
@ -783,7 +731,7 @@ public partial class MapGenerator : TextureRect
|
||||||
float actualDist = Mathf.Sqrt(shortestDist);
|
float actualDist = Mathf.Sqrt(shortestDist);
|
||||||
|
|
||||||
// NEW: Live Diagnostics! Print the exact town we are testing BEFORE we calculate.
|
// NEW: Live Diagnostics! Print the exact town we are testing BEFORE we calculate.
|
||||||
GD.Print($"{T()} -> ({count}/{totalToConnect}) Pathing {bestUnconnected.Tier} to grid. Dist: {actualDist:F1}px");
|
GD.Print($" -> ({count}/{totalToConnect}) Pathing {bestUnconnected.Tier} to grid. Dist: {actualDist:F1}px");
|
||||||
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); // BREATHE!
|
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); // BREATHE!
|
||||||
|
|
||||||
Vector2[] countyPath = new Vector2[0];
|
Vector2[] countyPath = new Vector2[0];
|
||||||
|
|
@ -791,7 +739,7 @@ public partial class MapGenerator : TextureRect
|
||||||
// THE ABANDON PROTOCOL: If it's more than 8% of the map away, it's a hermit camp. Leave it!
|
// THE ABANDON PROTOCOL: If it's more than 8% of the map away, it's a hermit camp. Leave it!
|
||||||
if (actualDist > (MapSize * 0.08f))
|
if (actualDist > (MapSize * 0.08f))
|
||||||
{
|
{
|
||||||
GD.Print($"{T()} [!] Too isolated. Abandoning road.");
|
GD.Print($" [!] Too isolated. Abandoning road.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -811,14 +759,14 @@ public partial class MapGenerator : TextureRect
|
||||||
}
|
}
|
||||||
else if (actualDist <= (MapSize * 0.08f))
|
else if (actualDist <= (MapSize * 0.08f))
|
||||||
{
|
{
|
||||||
GD.Print($"{T()} [!] Path impossible (Trapped by terrain). Abandoning road.");
|
GD.Print($" [!] Path impossible (Trapped by terrain). Abandoning road.");
|
||||||
}
|
}
|
||||||
|
|
||||||
unconnectedTowns.Remove(bestUnconnected);
|
unconnectedTowns.Remove(bestUnconnected);
|
||||||
connectedTowns.Add(bestUnconnected);
|
connectedTowns.Add(bestUnconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
GD.Print($"{T()} [A*] Logistics Network Complete!");
|
GD.Print("[A*] Logistics Network Complete!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue