UIEntity now depends on the grid DATA layer only: - GridData gains cell_width_px/cell_height_px (mirrored from the grid texture in UIGrid's 5-arg ctor; texture is write-once) so entity tile<->pixel math no longer reaches into rendering (getTexture()). - GridData gains markDirty()/markCompositeDirty(): set the flag on the UIGrid subobject AND notify owning_view, covering both render paths. UIGrid disambiguates via 'using UIDrawable::markDirty' so all pre-existing UIGrid-receiver calls resolve exactly as before. - The three Python wrappers that still need the full UIGrid (GridPoint from entity.at(), the _GridData fallback in get_grid, find_path's temp wrapper) reconstruct it via a single aliasing-downcast helper (grid_as_uigrid) that documents the never-independently-allocated GridData invariant; init/set_grid simplify (share grid_data directly). Removing the casts is deferred to #252. entity.texture (new, frozen surface +1): thin get/set over the entity's own UISprite. Entities render with their OWN texture (default_texture fallback at construction); the grid's texture only determines cell size. Setter preserves sprite_index; rejects non-Texture (TypeError), null-data Texture wrappers (ValueError), and deletion. Adversarial review fixes folded in: - set_texture/get_texture guard uninitialized Entity wrappers (RuntimeError), isinstance errors, and null-data Textures. - PyUIGridViewType tp_dealloc no longer unconditionally severs GridData::owning_view: gated on last-owner (#251 use_count pattern) plus owning-view identity. Previously ANY Grid wrapper GC while the view lived (e.g. scene.children.append(mcrfpy.Grid(...))) silently broke entity.grid -> Grid identity and data-layer dirty notification. Tests: tests/regression/issue_313_entity_grid_data_test.py (texture semantics, grid-cell-size invariance, entity.grid identity, #251 gate survival, GridPoint outliving teardown, review-fix guards, owning_view survival) + tests/unit/entity_texture_test.py. API snapshot golden re-baselined: exactly +1 surface line (Entity.texture) + writability probe flip. Docs/stubs regenerated. Native + Emscripten builds verified. Known edges recorded in docs/api-audit-2026-04.md: texture read-back is a fresh wrapper each get (no Texture __eq__); sprite_index not re-validated against a new atlas. Multi-view markDirty broadcast and pure-GridData wrappers remain deferred to #252. Addresses #314. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .gitea/workflows | ||
| assets | ||
| cmake/toolchains | ||
| deps/platform | ||
| docs | ||
| explanation | ||
| modules | ||
| sanitizers | ||
| shade_sprite | ||
| src | ||
| stubs | ||
| tests | ||
| tools | ||
| wasm_stdlib/lib/python3.14 | ||
| web | ||
| .gitignore | ||
| .gitmodules | ||
| build.sh | ||
| BUILD_FROM_SOURCE.md | ||
| build_windows.bat | ||
| build_windows_cmake.bat | ||
| CLAUDE.md | ||
| CMakeLists.txt | ||
| compile_commands.json | ||
| forgejo-mcp.linux.amd64 | ||
| LICENSE.md | ||
| Makefile | ||
| mcrf-init.sh | ||
| pyrightconfig.json | ||
| README.md | ||
| ROADMAP.md | ||
McRogueFace
Blame my wife for the name
A Python-powered 2D game engine for creating roguelike games, built with C++ and SFML.
- Core roguelike logic from libtcod: field of view, pathfinding
- Animate sprites with multiple frames. Smooth transitions for positions, sizes, zoom, and camera
- Simple GUI element system allows keyboard and mouse input, composition
- No compilation or installation necessary. The runtime is a full Python environment; "Zip And Ship"
📖 Full Documentation & Tutorials - Quickstart guide, API reference, and cookbook
Quick Start
Download the latest release:
- Windows:
McRogueFace-*-Win.zip - Linux:
McRogueFace-*-Linux.tar.bz2
Extract and run mcrogueface (or mcrogueface.exe on Windows) to see the demo game.
Your First Game
Create scripts/game.py (or edit the existing one):
import mcrfpy
# Create and activate a scene
scene = mcrfpy.Scene("game")
scene.activate()
# Load a sprite sheet
texture = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
# Create a tile grid
grid = mcrfpy.Grid(grid_size=(20, 15), texture=texture, pos=(50, 50), size=(640, 480))
grid.zoom = 2.0
scene.children.append(grid)
# Add a player entity
player = mcrfpy.Entity(pos=(10, 7), texture=texture, sprite_index=84)
grid.entities.append(player)
# Handle keyboard input
def on_key(key, state):
if state != "start":
return
x, y = int(player.x), int(player.y)
if key == "W": y -= 1
elif key == "S": y += 1
elif key == "A": x -= 1
elif key == "D": x += 1
player.x, player.y = x, y
scene.on_key = on_key
Run mcrogueface and you have a movable character!
Visual Framework
- Sprite: Single image or sprite from a shared sheet
- Caption: Text rendering with fonts
- Frame: Container rectangle for composing UIs
- Grid: 2D tile array with zoom and camera control
- Entity: Grid-based game object with sprite and pathfinding
- Animation: Interpolate any property over time with easing
Building from Source
For most users, pre-built releases are available. If you need to build from source:
Quick Build (with pre-built dependencies)
Download build_deps.tar.gz from the releases page, then:
git clone <repository-url> McRogueFace
cd McRogueFace
tar -xzf /path/to/build_deps.tar.gz
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
Full Build (compiling all dependencies)
git clone --recursive <repository-url> McRogueFace
cd McRogueFace
# See BUILD_FROM_SOURCE.md for complete instructions
BUILD_FROM_SOURCE.md - Complete build guide including:
- System dependency installation
- Compiling SFML, Python, and libtcod-headless from source
- Creating
build_depsarchives for distribution - Troubleshooting common build issues
System Requirements
- Linux: Debian/Ubuntu tested; other distros should work
- Windows: Supported (see build guide for details)
- macOS: Untested
Example: Main Menu with Buttons
import mcrfpy
# Create a scene
scene = mcrfpy.Scene("menu")
# Add a background frame
bg = mcrfpy.Frame(pos=(0, 0), size=(1024, 768),
fill_color=mcrfpy.Color(20, 20, 40))
scene.children.append(bg)
# Add a title
title = mcrfpy.Caption(pos=(312, 100), text="My Roguelike",
fill_color=mcrfpy.Color(255, 255, 100))
title.font_size = 48
scene.children.append(title)
# Create a button
button = mcrfpy.Frame(pos=(362, 300), size=(300, 80),
fill_color=mcrfpy.Color(50, 150, 50))
button_text = mcrfpy.Caption(pos=(90, 25), text="Start Game")
button.children.append(button_text)
def on_click(x, y, btn):
print("Game starting!")
button.on_click = on_click
scene.children.append(button)
scene.activate()
Documentation
📚 Developer Documentation
For comprehensive documentation about systems, architecture, and development workflows:
Key wiki pages:
- Home - Documentation hub with multiple entry points
- Grid System - Three-layer grid architecture
- Python Binding System - C++/Python integration
- Performance and Profiling - Optimization tools
- Adding Python Bindings - Step-by-step binding guide
- Issue Roadmap - All open issues organized by system
📖 Development Guides
In the repository root:
- CLAUDE.md - Build instructions, testing guidelines, common tasks
- ROADMAP.md - Strategic vision and development phases
- roguelike_tutorial/ - Complete roguelike tutorial implementations
Build Requirements
- C++17 compiler (GCC 7+ or Clang 5+)
- CMake 3.14+
- Python 3.14 (embedded)
- SFML 2.6
- Linux or Windows (macOS untested)
See BUILD_FROM_SOURCE.md for detailed compilation instructions.
Project Structure
McRogueFace/
├── assets/ # Sprites, fonts, audio
├── build/ # Build output: this is what you distribute
│ ├── assets/ # (copied from assets/)
│ ├── scripts/ # (copied from src/scripts/)
│ └── lib/ # Python stdlib and extension modules
├── docs/ # Generated HTML, markdown API docs
├── src/ # C++ engine source
│ └── scripts/ # Python game scripts
├── stubs/ # .pyi type stubs for IDE integration
├── tests/ # Automated test suite
└── tools/ # Documentation generation scripts
If you are building McRogueFace to implement game logic or scene configuration in C++, you'll have to compile the project.
If you are writing a game in Python using McRogueFace, you only need to rename and zip/distribute the build directory.
Philosophy
- C++ every frame, Python every tick: All rendering data is handled in C++. Structure your UI and program animations in Python, and they are rendered without Python. All game logic can be written in Python.
- No Compiling Required; Zip And Ship: Implement your game objects with Python, zip up McRogueFace with your "game.py" to ship
- Built-in Roguelike Support: Dungeon generation, pathfinding, and field-of-view via libtcod
- Hands-Off Testing: PyAutoGUI-inspired event generation framework. All McRogueFace interactions can be performed headlessly via script: for software testing or AI integration
- Interactive Development: Python REPL integration for live game debugging. Use
mcroguefacelike a Python interpreter
Contributing
PRs will be considered! Please include explicit mention that your contribution is your own work and released under the MIT license in the pull request.
Issue Tracking
The project uses Gitea Issues for task tracking and bug reports. Issues are organized with labels:
- System labels (grid, animation, python-binding, etc.) - identify which codebase area
- Priority labels (tier1-active, tier2-foundation, tier3-future) - development timeline
- Type labels (Major Feature, Minor Feature, Bugfix, etc.) - effort and scope
See the Issue Roadmap on the wiki for organized view of all open tasks.
License
This project is licensed under the MIT License - see LICENSE file for details.