Commit graph

133 commits

Author SHA1 Message Date
80e14163f9 Shade sprite module: faction generation, asset scanning, TextureCache
Extends the shade_sprite module (for merchant-shade.itch.io character
sprite sheets) with procedural faction generation and asset management:

- FactionGenerator: seed-based faction recipes with Biome, Element,
  Aesthetic, and RoleType enums for thematic variety
- AssetLibrary: filesystem scanner that discovers and categorizes
  layer PNGs by type (skins, clothes, hair, etc.)
- TextureCache: avoids redundant disk I/O when building many variants
- CharacterAssembler: HSL shift documentation, method improvements
- Demo expanded to 6 interactive scenes (animation viewer, HSL recolor,
  character gallery, faction generator, layer compositing, equipment)
- EVALUATION.md: 7DRL readiness assessment of the full module
- 329-line faction generation test suite

Assets themselves are not included -- sprite sheets are external
dependencies, some under commercial license.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 23:17:24 -05:00
9718153709 Fix callback/timer GC: prevent premature destruction of Python callbacks
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>
2026-02-19 20:53:50 -05:00
97dbec9106 Add SoundBuffer type: procedural audio, sfxr synthesis, DSP effects
New SoundBuffer Python type enables procedural audio generation:
- Tone synthesis (sine, square, saw, triangle, noise) with ADSR envelopes
- sfxr retro sound effect engine (7 presets, 24 params, mutation, seeding)
- DSP effects chain: pitch_shift, low/high pass, echo, reverb,
  distortion, bit_crush, normalize, reverse, slice
- Composition: concat (with crossfade overlap) and mix
- Sound() now accepts SoundBuffer or filename string
- Sound gains pitch property and play_varied() method
- Platform stubs for HeadlessTypes and SDL2Types (loadFromSamples, pitch)
- Interactive demo: sfxr clone UI + Animalese speech synthesizer
- 62 unit tests across 6 test files (all passing)

Refs #251

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 18:58:11 -05:00
6fdf7279ce Shade (merchant-shade.itch.io) entity animation tests 2026-02-16 20:19:39 -05:00
52fdfd0347 Test suite modernization 2026-02-09 08:15:18 -05:00
ef05152ea0 Implement Entity3D.animate(), closes #242
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>
2026-02-07 20:16:02 -05:00
9e2444da69 Add pop/find/extend to EntityCollection3D, closes #243
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>
2026-02-07 20:15:55 -05:00
f766e9efa2 Add y_plane parameter to screen_to_world(), closes #245
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>
2026-02-07 20:15:48 -05:00
2062e4e4ad Fix Entity3D.viewport returning None, closes #244
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>
2026-02-07 20:15:38 -05:00
de7778b147 LDtk import support 2026-02-07 11:34:38 -05:00
322beeaf78 add __ne__ support to enum types for input 2026-02-06 21:43:52 -05:00
b093e087e1 Tiled XML/JSON import support 2026-02-06 21:43:03 -05:00
71cd2b9b41 3D / voxel unit tests 2026-02-06 16:15:07 -05:00
de5616f3a4 voxel, animation, and pathfinding combined demo 2026-02-05 22:57:08 -05:00
992ea781cb Voxel functionality extension 2026-02-05 12:52:18 -05:00
3e6b6a5847 voxel example 2026-02-05 10:49:31 -05:00
7e8efe82ec 3D target demo 2026-02-04 23:41:37 -05:00
cc027a2517 rigging and animation 2026-02-04 23:19:03 -05:00
b85f225789 billboards 2026-02-04 20:47:51 -05:00
544c44ca31 glTF model loading 2026-02-04 19:35:48 -05:00
f4c9db8436 3D entities 2026-02-04 17:45:12 -05:00
63008bdefd pathfinding on heightmap 2026-02-04 16:36:21 -05:00
e572269eac Terrain mesh, vertex color from heightmaps 2026-02-04 14:51:31 -05:00
9c29567349 Viewport scene explorer + object cache integration 2026-02-04 13:44:20 -05:00
e277663ba0 3D viewport, milestone 1 2026-02-04 13:33:14 -05:00
d2ea64bc32 fix: animations modifying animations during callback is now safe 2026-02-04 10:25:59 -05:00
d8fec5fea0 DiscreteMap class - mask for operations or uint8 tile data 2026-02-03 20:36:42 -05:00
001cc6efd6 grid layer API modernization 2026-02-03 20:18:12 -05:00
ff46043023 Add Game-to-API Bridge for external client integration
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>
2026-01-29 23:08:26 -05:00
55f6ea9502 Add cookbook examples with updated callback signatures for #229, #230
Cookbook structure:
- lib/: Reusable component library (Button, StatBar, AnimationChain, etc.)
- primitives/: Demo apps for individual components
- features/: Demo apps for complex features (animation chaining, shaders)
- apps/: Complete mini-applications (calculator, dialogue system)
- automation/: Screenshot capture utilities

