Commit graph

19 commits

Author SHA1 Message Date
ab6aecd0b0 Add read-only Caption.font getter; addresses #320
The Caption class docstring listed `font` under its Attributes, but no getter
existed -- the PyUICaptionObject.font slot was GC-managed yet never populated by
init(). Wire it up:
  - init() now stores the supplied Font (incref) or, when none/None was given, a
    wrapper around the engine default font, so the getter reflects what is
    actually rendered rather than returning None.
  - New read-only `font` getset (consistent with Sprite.texture being read-only).

Regenerated API docs/stubs/man page and rebaselined the api-surface snapshot
(one added line: Caption `prop font: Font (ro)`). Frozen docstring gate 100%,
suite 297/297.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KnywUddaFRhkxo5kijxJnv
2026-06-21 12:18:53 -04:00
98489a96fd Fix verify-pass code bugs #317/#318/#319
Three small bugs surfaced by the #314 docstring-accuracy verify pass:

#317 automation.scroll() dropped the x of its position argument: scroll()
resolved (x, y) but called injectMouseEvent(MouseWheelScrolled, clicks, y),
passing the scroll amount as x. injectMouseEvent now takes the scroll delta as
its own parameter and scroll() forwards the real x/y.

#318 GridView.texture always returned None (a TODO stub). It now returns a
Texture wrapper sharing the underlying shared_ptr<PyTexture>, mirroring
Grid.texture. (mcrfpy.Grid and mcrfpy.GridView are the same type post-#252, so
this fixes both names.)

#319 Entity.visible_entities(radius=None) raised TypeError: radius was parsed
with the 'i' format code, which rejects None. It now parses radius as an object
and treats None / omitted / -1 as "use the grid's default fov_radius"; a
non-int, non-None radius raises a clear TypeError.

- regression tests for each under tests/regression/
- api_surface snapshot re-baselined (visible_entities signature; texture
  property now Texture | None) and docs/stubs regenerated; frozen docstring
  gate still 100%

closes #317
closes #318
closes #319

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KnywUddaFRhkxo5kijxJnv
2026-06-21 10:12:41 -04:00
265425321c Windowed perspective writeback in UIEntity::updateVisibility; closes #316
Clip the demote+promote passes in updateVisibility() to an AABB sized to
fov_radius instead of two full-W*H walks per entity. A prev_fov window cache
demotes last tick's promoted rect (not the current one) so a moving entity
leaves no trailing "ghost vision". On a 1000x1000 grid the Phase 5.2 benchmark's
flat ~25-36 ms/entity writeback overhead collapses to single-digit microseconds
(384x-6577x speedup on the cheap algorithms; below timing noise on the rest).

Adversarial verify caught a regression the happy-path test missed: the
documented from_bytes -> assign -> update_visibility() load/resume path left
permanent ghost-VISIBLE cells, because prev_fov only bounds engine-promoted
cells. Fixed with a one-shot perspective_full_demote_pending flag (full demote
only on the tick after an external assignment; per-turn hot path stays
windowed). Documented the engine-owned demote contract on the perspective_map
property.

- src/UIEntity.cpp/.h: windowed demote/promote, prev_fov cache, demote flag
- src/DiscreteMap.cpp/.h: demoteVisibleRect(x0, y0, x1, y1)
- tests/regression/issue_316_sparse_perspective_test.py: 7-section regression
  (equivalence matrix, radius-0, moving disjoint, trailing-edge, grid resize,
  load/resume assignment, AABB margin lock)
