Commit graph

335 commits

Author SHA1 Message Date
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
b66b934d8f new build for emscripten details 2026-02-03 12:18:40 -05:00
045b625655 opacity + animation fixes 2026-02-03 12:18:21 -05:00
2fb29a102e Animation and Scene clean up functions. Playground build target 2026-02-01 21:17:29 -05:00
3b27401f29 Remove debugging output 2026-02-01 16:40:23 -05:00
69a59ad1e8 Version bump: 0.2.3-prerelease-7drl2026 (67aa413) -> 0.2.4-prerelease-7drl2026 2026-01-31 20:16:11 -05:00
67aa413a78 Replace stb_truetype with FreeType for proper text outline rendering 0.2.3-prerelease-7drl2026-emscripten
- Add -sUSE_FREETYPE=1 to Emscripten build flags
- Extend Font class with FT_Library, FT_Face, and FT_Stroker handles
- Rewrite FontAtlas to use FreeType with on-demand stroked glyph loading
- Text outlines now use FT_Stroker for vector-based stroking before
  rasterization, eliminating gaps at corners with thick outlines
- Use glTexSubImage2D for incremental atlas updates (major perf fix)
- Disable canvas border in shell.html per Emscripten docs (alignment fix)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 16:45:10 -05:00
1be2714240 Add Python REPL widget for WebGL build
Features:
- JSFiddle-style code editor panel alongside game canvas
- Run button (or Ctrl+Enter) executes Python code
- Reset button reloads game.py
- Tab key inserts 4 spaces for proper indentation
- Shows >>> prompt with code preview
- Displays repr of last expression (like Python REPL)
- Error highlighting in red, success in green
- Canvas focus handling with visual indicator

C++ exports (callable from JavaScript):
- run_python_string(code) - simple execution
- run_python_string_with_output(code) - captures stdout/stderr + expr repr
- reset_python_environment() - reloads game.py

JavaScript API available in console:
- runPython(code) - execute Python and get output
- resetGame() - reset to initial state
- FS.readFile/writeFile - access virtual filesystem

Also fixes canvas focus issues on page load.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 15:31:18 -05:00
bc7046180a Add Emscripten shell and pre-JS for browser compatibility
- src/shell.html: Custom HTML shell with crisp pixel CSS
  (image-rendering: pixelated) and zoom prevention on canvas
- src/emscripten_pre.js: Patches browser quirks that cause crashes:
  - Intercepts resize/scroll events to ensure e.detail is always 0
  - Wraps window properties (innerWidth, outerWidth, etc.) to
    always return integers, fixing browser zoom crashes
- CMakeLists.txt: Output as .html, include shell and pre-js files

The pre-JS fix addresses "attempt to write non-integer (undefined)
into integer heap" errors that occurred when users zoomed the browser
via Ctrl+scroll or browser menu.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 14:36:22 -05:00
1abec8f808 Add text outline support for SDL2/WebGL backend
Implements SFML-compatible text outlines using multi-pass rendering:
- Draws text 8 times at offset positions (N, NE, E, SE, S, SW, W, NW)
  with outline color, then draws fill text on top
- Uses existing outlineThickness_ and outlineColor_ properties
- Refactored Text::draw() with helper lambda for code reuse
- Removed debug logging code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 14:01:50 -05:00
0811b76946 Fix FontAtlas texture deletion bug - text now renders in WebGL
The FontAtlas class was missing move semantics, causing the GPU texture
to be deleted immediately after creation. When FontAtlas was moved into
the cache with std::move(), the default move constructor copied textureId_,
then the original's destructor deleted the texture the cache was using.

Added:
- Move constructor that transfers ownership and clears source textureId_
- Move assignment operator with proper cleanup of existing resources
- Deleted copy operations since GPU textures can't be shared