API signature updates applied:
- on_enter/on_exit/on_move callbacks now only receive (pos) per #230
- on_cell_enter/on_cell_exit callbacks only receive (cell_pos) per #230
- Animation chain library uses Timer-based sequencing (unaffected by #229)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 18:58:25 -05:00
2daebc84b5 Simplify on_enter/on_exit callbacks to position-only signature
BREAKING CHANGE: Hover callbacks now take only (pos) instead of (pos, button, action)

- Add PyHoverCallable class for on_enter/on_exit/on_move callbacks (position-only)
- Add PyCellHoverCallable class for on_cell_enter/on_cell_exit callbacks
- Change UIDrawable member types from PyClickCallable to PyHoverCallable
- Update PyScene::do_mouse_hover() to call hover callbacks with only position
- Add tryCallPythonMethod overload for position-only subclass method calls
- Update UIGrid::fireCellEnter/fireCellExit to use position-only signature
- Update all tests for new callback signatures

New callback signatures:
| Callback       | Old                      | New        |
|----------------|--------------------------|------------|
| on_enter       | (pos, button, action)    | (pos)      |
| on_exit        | (pos, button, action)    | (pos)      |
| on_move        | (pos, button, action)    | (pos)      |
| on_cell_enter  | (cell_pos, button, action)| (cell_pos)|
| on_cell_exit   | (cell_pos, button, action)| (cell_pos)|
| on_click       | unchanged                | unchanged  |
| on_cell_click  | unchanged                | unchanged  |

closes #230

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 17:36:02 -05:00
e14f3cb9fc Animation callbacks now pass (target, property, value) instead of (None, None)
- 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>
2026-01-28 17:35:47 -05:00
d12bfd224c cell, scene callbacks support for derived classes + enum args 2026-01-27 22:38:37 -05:00
c7cf3f0e5b standardize mouse callback signature on derived classes 2026-01-27 20:42:50 -05:00
86bfebefcb Fix: Derivable drawable types participate in garbage collector cycle detection 2026-01-27 13:21:10 -05:00
16b5508233 Fix borrowed reference return in some callbacks 2026-01-27 10:43:10 -05:00
da434dcc64 Rotation 2026-01-25 23:20:52 -05:00
486087b9cb Shaders 2026-01-25 21:04:01 -05:00
41d551e6e1 Shader POC: Add shader_enabled property to UIFrame (#106)
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>
2026-01-24 20:28:53 -05:00
3fea6418ff Fix UIFrame RenderTexture positioning and toggling issues
- 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>
2026-01-22 22:54:50 -05:00
165db91b8d Organize test suite: add README, move loose tests to proper directories
- 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>
2026-01-21 21:34:22 -05:00
baa7ee354b Add input validation and fix Entity repr
- #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>
2026-01-16 19:18:27 -05:00
Frick
a1b692bb1f Add cookbook and tutorial showcase demos
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>
2026-01-15 04:06:24 +00:00
Frick
23afae69ad Add API verification test suite and documentation
tests/docs/:
- API_FINDINGS.md: Comprehensive migration guide from deprecated to modern API
- test_*.py: 9 executable tests verifying actual runtime behavior
- screenshots/: Visual verification of working examples

tests/conftest.py:
- Add 'docs' and 'demo' to pytest collection paths

Key findings documented:
- Entity uses grid_pos= not pos=
- Scene API: Scene() + activate() replaces createScene/setScene
- scene.children replaces sceneUI()
- scene.on_key replaces keypressScene()
- mcrfpy.current_scene (property) replaces currentScene() (function)
- Timer callback signature: (timer, runtime)
- Opacity animation does NOT work on Frame (documented bug)

🤖 Generated with Claude Code (https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-15 04:05:32 +00:00
Frick
be450286f8 Refactor 11 more tests to mcrfpy.step() pattern
Converted from Timer-based async to step()-based sync:
- test_simple_callback.py
- test_empty_animation_manager.py
- test_frame_clipping.py
- test_frame_clipping_advanced.py
- test_grid_children.py
- test_color_helpers.py
- test_no_arg_constructors.py
- test_properties_quick.py
- test_simple_drawable.py
- test_python_object_cache.py
- WORKING_automation_test_example.py

Only 4 tests remain with Timer-based patterns (2 are headless detection
tests that may require special handling).

🤖 Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Frack <frack@goblincorps.dev>
Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-14 03:09:47 +00:00
Frick
4528ece0a7 Refactor timing tests to use mcrfpy.step() for synchronous execution
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>
2026-01-14 02:56:21 +00:00
Frick
f063d0af0c Fix alignment_test.py margin default expectations
- 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>
2026-01-14 02:02:08 +00:00
Frick
4579be2791 Test suite modernization: pytest wrapper and runner fixes
- Add LD_LIBRARY_PATH auto-configuration in run_tests.py
- Add --timeout and --quiet command-line flags
- Create pytest wrapper (conftest.py, test_mcrogueface.py) for IDE integration
- Configure pytest.ini to avoid importing mcrfpy modules
- Document known issues: 120/179 passing, 40 timeouts, 19 failures

🤖 Generated with Claude Code (https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-14 01:54:31 +00:00
b22dfe9524 Djikstra to Heightmap: convert pathfinding data into a heightmap for use in procedural generation processes 2026-01-13 20:41:23 -05:00
4bf590749c Alignment: reactive or automatically calculated repositioning of UIDrawables on their parent 2026-01-13 20:40:34 -05:00