18 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Gitea-First Workflow
IMPORTANT: This project uses Gitea for issue tracking, documentation, and project management. Always consult and update Gitea resources before and during development work.
Gitea Instance: https://gamedev.ffwf.net/gitea/john/McRogueFace
Core Principles
-
Gitea is the Single Source of Truth
- Issue tracker contains current tasks, bugs, and feature requests
- Wiki contains living documentation and architecture decisions
- Use Gitea MCP tools to query and update issues programmatically
-
Always Check Gitea First
- Before starting work: Check open issues for related tasks or blockers
- When using
/roadmapcommand: Query Gitea for up-to-date issue status - When researching a feature: Search Gitea wiki and issues before grepping codebase
- When encountering a bug: Check if an issue already exists
-
Create Granular Issues
- Break large features into separate, focused issues
- Each issue should address one specific problem or enhancement
- Tag issues appropriately:
[Bugfix],[Major Feature],[Minor Feature], etc. - Link related issues using dependencies or blocking relationships
-
Document as You Go
- When work on one issue interacts with another system: Add notes to related issues
- When discovering undocumented behavior: Create task to document it
- When documentation misleads you: Create task to correct or expand it
- When implementing a feature: Update the Gitea wiki if appropriate
-
Cross-Reference Everything
- Commit messages should reference issue numbers (e.g., "Fixes #104", "Addresses #125")
- Issue comments should link to commits when work is done
- Wiki pages should reference relevant issues for implementation details
- Issues should link to each other when dependencies exist
Workflow Pattern
┌─────────────────────────────────────────────────────┐
│ 1. Check Gitea Issues & Wiki │
│ - Is there an existing issue for this? │
│ - What's the current status? │
│ - Are there related issues or blockers? │
└─────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 2. Create Issues (if needed) │
│ - Break work into granular tasks │
│ - Tag appropriately │
│ - Link dependencies │
└─────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 3. Do the Work │
│ - Implement/fix/document │
│ - Write tests first (TDD) │
│ - Add inline documentation │
└─────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 4. Update Gitea │
│ - Add notes to affected issues │
│ - Create follow-up issues for discovered work │
│ - Update wiki if architecture/APIs changed │
│ - Add documentation correction tasks │
└─────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ 5. Commit & Reference │
│ - Commit messages reference issue numbers │
│ - Close issues or update status │
│ - Add commit links to issue comments │
└─────────────────────────────────────────────────────┘
Benefits of Gitea-First Approach
- Reduced Context Switching: Check brief issue descriptions instead of re-reading entire codebase
- Better Planning: Issues provide roadmap; avoid duplicate or contradictory work
- Living Documentation: Wiki and issues stay current as work progresses
- Historical Context: Issue comments capture why decisions were made
- Efficiency: MCP tools allow programmatic access to project state
MCP Tools Available
Claude Code has access to Gitea MCP tools for:
list_repo_issues- Query current issues with filteringget_issue- Get detailed issue informationcreate_issue- Create new issues programmaticallycreate_issue_comment- Add comments to issuesedit_issue- Update issue status, title, bodyadd_issue_labels- Tag issues appropriatelyadd_issue_dependency/add_issue_blocking- Link related issues- Plus wiki, milestone, and label management tools
Use these tools liberally to keep the project organized!
Build Commands
# Build the project (compiles to ./build directory)
make
# Or use the build script directly
./build.sh
# Run the game
make run
# Clean build artifacts
make clean
# The executable and all assets are in ./build/
cd build
./mcrogueface
Project Architecture
McRogueFace is a C++ game engine with Python scripting support, designed for creating roguelike games. The architecture consists of:
Core Engine (C++)
- Entry Point:
src/main.cppinitializes the game engine - Scene System:
Scene.h/cppmanages game states - Entity System:
UIEntity.h/cppprovides game objects - Python Integration:
McRFPy_API.h/cppexposes engine functionality to Python - UI Components:
UIFrame,UICaption,UISprite,UIGridfor rendering
Game Logic (Python)
- Main Script:
src/scripts/game.pycontains game initialization and scene setup - Entity System:
src/scripts/cos_entities.pyimplements game entities (Player, Enemy, Boulder, etc.) - Level Generation:
src/scripts/cos_level.pyuses BSP for procedural dungeon generation - Tile System:
src/scripts/cos_tiles.pyimplements Wave Function Collapse for tile placement
Key Python API (mcrfpy module)
The C++ engine exposes these primary functions to Python:
- Scene Management:
createScene(),setScene(),sceneUI() - Entity Creation:
Entity()with position and sprite properties - Grid Management:
Grid()for tilemap rendering - Input Handling:
keypressScene()for keyboard events - Audio:
createSoundBuffer(),playSound(),setVolume() - Timers:
setTimer(),delTimer()for event scheduling
Development Workflow
Running the Game
After building, the executable expects:
assets/directory with sprites, fonts, and audioscripts/directory with Python game files- Python 3.12 shared libraries in
./lib/
Modifying Game Logic
- Game scripts are in
src/scripts/ - Main game entry is
game.py - Entity behavior in
cos_entities.py - Level generation in
cos_level.py
Adding New Features
- C++ API additions go in
src/McRFPy_API.cpp - Expose to Python using the existing binding pattern
- Update Python scripts to use new functionality
Testing Game Changes
Currently no automated test suite. Manual testing workflow:
- Build with
make - Run
make runorcd build && ./mcrogueface - Test specific features through gameplay
- Check console output for Python errors
Quick Testing Commands
# Test basic functionality
make test
# Run in Python interactive mode
make python
# Test headless mode
cd build
./mcrogueface --headless -c "import mcrfpy; print('Headless test')"
Common Development Tasks
Compiling McRogueFace
# Standard build (to ./build directory)
make
# Full rebuild
make clean && make
# Manual CMake build
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
# The library path issue: if linking fails, check that libraries are in __lib/
# CMakeLists.txt expects: link_directories(${CMAKE_SOURCE_DIR}/__lib)
Running and Capturing Output
# Run with timeout and capture output
cd build
timeout 5 ./mcrogueface 2>&1 | tee output.log
# Run in background and kill after delay
./mcrogueface > output.txt 2>&1 & PID=$!; sleep 3; kill $PID 2>/dev/null
# Just capture first N lines (useful for crashes)
./mcrogueface 2>&1 | head -50
Debugging with GDB
# Interactive debugging
gdb ./mcrogueface
(gdb) run
(gdb) bt # backtrace after crash
# Batch mode debugging (non-interactive)
gdb -batch -ex run -ex where -ex quit ./mcrogueface 2>&1
# Get just the backtrace after a crash
gdb -batch -ex "run" -ex "bt" ./mcrogueface 2>&1 | head -50
# Debug with specific commands
echo -e "run\nbt 5\nquit\ny" | gdb ./mcrogueface 2>&1
Testing Different Python Scripts
# The game automatically runs build/scripts/game.py on startup
# To test different behavior:
# Option 1: Replace game.py temporarily
cd build
cp scripts/my_test_script.py scripts/game.py
./mcrogueface
# Option 2: Backup original and test
mv scripts/game.py scripts/game.py.bak
cp my_test.py scripts/game.py
./mcrogueface
mv scripts/game.py.bak scripts/game.py
# Option 3: For quick tests, create minimal game.py
echo 'import mcrfpy; print("Test"); mcrfpy.createScene("test")' > scripts/game.py
Understanding Key Macros and Patterns
RET_PY_INSTANCE Macro (UIDrawable.h)
This macro handles converting C++ UI objects to their Python equivalents:
RET_PY_INSTANCE(target);
// Expands to a switch on target->derived_type() that:
// 1. Allocates the correct Python object type (Frame, Caption, Sprite, Grid)
// 2. Sets the shared_ptr data member
// 3. Returns the PyObject*
Collection Patterns
UICollectionwrapsstd::vector<std::shared_ptr<UIDrawable>>UIEntityCollectionwrapsstd::list<std::shared_ptr<UIEntity>>- Different containers require different iteration code (vector vs list)
Python Object Creation Patterns
// Pattern 1: Using tp_alloc (most common)
auto o = (PyUIFrameObject*)type->tp_alloc(type, 0);
o->data = std::make_shared<UIFrame>();
// Pattern 2: Getting type from module
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity");
auto o = (PyUIEntityObject*)type->tp_alloc(type, 0);
// Pattern 3: Direct shared_ptr assignment
iterObj->data = self->data; // Shares the C++ object
Working Directory Structure
build/
├── mcrogueface # The executable
├── scripts/
│ └── game.py # Auto-loaded Python script
├── assets/ # Copied from source during build
└── lib/ # Python libraries (copied from __lib/)
Quick Iteration Tips
- Keep a test script ready for quick experiments
- Use
timeoutto auto-kill hanging processes - The game expects a window manager; use Xvfb for headless testing
- Python errors go to stderr, game output to stdout
- Segfaults usually mean Python type initialization issues
Important Notes
- The project uses SFML for graphics/audio and libtcod for roguelike utilities
- Python scripts are loaded at runtime from the
scripts/directory - Asset loading expects specific paths relative to the executable
- The game was created for 7DRL 2025 as "Crypt of Sokoban"
- Iterator implementations require careful handling of C++/Python boundaries
Testing Guidelines
Test-Driven Development
- Always write tests first: Create automation tests in
./tests/for all bugs and new features - Practice TDD: Write tests that fail to demonstrate the issue, then pass after the fix is applied
- Close the loop: Reproduce issue → change code → recompile → verify behavior change
Two Types of Tests
1. Direct Execution Tests (No Game Loop)
For tests that only need class initialization or direct code execution:
# These tests can treat McRogueFace like a Python interpreter
import mcrfpy
# Test code here
result = mcrfpy.some_function()
assert result == expected_value
print("PASS" if condition else "FAIL")
2. Game Loop Tests (Timer-Based)
For tests requiring rendering, game state, or elapsed time:
import mcrfpy
from mcrfpy import automation
import sys
def run_test(runtime):
"""Timer callback - runs after game loop starts"""
# Now rendering is active, screenshots will work
automation.screenshot("test_result.png")
# Run your tests here
automation.click(100, 100)
# Always exit at the end
print("PASS" if success else "FAIL")
sys.exit(0)
# Set up the test scene
mcrfpy.createScene("test")
# ... add UI elements ...
# Schedule test to run after game loop starts
mcrfpy.setTimer("test", run_test, 100) # 0.1 seconds
Key Testing Principles
- Timer callbacks are essential: Screenshots and UI interactions only work after the render loop starts
- Use automation API: Always create and examine screenshots when visual feedback is required
- Exit properly: Call
sys.exit()at the end of timer-based tests to prevent hanging - Headless mode: Use
--execflag for automated testing:./mcrogueface --headless --exec tests/my_test.py
Example Test Pattern
# Run a test that requires game loop
./build/mcrogueface --headless --exec tests/issue_78_middle_click_test.py
# The test will:
# 1. Set up the scene during script execution
# 2. Register a timer callback
# 3. Game loop starts
# 4. Timer fires after 100ms
# 5. Test runs with full rendering available
# 6. Test takes screenshots and validates behavior
# 7. Test calls sys.exit() to terminate
Development Best Practices
Testing and Deployment
- Keep tests in ./tests, not ./build/tests - ./build gets shipped, and tests shouldn't be included
Documentation Guidelines
Documentation Macro System
As of 2025-10-30, McRogueFace uses a macro-based documentation system (src/McRFPy_Doc.h) that ensures consistent, complete docstrings across all Python bindings.
Include the Header
#include "McRFPy_Doc.h"
Documenting Methods
For methods in PyMethodDef arrays, use MCRF_METHOD:
{"method_name", (PyCFunction)Class::method, METH_VARARGS,
MCRF_METHOD(ClassName, method_name,
MCRF_SIG("(arg1: type, arg2: type)", "return_type"),
MCRF_DESC("Brief description of what the method does."),
MCRF_ARGS_START
MCRF_ARG("arg1", "Description of first argument")
MCRF_ARG("arg2", "Description of second argument")
MCRF_RETURNS("Description of return value")
MCRF_RAISES("ValueError", "Condition that raises this exception")
MCRF_NOTE("Important notes or caveats")
MCRF_LINK("docs/guide.md", "Related Documentation")
)},
Documenting Properties
For properties in PyGetSetDef arrays, use MCRF_PROPERTY:
{"property_name", (getter)getter_func, (setter)setter_func,
MCRF_PROPERTY(property_name,
"Brief description of the property. "
"Additional details about valid values, side effects, etc."
), NULL},
Available Macros
MCRF_SIG(params, ret)- Method signatureMCRF_DESC(text)- Description paragraphMCRF_ARGS_START- Begin arguments sectionMCRF_ARG(name, desc)- Individual argumentMCRF_RETURNS(text)- Return value descriptionMCRF_RAISES(exception, condition)- Exception documentationMCRF_NOTE(text)- Important notesMCRF_LINK(path, text)- Reference to external documentation
Documentation Prose Guidelines
Keep C++ docstrings concise (1-2 sentences per section). For complex topics, use MCRF_LINK to reference external guides:
MCRF_LINK("docs/animation-guide.md", "Animation System Tutorial")
External documentation (in docs/) can be verbose with examples, tutorials, and design rationale.
Regenerating Documentation
After modifying C++ inline documentation with MCRF_* macros:
-
Rebuild the project:
make -j$(nproc) -
Generate all documentation (recommended - single command):
./tools/generate_all_docs.shThis creates:
docs/api_reference_dynamic.html- HTML API referencedocs/API_REFERENCE_DYNAMIC.md- Markdown API referencedocs/mcrfpy.3- Unix man page (section 3)stubs/mcrfpy.pyi- Type stubs for IDE support
-
Or generate individually:
# API docs (HTML + Markdown) ./build/mcrogueface --headless --exec tools/generate_dynamic_docs.py # Type stubs (manually-maintained with @overload support) ./build/mcrogueface --headless --exec tools/generate_stubs_v2.py # Man page (requires pandoc) ./tools/generate_man_page.sh
System Requirements:
pandocmust be installed for man page generation:sudo apt-get install pandoc
Important Notes
- Single source of truth: Documentation lives in C++ source files via MCRF_* macros
- McRogueFace as Python interpreter: Documentation scripts MUST be run using McRogueFace itself, not system Python
- Use --headless --exec: For non-interactive documentation generation
- Link transformation:
MCRF_LINKreferences are transformed to appropriate format (HTML, Markdown, etc.) - No manual dictionaries: The old hardcoded documentation system has been removed
Documentation Pipeline Architecture
- C++ Source → MCRF_* macros in PyMethodDef/PyGetSetDef arrays
- Compilation → Macros expand to complete docstrings embedded in module
- Introspection → Scripts use
dir(),getattr(),__doc__to extract - Generation → HTML/Markdown/Stub files created with transformed links
- No drift: Impossible for docs and code to disagree - they're the same file!
The macro system ensures complete, consistent documentation across all Python bindings.