Replace raw eval JSON with godot_game_call_method for checking current room, matching the actual Godot MCP tool interface used in practice.
8.4 KiB
KQ4 Room Navigator
Overview
This skill finds shortest paths between rooms and can execute them through the running game using Godot's MCP server. It combines offline .tscn file parsing for graph construction with runtime interaction via McpInteractionServer (TCP port 9090).
The room transition system uses TransitionPiece nodes: each has an exit_node_name (destination), a polygon, and connects rooms typically bidirectionally. Clicking a TransitionPiece triggers a 3-step animation (walk → fade/swap scenes → walk to entrance in new room).
When to Use
- Planning multi-room navigation paths
- Verifying room connectivity
- Debugging why a path can't be traversed
- Automating room transitions during testing
How It Works
.scenes/*.tscn files → adjacency graph (96 rooms, 233 exits) → BFS pathfinding → use MCP for execution
Room identification at runtime:
- Game node tree:
/root/Node2D/SceneViewport/background(always named "background") - Room identity comes from
background.get_script().resource_path(e.g.,res://scenes/kq4_010_forest_path/kq4_010_forest_path.gd) - TransitionPiece children have
.targetUIDs and.labelstrings for cross-referencing
Getting the current room name at runtime
The canonical way is via MainGame.get_current_room_name():
Use godot_game_call_method to call it directly on the node path:
Call method get_current_room_name on /root/Node2D
Returns a string like "kq4_010_forest_path". The implementation extracts the filename from the scene script's resource path (script_path.trim_suffix(".gd").get_file()), NOT the node name (which is always "background").
Quick Start
Step 1 — Make a plan:
python tools/kq4_room_navigator.py --from kq4_003_fountain_pool --to kq4_011_enchanted_grove
Output:
Path: kq4_003_fountain_pool → kq4_011_enchanted_grove (3 steps)
1. Click 'kq4_004_ogres_cottage' in kq4_003_fountain_pool → kq4_004_ogres_cottage [Ogre's Cottage] (click at: 1874, 714)
2. Click 'kq4_010_forest_path' in kq4_004_ogres_cottage → kq4_010_forest_path [Forest Path] (click at: 1042, 1078)
3. Click 'kq4_011_enchanted_grove' in kq4_010_forest_path → kq4_011_enchanted_grove [Enchanted Grove] (click at: 1898, 610)
The click coordinates are not used — the exit_node_name is what matters.
Step 2 — Launch and navigate
- Use the Godot MCP to start the game (
godot_run_project) - Poll room name to verify starting room:
get_tree().root.get_node('Node2D').get_current_room_name() - For each step, call
mock_interact(0)on the TransitionPiece node (see exact method name below) - Wait and poll until
get_current_room_name()returns the expected destination - Repeat for next step
Detailed Navigation Protocol
Identify current room
{"command": "eval", "params": {"code": "get_tree().root.get_node('Node2D').get_current_room_name()"}}
Returns "kq4_0xx_room_name", confirming both MCP connectivity and which room you're in.
Discover available exits from current room
Use the set-piece group to find all interactive polygons (TransitionPieces are automatically added):
{"command": "get_nodes_in_group", "params": {"group": "set-piece"}}
Filter results for node names starting with kq4_0 — these are transition exits.
Trigger a room transition
Critical: The method name is mock_interact, NOT mock_interaction.
Use godot_game_call_method to call it directly on the node path:
Call method mock_interact(0) on /root/Node2D/SceneViewport/background/<exit_node_name>
Argument 0 = ActionState.Action.WALK (1=LOOK, 2=TOUCH, 3=TALK).
Waiting — MCP busy protocol
IMPORTANT: Walk animations block the McpInteractionServer for ~30 seconds. During this time:
eval,wait,call_methodall fail with "timed out after 30s"- The server has a built-in
_busyflag auto-reset after ~30s ("_busy flag stuck for 30.Xs, force-resetting")
Robust polling pattern:
- Call
mock_interact(0)on the target node (returns immediately, animation starts) - Try
wait(frames=30)then poll room name - If both timeout (~30s elapsed), retry the room name poll once more
- The server will auto-reset and the next poll will succeed
Expected timing per transition: ~45-75 seconds total (walk ~15s, MCP block ~30s, fade-in ~5s, confirm ~2s)
Finding transitions by script handler
In room .gd files, handlers like _on_ogres_cottage_interacted() reference the TransitionPiece node ($kq4_004_ogres_cottage.default_script(self)). The handler's parameter name matches the node name you need to call mock_interact(0) on.
Room Graph Structure
The graph currently has:
- 96 rooms across 23 connected components
- Largest component: 36 rooms (starting from room 3 Fountain Pool)
- Second largest: 17 rooms (room 1 Beach area)
- Two additional 36-room components that are disconnected due to UID mismatches
Use python scripts/build_room_graph.py --from A --to B to find a path. If "no path" is returned but rooms feel like they should connect, the cause is likely a stale UID (see Common Issues).
UID mismatch example
Room kq4_018_cemetery has UID uid://b3fjmiaribbrl, but nearby rooms point to it with stale UIDs (uid://35amqvpjgpf2x). Runtime navigation works fine (Transitions use file paths), but the graph can't find the connection.
Common Issues
| Problem | Diagnosis | Fix |
|---|---|---|
mock_interaction method not found |
Wrong method name | Use mock_interact(0) — no trailing "tion" |
| MCP commands time out during transition | Walk animation blocks server (~30s) | Wait and retry; server auto-resets after ~30s |
| "No path" between adjacent rooms at graph level | Target UID in source .tscn doesn't match destination room's .uid file |
Runtime still works. Fix the target uid in the source tscn or update the destination room's .uid |
get_current_room_name() returns "background" |
Old implementation used scene.name |
Updated to use script_path.trim_suffix(".gd").get_file() |
Verified Navigation Example
Successfully tested navigating from kq4_003_fountain_pool to kq4_018_cemetery via 5 transitions:
kq4_003_fountain_pool → mock_interact(kq4_004_ogres_cottage) → kq4_004_ogres_cottage ✓
kq4_004_ogres_cottage → mock_interact(kq4_010_forest_path) → kq4_010_forest_path ✓
kq4_010_forest_path → mock_interact(kq4_011_enchanted_grove) → kq4_011_enchanted_grove ✓
kq4_011_enchanted_grove → mock_interact(kq4_017_spooky_house_exterior) → kq4_017_spooky_house_exterior ✓
kq4_017_spooky_house_exterior → mock_interact(kq4_018_cemetery) → kq4_018_cemetery ✓
API Reference
build_room_graph.py module
from pathlib import Path
from scripts.build_room_graph import build_graph, find_path, NavigationStep
graph = build_graph(Path('scenes')) # RoomInfo dict keyed by room name
steps = find_path(graph, "kq4_003_fountain_pool", "kq4_018_cemetery") # List[NavigationStep] or None
step.from_room # Source room name
step.exit_node_name # TransitionPiece node to call mock_interact(0) on
step.to_room # Destination room name
step.label # Human-readable label (e.g., "Ogre's Cottage")
kq4_room_navigator.py CLI
python tools/kq4_room_navigator.py --from ROOM --to ROOM
# For graph summary:
python tools/kq4_room_navigator.py summary
Key Files
| File | Purpose |
|---|---|
MainGame.gd |
Root game node; get_current_room_name() returns room from script path |
tools/kq4_room_navigator.py |
CLI combining graph + BFS + MCP navigation |
scripts/build_room_graph.py |
Room adjacency graph builder + BFS pathfinding |
TransitionPiece.gd |
Exit nodes; mock_interact(action) triggers default_script() |
SetPiece_.gd |
Base interactive polygon class (class_name SetPiece) |
.opencode/skills/kq4-room-creator/SKILL.md |
Related: creating new rooms with transitions |