Also cleaned up the text fragment shader to use proper alpha sampling.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 13:54:48 -05:00
d89376901a Fix RenderTexture Y-flip and add text debug output
RenderTexture fix:
- Add flippedY_ flag to Texture class for tracking FBO textures
- RenderTexture marks its texture as Y-flipped
- Sprite::draw() flips V coordinates for flipped textures
- This should fix upside-down entities and grid content

Text debug:
- Log first glyph UV coordinates
- Log texture binding in drawTriangles for Text shader
- Warn if Text shader used without texture bound

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-31 13:05:26 -05:00
50b63a8d06 Fix RenderTexture texture size + add text/font debug output
- Add Texture::setSize() method for RenderTexture to set its texture dimensions
- RenderTexture::create() now sets texture size (was 0,0 causing Sprite::draw to bail)
- Add debug output to FontAtlas::load showing glyph count and non-zero pixels
- Add debug output to Text::draw showing atlas texture ID and colors

These fixes should enable grid rendering (RenderTexture now has proper size).
Debug output will help diagnose why text shows as solid boxes instead of glyphs.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-31 12:52:35 -05:00
e08f189d60 Fix Text shader and RenderTexture viewport for SDL2 backend
Text rendering fix:
- Add ShaderType parameter to drawTriangles() to allow explicit shader selection
- Text::draw() now properly uses text shader (alpha-only sampling)
- Previously drawTriangles() always overrode to sprite shader

RenderTexture fix:
- Add pushRenderState/popRenderState to save/restore viewport and projection
- RenderTexture::clear() now sets viewport/projection to FBO dimensions
- RenderTexture::display() restores previous state
- This fixes grid rendering which uses RenderTexture for tile chunks

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-31 12:42:12 -05:00
be6fe23499 Implement Sprite, Text, and VertexArray draw() for SDL2 backend
- Sprite::draw(): Textured quad rendering with UV coordinates and color tint
- Text::draw(): Glyph rendering using FontAtlas cache, supports multi-line
- Text::getLocalBounds()/getGlobalBounds(): Calculate text dimensions
- VertexArray::draw(): Full primitive support (Triangles, TriangleFan,
  TriangleStrip, Quads, Lines, LineStrip, Points)
- View::getTransform(): Proper camera transform using translate/rotate/scale

Frames and sprites now render in browser. Text shows glyph positions but
texture sampling needs debugging. Grid rendering not yet working.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-31 12:38:03 -05:00
a702d3cab4 Implement Shape rendering and Transform math for SDL2 backend
Shape rendering now works:
- Shape::draw() generates triangle vertices for fill and outline
- RectangleShape, CircleShape, ConvexShape provide getPointCount()/getPoint()
- Shapes render with correct fill color, outline color, and outline thickness

Transform class fully implemented:
- translate(), rotate(), scale() modify the 3x3 affine matrix
- transformPoint() applies transform to Vector2f
- operator* combines transforms
- getInverse() computes inverse transform

Transformable::getTransform() now computes proper transform from:
- position, rotation, scale, and origin

RenderStates now has transform, blendMode, shader members

Canvas sizing fixed for Emscripten:
- EM_ASM sets canvas size after SDL window creation
- SDL_GL_MakeCurrent called after canvas resize

Result: RectangleShape UI elements render in correct positions!

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 11:58:42 -05:00
c5cc022aa2 Add SDL2+OpenGL ES 2 renderer backend for Emscripten/WebGL
Implements Phase 1 of renderer abstraction plan:
- SDL2Types.h: SFML-compatible type stubs in sf:: namespace
- SDL2Renderer.h/cpp: OpenGL ES 2 rendering implementation
- EmscriptenStubs.cpp: Stubs for missing POSIX functions (wait3, wait4, wcsftime)

Build system changes:
- Add MCRF_SDL2 compile-time backend selection
- Add Emscripten SDL2 link options (-sUSE_SDL=2, -sFULL_ES2=1)
- Fix LONG_BIT mismatch for Emscripten in pyport.h

