12 KiB
AGENTS.md - AI Agent Guidelines for ai-game-2
Project Overview
This is a Godot 4.6 game project (King's Quest IV remake). The project uses:
- Godot's scene system (
.tscnfiles) - GDScript (
.gdfiles) for game logic - NavigationServer2D for pathfinding
- Custom script builder pattern for game scripts
Tools
- Helper scripts (python) exist in ./tools
- a virtual env exists there, too. When you need to run one, you should source ./tools/venv/bin/activate
Build & Development Commands
Running the Game
# Open in Godot editor (headless for CI)
godot --headless --path .
# Or run the exported game (if built)
./export/linux/ai-game
Single Test/Scene Testing
Godot doesn't have a traditional test framework. To test a single scene:
- Open the project in Godot
- Set the scene as the main scene in project.godot
- Run with F5
Code Quality
# Godot's built-in lint (via editor)
# Tools > Analyze > Check Code
# For CI, you can run headless:
godot --headless --script-check .
Code Style Guidelines
*** CRITICAL **** When you need to create a new uid, run python make_uid.py, and it will return a unique uid. When creating scenes, at a .tscn.uid with the contents uid://
File Organization
- Scenes:
scenes/kq4_XXX_room_name/ - Scripts:
scenes/kq4_XXX_room_name/kq4_XXX_room_name.gd - Resources: Visual assets copied to room folders
- Template: Use
scenes/kq4_placeholder_template/when creating new rooms
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Scenes | kq4_XXX_description (snake_case) |
kq4_010_forest_path |
| Nodes | PascalCase | kq4_004_ogres_cottage |
| Scripts | snake_case.gd | kq4_010_forest_path.gd |
| Variables | snake_case | appear_at_node |
| Constants | SCREAMING_SNAKE | const MAX_SPEED = 100 |
| Classes | PascalCase | class_name Scene |
GDScript Patterns
Class Definition
extends Node2D
class_name MyClass
# Use @export for editor-exposed variables
@export var my_var: int = 0
@export_file("*.tscn") var target_scene: String
Type Hints (Required)
var player: Node2D
var score: int = 0
var name: String = ""
func move_to(position: Vector2) -> void:
pass
Signals
signal my_signal(value: int)
# Connecting
some_node.my_signal.connect(_on_my_signal)
# Or in editor-created connections:
[connection signal="interacted" from="transition_piece" to="." method="_on_interacted"]
Scene Structure (.tscn)
Header
[gd_scene format=3 uid="uid://xxxxx"]
[ext_resource type="Script" uid="uid://xxxxx" path="res://path/to/script.gd" id="1_abc"]
[ext_resource type="Texture2D" uid="uid://xxxxx" path="res://path/to/texture.png" id="2_abc"]
[ext_resource type="PackedScene" uid="uid://xxxxx" path="res://TransitionPiece.tscn" id="3_abc"]
Node Hierarchy
- Use meaningful node names
- Prefix private nodes with underscore when appropriate
- Use
unique_id=for editor-managed nodes
Transition Pieces
When adding exits to rooms:
[node name="kq4_004_ogres_cottage" parent="." instance=ExtResource("4_abc")]
position = Vector2(910, -213)
polygon = PackedVector2Array(...)
appear_at_node = "kq4_010_forest_path" # Node name in target scene
target = "uid://xxxxxxxx" # Scene UID
label = "Ogre's Cottage"
[node name="entrance" parent="kq4_004_ogres_cottage" index="0"]
position = Vector2(163, 598)
[node name="exit" parent="kq4_004_ogres_cottage" index="1"]
position = Vector2(159, 459)
[connection signal="interacted" from="kq4_004_ogres_cottage" to="." method="_on_ogres_cottage_interacted"]
[editable path="kq4_004_ogres_cottage"]
Error Handling
- Use
push_error()for critical issues - Use
push_warning()for non-critical issues - Always check
load()returns withif resource:before using - Use
@onreadyfor node references (handles load order)
Room Creation Process
- Copy template:
cp -r scenes/kq4_placeholder_template scenes/kq4_XXX_name - Rename files: Update
.gdand.tscnfilenames - Copy visual: Copy
pic_XXX_visual.pngfrom decompile - Update .tscn:
- Remove hardcoded UIDs (let Godot generate)
- Add transition pieces for cardinal directions
- Set labels to human-readable names
- Update .gd: Add signal handlers for connected rooms
- Generate UIDs: Let Godot import, then create
.uidfiles - Wire transitions: Update
targetfields with real UIDs
Import Files
When creating new room textures, create .import files:
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://xxxxxxxx"
path="res://.godot/imported/image.png-xxx.ctex"
[deps]
source_file="res://scenes/room_name/image.png"
dest_files=[...]
[params]
compress/mode=0
# ... standard Godot texture params
What NOT to Do
- Don't commit secrets/keys (use
.gitignore) - Don't use
print()for debugging in production (usepush_warning()) - Don't hardcode paths - use
res://relative paths - Don't modify
project.godotunless necessary - Don't delete the
meadow-2scene (unrelated to KQ4 rooms)
File Extensions to Know
| Extension | Purpose |
|---|---|
.gd |
GDScript source |
.tscn |
Godot scene |
.tres |
Godot resource |
.import |
Import configuration |
.uid |
Resource UID cache |
.md |
Room documentation (in kq4-sierra-decompile) |
Room Specs Reference
Room specifications are in kq4-sierra-decompile/rooms/kq4-XXX-name/.
Each room has:
kq4-XXX-name.md- Technical documentationpic_XXX_visual.png- Background imagepic_XXX_control.png- Control/hitbox areaspic_XXX_priority.png- Priority/z-ordering
Key Classes
Scene(Scene.gd) - Base room class with navigationTransitionPiece(TransitionPiece.gd) - Exit/entry pointsGameScript(GameScript.gd) - Script builder for cutscenesEgo(Ego.gd) - Player character
Interaction System (Global)
The game uses a cursor-based action system with 4 modes that cycle via right-click:
| Cursor | Action | Icon | Purpose |
|---|---|---|---|
| 0 | Walk | boot_icon.png | Move player to location |
| 1 | Look | eye_icon.png | Examine objects/room |
| 2 | Touch | hand_icon.png | Interact with objects |
| 3 | Talk | speech_icon.png | Talk to characters |
Key Components
ActionState (Autoload)
Global singleton (ActionState.gd) that tracks the current cursor action:
ActionState.current_action # 0-3 (WALK, LOOK, TOUCH, TALK)
ActionState.Action.WALK # Enum values
SetPiece Nodes
Interactive areas in rooms use SetPiece_.gd (extends Polygon2D):
Signals emitted:
interacted- Generic interaction (takes precedence over cursor actions)walked- When walk cursor clicks the piecelooked- When look cursor clicks the piecetouched- When touch cursor clicks the piecetalked- When talk cursor clicks the piece
Priority Rule: If interacted signal has connections, it overrides cursor-specific actions.
Room Setup for Interactions
- Add SetPiece nodes to room scene for interactive objects:
[node name="pool" type="Polygon2D" parent="."]
polygon = PackedVector2Array(...)
script = ExtResource("SetPiece_.gd")
label = "Pool"
- Connect signals in .tscn:
[connection signal="looked" from="pool" to="." method="_on_pool_looked"]
- Implement handlers in room script:
func _on_pool_looked() -> void:
start_main_script(ScriptBuilder.init(
ScriptBuilder.say(ego, "The water looks inviting.")
).build(self, "_on_script_complete"))
Room-Wide Look
Clicking empty space with look cursor triggers _on_room_looked():
func _on_room_looked() -> void:
# Override in room script for room description
start_main_script(ScriptBuilder.init(
ScriptBuilder.say(ego, "Room description here...")
).build(self, "_on_script_complete"))
Behavior Rules
- Precedence:
interactedsignal always wins if connected - Fallback: If no handler for cursor action, do nothing (silent)
- SetPiece detection: Mouse must be inside polygon
- Unhandled clicks: Scene._unhandled_input handles empty space
Common Patterns
Navigation
var path = NavigationServer2D.map_get_path(map, start, end, true)
Pathfinding Movement
scene.start_main_script(scene.ScriptBuilder.init(scene.ScriptBuilder.walk_path(ego, path)).build())
Scene Transitions
func _on_exit_interacted() -> void:
$target_scene.default_script(self)
Branching strategy
Do all work in a branch. Squash the branch before merging into master.
Issue Tracking with bd (beads)
IMPORTANT: This project uses bd (beads) for ALL issue tracking. Do NOT use markdown TODOs, task lists, or other tracking methods.
Why bd?
- Dependency-aware: Track blockers and relationships between issues
- Git-friendly: Dolt-powered version control with native sync
- Agent-optimized: JSON output, ready work detection, discovered-from links
- Prevents duplicate tracking systems and confusion
Quick Start
Check for ready work:
bd ready --json
Create new issues:
bd create "Issue title" --description="Detailed context" -t bug|feature|task -p 0-4 --json
bd create "Issue title" --description="What this issue is about" -p 1 --deps discovered-from:bd-123 --json
Claim and update:
bd update <id> --claim --json
bd update bd-42 --priority 1 --json
Complete work:
bd close bd-42 --reason "Completed" --json
Issue Types
bug- Something brokenfeature- New functionalitytask- Work item (tests, docs, refactoring)epic- Large feature with subtaskschore- Maintenance (dependencies, tooling)
Priorities
0- Critical (security, data loss, broken builds)1- High (major features, important bugs)2- Medium (default, nice-to-have)3- Low (polish, optimization)4- Backlog (future ideas)
Workflow for AI Agents
- Check ready work:
bd readyshows unblocked issues - Claim your task atomically:
bd update <id> --claim - Work on it: Implement, test, document
- Discover new work? Create linked issue:
bd create "Found bug" --description="Details about what was found" -p 1 --deps discovered-from:<parent-id>
- Complete:
bd close <id> --reason "Done"
Auto-Sync
bd automatically syncs via Dolt:
- Each write auto-commits to Dolt history
- Use
bd dolt push/bd dolt pullfor remote sync - No manual export/import needed!
Important Rules
- ✅ Use bd for ALL task tracking
- ✅ Always use
--jsonflag for programmatic use - ✅ Link discovered work with
discovered-fromdependencies - ✅ Check
bd readybefore asking "what should I work on?" - ❌ Do NOT create markdown TODO lists
- ❌ Do NOT use external issue trackers
- ❌ Do NOT duplicate tracking systems
For more details, see README.md and docs/QUICKSTART.md.
Landing the Plane (Session Completion)
When ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, linters, builds
- Update issue status - Close finished work, update in-progress items
- PUSH TO REMOTE - This is MANDATORY:
git pull --rebase bd sync git push git status # MUST show "up to date with origin" - Clean up - Clear stashes, prune remote branches
- Verify - All changes committed AND pushed
- Hand off - Provide context for next session
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds