using Godot; // Headless map rebuild for agents (same as pressing each map's "Rebuild" button in the editor and // saving): loads each map scene, regenerates its children from code and saves it back. // godot-mono --headless --path . tools/rebuild_maps.tscn [-- only=playground|crag] // Prints "MAPS: rebuilt ( nodes)" per map and "MAPS: done". Overwrites hand edits in those // scenes — that's what Rebuild means. public partial class RebuildMaps : Node { public static readonly string[] Maps = { "res://scenes/maps/playground.tscn", "res://scenes/maps/crag.tscn" }; public override void _Ready() { string only = null; foreach (string a in OS.GetCmdlineUserArgs()) if (a.StartsWith("only=")) only = a[5..]; bool ok = true; foreach (string path in Maps) { if (only != null && !path.Contains(only)) continue; ok &= RebuildOne(path); } GD.Print(ok ? "MAPS: done" : "MAPS: FAILED"); GetTree().Quit(ok ? 0 : 1); } private bool RebuildOne(string path) { Node map = ResourceLoader.Load(path, null, ResourceLoader.CacheMode.Ignore).Instantiate(); AddChild(map); // in the tree, so joint paths and globals resolve like in the editor switch (map) { case Playground p: p.Rebuild(); break; case Crag c: c.Rebuild(); break; default: GD.PushError($"MAPS: {path} root is not a map ({map.GetType().Name})"); return false; } RemoveChild(map); PackedScene packed = new(); Error err = packed.Pack(map); if (err == Error.Ok) err = ResourceSaver.Save(packed, path); int count = CountOwned(map, map); map.Free(); if (err != Error.Ok) { GD.PushError($"MAPS: saving {path} failed: {err}"); return false; } GD.Print($"MAPS: rebuilt {path} ({count} nodes)"); return true; } private static int CountOwned(Node root, Node n) { int c = 0; foreach (Node child in n.GetChildren()) c += (child.Owner == root ? 1 : 0) + CountOwned(root, child); return c; } }