Code changes for SDL2/headless compatibility:
- Guard ImGui includes with !MCRF_HEADLESS && !MCRF_SDL2
- Defer GL shader initialization until after context creation

Current status: Python runs in browser, rendering WIP (canvas sizing issues)

Build commands:
  emcmake cmake -DMCRF_SDL2=ON -B build-emscripten
  emmake make -C build-emscripten

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 11:13:15 -05:00
8c3128e29c WASM Python integration milestone - game.py runs in browser
Major milestone for issue #158 (Emscripten/WebAssembly build target):
- Python 3.14 successfully initializes and runs in WASM
- mcrfpy module loads and works correctly
- Game scripts execute with full level generation
- Entities (boulders, rats, cyclops, spawn points) placed correctly

Key changes:
- CMakeLists.txt: Add 2MB stack, Emscripten link options, preload files
- platform.h: Add WASM-specific implementations for executable paths
- HeadlessTypes.h: Make Texture/Font/Sound stubs return success
- CommandLineParser.cpp: Guard filesystem operations for WASM
- McRFPy_API.cpp: Add WASM path configuration, debug output
- game.py: Make 'code' module import optional (not available in WASM)
- wasm_stdlib/: Add minimal Python stdlib for WASM (~4MB)

Build with: emmake make (from build-emscripten/)
Test with: node mcrogueface.js

Next steps:
- Integrate VRSFML for actual WebGL rendering
- Create HTML page to host WASM build
- Test in actual browsers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 05:15:11 -05:00
07fd12373d First successful Emscripten/WASM build for #158
Build produces mcrogueface.wasm (8.9MB) + mcrogueface.js (126KB):
- All 68 C++ source files compile with emcc
- Links Python 3.14 (wasm32-emscripten target)
- Links libtcod-headless (built for Emscripten)
- Uses Emscripten ports: zlib, bzip2, sqlite3
- Includes HACL crypto, expat, mpdec, ffi dependencies

CMakeLists.txt updates:
- Add HACL .o files (not included in libpython3.14.a)
- Add expat, mpdec, ffi static libraries from Python build
- Add libtcod WASM build with lodepng and utf8proc
- Add Emscripten port link options

libtcod-headless submodule updated with Emscripten build.

Next: Bundle Python stdlib into WASM filesystem.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 00:34:11 -05:00
3bd996f317 Add Emscripten Python 3.14 WASM integration to CMake
Major progress on Emscripten build:
- Built Python 3.14.2 for wasm32-emscripten using official Tools/wasm/emscripten
- Add EMSCRIPTEN detection with proper Python headers from cross-build
- Force MCRF_HEADLESS for Emscripten builds (no SFML yet)
- Link against libpython3.14.a (47MB WASM static lib)

Result: ALL 68 C++ source files compile successfully with emcc!
Only blocker remaining: libtcod needs WASM compilation

Build verified:
- Python LONG_BIT errors: FIXED (using wasm32-emscripten pyconfig.h)
- Source compilation: 100% success
- Link stage: fails on libtcod.so (next step)

Contributes to #158

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 00:23:28 -05:00
5081a37c25 Document first Emscripten build attempt results
First emcc build confirms Python headers are the blocker:
- HeadlessTypes.h stubs compile fine with Emscripten
- Game engine code compiles fine
- Python C API headers fail: LONG_BIT check, 48-bit shifts on 32-bit WASM

Options identified:
1. Pyodide - pre-built Python WASM (recommended)
2. CPython WASM - build ourselves (complex)
3. No-Python mode - test build without scripting (simplest for now)

Contributes to #158

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 00:05:07 -05:00
8b6eb1e7ae Extract GameEngine::doFrame() for Emscripten callback support
Refactors the main game loop to support both:
- Desktop: traditional blocking while(running) loop
- Browser: emscripten_set_main_loop_arg() callback (build-time conditional)