- docs regenerated for the perspective_map docstring change

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KnywUddaFRhkxo5kijxJnv
2026-06-21 09:40:05 -04:00
eafe65683f F15: correct docstring accuracy from adversarial verify pass (#314)
Follow-up to the macro conversion: an adversarial verify pass (one agent per
file vs. the C++ implementation + stubs) found 62 content issues; the real
ones are fixed here.

Callbacks (centralized):
- on_click: receives (pos, MouseButton, InputState), not str (8 files).
- on_enter/on_exit/on_move (UIBase.h): hover passes only (pos) -- removed the
  fictional button/action args.
- bounds/global_bounds (UIBase.h): mark (tuple, read-only).

Signatures / types:
- Grid.find_path: document heuristic + weight; get_dijkstra_map: document roots;
  compute_fov: FOV | int = FOV.BASIC (not the C constant FOV_BASIC) + Returns;
  at/is_in_fov: document (pos) and (x, y) call forms.
- get_metrics: document all 16 returned dict keys (was 8); bresenham: drop the
  bogus '*' keyword-only separator.
- Nullable defaults typed correctly: BSP seed/size, ColorLayer draw_fov/
  apply_perspective Color|None, Entity.visible_entities radius int=-1 (None is
  rejected by the 'i' parser -> see #319).
- Type-token fixes: GridView.center -> Vector; GridView.texture -> (None,
  read-only) (unimplemented, #318); GridPoint.grid_pos -> (tuple, read-only);
  EntityCollection.find -> Entity | list[Entity] | None; extend RuntimeError;
  UniformCollection.values -> list[float | tuple | None].
- automation: onScreen (x, y) form documented; scroll notes x is ignored (#317).

Also: correct stale AStarPath/DijkstraMap signatures in docs/api-audit-2026-04.md
(the bindings were right, the audit table was outdated). Rebaseline the API
snapshot golden and regenerate docs/stubs.

Code-level bugs surfaced by the pass are filed as #317, #318, #319.

Refs #314, #317, #318, #319

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 06:43:47 -04:00
5725a4f035 F15: convert frozen binding docstrings to MCRF_* macros (#314)
Convert 289 raw PyMethodDef/PyGetSetDef docstring slots to MCRF_METHOD /
MCRF_PROPERTY across the 20 frozen (non-3D) binding files, bringing the
frozen surface to 100% macro compliance (check_frozen_docstrings.sh PASS).
Done via a one-agent-per-file workflow gated by validate_file_docstrings.sh
and per-wave build/doc-rebuild checks.

- Adds #include "McRFPy_Doc.h" where missing; fills the lone genuine doc
  gap (UIGrid.at, which was MISSING a doc field in two arrays).
- McRFPy_Doc.h: comment documenting the MCRF_METHOD_DOC comma rule (the
  trap that broke the GridLayers conversion mid-run).
- Rebaseline api_surface golden: property types now resolve to real types
  instead of "Any" (e.g. grid_pos: Vector, on_cell_click: Callable | None),
  and 11 properties correctly flip rw->ro now that their docstrings carry
  "read-only" (collections, grid_size, hovered_cell, texture, view — all
  verified against NULL setter slots).
- Regenerate docs/stubs/man page from the new docstrings.

Module-level functions use MCRF_METHOD(<name>, ...) (expands identically to
the intended MCRF_FUNCTION; the audit's compliance set is METHOD/PROPERTY).
Experimental 3D/Voxel bindings (src/3d/) remain exempt from the freeze.

Pre-existing failures unrelated to this change: test_animation_*,
test_constructor_comprehensive (reference the removed mcrfpy.Animation and
old constructor arity).

Refs #314

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 01:20:55 -04:00
2d2c333cd7 Refactor UIEntity::grid to shared_ptr<GridData>; add entity.texture; closes #313
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>
2026-06-11 00:51:22 -04:00
98d2b36739 Regenerate docs and stubs after API freeze pass
Picks up the five 1.0 freeze commits (groups A-D + Grid follow-up):
  * mcrfpy.Animation removed from module exports
  * parent= kwarg on Frame/Caption/Sprite/Line/Circle/Arc/Grid
  * grid= kwarg on ColorLayer/TileLayer
  * Constructor positional reorders (Circle, Caption, Layers)
  * __getitem__/__setitem__ on ColorLayer/TileLayer/Grid

PyMethodDef/PyGetSetDef compliance baseline (per tools/audit_pymethoddef.py):
  PyGetSetDef: 190/410 MACRO (46.3%)
  PyMethodDef: 150/345 MACRO (43.5%)
413 raw-string docstring entries remain to migrate to MCRF_METHOD/
MCRF_PROPERTY in a separate sweep.
2026-04-18 13:35:26 -04:00
2086d25581 Phase 5.3: documentation regeneration + introspection-based stub generator
Regenerate HTML/Markdown API reference, man page, and type stubs against
current committed HEAD (post API-freeze pass #304-#308, #252 overhaul, and
Phase 3/4/5.1/5.2 changes).

The previous tools/generate_stubs_v2.py hand-maintained hardcoded strings,
which drifted badly: stubs still contained removed module functions
(setScale, findAll, getMetrics, setDevConsole), lacked new types (GridView,
Behavior, Trigger, DiscreteMap, Viewport3D, Entity3D, Model3D, Billboard,
NoiseSource, WangSet, LdtkProject, HeightMap, DijkstraMap, AStarPath,
ColorLayer, TileLayer, etc.), and missed post-overhaul properties
(tile_width/tile_height, sprite_grid, perspective_map, cell_pos, labels,
turn_order, move_speed, etc.).

Rewrite the generator as a runtime-introspection script mirroring
generate_dynamic_docs.py's approach:
  - classify mcrfpy members (classes, enums, functions, constants, submodules)
  - parse signatures from docstring first line with proper paren-depth tracking
  - translate multi-form signatures (foo(x,y) or foo(pos)) to *args/**kwargs
  - sanitize docstring '...' placeholder params into '**kwargs'
  - emit IntEnum blocks for int-subclass types with uppercase members
  - discover delegated methods via instance probing (Grid/GridView -> _GridData)
  - conservative property type inference (only accept recognized primitives
    and CapitalCase class names in parenthesized hints)

Resulting stubs/mcrfpy.pyi (2069 lines) parses as valid Python.
Markdown/HTML/man-page regeneration is otherwise timestamp-only since the
introspection path was already current -- the stubs were the stale artifact.
2026-04-18 07:33:51 -04:00
f797120d53 Replace UIEntity gridstate with DiscreteMap perspective_map; closes #294
Per-entity FOV memory moves from std::vector<UIGridPointState> (two-bool
visible/discovered pairs) to a 3-state DiscreteMap (0=UNKNOWN, 1=DISCOVERED,
2=VISIBLE), exposed as entity.perspective_map. The invariant
visible-subset-of-discovered becomes structural (single value per cell), and
the map is a live, serializable, first-class object rather than an implicit
internal array.

Changes:
- New DiscreteMap C++ class with shared ownership; PyDiscreteMapObject now
  holds shared_ptr<DiscreteMap>. UIEntity holds the same shared_ptr.
- New mcrfpy.Perspective IntEnum (UNKNOWN/DISCOVERED/VISIBLE), modelled on
  PyInputState.
- entity.perspective_map: lazy-allocated on first access with a grid;
  setter validates size against grid and raises ValueError on mismatch;
  None clears (next access lazy-reallocates fresh).
- updateVisibility() now demotes 2->1 then promotes visible cells to 2.
- entity.at(x, y) returns grid.at(x, y) when VISIBLE, else None.
- Fog-of-war rendering in UIGridView and UIGrid reads the 3-state map.
- Removed: UIEntity::gridstate, ensureGridstate(), entity.gridstate getter,
  UIGridPointState struct + PyUIGridPointStateType.
- Obsolete tests deleted (test_gridpointstate_point,
  issue_265_gridpointstate_dangle); 4 new tests cover lazy allocation,
  identity, serialization round-trip, size validation, and the
  visible-subset-of-discovered invariant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 23:04:27 -04: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
a3a0618524 rebuild docs 2026-01-23 20:49:11 -05:00
e64c5c147f docs: Fix property extraction and add Scene documentation
Doc generator fixes (tools/generate_dynamic_docs.py):
- Add types.GetSetDescriptorType detection for C++ extension properties
- All 22 classes with properties now have documented Properties sections
- Read-only detection via "read-only" docstring convention

Scene class documentation (src/PySceneObject.h):
- Expanded tp_doc with constructor, properties, lifecycle callbacks
- Documents key advantage: on_key works on ANY scene
- Includes usage examples for basic and subclass patterns

CLAUDE.md additions:
- New section "Adding Documentation for New Python Types"
- Step-by-step guide for tp_doc, PyMethodDef, PyGetSetDef
- Documentation extraction details and troubleshooting

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-29 19:48:21 -05:00
c9c7375827 refactor: Rename click kwarg to on_click for API consistency (closes #126)
BREAKING CHANGE: Constructor keyword argument renamed from `click` to
`on_click` for all UIDrawable types (Frame, Caption, Sprite, Grid, Line,
Circle, Arc).

Before: Frame(pos=(0,0), size=(100,100), click=handler)
After:  Frame(pos=(0,0), size=(100,100), on_click=handler)

The property name was already `on_click` - this makes the constructor
kwarg match, completing the callback naming standardization from #139.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 14:31:22 -05:00
d761b53d48 docs: Update grid demo and regenerate API docs
- grid_demo.py: Updated for new layer-based rendering
- Screenshots: Refreshed demo screenshots
- API docs: Regenerated with latest method signatures

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 09:21:43 -05:00
4e94291cfb docs: Complete Phase 7 documentation system with parser fixes and man pages
Fixed critical documentation generation bugs and added complete multi-format
output support. All documentation now generates cleanly from MCRF_* macros.

## Parser Fixes (tools/generate_dynamic_docs.py)

Fixed parse_docstring() function:
- Added "Raises:" section support (was missing entirely)
- Fixed function name duplication in headings
  - Was: `createSoundBuffercreateSoundBuffer(...)`
  - Now: `createSoundBuffer(filename: str) -> int`
- Proper section separation between Returns and Raises
- Handles MCRF_* macro format correctly

Changes:
- Rewrote parse_docstring() to parse by section markers
- Fixed markdown generation (lines 514-539)
- Fixed HTML generation (lines 385-413, 446-473)
- Added "raises" field to parsed output dict

## Man Page Generation

New files:
- tools/generate_man_page.sh - Pandoc wrapper for man page generation
- docs/mcrfpy.3 - Unix man page (section 3 for library functions)

Uses pandoc with metadata:
- Section 3 (library functions)
- Git version tag in footer
- Current date in header

## Master Orchestration Script

New file: tools/generate_all_docs.sh

Single command generates all documentation formats:
- HTML API reference (docs/api_reference_dynamic.html)
- Markdown API reference (docs/API_REFERENCE_DYNAMIC.md)
- Unix man page (docs/mcrfpy.3)
- Type stubs (stubs/mcrfpy.pyi via generate_stubs_v2.py)

Includes error checking (set -e) and helpful output messages.

## Documentation Updates (CLAUDE.md)

Updated "Regenerating Documentation" section:
- Documents new ./tools/generate_all_docs.sh master script
- Lists all output files with descriptions
- Notes pandoc as system requirement
- Clarifies generate_stubs_v2.py is preferred (has @overload support)

## Type Stub Decision

Assessed generate_stubs.py vs generate_stubs_v2.py:
- generate_stubs.py has critical bugs (missing commas in method signatures)
- generate_stubs_v2.py produces high-quality manually-maintained stubs
- Decision: Keep v2, use it in master script

## Files Modified

Modified:
- CLAUDE.md (25 lines changed)
- tools/generate_dynamic_docs.py (121 lines changed)
- docs/api_reference_dynamic.html (359 lines changed)

Created:
- tools/generate_all_docs.sh (28 lines)
- tools/generate_man_page.sh (12 lines)
- docs/mcrfpy.3 (1070 lines)
- stubs/mcrfpy.pyi (532 lines)
- stubs/mcrfpy/__init__.pyi (213 lines)
- stubs/mcrfpy/automation.pyi (24 lines)
- stubs/py.typed (0 bytes)

Total: 2159 insertions, 225 deletions

## Testing

Verified:
- Man page viewable with `man docs/mcrfpy.3`
- No function name duplication in docs/API_REFERENCE_DYNAMIC.md
- Raises sections properly separated from Returns
- Master script successfully generates all formats

## Related Issues

Addresses requirements from Phase 7 documentation issues.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 21:20:50 -04:00
67aba5ef1f docs: convert Phase 1 classes to documentation macros (Color, Font, Texture, Timer)
Converted 4 files to use MCRF_* documentation macros:
- PyColor.cpp: 3 methods + 4 properties
- PyFont.cpp: 2 properties (read-only)
- PyTexture.cpp: 6 properties (read-only)
- PyTimer.cpp: 4 methods + 7 properties

All conversions verified with test_phase1_docs.py - 0 placeholders.
Documentation regenerated with enhanced descriptions.

Progress: 11/12 class files complete (92%)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 16:59:40 -04:00
4c61bee512 docs: update CLAUDE.md with MCRF_* macro documentation system
Updated documentation guidelines to reflect the new macro-based system:
- Documented MCRF_METHOD and MCRF_PROPERTY usage
- Listed all available macros (MCRF_SIG, MCRF_DESC, MCRF_ARG, etc.)
- Added prose guidelines (concise C++, verbose external docs)
- Updated regeneration workflow (removed references to deleted scripts)
- Emphasized single source of truth and zero-drift architecture

Removed references to obsolete hardcoded documentation scripts that were
deleted in previous commits.

Related: #92 (Inline C++ documentation system)
2025-10-30 12:37:04 -04:00
7f253da581 fix: escape HTML in descriptions before link transformation
Fixes HTML injection vulnerability in generate_dynamic_docs.py where
description text was not HTML-escaped before being inserted into HTML
output. Special characters like <, >, & could be interpreted as HTML.

Changes:
- Modified transform_doc_links() to escape all non-link text when
  format='html' or format='web'
- Link text and hrefs are also properly escaped
- Non-HTML formats (markdown, python) remain unchanged
- Added proper handling for descriptions with mixed plain text and links

The fix splits docstrings into link and non-link segments, escapes
non-link segments, and properly escapes content within link patterns.

Tested with comprehensive test suite covering:
- Basic HTML special characters
- Special chars with links
- Special chars in link text
- Multiple links with special chars

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-30 11:48:09 -04:00
f4343e1e82 Squashed commit of the following: [alpha_presentable]
Author: John McCardle <mccardle.john@gmail.com>
Co-Authored-By: Claude <noreply@anthropic.com>

commit dc47f2474c7b2642d368f9772894aed857527807
    the UIEntity rant

commit 673ca8e1b089ea670257fc04ae1a676ed95a40ed
    I forget when these tests were written, but I want them in the squash merge

commit 70c71565c684fa96e222179271ecb13a156d80ad
    Fix UI object segfault by switching from managed to manual weakref management

    The UI types (Frame, Caption, Sprite, Grid, Entity) were using
    Py_TPFLAGS_MANAGED_WEAKREF while also trying to manually create weakrefs
    for the PythonObjectCache. This is fundamentally incompatible - when
    Python manages weakrefs internally, PyWeakref_NewRef() cannot access the
    weakref list properly, causing segfaults.

    Changed all UI types to use manual weakref management (like PyTimer):
    - Restored weakreflist field in all UI type structures
    - Removed Py_TPFLAGS_MANAGED_WEAKREF from all UI type flags
    - Added tp_weaklistoffset for all UI types in module initialization
    - Initialize weakreflist=NULL in tp_new and init methods
    - Call PyObject_ClearWeakRefs() in dealloc functions

    This allows the PythonObjectCache to continue working correctly,
    maintaining Python object identity for C++ objects across the boundary.

    Fixes segfault when creating UI objects (e.g., Caption, Grid) that was
    preventing tutorial scripts from running.

This is the bulk of the required behavior for Issue #126.
that issure isn't ready for closure yet; several other sub-issues left.
    closes #110
    mention issue #109 - resolves some __init__ related nuisances

commit 3dce3ec539ae99e32d869007bf3f49d03e4e2f89
    Refactor timer system for cleaner architecture and enhanced functionality

    Major improvements to the timer system:
    - Unified all timer logic in the Timer class (C++)
    - Removed PyTimerCallable subclass, now using PyCallable directly
    - Timer objects are now passed to callbacks as first argument
    - Added 'once' parameter for one-shot timers that auto-stop
    - Implemented proper PythonObjectCache integration with weakref support

    API enhancements:
    - New callback signature: callback(timer, runtime) instead of just (runtime)
    - Timer objects expose: name, interval, remaining, paused, active, once properties
    - Methods: pause(), resume(), cancel(), restart()
    - Comprehensive documentation with examples
    - Enhanced repr showing timer state (active/paused/once/remaining time)

    This cleanup follows the UIEntity/PyUIEntity pattern and makes the timer
    system more Pythonic while maintaining backward compatibility through
    the legacy setTimer/delTimer API.

    closes #121

commit 145834cfc31b8dabc4cb3591b9cb4ed99fc8b964
    Implement Python object cache to preserve derived types in collections

    Add a global cache system that maintains weak references to Python objects,
    ensuring that derived Python classes maintain their identity when stored in
    and retrieved from C++ collections.

    Key changes:
    - Add PythonObjectCache singleton with serial number system
    - Each cacheable object (UIDrawable, UIEntity, Timer, Animation) gets unique ID
    - Cache stores weak references to prevent circular reference memory leaks
    - Update all UI type definitions to support weak references (Py_TPFLAGS_MANAGED_WEAKREF)
    - Enable subclassing for all UI types (Py_TPFLAGS_BASETYPE)
    - Collections check cache before creating new Python wrappers
    - Register objects in cache during __init__ methods
    - Clean up cache entries in C++ destructors

    This ensures that Python code like:
    ```python
    class MyFrame(mcrfpy.Frame):
        def __init__(self):
            super().__init__()
            self.custom_data = "preserved"

    frame = MyFrame()
    scene.ui.append(frame)
    retrieved = scene.ui[0]  # Same MyFrame instance with custom_data intact
    ```

    Works correctly, with retrieved maintaining the derived type and custom attributes.

    Closes #112

commit c5e7e8e298
    Update test demos for new Python API and entity system

    - Update all text input demos to use new Entity constructor signature
    - Fix pathfinding showcase to work with new entity position handling
    - Remove entity_waypoints tracking in favor of simplified movement
    - Delete obsolete exhaustive_api_demo.py (superseded by newer demos)
    - Adjust entity creation calls to match Entity((x, y), texture, sprite_index) pattern

commit 6d29652ae7
    Update animation demo suite with crash fixes and improvements

    - Add warnings about AnimationManager segfault bug in sizzle_reel_final.py
    - Create sizzle_reel_final_fixed.py that works around the crash by hiding objects instead of removing them
    - Increase font sizes for better visibility in demos
    - Extend demo durations for better showcase of animations
    - Remove debug prints from animation_sizzle_reel_working.py
    - Minor cleanup and improvements to all animation demos

commit a010e5fa96
    Update game scripts for new Python API

    - Convert entity position access from tuple to x/y properties
    - Update caption size property to font_size
    - Fix grid boundary checks to use grid_size instead of exceptions
    - Clean up demo timer on menu exit to prevent callbacks

    These changes adapt the game scripts to work with the new standardized
    Python API constructors and property names.

commit 9c8d6c4591
    Fix click event z-order handling in PyScene

    Changed click detection to properly respect z-index by:
    - Sorting ui_elements in-place when needed (same as render order)
    - Using reverse iterators to check highest z-index elements first
    - This ensures top-most elements receive clicks before lower ones

commit dcd1b0ca33
    Add roguelike tutorial implementation files

    Implement Parts 0-2 of the classic roguelike tutorial adapted for McRogueFace:
    - Part 0: Basic grid setup and tile rendering
    - Part 1: Drawing '@' symbol and basic movement
    - Part 1b: Variant with sprite-based player
    - Part 2: Entity system and NPC implementation with three movement variants:
      - part_2.py: Standard implementation
      - part_2-naive.py: Naive movement approach
      - part_2-onemovequeued.py: Queued movement system

    Includes tutorial assets:
    - tutorial2.png: Tileset for dungeon tiles
    - tutorial_hero.png: Player sprite sheet

commit 6813fb5129
    Standardize Python API constructors and remove PyArgHelpers

    - Remove PyArgHelpers.h and all macro-based argument parsing
    - Convert all UI class constructors to use PyArg_ParseTupleAndKeywords
    - Standardize constructor signatures across UICaption, UIEntity, UIFrame, UIGrid, and UISprite
    - Replace PYARGHELPER_SINGLE/MULTI macros with explicit argument parsing
    - Improve error messages and argument validation
    - Maintain backward compatibility with existing Python code

    This change improves code maintainability and consistency across the Python API.

commit 6f67fbb51e
    Fix animation callback crashes from iterator invalidation (#119)

    Resolved segfaults caused by creating new animations from within
    animation callbacks. The issue was iterator invalidation in
    AnimationManager::update() when callbacks modified the active
    animations vector.

    Changes:
    - Add deferred animation queue to AnimationManager
    - New animations created during update are queued and added after
    - Set isUpdating flag to track when in update loop
    - Properly handle Animation destructor during callback execution
    - Add clearCallback() method for safe cleanup scenarios

    This fixes the "free(): invalid pointer" and "malloc(): unaligned
    fastbin chunk detected" errors that occurred with rapid animation
    creation in callbacks.

commit eb88c7b3aa
    Add animation completion callbacks (#119)

    Implement callbacks that fire when animations complete, enabling direct
    causality between animation end and game state changes. This eliminates
    race conditions from parallel timer workarounds.

    - Add optional callback parameter to Animation constructor
    - Callbacks execute synchronously when animation completes
    - Proper Python reference counting with GIL safety
    - Callbacks receive (anim, target) parameters (currently None)
    - Exception handling prevents crashes from Python errors

    Example usage:
    ```python
    def on_complete(anim, target):
        player_moving = False

    anim = mcrfpy.Animation("x", 300.0, 1.0, "easeOut", callback=on_complete)
    anim.start(player)
    ```

    closes #119

commit 9fb428dd01
    Update ROADMAP with GitHub issue numbers (#111-#125)

    Added issue numbers from GitHub tracker to roadmap items:
    - #111: Grid Click Events Broken in Headless
    - #112: Object Splitting Bug (Python type preservation)
    - #113: Batch Operations for Grid
    - #114: CellView API
    - #115: SpatialHash Implementation
    - #116: Dirty Flag System
    - #117: Memory Pool for Entities
    - #118: Scene as Drawable
    - #119: Animation Completion Callbacks
    - #120: Animation Property Locking
    - #121: Timer Object System
    - #122: Parent-Child UI System
    - #123: Grid Subgrid System
    - #124: Grid Point Animation
    - #125: GitHub Issues Automation

    Also updated existing references:
    - #101/#110: Constructor standardization
    - #109: Vector class indexing

    Note: Tutorial-specific items and Python-implementable features
    (input queue, collision reservation) are not tracked as engine issues.

commit 062e4dadc4
    Fix animation segfaults with RAII weak_ptr implementation

    Resolved two critical segmentation faults in AnimationManager:
    1. Race condition when creating multiple animations in timer callbacks
    2. Exit crash when animations outlive their target objects

    Changes:
    - Replace raw pointers with std::weak_ptr for automatic target invalidation
    - Add Animation::complete() to jump animations to final value
    - Add Animation::hasValidTarget() to check if target still exists
    - Update AnimationManager to auto-remove invalid animations
    - Add AnimationManager::clear() call to GameEngine::cleanup()
    - Update Python bindings to pass shared_ptr instead of raw pointers

    This ensures animations can never reference destroyed objects, following
    proper RAII principles. Tested with sizzle_reel_final.py and stress
    tests creating/destroying hundreds of animated objects.

commit 98fc49a978
    Directory structure cleanup and organization overhaul
2025-07-15 21:30:49 -04:00