Files
ai-game-2/AGENTS.md
2026-03-10 13:08:26 -07:00

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 (.tscn files)
  • GDScript (.gd files) 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:

  1. Open the project in Godot
  2. Set the scene as the main scene in project.godot
  3. 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 with if resource: before using
  • Use @onready for node references (handles load order)

Room Creation Process

  1. Copy template: cp -r scenes/kq4_placeholder_template scenes/kq4_XXX_name
  2. Rename files: Update .gd and .tscn filenames
  3. Copy visual: Copy pic_XXX_visual.png from decompile
  4. Update .tscn:
    • Remove hardcoded UIDs (let Godot generate)
    • Add transition pieces for cardinal directions
    • Set labels to human-readable names
  5. Update .gd: Add signal handlers for connected rooms
  6. Generate UIDs: Let Godot import, then create .uid files
  7. Wire transitions: Update target fields 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 (use push_warning())
  • Don't hardcode paths - use res:// relative paths
  • Don't modify project.godot unless necessary
  • Don't delete the meadow-2 scene (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 documentation
  • pic_XXX_visual.png - Background image
  • pic_XXX_control.png - Control/hitbox areas
  • pic_XXX_priority.png - Priority/z-ordering

Key Classes

  • Scene (Scene.gd) - Base room class with navigation
  • TransitionPiece (TransitionPiece.gd) - Exit/entry points
  • GameScript (GameScript.gd) - Script builder for cutscenes
  • Ego (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 piece
  • looked - When look cursor clicks the piece
  • touched - When touch cursor clicks the piece
  • talked - When talk cursor clicks the piece

Priority Rule: If interacted signal has connections, it overrides cursor-specific actions.

Room Setup for Interactions

  1. Add SetPiece nodes to room scene for interactive objects:
[node name="pool" type="Polygon2D" parent="."]
polygon = PackedVector2Array(...)
script = ExtResource("SetPiece_.gd")
label = "Pool"
  1. Connect signals in .tscn:
[connection signal="looked" from="pool" to="." method="_on_pool_looked"]
  1. 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

  1. Precedence: interacted signal always wins if connected
  2. Fallback: If no handler for cursor action, do nothing (silent)
  3. SetPiece detection: Mouse must be inside polygon
  4. 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 broken
  • feature - New functionality
  • task - Work item (tests, docs, refactoring)
  • epic - Large feature with subtasks
  • chore - 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

  1. Check ready work: bd ready shows unblocked issues
  2. Claim your task atomically: bd update <id> --claim
  3. Work on it: Implement, test, document
  4. Discover new work? Create linked issue:
    • bd create "Found bug" --description="Details about what was found" -p 1 --deps discovered-from:<parent-id>
  5. 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 pull for remote sync
  • No manual export/import needed!

Important Rules

  • Use bd for ALL task tracking
  • Always use --json flag for programmatic use
  • Link discovered work with discovered-from dependencies
  • Check bd ready before 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:

  1. File issues for remaining work - Create issues for anything that needs follow-up
  2. Run quality gates (if code changed) - Tests, linters, builds
  3. Update issue status - Close finished work, update in-progress items
  4. PUSH TO REMOTE - This is MANDATORY:
    git pull --rebase
    bd sync
    git push
    git status  # MUST show "up to date with origin"
    
  5. Clean up - Clear stashes, prune remote branches
  6. Verify - All changes committed AND pushed
  7. Hand off - Provide context for next session

CRITICAL RULES:

  • Work is NOT complete until git push succeeds
  • 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