Changes:
- Add doFrame() method containing single-frame update logic
- Add isRunning() accessor for Emscripten callback
- run() now conditionally uses #ifdef __EMSCRIPTEN__ for loop selection
- Add emscriptenMainLoopCallback() static function

This is a prerequisite for Emscripten builds - browsers require
cooperative multitasking with callback-based frame updates.

Both normal and headless builds verified working.

Contributes to #158

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:51:35 -05:00
4c70aee020 Add proper CMake MCRF_HEADLESS option for headless builds
- Add option(MCRF_HEADLESS) to CMakeLists.txt for official headless support
- Conditional compilation: skip ImGui sources when headless
- Conditional linking: no SFML/OpenGL libraries in headless mode
- Auto-define MCRF_HEADLESS preprocessor flag

Verified:
- Zero SFML/OpenGL dynamic dependencies (ldd confirms)
- Python interpreter fully functional in headless mode
- Core mcrfpy types work: Vector, Color, Scene, Frame, Grid
- Binary size: 1.6 MB headless vs 2.5 MB normal (36% reduction)

Build with: cmake .. -DMCRF_HEADLESS=ON

Contributes to #158

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:32:59 -05:00
7621ae35bb Add MCRF_HEADLESS compile-time build option for #158
This commit enables McRogueFace to compile without SFML dependencies
when built with -DMCRF_HEADLESS, a prerequisite for Emscripten/WebAssembly
support.

Changes:
- Add src/platform/HeadlessTypes.h (~900 lines of SFML type stubs)
- Consolidate all SFML includes through src/Common.h (15 files fixed)
- Wrap ImGui-SFML with #ifndef MCRF_HEADLESS guards
- Disable debug console/explorer in headless builds
- Add comprehensive research document: docs/EMSCRIPTEN_RESEARCH.md

The headless build compiles successfully but uses stub implementations
that return failure/no-op. This proves the abstraction boundary is clean
and enables future work on alternative backends (VRSFML, Emscripten).

What still works in headless mode:
- Python interpreter and script execution
- libtcod integrations (pathfinding, FOV, noise, BSP, heightmaps)
- Timer system and scene management
- All game logic and data structures

Build commands:
  Normal:   make
  Headless: cmake .. -DCMAKE_CXX_FLAGS="-DMCRF_HEADLESS" && make

Addresses #158

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:09:07 -05:00
96c66decba Add grid perspective support to API for FOV-aware entity filtering
When a Grid has a perspective entity set (typically the player), the API
now respects field-of-view by default. Only entities visible to the
perspective entity are returned in /scene responses.

Changes:
- serialize_grid() filters entities using grid.is_in_fov()
- Added ?omniscient=true query param to bypass FOV filtering
- Response includes perspective info with hidden_entities count
- Updated README with perspective documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 23:24:53 -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
b47132b052 Add MouseButton.MIDDLE, SCROLL_UP, SCROLL_DOWN support
- Register middle mouse button in PyScene (was missing, events were dropped)
- Add SCROLL_UP (10) and SCROLL_DOWN (11) to MouseButton enum
- Update button string-to-enum conversion in PyCallable and PyScene
- Legacy string comparisons work: MouseButton.SCROLL_UP == "wheel_up"

closes #231, closes #232

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 23:22:16 -05:00
5a1948699e Update documentation for API changes #229, #230, #184
CLAUDE.md updates:
- Fix Python version 3.12 -> 3.14
- Update keypressScene -> scene.on_key pattern
- Add API examples for new callback signatures
- Document animation callbacks (target, prop, value)
- Document hover callbacks (position-only)
- Document enum types (Key, MouseButton, InputState)

stubs/mcrfpy.pyi updates:
- Add Key, MouseButton, InputState, Easing enum classes
- Fix Drawable hover callback signatures per #230
- Fix Grid cell callback signatures per #230
- Fix Scene.on_key signature to use enums per #184
- Update Animation class with correct callback signature per #229
- Add deprecation notes to keypressScene, setTimer, delTimer

