changes for stuff

This commit is contained in:
2026-04-29 13:35:31 -07:00
parent 784867833d
commit ec4fc8e756
16 changed files with 5785 additions and 101 deletions

View File

@@ -5,7 +5,7 @@
"packages": {
"": {
"dependencies": {
"@opencode-ai/plugin": "1.14.27"
"@opencode-ai/plugin": "1.14.29"
}
},
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
@@ -87,13 +87,13 @@
]
},
"node_modules/@opencode-ai/plugin": {
"version": "1.14.27",
"resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.14.27.tgz",
"integrity": "sha512-8J8JkrInF5oWaR2rsLuwQktdF9Yq3xoyA8B42/B8Te74/q4rqOt7YzWK2I/ZSxvKA/Ct+iQ8f2OeUrpQ2INgSw==",
"version": "1.14.29",
"resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.14.29.tgz",
"integrity": "sha512-GwmBg7dajawma/6tzpoK/JMbcRcUTg27XHlnxZOFWG85WDz4M67hxFYnvE+BnJj9k7tTLXTfSR+pgfcdqbUDAg==",
"license": "MIT",
"dependencies": {
"@opencode-ai/sdk": "1.14.27",
"effect": "4.0.0-beta.48",
"@opencode-ai/sdk": "1.14.29",
"effect": "4.0.0-beta.57",
"zod": "4.1.8"
},
"peerDependencies": {
@@ -110,9 +110,9 @@
}
},
"node_modules/@opencode-ai/sdk": {
"version": "1.14.27",
"resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.14.27.tgz",
"integrity": "sha512-mpzDFDAGi+8wKEcm4aP0HVtS56rN/q2hVs8Ai6JziPu7NuTMddfFoEvddArYsgkRWUfHL5ypZc1mDmAMEiO1vg==",
"version": "1.14.29",
"resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.14.29.tgz",
"integrity": "sha512-y6wNTlHhgfwLdp01EwdnMFVxUS1FLgz7MZh7H3+jROG2v02GqGDy/gPH3ME0kI+sqQ4qSlk/9AJ+YbKAruPaZw==",
"license": "MIT",
"dependencies": {
"cross-spawn": "7.0.6"
@@ -149,9 +149,9 @@
}
},
"node_modules/effect": {
"version": "4.0.0-beta.48",
"resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.48.tgz",
"integrity": "sha512-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw==",
"version": "4.0.0-beta.57",
"resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.57.tgz",
"integrity": "sha512-rg32VgXnLKaPRs9tbRDaZ5jxmzNY7ojXt85gSHGUTwdlbWH5Ik+OCUY2q14TXliygPGoHwCAvNWS4bQJOqf00g==",
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.1.0",

View File

@@ -0,0 +1,164 @@
---
model: "auto"
kill: ["alpha-mask-creator"]
date_introduced: 2026-04-28
---
# Plan: kq4-room-navigator Skill (AI Agent Room Pathfinding via Godot MCP)
## Context
The game has ~96 rooms connected by TransitionPiece nodes. Each room's `.tscn` file defines its exits: node name (destination room), target UID, appear_at_node, label, and polygon coordinates. Room scripts connect the `interacted` signal to handlers that call `default_script()` on the transition piece, triggering a 3-step animated sequence (walk to exit → fade/swap scenes → walk to entrance in new room).
The custom MCP server (`scripts/mcp_interaction_server.gd`) runs as an autoload at TCP port 9090 with ~80 commands including `eval` (arbitrary GDScript execution), `click`, `screenshot`, `get_scene_tree`, `get_property`, and `wait`. No Python client exists yet.
**Key constraint**: Full runtime discovery via `eval` is fragile — instantiating .tscn files for graph discovery triggers script execution (`_ready()`) which references autoload singletons (ActionState, GameScript), causing errors when rooms aren't fully set up. **Solution**: Use targeted `find_nodes_by_class` MCP command for the currently-loaded room, and file parsing for global graph construction (leveraging existing `scripts/check_transitions.py`).
## Approach
Build a Python tool (`tools/kq4_room_navigator.py`) that:
1. Parses `.tscn` files to build complete room adjacency graph (reuse logic from `check_transitions.py`)
2. Runs BFS pathfinding between start and destination rooms
3. Connects to the MCP server on port 9090 for live navigation
4. Uses `eval` to compute click coordinates within TransitionPiece polygons in the running game
5. Executes step-by-step clicks with screenshot verification after each transition
The SKILL.md guides an AI agent through: starting Godot, invoking the tool, and optionally verifying steps manually with MCP commands.
## Implementation Plan
### 1. Extend room graph parser (`scripts/build_room_graph.py`)
**Purpose**: Parse all `.tscn` files to produce a queryable room adjacency graph with BFS pathfinding. Extends/extracts logic from existing `scripts/check_transitions.py`.
**What it does**:
1. Scan `scenes/kq4_*/kq4_*.tscn` (exclude placeholder_template)
2. Parse each file to extract:
- Scene UID (from header `[gd_scene format=3 uid="uid://XXX"]`)
- Room name/directory stem (e.g., `kq4_004_ogres_cottage`)
- All TransitionPiece nodes → build adjacency list
3. Build bidirectional graph: `{room_name: [(exit_node_name, target_uid, appear_at_node, label, polygon), ...]}`
**Output**: Returns a structure that supports BFS queries between any two rooms.
**Interface** (callable from Python or CLI):
```python
def build_graph(scenes_dir: Path) -> dict[str, list[TransitionInfo]]:
"""Parse all room .tscn files and return adjacency graph."""
def find_path(graph: dict, start_room: str, end_room: str) -> list[NavigationStep]:
"""BFS from start to end. Returns ordered list of steps or None."""
@dataclass
class NavigationStep:
from_room: str # Current room name (e.g., "kq4_004_ogres_cottage")
exit_node_name: str # TransitionPiece node name (e.g., "kq4_010_forest_path")
to_room: str # Destination room name
label: str # Human-readable label (e.g., "Forest Path")
polygon: list[tuple] # Polygon vertices for click coordinate computation
```
**File**: `scripts/build_room_graph.py`
### 3. Create the main navigator tool (`tools/kq4_room_navigator.py`)
**Purpose**: End-to-end CLI tool that connects to MCP, builds graph, finds path, and navigates.
**Workflow**:
```bash
python tools/kq4_room_navigator.py --from kq4_004_ogres_cottage --to kq4_092_lolottes_throne_room
```
**Implementation flow**:
1. **Build graph** (offline): Calls `build_graph()` to parse all rooms
2. **BFS pathfinding**: Finds shortest path from start → destination
3. **No path found**: Reports unreachable, lists connected components
4. **Path found**: Prints the step-by-step plan with click coordinates
5. **Connect to MCP** (runtime): Opens TCP connection to port 9090
6. **For each navigation step**:
a. `find_nodes_by_class(class_name="TransitionPiece")` — discover all transition pieces in current scene
b. Match the exit node name from our path → get runtime position + polygon
c. Compute click coordinate: centroid of the TransitionPiece's `polygon`, transformed to viewport coordinates
d. `click(x, y)` — trigger the transition
e. Wait for transition animation (2-3s via `wait` command or poll-loop)
f. **Verify**: `eval("return get_tree().root.get_node_or_null('Node2D/SceneViewport/background').name")` to confirm we've entered the expected room
g. If verification fails, retry up to 2 times with adjusted coordinates
h. Optionally take a screenshot via `screenshot()` for visual confirmation
7. **Complete**: Print summary of navigation path taken
**GDScript eval code used at runtime**:
Find clickable position within TransitionPiece polygon:
```gdscript
# Returns {node_name, centroid_x, centroid_y} for matching transition piece
var bg = get_tree().root.get_node_or_null("Node2D/SceneViewport/background")
if not bg: return null
for child in bg.get_children():
if child.has_method("is_class") and child.is_class("TransitionPiece"):
var p = child.position + child.polygon.reduce(func(p, a): return p + a, Vector2(0,0)) / child.polygon.size()
return {"node": child.name, "x": p.x, "y": p.y, "polygon_size": child.polygon.size()}
```
Verify current room:
```gdscript
var bg = get_tree().root.get_node_or_null("Node2D/SceneViewport/background")
return bg ? bg.name : null
```
**File**: `tools/kq4_room_navigator.py`
### 4. Write SKILL.md (`.opencode/skills/kq4-room-navigator/SKILL.md`)
The skill guide documents:
- **When to use**: Planning navigation between rooms, verifying room connectivity, debugging transitions
- **Pre-requisites**: Godot game running with MCP server active on port 9090
- **Quick start**: `python tools/kq4_room_navigator.py --from kq4_XXX --to kq4_YYY`
- **Manual MCP workflow** (for step-by-step agent control):
- Start Godot: `godot --path .` or run exported binary
- Verify connectivity: `{"command": "get_scene_tree"}` returns the current scene tree
- Discover exits in current room: `{"command": "find_nodes_by_class", "params": {"class_name": "TransitionPiece"}}`
- Click a transition: compute centroid from polygon data, then `{"command": "click", "params": {"x": px, "y": py}}`
- Wait and verify room change via `eval` or `screenshot`
- **Coordinate computation math**: How to convert TransitionPiece polygon (local-space) to viewport click coordinates: `viewport_x = transition_node.position.x + polygon_centroid.x`, accounting for any node scale transforms
- **Troubleshooting**: Common failures (server busy, node not found mid-transition, wrong room), escape hatches
**File**: `.opencode/skills/kq4-room-navigator/SKILL.md`
## Task Dependency Graph
```
[1. build_room_graph.py] ────────┐
├── [2. kq4_room_navigator.py] ─── [3. SKILL.md]
```
Tasks run sequentially. Task 3 documents the completed system.
## Verification
- Run `python scripts/build_room_graph.py` → should produce a graph with all rooms and their exits
- Test BFS: path from kq4_001_beach to an adjacent room should be 1 step; path to a far room tests multi-hop correctly
- Connect to running Godot instance via `tools/kq4_room_navigator.py` → verify it can navigate at least one transition end-to-end (from current room to an adjacent room)
- Verify failure handling: requesting a path between disconnected rooms returns "no path found"
- Test `_busy` retry logic by sending rapid commands
## Risks & Mitigations
| Risk | Impact | Mitigation |
|------|--------|------------|
| Room not fully set up — `TransitionPiece._ready()` crashes on eval-instantiation | Can't discover transitions from unloaded rooms via MCP | File parsing for graph discovery is the primary approach; MCP runtime only queries currently-loaded scene |
| Transition animation timing varies | Click at wrong time → missed transition | Poll current room name via eval every 0.5s until it changes, with 10s timeout |
| Polygon coordinates are local to node — need transform to viewport | Click lands outside polygon | Account for node `position` AND `scale` when computing centroid; use `get_node_info` runtime to get actual global polygon positions |
| MCP server `_busy` state blocks commands | Navigation stalls | Retry with exponential backoff (0.1s, 0.2s, 0.4s, ...) and 3-second max wait |
## Out of Scope
- Path optimization beyond BFS shortest path
- Inventory/item-based transitions (e.g., needing a key to enter a room)
- Cutscene-triggered room changes (non-interaction transitions)
- Multiplayer considerations
- Auto-starting Godot from the tool (user starts game manually)

View File

@@ -0,0 +1,170 @@
---
## name: kq4-room-navigator description: Navigate between KQ4 rooms using the Godot MCP server. BFS pathfinds through room transition graph, then executes step-by-step clicks via McpInteractionServer (port 9090). Use when planning navigation between any two rooms or simulating player movement through the game world at runtime. Trigger phrases: "navigate from", "go to room", "path from X to Y", "walk to", "how do I get to room", "room navigation".
# 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 clickable polygon, and connects rooms 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
- Generating step-by-step click instructions for agents
## 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 `.target` UIDs and `.label` strings for cross-referencing
## Quick Start
### Step 1 Make a plan:
```bash
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)
```
This will tell you which set pieces to click on to get there. Specifically, this is suggesting that you use the walk interaction with kq4_004_ogres_cottage. This can be done using the mcp server for godot, and using the eval tool.
Here's how to proceed.
1. start the game (godot --path . &)
```bash
# Wait for "McpInteractionServer: Listening on 127.0.0.1:9090" in console
```
2. Verify current room matches the starting point
3. For each step, find TransitionPiece coordinates at runtime via GDScript eval
4. Use the `func mock_interact(action = 0) -> void` on setpiece using the gdscript eval
5. Poll until room changes to expected destination
6. Verify final arrival
## More detailed
When implementing navigation step by step through MCP commands:
### Starting the game:
```bash
godot --path . &
# Wait for "McpInteractionServer: Listening on 127.0.0.1:9090" in console
```
### Then verify the connection:
Send `{"command": "get_scene_tree"}` via TCP to verify MCP responds. The response will show the full node hierarchy including `Node2D/SceneViewport/background`.
### Discover current room's exits
```json
{"command": "find_nodes_by_class", "params": {"class_name": "TransitionPiece"}}
```
Returns all TransitionPieces in the active scene with `.name`, `.label`, `.target` properties.
For rooms without scripts, identify via transition piece labels or target UIDs cross-referenced with `scripts/build_room_graph.py --room <name>` output.
### Simulate interactions
Use eval to find the centroid of a TransitionPiece's polygon in viewport space:
```json
{
"command": "eval",
"params": {
"code": " get_tree().root.get_node_or_null('Node2D/SceneViewport/background/kq4_010_forest_path').mock_interaction(0)"
}
}
```
Interactions match ActionState.Action (LOOK/WALK/ITEM/TALK)
### Waiting after interaction
```json
{"command": "wait", "params": {"frames": 60}}
```
Transition animation takes \~1-2 seconds (fade-out + scene swap + fade-in). Wait 30+ frames before checking room change.
## 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)
- Some rooms are fully disconnected (not all transitions wired bidirectionally yet)
Use `python scripts/build_room_graph.py --room <name>` to check a room's available exits.
## Common Issues
| Problem | Diagnosis | Fix |
| --- | --- | --- |
| "No path" between expected connected rooms | Room has no matching .uid file or transition pieces not wired bidirectionally | Check `build_room_graph.py` output for which component each room belongs to; verify the target room's `.tscn` has correct UID and exit nodes |
| MCP "Server busy" error during navigation | Previous command hasn't completed (30s timeout on server) | Tool handles automatic retry with exponential backoff. If persistent, restart game. |
| Click lands outside polygon → transition doesn't trigger | Scale transform not applied to coordinates | Use runtime eval (not offline centroid). The `--navigate` flag uses MCP eval which reads actual node transforms. |
| "Room not found in graph" | Room name mismatch — must use exact `.tscn` filename stem | Run `python scripts/build_room_graph.py` to see available room names |
| "Expected X but game reports Y" during --navigate | Game is on different room than specified | Restart at correct room, or adjust --from flag. Check current room name via MCP eval. |
## API Reference
### build_room_graph.py
```python
from build_room_graph import build_graph, find_path, NavigationStep
graph = build_graph(scenes_dir) # RoomInfo dict keyed by room name
steps = find_path(graph, "kq4_003_fountain_pool", "kq4_011_enchanted_grove") # List[NavigationStep] or None
step.from_room # Navigation source room name
step.exit_node_name # TransitionPiece node to click
step.to_room # Destination room name
step.label # Human-readable label
step.viewport_centroid() # (x, y) tuple — polygon center in viewport coords
```
### kq4_room_navigator.py CLI
```bash
python tools/kq4_room_navigator.py [--from ROOM] [--to ROOM] [summary]
```
## Key Files
| File | Purpose |
| --- | --- |
| `tools/kq4_room_navigator.py` | CLI combining graph + BFS + MCP navigation |
| `scripts/build_room_graph.py` | Room adjacency graph builder + BFS pathfinding |
| `scripts/check_transitions.py` | Existing transition validation (related) |
| `TransitionPiece.gd` | TransitionPiece node class (class_name TransitionPiece) |
| `SetPiece_.gd` | Base interactive polygon class (class_name SetPiece) |
| | |