# 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 1. **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 2. **Always Check Gitea First** - Before starting work: Check open issues for related tasks or blockers - When using `/roadmap` command: 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 3. **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 4. **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 5. **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 filtering - `get_issue` - Get detailed issue information - `create_issue` - Create new issues programmatically - `create_issue_comment` - Add comments to issues - `edit_issue` - Update issue status, title, body - `add_issue_labels` - Tag issues appropriately - `add_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 ```bash # 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.cpp` initializes the game engine - **Scene System**: `Scene.h/cpp` manages game states - **Entity System**: `UIEntity.h/cpp` provides game objects - **Python Integration**: `McRFPy_API.h/cpp` exposes engine functionality to Python - **UI Components**: `UIFrame`, `UICaption`, `UISprite`, `UIGrid` for rendering ### Game Logic (Python) - **Main Script**: `src/scripts/game.py` contains game initialization and scene setup - **Entity System**: `src/scripts/cos_entities.py` implements game entities (Player, Enemy, Boulder, etc.) - **Level Generation**: `src/scripts/cos_level.py` uses BSP for procedural dungeon generation - **Tile System**: `src/scripts/cos_tiles.py` implements 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 audio - `scripts/` 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 1. C++ API additions go in `src/McRFPy_API.cpp` 2. Expose to Python using the existing binding pattern 3. Update Python scripts to use new functionality ## Testing Game Changes Currently no automated test suite. Manual testing workflow: 1. Build with `make` 2. Run `make run` or `cd build && ./mcrogueface` 3. Test specific features through gameplay 4. Check console output for Python errors ### Quick Testing Commands ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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: ```cpp 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 - `UICollection` wraps `std::vector>` - `UIEntityCollection` wraps `std::list>` - Different containers require different iteration code (vector vs list) #### Python Object Creation Patterns ```cpp // Pattern 1: Using tp_alloc (most common) auto o = (PyUIFrameObject*)type->tp_alloc(type, 0); o->data = std::make_shared(); // 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 `timeout` to 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: ```python # 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: ```python 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 `--exec` flag for automated testing: `./mcrogueface --headless --exec tests/my_test.py` ### Example Test Pattern ```bash # 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 ### Inline C++ Documentation Format When adding new methods or modifying existing ones in C++ source files, use this documentation format in PyMethodDef arrays: ```cpp {"method_name", (PyCFunction)Class::method, METH_VARARGS | METH_KEYWORDS, "method_name(arg1: type, arg2: type = default) -> return_type\n\n" "Brief description of what the method does.\n\n" "Args:\n" " arg1: Description of first argument\n" " arg2: Description of second argument (default: value)\n\n" "Returns:\n" " Description of return value\n\n" "Example:\n" " result = obj.method_name(value1, value2)\n\n" "Note:\n" " Any important notes or caveats"}, ``` For properties in PyGetSetDef arrays: ```cpp {"property_name", (getter)getter_func, (setter)setter_func, "Brief description of the property. " "Additional details about valid values, side effects, etc.", NULL}, ``` ### Regenerating Documentation After modifying C++ inline documentation: 1. **Rebuild the project**: `make -j$(nproc)` 2. **Generate stub files** (for IDE support): ```bash ./build/mcrogueface --exec generate_stubs.py ``` 3. **Generate dynamic documentation** (recommended): ```bash ./build/mcrogueface --exec generate_dynamic_docs.py ``` This creates: - `docs/api_reference_dynamic.html` - `docs/API_REFERENCE_DYNAMIC.md` 4. **Update hardcoded documentation** (if still using old system): - `generate_complete_api_docs.py` - Update method dictionaries - `generate_complete_markdown_docs.py` - Update method dictionaries ### Important Notes - **McRogueFace as Python interpreter**: Documentation scripts MUST be run using McRogueFace itself, not system Python - **Use --exec flag**: `./build/mcrogueface --exec script.py` or `--headless --exec` for CI/automation - **Dynamic is better**: The new `generate_dynamic_docs.py` extracts documentation directly from compiled module - **Keep docstrings consistent**: Follow the format above for automatic parsing ### Documentation Pipeline Architecture 1. **C++ Source** → PyMethodDef/PyGetSetDef arrays with docstrings 2. **Compilation** → Docstrings embedded in compiled module 3. **Introspection** → Scripts use `dir()`, `getattr()`, `__doc__` to extract 4. **Generation** → HTML/Markdown/Stub files created The documentation is only as good as the C++ inline docstrings!