Regenerated docs:
- API_REFERENCE_DYNAMIC.md
- api_reference_dynamic.html
- mcrfpy.3 man page

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 19:20:04 -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
214037892e Fix UIGrid RenderTexture sizing - use game resolution instead of hard-coded 1080p
- Add ensureRenderTextureSize() helper that creates/resizes renderTexture to match game resolution
- Add renderTextureSize tracking member to detect when resize is needed
- Call helper in constructor and at start of render() to handle resolution changes
- Clamp maximum size to 4096x4096 (SFML texture limits)
- Only recreate texture when size actually changes (performance optimization)

closes #228

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 17:35:34 -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
475fe94148 Version bump: 0.2.2-prerelease-7drl2026 (9a241c9) -> 0.2.3-prerelease-7drl2026 2026-01-23 22:07:53 -05:00
a3a0618524 rebuild docs 2026-01-23 20:49:11 -05:00
f30e5bb8a1 libtcod experiments. Following feature branch API 2026-01-23 20:48:46 -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
c23da11d7d Modernize Crypt of Sokoban demo game and fix timer segfault
Game script updates (src/scripts/):
- Migrate Sound/Music API: createSoundBuffer() -> Sound() objects
- Migrate Scene API: sceneUI("name") -> scene.children
- Migrate Timer API: setTimer/delTimer -> Timer objects with stop()
- Fix callback signatures: (x,y,btn,event) -> (pos,btn,action) with Vector
- Fix grid_size unpacking: now returns Vector, use .x/.y with int()

Segfault fix (src/PyTimer.cpp):
- Remove direct map erase in PyTimer::stop() that caused iterator
  invalidation when timer.stop() was called from within a callback
- Now just marks timer as stopped; testTimers() handles safe removal

The game now starts and runs without crashes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 23:47:46 -05:00
5e45ab015c Add ImGui Scene Explorer (F4) for runtime object inspection (#136)
New Features:
- Scene Explorer window (F4) displays hierarchical tree of all scenes
- Shows UIDrawable hierarchy with type, name, and visibility status
- Click scene name to switch active scene
- Double-click drawables to toggle visibility
- Displays Python repr() for cached objects, enabling custom class debugging
- Entity display within Grid nodes

Bug Fixes:
- Fix PythonObjectCache re-registration: when retrieving objects from
  collections, newly created Python wrappers are now re-registered in
  the cache. Previously, inline-created objects (e.g.,
  scene.children.append(Frame(...))) would lose their cache entry when
  the temporary Python object was GC'd, causing repeated wrapper
  allocation on each access.
- Fix console focus stealing: removed aggressive focus reclaim that
  caused title bar flashing when clicking in Scene Explorer

Infrastructure:
- Add GameEngine::getSceneNames() to expose scene list for explorer
- Scene Explorer uses same enabled flag as console (ImGuiConsole::isEnabled())

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 23:26:33 -05:00
4be2502a10 Fix #161: Update Grid, GridPoint, GridPointState stubs to match current API
- Grid: Update constructor (pos, size, grid_size, texture, ...) and add all
  current properties (zoom, center, layers, FOV, cell events, etc.)
- Grid: Add all methods (find_path, compute_fov, add_layer, entities_in_radius, etc.)
- GridPoint: Replace incorrect properties (texture_index, solid, color) with
  actual API (walkable, transparent, entities, grid_pos)
- GridPointState: Replace incorrect properties with actual API (visible, discovered, point)
- Add missing types: ColorLayer, TileLayer, FOV, AStarPath, DijkstraMap,
  HeightMap, NoiseSource, BSP

Closes #161

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 21:47:26 -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
a4217b49d7 README.md updates: closes #168 2026-01-21 21:34:13 -05:00