Commit graph

12 commits

Author SHA1 Message Date
bb72040396 Migrate static PyTypeObject to inline, delete PyTypeCache workarounds
All 27 PyTypeObject declarations in namespace mcrfpydef headers changed
from `static` to `inline` (C++17), ensuring a single global instance
across translation units. This fixes the root cause of stale-type-pointer
segfaults where only the McRFPy_API.cpp copy was PyType_Ready'd.

Replaced ~20 PyTypeCache call sites and 2 PyRAII::PyTypeRef lookups with
direct &mcrfpydef::Type references. Deleted PyTypeCache.h/.cpp,
PyObjectUtils.h, and PyRAII.h (all were workarounds for the static bug).

228/228 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 20:58:09 -05:00
001cc6efd6 grid layer API modernization 2026-02-03 20:18:12 -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
a4b1ab7d68 Grid layers: add HeightMap-based procedural generation methods
TileLayer (closes #200):
- apply_threshold(source, range, tile): Set tile index where heightmap value is in range
- apply_ranges(source, ranges): Apply multiple tile assignments in one pass

ColorLayer (closes #201):
- apply_threshold(source, range, color): Set fixed color where value is in range
- apply_gradient(source, range, color_low, color_high): Interpolate colors based on value
- apply_ranges(source, ranges): Apply multiple color assignments (fixed or gradient)

All methods return self for chaining. HeightMap size must match layer dimensions.
Later ranges override earlier ones if overlapping. Cells not matching any range are unchanged.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 22:35:44 -05:00
b6eb70748a Remove YAGNI methods from performance systems
GridChunk: Remove getWorldBounds, markAllDirty, getVisibleChunks
- getWorldBounds: Chunk visibility handled by isVisible() instead
- markAllDirty: GridLayers uses per-cell markDirty() pattern
- getVisibleChunks: GridLayers computes visible range inline
- Keep dirtyChunks() for diagnostics

GridLayers: Remove getChunkCoords
- Trivial helper replaced by inline division throughout codebase

SpatialHash: Remove queryRect, totalEntities, cleanBucket
- queryRect: Exceeds #115 scope (only queryRadius required)
- totalEntities: Redundant with separate entity count tracking
- cleanBucket: Dead code - expired weak_ptrs cleaned during remove/update

All removals identified via cppcheck static analysis. Core functionality
of each system remains intact and actively used.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 15:40:13 -05:00
c6233fa47f Expand TileLayer and ColorLayer __init__ documentation; closes #190
Enhanced tp_doc strings for both layer types to include:
- What happens when grid_size=None (inherits from parent Grid)
- That layers are created via Grid.add_layer() rather than directly
- FOV-related methods for ColorLayer
- Tile index -1 meaning no tile/transparent for TileLayer
- fill_rect method documentation
- Comprehensive usage examples

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 22:24:36 -05:00
d2e4791f5a Positions are always mcrfpy.Vector, Vector/tuple/iterables expected as inputs, and for position-only inputs we permit x,y args to prevent requiring double-parens 2026-01-05 10:16:16 -05:00
a529e5eac3 feat: Add ColorLayer perspective methods and patrol demo (addresses #113)
ColorLayer enhancements:
- fill_rect(x, y, w, h, color): Fill rectangular region
- draw_fov(source, radius, fov, visible, discovered, unknown): One-time FOV draw
- apply_perspective(entity, visible, discovered, unknown): Bind layer to entity
- update_perspective(): Refresh layer from bound entity's gridstate
- clear_perspective(): Remove entity binding

New demo: tests/demo/perspective_patrol_demo.py
- Entity patrols around 10x10 central obstacle
- FOV layer shows visible/discovered/unknown states
- [R] to reset vision, [Space] to pause, [Q] to quit
- Demonstrates fog of war memory system

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 16:26:30 -05:00
018e73590f feat: Implement FOV enum and layer draw_fov for #114 and #113
Phase 1 - FOV Enum System:
- Create PyFOV.h/cpp with mcrfpy.FOV IntEnum (BASIC, DIAMOND, SHADOW, etc.)
- Add mcrfpy.default_fov module property initialized to FOV.BASIC
- Add grid.fov and grid.fov_radius properties for per-grid defaults
- Remove deprecated module-level FOV_* constants (breaking change)

Phase 2 - Layer Operations:
- Implement ColorLayer.fill_rect(pos, size, color) for rectangle fills
- Implement TileLayer.fill_rect(pos, size, index) for tile rectangle fills
- Implement ColorLayer.draw_fov(source, radius, fov, visible, discovered, unknown)
  to paint FOV-based visibility on color layers using parent grid's TCOD map

The FOV enum uses Python's IntEnum for type safety while maintaining
backward compatibility with integer values. Tests updated to use new API.

Addresses #114 (FOV enum), #113 (layer operations)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 15:18:10 -05:00
a258613faa feat: Migrate Grid to user-driven layer rendering (closes #150)
- Add `layers` dict parameter to Grid constructor for explicit layer definitions
  - `layers={"ground": "color", "terrain": "tile"}` creates named layers
  - `layers={}` creates empty grid (entities + pathfinding only)
  - Default creates single TileLayer named "tilesprite" for backward compat

- Implement dynamic GridPoint property access via layer names
  - `grid.at(x,y).layer_name = value` routes to corresponding layer
  - Protected names (walkable, transparent, etc.) still use GridPoint

- Remove base layer rendering from UIGrid::render()
  - Layers are now the sole source of grid rendering
  - Old chunk_manager remains for GridPoint data access
  - FOV overlay unchanged

- Update test to use explicit `layers={}` parameter

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 23:04:09 -05:00
abb3316ac1 feat: Add dirty flag and RenderTexture caching for Grid layers (closes #148)
Implement per-layer dirty tracking and RenderTexture caching for
ColorLayer and TileLayer. Each layer now maintains its own cached
texture and only re-renders when content changes.

Key changes:
- Add dirty flag, cached_texture, and cached_sprite to GridLayer base
- Implement renderToTexture() for both ColorLayer and TileLayer
- Mark layers dirty on: set(), fill(), resize(), texture change
- Viewport changes (center/zoom) just blit cached texture portion
- Fallback to direct rendering if texture creation fails
- Add regression test with performance benchmarks

Expected performance improvement: Static layers render once, then
viewport panning/zooming only requires texture blitting instead of
re-rendering all cells.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 21:44:33 -05:00
4b05a95efe feat: Add dynamic layer system for Grid (closes #147)
Implements ColorLayer and TileLayer classes with z_index ordering:
- ColorLayer: stores RGBA color per cell for overlays, fog of war, etc.
- TileLayer: stores sprite index per cell with optional texture
- z_index < 0: renders below entities
- z_index >= 0: renders above entities

Python API:
- grid.add_layer(type, z_index, texture) - create layer
- grid.remove_layer(layer) - remove layer
- grid.layers - list of layers sorted by z_index
- grid.layer(z_index) - get layer by z_index
- layer.at(x,y) / layer.set(x,y,value) - cell access
- layer.fill(value) - fill entire layer

Layers are allocated separately from UIGridPoint, reducing memory
for grids that don't need all features. Base grid retains walkable/
transparent arrays for TCOD pathfinding.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 21:35:38 -05:00