closes#251
Two related bugs where Python garbage collection destroyed callbacks
that were still needed by live C++ objects:
1. **Drawable callbacks (all 8 types)**: tp_dealloc unconditionally called
click_unregister() etc., destroying callbacks even when the C++ object
was still alive in a parent's children vector. Fixed by guarding with
shared_ptr::use_count() <= 1 — only unregister when the Python wrapper
is the last owner.
2. **Timer GC prevention**: Active timers now hold a Py_INCREF'd reference
to their Python wrapper (Timer::py_wrapper), preventing GC while the
timer is registered in the engine. Released on stop(), one-shot fire,
or destruction. mcrfpy.Timer("name", cb, 100) now works without storing
the return value.
Also includes audio synth demo UI fixes: button click handling (don't set
on_click on Caption children), single-column slider layout, improved
Animalese contrast.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaced the NotImplementedError stub with a full animation
implementation. Entity3D now supports animating: x, y, z,
world_x, world_y, world_z, rotation, rot_y, scale, scale_x,
scale_y, scale_z, sprite_index, visible.
Added Entity3D as a third target type in the Animation system
(alongside UIDrawable and UIEntity), with startEntity3D(),
applyValue(Entity3D*), and proper callback support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EntityCollection3D now has API parity with UIEntityCollection:
- pop(index=-1): Remove and return entity at index
- find(name): Search by entity name, return Entity3D or None
- extend(iterable): Append multiple Entity3D objects
Also adds `name` property to Entity3D for use with find().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
screen_to_world() previously only intersected the Y=0 plane.
Now accepts an optional y_plane parameter (default 0.0) for
intersecting arbitrary horizontal planes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The root cause was PyViewport3DType being declared `static` in
Viewport3D.h, creating per-translation-unit copies. Entity3D.cpp's
copy was never passed through PyType_Ready, causing segfaults when
tp_alloc was called.
Changed `static` to `inline` (matching PyEntity3DType and
PyModel3DType patterns), and implemented get_viewport using the
standard type->tp_alloc pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements a general-purpose HTTP API that exposes McRogueFace games
to external clients (LLMs, accessibility tools, Twitch integrations,
testing harnesses).
API endpoints:
- GET /scene - Full scene graph with all UI elements
- GET /affordances - Interactive elements with semantic labels
- GET /screenshot - PNG screenshot (binary or base64)
- GET /metadata - Game metadata for LLM context
- GET /wait - Long-poll for state changes
- POST /input - Inject clicks, keys, or affordance clicks
Key features:
- Automatic affordance detection from Frame+Caption+on_click patterns
- Label extraction from caption text with fallback to element.name
- Thread-safe scene access via mcrfpy.lock()
- Fuzzy label matching for click_affordance
- Full input injection via mcrfpy.automation
Usage: from api import start_server; start_server(8765)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add convertDrawableToPython() and convertEntityToPython() helper functions
- Add animationValueToPython() to convert AnimationValue to Python objects
- Rewrite triggerCallback() to pass meaningful data:
- target: The animated Frame/Sprite/Grid/Entity/etc.
- property: String property name like "x", "opacity", "fill_color"
- final_value: float, int, tuple (for colors/vectors), or string
- Update test_animation_callback_simple.py for new signature
closes#229
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Proof of concept for shader support on UIFrame:
- Add shader and shader_enabled members to UIFrame
- Add initializeTestShader() with hardcoded wave/glow fragment shader
- Add shader_enabled Python property for toggling
- Apply shader when drawing RenderTexture sprite
- Auto-update time uniform for animated effects
Also fixes position corruption when toggling RenderTexture usage:
- Standard rendering path now uses `position` as source of truth
- Prevents box position from staying at (0,0) after texture render
Test files:
- tests/shader_poc_test.py: Visual test of 6 render variants
- tests/shader_toggle_test.py: Regression test for position bug
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix#223: Use `position` instead of `box.getPosition()` for render_sprite
positioning. The box was being set to (0,0) for texture rendering and
never restored, causing frames to render at wrong positions.
- Fix#224: Add disableRenderTexture() method and call it when toggling
clip_children or cache_subtree off. This properly cleans up the texture
and prevents stale rendering.
- Fix#225: Improve dirty propagation in markContentDirty() to propagate
to parent even when already dirty, if the parent was cleared (rendered)
since last propagation. Prevents child changes from being invisible.
- Fix#226: Add fallback to standard rendering when RenderTexture can't
be created (e.g., zero-size frame). Prevents inconsistent state.
Closes#223, closes#224, closes#225, closes#226
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add tests/README.md documenting test structure and usage
- Move issue_*_test.py files to tests/regression/ (9 files)
- Move loose test_*.py files to tests/unit/ (18 files)
- tests/ root now contains only pytest infrastructure
Addresses #166
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- #212: Add GRID_MAX (8192) validation to Grid, ColorLayer, TileLayer
- #213: Validate color components are in 0-255 range
- #214: Add null pointer checks before HeightMap operations
- #216: Change entities_in_radius(x, y, radius) to (pos, radius)
- #217: Fix Entity __repr__ to show actual draw_pos float values
Closes#212, closes#213, closes#214, closes#216, closes#217
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
tests/demo/:
- cookbook_showcase.py: Interactive demo of cookbook recipes
- tutorial_showcase.py: Visual walkthrough of tutorial content
- tutorial_screenshots.py: Automated screenshot generation
- new_features_showcase.py: Demo of modern API features
- procgen_showcase.py: Procedural generation examples
- simple_showcase.py: Minimal working examples
Created during docs modernization to verify cookbook examples work.
🤖 Generated with Claude Code (https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Converts tests from Timer-based async patterns to step()-based sync
patterns, eliminating timeout issues in headless testing.
Refactored tests:
- simple_timer_screenshot_test.py
- test_animation_callback_simple.py
- test_animation_property_locking.py
- test_animation_raii.py
- test_animation_removal.py
- test_timer_callback.py
Also updates KNOWN_ISSUES.md with comprehensive documentation on
the step()-based testing pattern including examples and best practices.
🤖 Generated with Claude Code (https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- margin returns 0 when unset (effective default)
- horiz_margin/vert_margin return -1 (sentinel for unset)
🤖 Generated with Claude Code (https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>