Maps are editor-built scenes: [Tool] Playground/Crag, headless rebuild for agents
scenes/maps/playground.tscn and crag.tscn are generated by the map scripts' Rebuild (inspector button, or godot-mono --headless --path . tools/rebuild_maps.tscn) and loaded as saved by the game, so hand edits stick. The crag route is saved as Marker3D nodes and crag_spike checks the saved scene. Layout verified identical to the code-built one (tools/map_dump.tscn). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3e300c7f04
commit
df7a2c5a89
17 files changed
+6863
-65
No files matched your search
@@ -0,0 +1,80 @@
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
// Shared plumbing for the editor-built maps (Playground, Crag). Each map is the ROOT of its own scene
|
||||
// (scenes/maps/*.tscn) with a [Tool] script whose Rebuild() regenerates every child from code and
|
||||
// gives it to the scene (Owner = the map root) so it is saved into the .tscn like hand-made nodes.
|
||||
// The game never regenerates: it loads what was saved, so edits made in the editor stick (until the
|
||||
// next Rebuild, which replaces ALL children). Rebuild from the inspector button with the map scene
|
||||
// open, or headless for agents: godot-mono --headless --path . tools/rebuild_maps.tscn
|
||||
public static class MapScene
|
||||
{
|
||||
public const string PropGroup = "prop";
|
||||
|
||||
// The inspector button only works with the map scene itself open (nodes owned by an instanced
|
||||
// sub-scene's root aren't saved into the outer scene).
|
||||
public static bool CanRebuildInEditor(Node map)
|
||||
{
|
||||
if (map.GetTree()?.EditedSceneRoot == map)
|
||||
return true;
|
||||
GD.PushWarning($"{map.Name}: open its own scene (scenes/maps/) to rebuild it — it's instanced here.");
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void Clear(Node map)
|
||||
{
|
||||
foreach (Node child in map.GetChildren())
|
||||
{
|
||||
map.RemoveChild(child);
|
||||
child.Free();
|
||||
}
|
||||
}
|
||||
|
||||
// Every generated node belongs to the map scene; children of instanced sub-scenes (e.g. a
|
||||
// teleport marker) keep their own owner.
|
||||
public static void Adopt(Node map)
|
||||
{
|
||||
foreach (Node child in map.GetChildren())
|
||||
Adopt(map, child);
|
||||
}
|
||||
|
||||
private static void Adopt(Node root, Node node)
|
||||
{
|
||||
if (node.Owner == null)
|
||||
node.Owner = root;
|
||||
if (node.SceneFilePath != "")
|
||||
return;
|
||||
foreach (Node child in node.GetChildren())
|
||||
Adopt(root, child);
|
||||
}
|
||||
|
||||
public static void MarkUnsaved()
|
||||
{
|
||||
#if TOOLS
|
||||
if (Engine.IsEditorHint())
|
||||
EditorInterface.Singleton.MarkSceneAsUnsaved();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Runtime: the dynamic props (group "prop") and where they start, for Reset().
|
||||
public static void CollectProps(Node map, List<(RigidBody3D Body, Transform3D Start)> props)
|
||||
{
|
||||
props.Clear();
|
||||
foreach (Node child in map.GetChildren())
|
||||
if (child is RigidBody3D body && body.IsInGroup(PropGroup))
|
||||
props.Add((body, body.GlobalTransform));
|
||||
}
|
||||
|
||||
public static void Reset(List<(RigidBody3D Body, Transform3D Start)> props)
|
||||
{
|
||||
foreach (var (body, start) in props)
|
||||
{
|
||||
if (!GodotObject.IsInstanceValid(body))
|
||||
continue;
|
||||
body.GlobalTransform = start;
|
||||
body.LinearVelocity = Vector3.Zero;
|
||||
body.AngularVelocity = Vector3.Zero;
|
||||
body.ResetPhysicsInterpolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user